From patchwork Fri Mar 9 02:54:01 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Xiao Liang X-Patchwork-Id: 883445 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="I5z7djB4"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3zyBnd3MxDz9sd1 for ; Fri, 9 Mar 2018 13:55:16 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id EC9781082; Fri, 9 Mar 2018 02:55:13 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id E20D31036 for ; Fri, 9 Mar 2018 02:55:12 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-pg0-f45.google.com (mail-pg0-f45.google.com [74.125.83.45]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id A7D4A19B for ; Fri, 9 Mar 2018 02:55:11 +0000 (UTC) Received: by mail-pg0-f45.google.com with SMTP id l4so3042243pgp.11 for ; Thu, 08 Mar 2018 18:55:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=HCEs1+eCzKr3BlBN1/0AR3Ywg64XIhOevAtQ30F29FI=; b=I5z7djB4+88S5T5BZXBbJNG/xScByFUMzfiYxSCfDAwygk//5pBeQEoh+HV5rYnHO5 naS7dMHXEeMqRqVVvRGLOqiGNTAJpAETqwsV4lkJfOacPcxaYm/5cqFLUuU4aIqghHx2 enkqQBxlrfRbls2TLS9EpUzP0lOpi5yUczB4re7jKgEJhMPNKT2iopWrmvtAK0qj5gTg ER6Zt2P6Fwvab6pBlFP+UIy6SRsCVrj/giHg1TljyN7BwnDXtzhw2RYu7TMHVcfdHRbo DNZoW2fE6Yhs8qzCcMzze1nneK4Gaf7O4Ri2JMHl9SL2vTOedgIglZDzT374TNHM7mEK 84yA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=HCEs1+eCzKr3BlBN1/0AR3Ywg64XIhOevAtQ30F29FI=; b=rNkgjuGBD92pTMkwMmPWM9FVAAMa5sE1eJOcQns7EOYT1GxWCy3X8lPbL3SZP23IUP 9X41Tlx5O4Ft4ek+uqFj5/RHzwE80fgt538SKdT1OYY8VH6CGLHvZoZm5VMfP+4ZSUDK D1lqI2G7hUivsK5176sq9C4/K/me5wSebwzRWpm/8gBS8jNdQXQL4kpjrMIFbfD7Bt34 ZTbPdF8D31h/iRAN2ZPJ6H1eRxFBMa4g752wANoU3ImZp+IF+Ctwwrm2PeLw5QENa0ds FOMruIy/sENCfNcBtrVJg0IRJlFaYyjFQHzGvQopRK14RfNVhLVr3fn3zBttzfE/wtCy u/6g== X-Gm-Message-State: AElRT7HEV82jh93+u2+ZB9zyE+XPHrcwPFzG+Y53KS60SPGw7jCG9AY/ bC33Jdcvb3Bnqoba9qvL659lVBKIBWY= X-Google-Smtp-Source: AG47ELvfjz0VaAT4fevQQrzXIw9cL5XYsQmSKGckMIrUcUX5eOUZVmxNAYJNH+RkkIXFcMqIFE6emw== X-Received: by 10.99.38.135 with SMTP id m129mr15429997pgm.2.1520564110803; Thu, 08 Mar 2018 18:55:10 -0800 (PST) Received: from localhost.localdomain (li451-78.members.linode.com. [50.116.9.78]) by smtp.gmail.com with ESMTPSA id f82sm77674pfd.175.2018.03.08.18.55.07 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 08 Mar 2018 18:55:10 -0800 (PST) From: Xiao Liang To: ovs dev Date: Fri, 9 Mar 2018 10:54:01 +0800 Message-Id: <20180309025401.1863-1-shaw.leon@gmail.com> X-Mailer: git-send-email 2.16.2 X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Cc: William Fisher Subject: [ovs-dev] [RFC PATCH] utilities: Add OpenFlow proxy ovs-ofproxy X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Add ovs-ofproxy to enable tools like ovs-ofctl to work with non-OVS switches which don't support controller-initiated connection. The proxy listens for switches connection, opens a unix socket on behalf of each switch. Example: Start proxy $ ovs-ofproxy -O OpenFlow13 ptcp:6653 After switch is connected, use ovs-ofctl: $ ovs-ofctl -O Openflow13 show unix:/var/run/openvswitch/tcp:... (see log of proxy for the socket path) Signed-off-by: Xiao Liang --- utilities/.gitignore | 1 + utilities/automake.mk | 8 +- utilities/ovs-ofproxy.c | 680 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 688 insertions(+), 1 deletion(-) create mode 100644 utilities/ovs-ofproxy.c diff --git a/utilities/.gitignore b/utilities/.gitignore index 34c58f20f..bc702f49a 100644 --- a/utilities/.gitignore +++ b/utilities/.gitignore @@ -18,6 +18,7 @@ /ovs-lib /ovs-ofctl /ovs-ofctl.8 +/ovs-ofproxy /ovs-parse-backtrace /ovs-pcap /ovs-pcap.1 diff --git a/utilities/automake.mk b/utilities/automake.mk index 1636cb93e..a58681b50 100644 --- a/utilities/automake.mk +++ b/utilities/automake.mk @@ -3,7 +3,8 @@ bin_PROGRAMS += \ utilities/ovs-testcontroller \ utilities/ovs-dpctl \ utilities/ovs-ofctl \ - utilities/ovs-vsctl + utilities/ovs-vsctl \ + utilities/ovs-ofproxy bin_SCRIPTS += utilities/ovs-docker \ utilities/ovs-pki if HAVE_PYTHON @@ -134,6 +135,11 @@ utilities_ovs_ofctl_LDADD = \ utilities_ovs_vsctl_SOURCES = utilities/ovs-vsctl.c utilities_ovs_vsctl_LDADD = lib/libopenvswitch.la +utilities_ovs_ofproxy_SOURCES = utilities/ovs-ofproxy.c +utilities_ovs_ofproxy_LDADD = \ + ofproto/libofproto.la \ + lib/libopenvswitch.la + if LINUX sbin_PROGRAMS += utilities/ovs-vlan-bug-workaround utilities_ovs_vlan_bug_workaround_SOURCES = utilities/ovs-vlan-bug-workaround.c diff --git a/utilities/ovs-ofproxy.c b/utilities/ovs-ofproxy.c new file mode 100644 index 000000000..fde450936 --- /dev/null +++ b/utilities/ovs-ofproxy.c @@ -0,0 +1,680 @@ +#include +#include +#include +#include + +#include "openflow/openflow.h" + +#include "openvswitch/hmap.h" +#include "openvswitch/list.h" +#include "openvswitch/ofp-msgs.h" +#include "openvswitch/ofp-util.h" +#include "openvswitch/ofpbuf.h" +#include "openvswitch/poll-loop.h" +#include "openvswitch/rconn.h" +#include "openvswitch/vconn.h" +#include "openvswitch/vlog.h" + +#include "lib/command-line.h" +#include "lib/daemon.h" +#include "lib/dirs.h" +#include "lib/fatal-signal.h" +#include "lib/hash.h" +#include "lib/ofp-version-opt.h" +#include "lib/socket-util.h" +#include "lib/stream-ssl.h" +#include "lib/timeval.h" +#include "lib/unixctl.h" +#include "lib/util.h" + +VLOG_DEFINE_THIS_MODULE(ofproxy); + +/* XID management + * + * Maintain an hmap and an ordered list of outstanding XID entries for each + * switch, and a list for controllers. + * + * On message received from controller: + * - Allocate a new XID and entry for the message. + * - Insert the XID entry to hmap and lists. + * - Set new XID and send to switch. + * + * On message received from switch: + * - Lookup XID entry in hmap. + * - If type is BARRIER_REPLY, remove entries prior to the corresponding + * BARRIER_REQUEST. + * - If the entry has controller information, set original XID and send to + * controller. + * + * Periodically send BARRIER_REQUEST if there're XID entries. + */ +struct xid_entry { + struct hmap_node map_node; /* Links to xid_map of switch. */ + uint32_t xid; + + struct switch_context *sw; + struct ovs_list sw_node; /* Links to xid_list of switch. */ + + struct ctlr_context *ctlr; /* Originating controller, if any */ + uint32_t orig_xid; + struct ovs_list ctlr_node; /* Links to xid_list of controller. */ +}; + +enum switch_state { + S_CONNECTING, + S_ESTABLISHED, + S_ERROR +}; + +struct ctlr_context { + struct ovs_list list_node; /* List to ctlrs of switch. */ + struct switch_context *sw; + struct rconn *rconn; + struct ovs_list xid_list; /* Outstanding XIDs. */ +}; + +struct switch_context { + struct ovs_list list_node; + struct rconn *rconn; + const char *name; + enum switch_state state; + uint32_t protocol_version; + + struct pvconn *pvconn; + struct ovs_list ctlrs; /* List of active controllers. */ + + uint32_t next_xid; + struct ovs_list xid_list; /* Outstanding XID entries, ordered. */ + struct hmap xid_map; /* XID entries indexed by XID */ + + long long barrier_timer; + + struct pvconn *snoop; +}; + +static struct switch_context *switch_create(struct vconn *vconn); +static void switch_destroy(struct switch_context *sw); +static void switch_run(struct switch_context *sw); +static void switch_wait(struct switch_context *sw); +static int switch_ctlr_pvconn_open(struct switch_context *sw); +static int switch_snoop_pvconn_open(struct switch_context *sw); +static void switch_send_barrier(struct switch_context *sw); +static void process_sw_message(struct switch_context *sw, struct ofpbuf *msg); +static void switch_xid_clear__(struct switch_context *sw); + +static struct ctlr_context *ctlr_create(struct vconn *vconn, + struct switch_context *sw); +static void ctlr_destroy(struct ctlr_context *ctlr); +static void ctlr_destroy__(struct ctlr_context *ctlr); +static void ctlr_xid_clear(struct ctlr_context *ctlr); +static void ctlr_run(struct ctlr_context *ctlr); +static void ctlr_wait(struct ctlr_context *ctlr); +static void process_ctlr_message(struct ctlr_context *ctlr, + struct ofpbuf *msg); + +static void xid_insert(struct xid_entry *entry); +static void xid_remove(struct xid_entry *entry); +static struct xid_entry *xid_find(struct hmap *xid_map, + uint32_t xid); +static void xid_barrier(struct xid_entry *entry); + +static void ofproxy_exit(struct unixctl_conn *conn, int argc, + const char *argv[], void *exiting); + +/* Options. */ +static char *unixctl_path = NULL; +static char *pvconn_name = "ptcp:"; +static uint32_t version_mask = 0; +static long long barrier_interval = 5; +static bool snoop = false; + +static void +xid_insert(struct xid_entry *entry) +{ + ovs_list_push_back(&entry->sw->xid_list, &entry->sw_node); + hmap_insert(&entry->sw->xid_map, &entry->map_node, + hash_int(entry->xid, 0)); + if (entry->ctlr) { + ovs_list_push_back(&entry->ctlr->xid_list, &entry->ctlr_node); + } +} + +static void +xid_remove(struct xid_entry *entry) +{ + ovs_list_remove(&entry->sw_node); + hmap_remove(&entry->sw->xid_map, &entry->map_node); + if (entry->ctlr) { + ovs_list_remove(&entry->ctlr_node); + } +} + +static struct xid_entry * +xid_find(struct hmap *xid_map, uint32_t xid) +{ + struct xid_entry *entry; + HMAP_FOR_EACH_WITH_HASH (entry, map_node, + hash_int(xid, 0), xid_map) { + if (entry->xid == xid) { + return entry; + } + } + return NULL; +} + +/* Flushes all prior XIDs. Called when BARRIER_REPLY is received. */ +static void +xid_barrier(struct xid_entry *entry) +{ + struct xid_entry *curr, *next; + LIST_FOR_EACH_SAFE (curr, next, sw_node, &entry->sw->xid_list) { + if (curr == entry) { + break; + } else { + xid_remove(curr); + free(curr); + } + } +} + +static struct switch_context * +switch_create(struct vconn *vconn) +{ + struct switch_context *sw = xzalloc(sizeof(struct switch_context)); + + sw->rconn = rconn_create(0, 0, DSCP_DEFAULT, version_mask); + rconn_connect_unreliably(sw->rconn, vconn, NULL); + sw->name = rconn_get_name(sw->rconn); + + ovs_list_init(&sw->ctlrs); + ovs_list_init(&sw->xid_list); + hmap_init(&sw->xid_map); + + if (barrier_interval > 0) { + sw->barrier_timer = time_msec() + barrier_interval; + } + + return sw; +} + +/* Clears all XIDs. Called only when switch is being destroyed. */ +static void +switch_xid_clear__(struct switch_context *sw) +{ + struct xid_entry *entry; + HMAP_FOR_EACH_POP (entry, map_node, &sw->xid_map) { + free(entry); + } + hmap_destroy(&sw->xid_map); +} + +static void +switch_destroy(struct switch_context *sw) +{ + if (sw->pvconn) { + pvconn_close(sw->pvconn); + } + rconn_destroy(sw->rconn); + + struct ctlr_context *ctlr; + LIST_FOR_EACH_POP (ctlr, list_node, &sw->ctlrs) { + ctlr_destroy__(ctlr); + } + + switch_xid_clear__(sw); + + free(sw); +} + +static void +switch_run(struct switch_context *sw) +{ + rconn_run(sw->rconn); + + /* TODO: Postpone pvconn_open after FEATURES_REPLY. */ + if (sw->state == S_CONNECTING) { + int version; + if ((version = rconn_get_version(sw->rconn)) != -1) { + sw->protocol_version = version; + VLOG_INFO("switch %s negotiated version: %s", + sw->name, ofputil_version_to_string(version)); + if (switch_ctlr_pvconn_open(sw) == 0) { + sw->state = S_ESTABLISHED; + } else { + sw->state = S_ERROR; + } + if (snoop) { + switch_snoop_pvconn_open(sw); + } + } + } + + while (sw->pvconn) { + struct vconn *new_vconn; + int error = pvconn_accept(sw->pvconn, &new_vconn); + if (!error) { + ctlr_create(new_vconn, sw); + } else if (error == EAGAIN) { + break; + } else { + VLOG_WARN("pvconn error, switch: %s", sw->name); + pvconn_close(sw->pvconn); + sw->pvconn = NULL; + } + } + + while (sw->snoop) { + struct vconn *new_vconn; + int error = pvconn_accept(sw->snoop, &new_vconn); + if (!error) { + rconn_add_monitor(sw->rconn, new_vconn); + } else if (error == EAGAIN) { + break; + } else { + VLOG_WARN("pvconn error, switch: %s", sw->name); + pvconn_close(sw->pvconn); + sw->pvconn = NULL; + } + } + + struct ctlr_context *ctlr, *next; + LIST_FOR_EACH_SAFE (ctlr, next, list_node, &sw->ctlrs) { + ctlr_run(ctlr); + if (!rconn_is_alive(ctlr->rconn)) { + ctlr_destroy(ctlr); + } + } + + for (int i = 0; i < 50; i++) { + struct ofpbuf *msg; + msg = rconn_recv(sw->rconn); + if (!msg) { + break; + } + process_sw_message(sw, msg); + ofpbuf_delete(msg); + } + + if (barrier_interval > 0 && + time_msec() >= sw->barrier_timer) { + if (!ovs_list_is_empty(&sw->xid_list)) { + switch_send_barrier(sw); + } + sw->barrier_timer = time_msec() + barrier_interval; + } +} + +static void +switch_wait(struct switch_context *sw) +{ + rconn_run_wait(sw->rconn); + rconn_recv_wait(sw->rconn); + if (sw->pvconn) { + pvconn_wait(sw->pvconn); + } + + struct ctlr_context *ctlr; + LIST_FOR_EACH (ctlr, list_node, &sw->ctlrs) { + ctlr_wait(ctlr); + } + + if (barrier_interval > 0) { + poll_timer_wait_until(sw->barrier_timer); + } +} + +static char * +switch_ctlr_pvconn_name(struct switch_context *sw) +{ + /* TODO: support user-provided template, like using datapath_id, etc. */ + return xasprintf("punix:%s/%s.proxy", ovs_rundir(), sw->name); +} + +static int +switch_ctlr_pvconn_open(struct switch_context *sw) +{ + char *name = switch_ctlr_pvconn_name(sw); + int error = pvconn_open(name, 1 << sw->protocol_version, + DSCP_DEFAULT, &sw->pvconn); + if (error) { + VLOG_WARN("failed to listen for controller on: %s", name); + } else { + VLOG_INFO("listening for controller on: %s", name); + } + free(name); + return error; +} + +static char * +switch_snoop_pvconn_name(struct switch_context *sw) +{ + return xasprintf("punix:%s/%s.snoop", ovs_rundir(), sw->name); +} + +static int +switch_snoop_pvconn_open(struct switch_context *sw) +{ + char *name = switch_snoop_pvconn_name(sw); + int error = pvconn_open(name, 0, + DSCP_DEFAULT, &sw->snoop); + if (error) { + VLOG_WARN("failed to listen for snooping on: %s", name); + } else { + VLOG_INFO("listening for snooping on: %s", name); + } + free(name); + return error; +} + +static struct ctlr_context * +ctlr_create(struct vconn *vconn, struct switch_context *sw) +{ + struct ctlr_context *ctlr = xzalloc(sizeof(struct ctlr_context)); + + ctlr->sw = sw; + ctlr->rconn = rconn_create(0, 0, DSCP_DEFAULT, 0); + rconn_connect_unreliably(ctlr->rconn, vconn, NULL); + ovs_list_init(&ctlr->xid_list); + + ovs_list_push_back(&sw->ctlrs, &ctlr->list_node); + + return ctlr; +} + +static void +ctlr_xid_clear(struct ctlr_context *ctlr) +{ + struct xid_entry *entry, *next; + LIST_FOR_EACH_SAFE (entry, next, ctlr_node, &ctlr->xid_list) { + xid_remove(entry); + free(entry); + } +} + +static void +ctlr_destroy(struct ctlr_context *ctlr) +{ + ovs_list_remove(&ctlr->list_node); + ctlr_xid_clear(ctlr); + ctlr_destroy__(ctlr); +} + +static void +ctlr_destroy__(struct ctlr_context *ctlr) +{ + rconn_destroy(ctlr->rconn); + free(ctlr); +} + +static void +ctlr_run(struct ctlr_context *ctlr) +{ + rconn_run(ctlr->rconn); + + for (int i = 0; i < 50; i++) { + struct ofpbuf *msg; + msg = rconn_recv(ctlr->rconn); + if (!msg) { + break; + } + process_ctlr_message(ctlr, msg); + ofpbuf_delete(msg); + } +} + +static void +ctlr_wait(struct ctlr_context *ctlr) +{ + rconn_run_wait(ctlr->rconn); + rconn_recv_wait(ctlr->rconn); +} + +/* Clone ofp message and alter xid */ +static struct ofpbuf * +ofp_msg_dup_xid(struct ofpbuf *msg, uint32_t xid) +{ + struct ofpbuf *new_msg = ofpbuf_clone(msg); + ((struct ofp_header *)new_msg->data)->xid = htonl(xid); + return new_msg; +} + +static void +process_ctlr_message(struct ctlr_context *ctlr, struct ofpbuf *msg) +{ + enum ofptype type; + const struct ofp_header *header = msg->data; + if (ofptype_decode(&type, header) == 0) { + /* TODO: handle controller state change (role, set_async, etc.) */ + if (type == OFPTYPE_ECHO_REQUEST) { + rconn_send(ctlr->rconn, ofputil_encode_echo_reply(header), NULL); + } else { + struct xid_entry *entry = + xzalloc(sizeof(struct xid_entry)); + entry->xid = ctlr->sw->next_xid++; + entry->orig_xid = ntohl(header->xid); + entry->sw = ctlr->sw; + entry->ctlr = ctlr; + xid_insert(entry); + rconn_send(ctlr->sw->rconn, + ofp_msg_dup_xid(msg, entry->xid), + NULL); + } + } +} + +static void +process_sw_message(struct switch_context *sw, struct ofpbuf *msg) +{ + enum ofptype type; + const struct ofp_header *header = msg->data; + if (ofptype_decode(&type, header) == 0) { + /* TODO: handle aync messages. */ + if (type == OFPTYPE_ECHO_REQUEST) { + rconn_send(sw->rconn, ofputil_encode_echo_reply(header), NULL); + } else { + uint32_t xid = ntohl(header->xid); + struct xid_entry *entry = xid_find(&sw->xid_map, xid); + if (entry) { + if (type == OFPTYPE_BARRIER_REPLY) { + xid_barrier(entry); + } + if (entry->ctlr) { + rconn_send(entry->ctlr->rconn, + ofp_msg_dup_xid(msg, entry->orig_xid), + NULL); + } else { + xid_remove(entry); + } + } + } + } +} + +static void +switch_send_barrier(struct switch_context *sw) +{ + struct xid_entry *entry = xzalloc(sizeof(struct xid_entry)); + entry->xid = sw->next_xid++; + entry->sw = sw; + xid_insert(entry); + struct ofpbuf *msg = ofputil_encode_barrier_request(sw->protocol_version); + ((struct ofp_header *)msg->data)->xid = htonl(entry->xid); + rconn_send(sw->rconn, msg, NULL); +} + +static void +ofproxy_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[] OVS_UNUSED, void *exiting) +{ + *(bool *)exiting = true; + unixctl_command_reply(conn, NULL); +} + +static void +parse_options(int argc, char *argv[]) +{ + enum { + OPT_UNIXCTL = UCHAR_MAX + 1, + OPT_BARRIER_INTERVAL, + OPT_SNOOP, + DAEMON_OPTION_ENUMS, + OFP_VERSION_OPTION_ENUMS, + VLOG_OPTION_ENUMS, + SSL_OPTION_ENUMS, + }; + + static const struct option long_options[] = { + {"unixctl", required_argument, NULL, OPT_UNIXCTL}, + {"barrier-interval", required_argument, NULL, OPT_BARRIER_INTERVAL}, + {"enable-snoop", no_argument, NULL, OPT_SNOOP}, + DAEMON_LONG_OPTIONS, + OFP_VERSION_LONG_OPTIONS, + VLOG_LONG_OPTIONS, + STREAM_SSL_LONG_OPTIONS, + {NULL, 0, NULL, 0}, + }; + char *short_options = ovs_cmdl_long_options_to_short_options(long_options); + + for (;;) { + int c; + + c = getopt_long(argc, argv, short_options, long_options, NULL); + if (c == -1) { + break; + } + + switch (c) { + case OPT_UNIXCTL: + unixctl_path = optarg; + break; + + case OPT_BARRIER_INTERVAL: + barrier_interval = (long long)atoi(optarg) * 1000; + break; + + case OPT_SNOOP: + snoop = true; + break; + + DAEMON_OPTION_HANDLERS + OFP_VERSION_OPTION_HANDLERS + VLOG_OPTION_HANDLERS + STREAM_SSL_OPTION_HANDLERS + + case '?': + exit(EXIT_FAILURE); + + case 0: + break; + + default: + ovs_abort(0, "unknow option"); + } + } + + free(short_options); + + version_mask = get_allowed_ofp_versions(); + + if (argc > optind) { + pvconn_name = argv[optind]; + } +} + + + +int +main(int argc, char *argv[]) +{ + int error; + bool exiting = false; + + struct unixctl_server *server = NULL; + struct ovs_list switches = OVS_LIST_INITIALIZER(&switches); + + set_program_name(argv[0]); + ovs_cmdl_proctitle_init(argc, argv); + service_start(&argc, &argv); + parse_options(argc, argv); + fatal_ignore_sigpipe(); + + daemon_become_new_user(false); + + struct pvconn *pvconn; + error = pvconn_open(pvconn_name, version_mask, DSCP_DEFAULT, &pvconn); + if (error) { + ovs_fatal(0, "failed to listen"); + } + + daemonize_start(false); + if (unixctl_path) { + error = unixctl_server_create(unixctl_path, &server); + if (error) { + ovs_fatal(error, "failed to create unixctl server"); + } + unixctl_command_register("exit", "", 0, 0, ofproxy_exit, &exiting); + } + daemonize_complete(); + + while (pvconn || !ovs_list_is_empty(&switches)) { + while (pvconn) { + struct vconn *new_vconn; + error = pvconn_accept(pvconn, &new_vconn); + if (!error) { + struct switch_context *sw = switch_create(new_vconn); + ovs_list_insert(&switches, &sw->list_node); + VLOG_INFO("switch %s connected", sw->name); + } else if (error == EAGAIN) { + break; + } else { + VLOG_WARN("pvconn error"); + pvconn_close(pvconn); + pvconn = NULL; + } + } + + struct switch_context *sw, *next; + LIST_FOR_EACH_SAFE (sw, next, list_node, &switches) { + switch_run(sw); + if (!rconn_is_alive(sw->rconn)) { + VLOG_INFO("switch %s disconnected", sw->name); + ovs_list_remove(&sw->list_node); + switch_destroy(sw); + } + } + + if (server) { + unixctl_server_run(server); + } + if (exiting) { + break; + } + + if (pvconn) { + pvconn_wait(pvconn); + } + LIST_FOR_EACH (sw, list_node, &switches) { + switch_wait(sw); + } + if (server) { + unixctl_server_wait(server); + } + + poll_block(); + } + + if (pvconn) { + pvconn_close(pvconn); + } + struct switch_context *sw; + LIST_FOR_EACH_POP (sw, list_node, &switches) { + switch_destroy(sw); + } + if (server) { + unixctl_server_destroy(server); + } + service_stop(); + + return 0; +} +