From e3eaf2b79c8f74daec55a5f0effcc3fd8af3054a Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 30 Jul 2008 15:00:01 -0700 Subject: [PATCH] Clean up secchan code. --- secchan/secchan.c | 672 ++++++++++++++++++++++++++++------------------ 1 file changed, 415 insertions(+), 257 deletions(-) diff --git a/secchan/secchan.c b/secchan/secchan.c index d0d64e3e..50ebebb5 100644 --- a/secchan/secchan.c +++ b/secchan/secchan.c @@ -1,6 +1,6 @@ /* Copyright (c) 2008 The Board of Trustees of The Leland Stanford * Junior University - * + * * We are making the OpenFlow specification and associated documentation * (Software) available for public use and benefit with the expectation * that others will use, modify and enhance the Software and contribute @@ -13,10 +13,10 @@ * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: - * + * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -25,13 +25,14 @@ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. - * + * * The name and trademarks of copyright holder(s) may NOT be used in * advertising or publicity pertaining to the Software or any * derivatives without specific, written prior permission. */ #include +#include #include #include #include @@ -49,6 +50,7 @@ #include "daemon.h" #include "dhcp.h" #include "dhcp-client.h" +#include "dynamic-string.h" #include "fault.h" #include "flow.h" #include "learning-switch.h" @@ -67,19 +69,41 @@ #include "vlog.h" #define THIS_MODULE VLM_secchan -static const char *listen_vconn_name; - -struct half { - struct rconn *rconn; - struct buffer *rxbuf; -}; - /* Behavior when the connection to the controller fails. */ enum fail_mode { FAIL_OPEN, /* Act as learning switch. */ FAIL_CLOSED /* Drop all packets. */ }; +/* Settings that may be configured by the user. */ +struct settings { + /* Overall mode of operation. */ + bool discovery; /* Discover the controller automatically? */ + bool in_band; /* Connect to controller in-band? */ + + /* Related vconns and network devices. */ + const char *nl_name; /* Local datapath (must be "nl:" vconn). */ + char *of_name; /* ofX network device name. */ + const char *controller_name; /* Controller (if not discovery mode). */ + const char *listen_vconn_name; /* Listens for mgmt connections. */ + + /* Failure behavior. */ + enum fail_mode fail_mode; /* Act as learning switch if no controller? */ + int max_idle; /* Idle time for flows in fail-open mode. */ + int probe_interval; /* # seconds idle before sending echo request. */ + int max_backoff; /* Max # seconds between connection attempts. */ + + /* Discovery behavior. */ + regex_t accept_controller_regex; /* Controller vconns to accept. */ + const char *accept_controller_re; /* String version of regex. */ + bool update_resolv_conf; /* Update /etc/resolv.conf? */ +}; + +struct half { + struct rconn *rconn; + struct buffer *rxbuf; +}; + struct relay { struct list node; @@ -88,68 +112,37 @@ struct relay { struct half halves[2]; bool is_mgmt_conn; - struct lswitch *lswitch; }; -static struct list relays = LIST_INITIALIZER(&relays); - -/* Mode of operation. Note that autodiscovery implies in-band - * communication. */ -static bool autodiscovery; /* Discover the controller automatically? */ -static bool in_band; /* Connect to controller in-band? */ - -/* MAC address of local port. */ -static uint8_t local_mac[ETH_ADDR_LEN]; - -/* MAC learning table for local port. */ -static struct mac_learning *local_ml; - -/* Controller vconn name, or null to perform controller autodiscovery. */ -static char *controller_name = NULL; - -/* -f, --fail: Behavior when the connection to the controller fails. */ -static enum fail_mode fail_mode = FAIL_OPEN; - -/* The OpenFlow virtual network device ofX. */ -static struct netdev *of_device; - -/* --inactivity-probe: Number of seconds without receiving a message from the - controller before sending an inactivity probe. */ -static int probe_interval = 15; - -/* --max-idle: Idle time to assign to flows created by learning switch when in - * fail-open mode. */ -static int max_idle = 15; - -/* --max-backoff: Maximum interval between controller connection attempts, in - * seconds. */ -static int max_backoff = 15; - -/* DHCP client, for controller autodiscovery. */ -static struct dhclient *dhcp; - -/* --accept-vconn: Regular expression specifying the class of controller vconns - * that we will accept during autodiscovery. */ -static const char *accept_controller_re; -static regex_t accept_controller_regex; - -/* --no-resolv-conf: Update resolv.conf upon successful controller - * discovery? */ -static bool update_resolv_conf = true; +struct hook { + bool (*packet_cb)(struct relay *, int half, void *aux); + void (*periodic_cb)(void *aux); + void *aux; +}; -static void parse_options(int argc, char *argv[]); +static void parse_options(int argc, char *argv[], struct settings *); static void usage(void) NO_RETURN; -static void new_management_connection(const char *nl_name, struct vconn *new_remote); static struct relay *relay_create(struct rconn *local, struct rconn *remote, bool is_mgmt_conn); -static void relay_run(struct relay *); +static struct relay *relay_accept(const struct settings *, struct vconn *); +static void relay_run(struct relay *, const struct hook[], size_t n_hooks); static void relay_wait(struct relay *); static void relay_destroy(struct relay *); -static bool local_hook(struct relay *r); -static bool failing_open(struct relay *r); -static bool fail_open_hook(struct relay *r); +static struct hook make_hook(bool (*packet_cb)(struct relay *, int, void *), + void (*periodic_cb)(void *), + void *aux); + +static struct discovery *discovery_init(const struct settings *); +static void discovery_question_connectivity(struct discovery *); +static bool discovery_run(struct discovery *, char **controller_name); +static void discovery_wait(struct discovery *); + +static struct hook in_band_hook_create(const struct settings *); +static struct hook fail_open_hook_create(const struct settings *, + struct rconn *local, + struct rconn *remote); static void modify_dhcp_request(struct dhcp_msg *, void *aux); static bool validate_dhcp_offer(const struct dhcp_msg *, void *aux); @@ -157,102 +150,41 @@ static bool validate_dhcp_offer(const struct dhcp_msg *, void *aux); int main(int argc, char *argv[]) { + struct settings s; + + struct list relays = LIST_INITIALIZER(&relays); + + struct hook hooks[3]; + size_t n_hooks; + struct rconn *local_rconn, *remote_rconn; struct vconn *listen_vconn; struct relay *controller_relay; - const char *nl_name; - char of_name[16]; + struct discovery *discovery; int retval; set_program_name(argv[0]); register_fault_handlers(); vlog_init(); - parse_options(argc, argv); - - argc -= optind; - argv += optind; - if (argc < 1 || argc > 2) { - fatal(0, "need one or two non-option arguments; use --help for usage"); - } - nl_name = argv[0]; - if (strncmp(nl_name, "nl:", 3) - || strlen(nl_name) < 4 - || nl_name[strspn(nl_name + 3, "0123456789") + 3]) { - fatal(0, "%s: argument is not of the form \"nl:DP_IDX\"", nl_name); - } - controller_name = argc > 1 ? xstrdup(argv[1]) : NULL; - autodiscovery = controller_name == NULL; - - if (!accept_controller_re) { - accept_controller_re = vconn_ssl_is_configured() ? "^ssl:.*" : ".*"; - } - retval = regcomp(&accept_controller_regex, accept_controller_re, - REG_NOSUB | REG_EXTENDED); - if (retval) { - size_t length = regerror(retval, &accept_controller_regex, NULL, 0); - char *buffer = xmalloc(length); - regerror(retval, &accept_controller_regex, buffer, length); - fatal(0, "%s: %s", accept_controller_re, buffer); - } + parse_options(argc, argv, &s); - if (listen_vconn_name) { - retval = vconn_open(listen_vconn_name, &listen_vconn); + /* Start listening for management connections. */ + if (s.listen_vconn_name) { + retval = vconn_open(s.listen_vconn_name, &listen_vconn); if (retval && retval != EAGAIN) { - fatal(retval, "opening %s", listen_vconn_name); + fatal(retval, "opening %s", s.listen_vconn_name); } if (!vconn_is_passive(listen_vconn)) { - fatal(0, "%s is not a passive vconn", listen_vconn_name); + fatal(0, "%s is not a passive vconn", s.listen_vconn_name); } } else { listen_vconn = NULL; } - snprintf(of_name, sizeof of_name, "of%s", nl_name + 3); - retval = netdev_open(of_name, NETDEV_ETH_TYPE_NONE, &of_device); - if (!retval) { - enum netdev_flags flags; - - if (autodiscovery) { - retval = netdev_turn_flags_on(of_device, NETDEV_UP, true); - if (retval) { - fatal(retval, "Could not bring %s device up", of_name); - } - } - - retval = netdev_get_flags(of_device, &flags); - if (!retval) { - if (flags & NETDEV_UP) { - struct in6_addr in6; - - in_band = true; - memcpy(local_mac, netdev_get_etheraddr(of_device), - ETH_ADDR_LEN); - if (netdev_get_in6(of_device, &in6)) { - VLOG_WARN("Ignoring IPv6 address on %s device: " - "IPv6 not supported", of_name); - } - local_ml = mac_learning_create(); - } - } else { - error(retval, "Could not get flags for %s device", of_name); - } - } else { - error(retval, "Could not open %s device", of_name); - } - if (autodiscovery && !in_band) { - fatal(retval, "In autodiscovery mode but failed to configure " - "in-band control"); - } - - if (autodiscovery) { - retval = dhclient_create(of_name, modify_dhcp_request, - validate_dhcp_offer, NULL, &dhcp); - if (retval) { - fatal(retval, "Failed to initialize DHCP client"); - } - dhclient_init(dhcp, 0); - } + /* Start controller discovery. */ + discovery = s.discovery ? discovery_init(&s) : NULL; + /* Start listening for vlogconf requests. */ retval = vlog_server_listen(NULL, NULL); if (retval) { fatal(retval, "Could not listen for vlog connections"); @@ -260,62 +192,65 @@ main(int argc, char *argv[]) daemonize(); - local_rconn = rconn_create(1, 0, max_backoff); - rconn_connect(local_rconn, nl_name); + /* Connect to datapath. */ + local_rconn = rconn_create(1, 0, s.max_backoff); + rconn_connect(local_rconn, s.nl_name); - remote_rconn = rconn_create(1, probe_interval, max_backoff); - if (controller_name) { - retval = rconn_connect(remote_rconn, controller_name); + /* Connect to controller. */ + remote_rconn = rconn_create(1, s.probe_interval, s.max_backoff); + if (s.controller_name) { + retval = rconn_connect(remote_rconn, s.controller_name); if (retval == EAFNOSUPPORT) { - fatal(0, "No support for %s vconn", controller_name); + fatal(0, "No support for %s vconn", s.controller_name); } } + + /* Start relaying. */ controller_relay = relay_create(local_rconn, remote_rconn, false); + list_push_back(&relays, &controller_relay->node); + + /* Set up hooks. */ + n_hooks = 0; + if (s.in_band) { + hooks[n_hooks++] = in_band_hook_create(&s); + } + if (s.fail_mode == FAIL_OPEN) { + hooks[n_hooks++] = fail_open_hook_create(&s, + local_rconn, remote_rconn); + } + assert(n_hooks <= ARRAY_SIZE(hooks)); + for (;;) { struct relay *r, *n; + size_t i; /* Do work. */ LIST_FOR_EACH_SAFE (r, n, struct relay, node, &relays) { - relay_run(r); + relay_run(r, hooks, n_hooks); } if (listen_vconn) { for (;;) { - struct vconn *new_remote; - retval = vconn_accept(listen_vconn, &new_remote); - if (retval) { - if (retval != EAGAIN) { - VLOG_WARN("accept failed (%s)", strerror(retval)); - } + struct relay *r = relay_accept(&s, listen_vconn); + if (!r) { break; } - new_management_connection(nl_name, new_remote); + list_push_back(&relays, &r->node); + } + } + for (i = 0; i < n_hooks; i++) { + if (hooks[i].periodic_cb) { + hooks[i].periodic_cb(hooks[i].aux); } } - failing_open(controller_relay); - if (dhcp) { + if (s.discovery) { + char *controller_name; if (rconn_is_connectivity_questionable(remote_rconn)) { - dhclient_force_renew(dhcp, 15); + discovery_question_connectivity(discovery); } - dhclient_run(dhcp); - if (dhclient_changed(dhcp)) { - dhclient_configure_netdev(dhcp); - if (update_resolv_conf) { - dhclient_update_resolv_conf(dhcp); - } - - if (dhclient_is_bound(dhcp)) { - free(controller_name); - controller_name = dhcp_msg_get_string( - dhclient_get_config(dhcp), - DHCP_CODE_OFP_CONTROLLER_VCONN); - VLOG_WARN("%s: discovered controller", - controller_name); + if (discovery_run(discovery, &controller_name)) { + if (controller_name) { rconn_connect(remote_rconn, controller_name); - } else if (controller_name) { - VLOG_WARN("%s: discover controller no longer available", - controller_name); - free(controller_name); - controller_name = NULL; + } else { rconn_disconnect(remote_rconn); } } @@ -328,8 +263,8 @@ main(int argc, char *argv[]) if (listen_vconn) { vconn_accept_wait(listen_vconn); } - if (dhcp) { - dhclient_wait(dhcp); + if (discovery) { + discovery_wait(discovery); } poll_block(); } @@ -337,38 +272,61 @@ main(int argc, char *argv[]) return 0; } -static void -new_management_connection(const char *nl_name, struct vconn *new_remote) +static struct hook +make_hook(bool (*packet_cb)(struct relay *, int half, void *aux), + void (*periodic_cb)(void *aux), + void *aux) { + struct hook h; + h.packet_cb = packet_cb; + h.periodic_cb = periodic_cb; + h.aux = aux; + return h; +} + +/* OpenFlow message relaying. */ + +static struct relay * +relay_accept(const struct settings *s, struct vconn *listen_vconn) +{ + struct vconn *new_remote, *new_local; char *nl_name_without_subscription; - struct vconn *new_local; struct rconn *r1, *r2; int retval; + retval = vconn_accept(listen_vconn, &new_remote); + if (retval) { + if (retval != EAGAIN) { + VLOG_WARN("accept failed (%s)", strerror(retval)); + } + return NULL; + } + /* nl:123 or nl:123:1 opens a netlink connection to local datapath 123. We * only accept the former syntax in main(). * * nl:123:0 opens a netlink connection to local datapath 123 without * obtaining a subscription for ofp_packet_in or ofp_flow_expired * messages.*/ - nl_name_without_subscription = xasprintf("%s:0", nl_name); + nl_name_without_subscription = xasprintf("%s:0", s->nl_name); retval = vconn_open(nl_name_without_subscription, &new_local); if (retval) { VLOG_ERR("could not connect to %s (%s)", nl_name_without_subscription, strerror(retval)); vconn_close(new_remote); free(nl_name_without_subscription); - return; + return NULL; } - /* Add it to the relay list. */ + /* Create and return relay. */ r1 = rconn_create(1, 0, 0); rconn_connect_unreliably(r1, nl_name_without_subscription, new_local); + free(nl_name_without_subscription); + r2 = rconn_create(1, 0, 0); rconn_connect_unreliably(r2, "passive", new_remote); - relay_create(r1, r2, true); - free(nl_name_without_subscription); + return relay_create(r1, r2, true); } static struct relay * @@ -384,13 +342,11 @@ relay_create(struct rconn *local, struct rconn *remote, bool is_mgmt_conn) r->halves[i].rxbuf = NULL; } r->is_mgmt_conn = is_mgmt_conn; - r->lswitch = NULL; - list_push_back(&relays, &r->node); return r; } static void -relay_run(struct relay *r) +relay_run(struct relay *r, const struct hook hooks[], size_t n_hooks) { int iteration; int i; @@ -408,10 +364,16 @@ relay_run(struct relay *r) if (!this->rxbuf) { this->rxbuf = rconn_recv(this->rconn); - if (this->rxbuf && !r->is_mgmt_conn && i == HALF_LOCAL - && (local_hook(r) || fail_open_hook(r))) { - buffer_delete(this->rxbuf); - this->rxbuf = NULL; + if (this->rxbuf) { + const struct hook *h; + for (h = hooks; h < &hooks[n_hooks]; h++) { + if (h->packet_cb(r, i, h->aux)) { + buffer_delete(this->rxbuf); + this->rxbuf = NULL; + progress = true; + break; + } + } } } @@ -471,6 +433,15 @@ relay_destroy(struct relay *r) } free(r); } + +/* In-band control. */ + +struct in_band_data { + const struct settings *s; + struct mac_learning *ml; + struct netdev *of_device; + uint8_t mac[ETH_ADDR_LEN]; +}; static void queue_tx(struct rconn *rc, struct buffer *b) @@ -481,8 +452,8 @@ queue_tx(struct rconn *rc, struct buffer *b) } static bool -is_controller_mac(const uint8_t dl_addr[ETH_ADDR_LEN], - struct rconn *controller) +is_controller_mac(const uint8_t dl_addr[ETH_ADDR_LEN], struct netdev *netdev, + struct rconn *controller) { static uint32_t ip, last_nonzero_ip; static uint8_t mac[ETH_ADDR_LEN], last_nonzero_mac[ETH_ADDR_LEN]; @@ -499,7 +470,7 @@ is_controller_mac(const uint8_t dl_addr[ETH_ADDR_LEN], /* Look up MAC address. */ memset(mac, 0, sizeof mac); if (ip) { - int retval = netdev_arp_lookup(of_device, ip, mac); + int retval = netdev_arp_lookup(netdev, ip, mac); if (retval) { VLOG_DBG("cannot look up controller hw address ("IP_FMT"): %s", IP_ARGS(&ip), strerror(retval)); @@ -531,8 +502,9 @@ is_controller_mac(const uint8_t dl_addr[ETH_ADDR_LEN], } static bool -local_hook(struct relay *r) +in_band_packet_cb(struct relay *r, int half, void *in_band_) { + struct in_band_data *in_band = in_band_; struct rconn *rc = r->halves[HALF_LOCAL].rconn; struct buffer *msg = r->halves[HALF_LOCAL].rxbuf; struct ofp_packet_in *opi; @@ -542,7 +514,7 @@ local_hook(struct relay *r) struct flow flow; uint16_t in_port, out_port; - if (!in_band) { + if (half != HALF_LOCAL || r->is_mgmt_conn) { return false; } @@ -566,16 +538,16 @@ local_hook(struct relay *r) /* Deal with local stuff. */ if (in_port == OFPP_LOCAL) { - out_port = mac_learning_lookup(local_ml, flow.dl_dst); - } else if (eth_addr_equals(flow.dl_dst, local_mac)) { + out_port = mac_learning_lookup(in_band->ml, flow.dl_dst); + } else if (eth_addr_equals(flow.dl_dst, in_band->mac)) { out_port = OFPP_LOCAL; - if (mac_learning_learn(local_ml, flow.dl_src, in_port)) { + if (mac_learning_learn(in_band->ml, flow.dl_src, in_port)) { VLOG_DBG("learned that "ETH_ADDR_FMT" is on port %"PRIu16, ETH_ADDR_ARGS(flow.dl_src), in_port); } } else if (flow.dl_type == htons(ETH_TYPE_ARP) && eth_addr_is_broadcast(flow.dl_dst) - && is_controller_mac(flow.dl_src, + && is_controller_mac(flow.dl_src, in_band->of_device, r->halves[HALF_REMOTE].rconn)) { out_port = OFPP_FLOOD; } else { @@ -585,7 +557,7 @@ local_hook(struct relay *r) if (out_port != OFPP_FLOOD) { /* The output port is known, so add a new flow. */ queue_tx(rc, make_add_simple_flow(&flow, ntohl(opi->buffer_id), - out_port, max_idle)); + out_port, in_band->s->max_idle)); /* If the switch didn't buffer the packet, we need to send a copy. */ if (ntohl(opi->buffer_id) == UINT32_MAX) { @@ -606,52 +578,171 @@ local_hook(struct relay *r) return true; } -/* Causes 'r' to enter or leave fail-open mode, if appropriate. Returns true - * if 'r' is in fail-open fail, false otherwise. */ -static bool -failing_open(struct relay *r) +static struct hook +in_band_hook_create(const struct settings *s) { - struct rconn *local = r->halves[HALF_LOCAL].rconn; - struct rconn *remote = r->halves[HALF_REMOTE].rconn; - int disconnected_duration; + struct in_band_data *in_band; + int retval; - if (fail_mode == FAIL_CLOSED) { - /* We fail closed, so there's never anything to do. */ - return false; + in_band = xmalloc(sizeof *in_band); + in_band->s = s; + in_band->ml = mac_learning_create(); + retval = netdev_open(s->of_name, NETDEV_ETH_TYPE_NONE, + &in_band->of_device); + if (retval) { + fatal(retval, "Could not open %s device", s->of_name); } + memcpy(in_band->mac, netdev_get_etheraddr(in_band->of_device), + ETH_ADDR_LEN); + + return make_hook(in_band_packet_cb, NULL, in_band); +} + +/* Fail open support. */ + +struct fail_open_data { + const struct settings *s; + struct rconn *local_rconn; + struct rconn *remote_rconn; + struct lswitch *lswitch; + int last_disconn_secs; +}; - disconnected_duration = rconn_disconnected_duration(remote); - if (disconnected_duration < probe_interval * 3) { - /* It's not time to fail open yet. */ - if (r->lswitch && rconn_is_connected(remote)) { - /* We're connected, so drop the learning switch. */ +/* Causes 'r' to enter or leave fail-open mode, if appropriate. */ +static void +fail_open_periodic_cb(void *fail_open_) +{ + struct fail_open_data *fail_open = fail_open_; + int disconn_secs; + bool open; + + disconn_secs = rconn_disconnected_duration(fail_open->remote_rconn); + open = disconn_secs >= fail_open->s->probe_interval * 3; + if (open != (fail_open->lswitch != NULL)) { + if (!open) { VLOG_WARN("No longer in fail-open mode"); - lswitch_destroy(r->lswitch); - r->lswitch = NULL; + lswitch_destroy(fail_open->lswitch); + fail_open->lswitch = NULL; + } else { + VLOG_WARN("Could not connect to controller for %d seconds, " + "failing open", disconn_secs); + fail_open->lswitch = lswitch_create(fail_open->local_rconn, true, + fail_open->s->max_idle); + fail_open->last_disconn_secs = disconn_secs; } + } else if (open && disconn_secs > fail_open->last_disconn_secs + 60) { + VLOG_WARN("Still in fail-open mode after %d seconds disconnected " + "from controller", disconn_secs); + fail_open->last_disconn_secs = disconn_secs; + } +} + +static bool +fail_open_packet_cb(struct relay *r, int half, void *fail_open_) +{ + struct fail_open_data *fail_open = fail_open_; + if (half != HALF_LOCAL || r->is_mgmt_conn || !fail_open->lswitch) { return false; + } else { + lswitch_process_packet(fail_open->lswitch, fail_open->local_rconn, + r->halves[HALF_LOCAL].rxbuf); + rconn_run(fail_open->local_rconn); + return true; } +} + +static struct hook +fail_open_hook_create(const struct settings *s, struct rconn *local_rconn, + struct rconn *remote_rconn) +{ + struct fail_open_data *fail_open = xmalloc(sizeof *fail_open); + fail_open->s = s; + fail_open->local_rconn = local_rconn; + fail_open->remote_rconn = remote_rconn; + fail_open->lswitch = NULL; + return make_hook(fail_open_packet_cb, fail_open_periodic_cb, fail_open); +} + +/* Controller discovery. */ - if (!r->lswitch) { - VLOG_WARN("Could not connect to controller for %d seconds, " - "failing open", disconnected_duration); - r->lswitch = lswitch_create(local, true, max_idle); +struct discovery +{ + const struct settings *s; + struct dhclient *dhcp; + bool ever_successful; +}; + +static struct discovery * +discovery_init(const struct settings *s) +{ + struct netdev *netdev; + struct discovery *d; + struct dhclient *dhcp; + int retval; + + /* Bring ofX network device up. */ + retval = netdev_open(s->of_name, NETDEV_ETH_TYPE_NONE, &netdev); + if (retval) { + fatal(retval, "Could not open %s device", s->of_name); } - return true; + retval = netdev_turn_flags_on(netdev, NETDEV_UP, true); + if (retval) { + fatal(retval, "Could not bring %s device up", s->of_name); + } + netdev_close(netdev); + + /* Initialize DHCP client. */ + retval = dhclient_create(s->of_name, modify_dhcp_request, + validate_dhcp_offer, (void *) s, &dhcp); + if (retval) { + fatal(retval, "Failed to initialize DHCP client"); + } + dhclient_init(dhcp, 0); + + d = xmalloc(sizeof *d); + d->s = s; + d->dhcp = dhcp; + d->ever_successful = false; + return d; +} + +static void +discovery_question_connectivity(struct discovery *d) +{ + dhclient_force_renew(d->dhcp, 15); } static bool -fail_open_hook(struct relay *r) +discovery_run(struct discovery *d, char **controller_name) { - if (!failing_open(r)) { + dhclient_run(d->dhcp); + if (!dhclient_changed(d->dhcp)) { return false; - } else { - struct buffer *msg = r->halves[HALF_LOCAL].rxbuf; - struct rconn *local = r->halves[HALF_LOCAL].rconn; - lswitch_process_packet(r->lswitch, local, msg); - rconn_run(local); - return true; } + + dhclient_configure_netdev(d->dhcp); + if (d->s->update_resolv_conf) { + dhclient_update_resolv_conf(d->dhcp); + } + + if (dhclient_is_bound(d->dhcp)) { + *controller_name = dhcp_msg_get_string(dhclient_get_config(d->dhcp), + DHCP_CODE_OFP_CONTROLLER_VCONN); + VLOG_WARN("%s: discovered controller", *controller_name); + d->ever_successful = true; + } else if (controller_name) { + *controller_name = NULL; + if (d->ever_successful) { + VLOG_WARN("discovered controller no longer available"); + } + } + return true; +} + +static void +discovery_wait(struct discovery *d) +{ + dhclient_wait(d->dhcp); } static void @@ -661,8 +752,9 @@ modify_dhcp_request(struct dhcp_msg *msg, void *aux) } static bool -validate_dhcp_offer(const struct dhcp_msg *msg, void *aux) +validate_dhcp_offer(const struct dhcp_msg *msg, void *s_) { + const struct settings *s = s_; char *vconn_name; bool accept; @@ -671,17 +763,19 @@ validate_dhcp_offer(const struct dhcp_msg *msg, void *aux) VLOG_WARN("rejecting DHCP offer missing controller vconn"); return false; } - accept = !regexec(&accept_controller_regex, vconn_name, 0, NULL, 0); + accept = !regexec(&s->accept_controller_regex, vconn_name, 0, NULL, 0); if (!accept) { VLOG_WARN("rejecting controller vconn that fails to match %s", - accept_controller_re); + s->accept_controller_re); } free(vconn_name); return accept; } + +/* User interface. */ static void -parse_options(int argc, char *argv[]) +parse_options(int argc, char *argv[], struct settings *s) { enum { OPT_ACCEPT_VCONN = UCHAR_MAX + 1, @@ -707,7 +801,16 @@ parse_options(int argc, char *argv[]) {0, 0, 0, 0}, }; char *short_options = long_options_to_short_options(long_options); - + char *accept_re = NULL; + int retval; + + /* Set defaults that we can figure out before parsing options. */ + s->listen_vconn_name = NULL; + s->fail_mode = FAIL_OPEN; + s->max_idle = 15; + s->probe_interval = 15; + s->max_backoff = 15; + s->update_resolv_conf = true; for (;;) { int c; @@ -718,20 +821,18 @@ parse_options(int argc, char *argv[]) switch (c) { case OPT_ACCEPT_VCONN: - accept_controller_re = (optarg[0] == '^' - ? optarg - : xasprintf("^%s", optarg)); + accept_re = optarg[0] == '^' ? optarg : xasprintf("^%s", optarg); break; case OPT_NO_RESOLV_CONF: - update_resolv_conf = false; + s->update_resolv_conf = false; break; case 'f': if (!strcmp(optarg, "open")) { - fail_mode = FAIL_OPEN; + s->fail_mode = FAIL_OPEN; } else if (!strcmp(optarg, "closed")) { - fail_mode = FAIL_CLOSED; + s->fail_mode = FAIL_CLOSED; } else { fatal(0, "-f or --fail argument must be \"open\" or \"closed\""); @@ -739,18 +840,18 @@ parse_options(int argc, char *argv[]) break; case OPT_INACTIVITY_PROBE: - probe_interval = atoi(optarg); - if (probe_interval < 5) { + s->probe_interval = atoi(optarg); + if (s->probe_interval < 5) { fatal(0, "--inactivity-probe argument must be at least 5"); } break; case OPT_MAX_IDLE: if (!strcmp(optarg, "permanent")) { - max_idle = OFP_FLOW_PERMANENT; + s->max_idle = OFP_FLOW_PERMANENT; } else { - max_idle = atoi(optarg); - if (max_idle < 1 || max_idle > 65535) { + s->max_idle = atoi(optarg); + if (s->max_idle < 1 || s->max_idle > 65535) { fatal(0, "--max-idle argument must be between 1 and " "65535 or the word 'permanent'"); } @@ -758,11 +859,11 @@ parse_options(int argc, char *argv[]) break; case OPT_MAX_BACKOFF: - max_backoff = atoi(optarg); - if (max_backoff < 1) { + s->max_backoff = atoi(optarg); + if (s->max_backoff < 1) { fatal(0, "--max-backoff argument must be at least 1"); - } else if (max_backoff > 3600) { - max_backoff = 3600; + } else if (s->max_backoff > 3600) { + s->max_backoff = 3600; } break; @@ -775,10 +876,10 @@ parse_options(int argc, char *argv[]) break; case 'l': - if (listen_vconn_name) { + if (s->listen_vconn_name) { fatal(0, "-l or --listen may be only specified once"); } - listen_vconn_name = optarg; + s->listen_vconn_name = optarg; break; case 'h': @@ -802,6 +903,63 @@ parse_options(int argc, char *argv[]) } } free(short_options); + + argc -= optind; + argv += optind; + if (argc < 1 || argc > 2) { + fatal(0, "need one or two non-option arguments; use --help for usage"); + } + + /* Local and remote vconns. */ + s->nl_name = argv[0]; + if (strncmp(s->nl_name, "nl:", 3) + || strlen(s->nl_name) < 4 + || s->nl_name[strspn(s->nl_name + 3, "0123456789") + 3]) { + fatal(0, "%s: argument is not of the form \"nl:DP_IDX\"", s->nl_name); + } + s->of_name = xasprintf("of%s", s->nl_name + 3); + s->controller_name = argc > 1 ? xstrdup(argv[1]) : NULL; + + /* Set accept_controller_regex. */ + if (!accept_re) { + accept_re = vconn_ssl_is_configured() ? "^ssl:.*" : ".*"; + } + retval = regcomp(&s->accept_controller_regex, accept_re, + REG_NOSUB | REG_EXTENDED); + if (retval) { + size_t length = regerror(retval, &s->accept_controller_regex, NULL, 0); + char *buffer = xmalloc(length); + regerror(retval, &s->accept_controller_regex, buffer, length); + fatal(0, "%s: %s", accept_re, buffer); + } + s->accept_controller_re = accept_re; + + /* Mode of operation. */ + s->discovery = s->controller_name == NULL; + if (s->discovery) { + s->in_band = true; + } else { + enum netdev_flags flags; + struct netdev *netdev; + + retval = netdev_open(s->of_name, NETDEV_ETH_TYPE_NONE, &netdev); + if (retval) { + fatal(retval, "Could not open %s device", s->of_name); + } + + retval = netdev_get_flags(netdev, &flags); + if (retval) { + fatal(retval, "Could not get flags for %s device", s->of_name); + } + + s->in_band = (flags & NETDEV_UP) != 0; + if (s->in_band && netdev_get_in6(netdev, NULL)) { + VLOG_WARN("Ignoring IPv6 address on %s device: IPv6 not supported", + s->of_name); + } + + netdev_close(netdev); + } } static void @@ -811,7 +969,7 @@ usage(void) "usage: %s [OPTIONS] nl:DP_IDX [CONTROLLER]\n" "where nl:DP_IDX is a datapath that has been added with dpctl.\n" "CONTROLLER is an active OpenFlow connection method; if it is\n" - "omitted, then secchan performs controller autodiscovery.\n", + "omitted, then secchan performs controller discovery.\n", program_name, program_name); vconn_usage(true, true); printf("\nController discovery options:\n" -- 2.30.2