From: Ben Pfaff Date: Wed, 4 Mar 2009 18:04:23 +0000 (-0800) Subject: secchan: Make ofproto reconfigurable after it is created. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3e64a86106ddc707c3c5717e14291be026376879;p=openvswitch secchan: Make ofproto reconfigurable after it is created. This will allow vswitchd to reconfigure the ofprotos that it instantiates based on changes in the vswitchd configuration file. --- diff --git a/secchan/discovery.c b/secchan/discovery.c index 56beed25..087fa2a1 100644 --- a/secchan/discovery.c +++ b/secchan/discovery.c @@ -33,6 +33,7 @@ #include #include "discovery.h" +#include #include #include #include @@ -44,16 +45,18 @@ #include "openflow/openflow.h" #include "packets.h" #include "status.h" +#include "vconn-ssl.h" #define THIS_MODULE VLM_discovery #include "vlog.h" struct discovery { - const char *accept_controller_re; + char *re; bool update_resolv_conf; - regex_t accept_controller_regex; + regex_t *regex; struct dhclient *dhcp; int n_changes; + struct status_category *ss_cat; }; static void modify_dhcp_request(struct dhcp_msg *, void *aux); @@ -66,7 +69,7 @@ discovery_status_cb(struct status_reply *sr, void *d_) { struct discovery *d = d_; - status_reply_put(sr, "accept-remote=%s", d->accept_controller_re); + status_reply_put(sr, "accept-remote=%s", d->re); status_reply_put(sr, "n-changes=%d", d->n_changes); if (d->dhcp) { status_reply_put(sr, "state=%s", dhclient_get_state(d->dhcp)); @@ -106,9 +109,10 @@ discovery_status_cb(struct status_reply *sr, void *d_) } } -struct discovery * -discovery_create(const char *accept_controller_re, bool update_resolv_conf, - struct dpif *dpif, struct switch_status *ss) +int +discovery_create(const char *re, bool update_resolv_conf, + struct dpif *dpif, struct switch_status *ss, + struct discovery **discoveryp) { struct discovery *d; struct odp_port port; @@ -117,46 +121,94 @@ discovery_create(const char *accept_controller_re, bool update_resolv_conf, d = xcalloc(1, sizeof *d); /* Controller regular expression. */ - d->accept_controller_re = accept_controller_re; - error = regcomp(&d->accept_controller_regex, accept_controller_re, - REG_NOSUB | REG_EXTENDED); + error = discovery_set_accept_controller_re(d, re); if (error) { - size_t length = regerror(error, &d->accept_controller_regex, NULL, 0); - char *buffer = xmalloc(length); - regerror(error, &d->accept_controller_regex, buffer, length); - ofp_fatal(0, "%s: %s", accept_controller_re, buffer); + goto error_free; } d->update_resolv_conf = update_resolv_conf; /* Initialize DHCP client. */ error = dpif_port_query_by_number(dpif, ODPP_LOCAL, &port); if (error) { - ofp_fatal(error, "failed to query datapath local port"); + VLOG_ERR("failed to query datapath local port: %s", strerror(error)); + goto error_regfree; } error = dhclient_create(port.devname, modify_dhcp_request, validate_dhcp_offer, d, &d->dhcp); if (error) { - ofp_fatal(error, "failed to initialize DHCP client"); + VLOG_ERR("failed to initialize DHCP client: %s", strerror(error)); + goto error_regfree; } dhclient_set_max_timeout(d->dhcp, 3); dhclient_init(d->dhcp, 0); - switch_status_register_category(ss, "discovery", discovery_status_cb, d); + d->ss_cat = switch_status_register(ss, "discovery", + discovery_status_cb, d); + + *discoveryp = d; + return 0; - return d; +error_regfree: + regfree(d->regex); + free(d->regex); +error_free: + free(d); + *discoveryp = 0; + return error; } void discovery_destroy(struct discovery *d) { if (d) { - /* We don't own d->accept_controller_re. */ - regfree(&d->accept_controller_regex); + free(d->re); + regfree(d->regex); + free(d->regex); dhclient_destroy(d->dhcp); + switch_status_unregister(d->ss_cat); free(d); } } +void +discovery_set_update_resolv_conf(struct discovery *d, + bool update_resolv_conf) +{ + d->update_resolv_conf = update_resolv_conf; +} + +int +discovery_set_accept_controller_re(struct discovery *d, const char *re_) +{ + regex_t *regex; + int error; + char *re; + + re = (!re_ ? xstrdup(vconn_ssl_is_configured() ? "^ssl:.*" : ".*") + : re_[0] == '^' ? xstrdup(re_) : xasprintf("^%s", re_)); + regex = xmalloc(sizeof *regex); + error = regcomp(regex, re, REG_NOSUB | REG_EXTENDED); + if (error) { + size_t length = regerror(error, regex, NULL, 0); + char *buffer = xmalloc(length); + regerror(error, regex, buffer, length); + VLOG_WARN("%s: %s", re, buffer); + free(regex); + free(re); + return EINVAL; + } else { + if (d->regex) { + regfree(d->regex); + free(d->regex); + } + free(d->re); + + d->regex = regex; + d->re = re; + return 0; + } +} + void discovery_question_connectivity(struct discovery *d) { @@ -224,11 +276,10 @@ validate_dhcp_offer(const struct dhcp_msg *msg, void *d_) VLOG_WARN_RL(&rl, "rejecting DHCP offer missing controller vconn"); return false; } - accept = !regexec(&d->accept_controller_regex, vconn_name, 0, NULL, - 0); + accept = !regexec(d->regex, vconn_name, 0, NULL, 0); if (!accept) { VLOG_WARN_RL(&rl, "rejecting controller vconn that fails to match %s", - d->accept_controller_re); + d->re); } free(vconn_name); return accept; diff --git a/secchan/discovery.h b/secchan/discovery.h index ba6d988e..91f9af29 100644 --- a/secchan/discovery.h +++ b/secchan/discovery.h @@ -37,13 +37,17 @@ #include struct dpif; +struct discovery; struct settings; struct switch_status; -struct discovery *discovery_create(const char *accept_controller_re, - bool update_resolv_conf, - struct dpif *, struct switch_status *); +int discovery_create(const char *accept_controller_re, bool update_resolv_conf, + struct dpif *, struct switch_status *, + struct discovery **); void discovery_destroy(struct discovery *); +void discovery_set_update_resolv_conf(struct discovery *, + bool update_resolv_conf); +int discovery_set_accept_controller_re(struct discovery *, const char *re); void discovery_question_connectivity(struct discovery *); bool discovery_run(struct discovery *, char **controller_name); void discovery_wait(struct discovery *); diff --git a/secchan/executer.c b/secchan/executer.c index ab7ae58c..b321f981 100644 --- a/secchan/executer.c +++ b/secchan/executer.c @@ -78,8 +78,8 @@ struct child { struct executer { /* Settings. */ - const char *command_acl; /* Command white/blacklist, as shell globs. */ - const char *command_dir; /* Directory that contains commands. */ + char *command_acl; /* Command white/blacklist, as shell globs. */ + char *command_dir; /* Directory that contains commands. */ /* Children. */ struct child children[MAX_CHILDREN]; @@ -456,16 +456,19 @@ sigchld_handler(int signr UNUSED) write(signal_fds[1], "", 1); } -struct executer * -executer_create(const char *command_acl, const char *command_dir) +int +executer_create(const char *command_acl, const char *command_dir, + struct executer **executerp) { struct executer *e; struct sigaction sa; + *executerp = NULL; if (null_fd == -1) { /* Create pipe for notifying us that SIGCHLD was invoked. */ if (pipe(signal_fds)) { - ofp_fatal(errno, "pipe failed"); + VLOG_ERR("pipe failed: %s", strerror(errno)); + return errno; } set_nonblocking(signal_fds[0]); set_nonblocking(signal_fds[1]); @@ -473,7 +476,11 @@ executer_create(const char *command_acl, const char *command_dir) /* Open /dev/null. */ null_fd = open("/dev/null", O_RDWR); if (null_fd < 0) { - ofp_fatal(errno, "could not open /dev/null"); + int error = errno; + VLOG_ERR("could not open /dev/null: %s", strerror(error)); + close(signal_fds[0]); + close(signal_fds[1]); + return error; } } @@ -483,14 +490,16 @@ executer_create(const char *command_acl, const char *command_dir) sigemptyset(&sa.sa_mask); sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; if (sigaction(SIGCHLD, &sa, NULL)) { - ofp_fatal(errno, "sigaction(SIGCHLD) failed"); + VLOG_ERR("sigaction(SIGCHLD) failed: %s", strerror(errno)); + return errno; } e = xcalloc(1, sizeof *e); - e->command_acl = command_acl; - e->command_dir = command_dir; + e->command_acl = xstrdup(command_acl); + e->command_dir = xstrdup(command_dir); e->n_children = 0; - return e; + *executerp = e; + return 0; } void @@ -499,7 +508,8 @@ executer_destroy(struct executer *e) if (e) { size_t i; - /* We don't own e->command_acl or e->command_dir. */ + free(e->command_acl); + free(e->command_dir); for (i = 0; i < e->n_children; i++) { struct child *child = &e->children[i]; @@ -512,3 +522,12 @@ executer_destroy(struct executer *e) free(e); } } + +void +executer_set_acl(struct executer *e, const char *acl, const char *dir) +{ + free(e->command_acl); + e->command_acl = xstrdup(acl); + free(e->command_dir); + e->command_dir = xstrdup(dir); +} diff --git a/secchan/executer.h b/secchan/executer.h index 30bb0a28..c9deca06 100644 --- a/secchan/executer.h +++ b/secchan/executer.h @@ -34,11 +34,12 @@ #ifndef EXECUTER_H #define EXECUTER_H 1 +struct executer; struct nicira_header; struct rconn; -struct executer *executer_create(const char *command_acl, - const char *command_dir); +int executer_create(const char *acl, const char *dir, struct executer **); +void executer_set_acl(struct executer *, const char *acl, const char *dir); void executer_destroy(struct executer *); void executer_run(struct executer *); void executer_wait(struct executer *); diff --git a/secchan/fail-open.c b/secchan/fail-open.c index 203e0930..90915a33 100644 --- a/secchan/fail-open.c +++ b/secchan/fail-open.c @@ -50,10 +50,10 @@ struct fail_open { int trigger_duration; int last_disconn_secs; struct mac_learning *mac_learning; + struct status_category *ss_cat; }; /* Causes 'r' to enter or leave fail-open mode, if appropriate. */ - void fail_open_run(struct fail_open *fo) { @@ -151,17 +151,24 @@ fail_open_create(int trigger_duration, struct switch_status *switch_status, fo->trigger_duration = trigger_duration; fo->last_disconn_secs = 0; fo->mac_learning = NULL; - switch_status_register_category(switch_status, "fail-open", - fail_open_status_cb, fo); + fo->ss_cat = switch_status_register(switch_status, "fail-open", + fail_open_status_cb, fo); return fo; } +void +fail_open_set_trigger_duration(struct fail_open *fo, int trigger_duration) +{ + fo->trigger_duration = trigger_duration; +} + void fail_open_destroy(struct fail_open *fo) { if (fo) { /* We don't own fo->controller. */ mac_learning_destroy(fo->mac_learning); + switch_status_unregister(fo->ss_cat); free(fo); } } diff --git a/secchan/fail-open.h b/secchan/fail-open.h index cb1ac761..207a7915 100644 --- a/secchan/fail-open.h +++ b/secchan/fail-open.h @@ -46,6 +46,7 @@ struct switch_status; struct fail_open *fail_open_create(int trigger_duration, struct switch_status *, struct rconn *controller); +void fail_open_set_trigger_duration(struct fail_open *, int trigger_duration); void fail_open_destroy(struct fail_open *); void fail_open_wait(struct fail_open *); void fail_open_run(struct fail_open *); diff --git a/secchan/in-band.c b/secchan/in-band.c index 5c223e96..fe1301f0 100644 --- a/secchan/in-band.c +++ b/secchan/in-band.c @@ -58,6 +58,7 @@ struct in_band { struct mac_learning *mac_learning; struct netdev *netdev; struct rconn *controller; + struct status_category *ss_cat; }; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60); @@ -241,40 +242,46 @@ in_band_wait(struct in_band *in_band) mac_learning_wait(in_band->mac_learning); } -struct in_band * +int in_band_create(struct dpif *dpif, struct switch_status *ss, - struct rconn *controller) + struct rconn *controller, struct in_band **in_bandp) { struct in_band *in_band; struct netdev *netdev; struct odp_port port; int error; + *in_bandp = NULL; error = dpif_port_query_by_number(dpif, ODPP_LOCAL, &port); if (error) { - ofp_fatal(error, "failed to query datapath local port"); + VLOG_ERR("failed to query datapath local port: %s", strerror(error)); + return error; } error = netdev_open(port.devname, NETDEV_ETH_TYPE_NONE, &netdev); if (error) { - ofp_fatal(error, "failed to open %s network device", port.devname); + VLOG_ERR("failed to open %s network device: %s", + port.devname, strerror(error)); + return error; } in_band = xcalloc(1, sizeof *in_band); in_band->mac_learning = mac_learning_create(); in_band->netdev = netdev; in_band->controller = controller; - switch_status_register_category(ss, "in-band", in_band_status_cb, in_band); + in_band->ss_cat = switch_status_register(ss, "in-band", + in_band_status_cb, in_band); - return in_band; + *in_bandp = in_band; + return 0; } - void in_band_destroy(struct in_band *in_band) { if (in_band) { mac_learning_destroy(in_band->mac_learning); netdev_close(in_band->netdev); + switch_status_unregister(in_band->ss_cat); /* We don't own the rconn. */ } } diff --git a/secchan/in-band.h b/secchan/in-band.h index 59516929..cc37af14 100644 --- a/secchan/in-band.h +++ b/secchan/in-band.h @@ -37,14 +37,15 @@ #include "flow.h" struct dpif; +struct in_band; struct ofproto; struct rconn; struct secchan; struct settings; struct switch_status; -struct in_band *in_band_create(struct dpif *, struct switch_status *, - struct rconn *controller); +int in_band_create(struct dpif *, struct switch_status *, + struct rconn *controller, struct in_band **); void in_band_destroy(struct in_band *); void in_band_run(struct in_band *); void in_band_wait(struct in_band *); diff --git a/secchan/main.c b/secchan/main.c index 197055af..d0bb73a4 100644 --- a/secchan/main.c +++ b/secchan/main.c @@ -62,6 +62,7 @@ #include "rconn.h" #include "signals.h" #include "status.h" +#include "svec.h" #include "timeval.h" #include "util.h" #include "vconn-ssl.h" @@ -71,7 +72,58 @@ #include "vlog.h" #define THIS_MODULE VLM_secchan -static void reconfigure(struct ofproto *); +/* 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 ofsettings { + /* Configuration. */ + const char *br_name; /* Bridge name to use for configuration lookup */ + + /* Overall mode of operation. */ + bool discovery; /* Discover the controller automatically? */ + bool in_band; /* Connect to controller in-band? */ + + /* Datapath. */ + uint64_t datapath_id; /* Datapath ID. */ + const char *dp_name; /* Name of local datapath. */ + + /* Description strings. */ + const char *mfr_desc; /* Manufacturer. */ + const char *hw_desc; /* Hardware. */ + const char *sw_desc; /* Software version. */ + const char *serial_desc; /* Serial number. */ + + /* Related vconns and network devices. */ + const char *controller_name; /* Controller (if not discovery mode). */ + struct svec listeners; /* Listen for management 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. */ + + /* Packet-in rate-limiting. */ + int rate_limit; /* Tokens added to bucket per second. */ + int burst_limit; /* Maximum number token bucket size. */ + + /* Discovery behavior. */ + const char *accept_controller_re; /* Controller vconns to accept. */ + bool update_resolv_conf; /* Update /etc/resolv.conf? */ + + /* Spanning tree protocol. */ + bool enable_stp; + + /* Remote command execution. */ + char *command_acl; /* Command white/blacklist, as shell globs. */ + char *command_dir; /* Directory that contains commands. */ +}; + +static void reconfigure(struct ofproto *, const char *br_name); static void parse_options(int argc, char *argv[], struct ofsettings *); static void usage(void) NO_RETURN; @@ -81,7 +133,7 @@ main(int argc, char *argv[]) struct signal *sighup; struct ofproto *ofproto; struct ofsettings s; - int retval; + int error; set_program_name(argv[0]); register_fault_handlers(); @@ -95,21 +147,60 @@ main(int argc, char *argv[]) daemonize(); /* Start listening for vlogconf requests. */ - retval = vlog_server_listen(NULL, NULL); - if (retval) { - ofp_fatal(retval, "Could not listen for vlog connections"); + error = vlog_server_listen(NULL, NULL); + if (error) { + ofp_fatal(error, "Could not listen for vlog connections"); } VLOG_INFO("OpenFlow reference implementation version %s", VERSION BUILDNR); VLOG_INFO("OpenFlow protocol version 0x%02x", OFP_VERSION); /* Start OpenFlow processing. */ - ofproto = ofproto_create(&s); + error = ofproto_create(s.dp_name, &ofproto); + if (error) { + ofp_fatal(error, "could not initialize openflow switch"); + } + error = ofproto_set_in_band(ofproto, s.in_band); + if (error) { + ofp_fatal(error, "failed to configure in-band control"); + } + error = ofproto_set_discovery(ofproto, s.discovery, s.accept_controller_re, + s.update_resolv_conf); + if (error) { + ofp_fatal(error, "failed to configure controller discovery"); + } + if (s.datapath_id) { + ofproto_set_datapath_id(ofproto, s.datapath_id); + } + ofproto_set_desc(ofproto, s.mfr_desc, s.hw_desc, s.sw_desc, s.serial_desc); + error = ofproto_set_listeners(ofproto, &s.listeners); + if (error) { + ofp_fatal(error, "failed to configure management connections"); + } + ofproto_set_failure(ofproto, s.fail_mode == FAIL_OPEN); + ofproto_set_probe_interval(ofproto, s.probe_interval); + ofproto_set_max_backoff(ofproto, s.max_backoff); + ofproto_set_rate_limit(ofproto, s.rate_limit, s.burst_limit); + error = ofproto_set_stp(ofproto, s.enable_stp); + if (error) { + ofp_fatal(error, "failed to configure STP"); + } + error = ofproto_set_remote_execution(ofproto, s.command_acl, + s.command_dir); + if (error) { + ofp_fatal(error, "failed to configure remote command execution"); + } + if (!s.discovery) { + error = ofproto_set_controller(ofproto, s.controller_name); + if (error) { + ofp_fatal(error, "failed to configure controller"); + } + } - reconfigure(ofproto); + reconfigure(ofproto, s.br_name); while (ofproto_is_alive(ofproto)) { if (signal_poll(sighup)) { - reconfigure(ofproto); + reconfigure(ofproto, s.br_name); } /* Do work. */ @@ -125,10 +216,17 @@ main(int argc, char *argv[]) } static void -reconfigure(struct ofproto *ofproto) +reconfigure(struct ofproto *ofproto, const char *br_name) { cfg_read(); - ofproto_reconfigure(ofproto); + if (br_name) { + struct svec collectors; + + svec_init(&collectors); + cfg_get_all_keys(&collectors, "netflow.%s.host", br_name); + ofproto_set_netflow(ofproto, &collectors); + svec_destroy(&collectors); + } } /* User interface. */ @@ -201,15 +299,15 @@ parse_options(int argc, char *argv[], struct ofsettings *s) /* Set defaults that we can figure out before parsing options. */ s->br_name = NULL; s->datapath_id = 0; - s->mfr_desc = "Nicira Networks, Inc."; - s->hw_desc = "Reference Implementation"; - s->sw_desc = VERSION BUILDNR; - s->serial_desc = "None"; - s->n_listeners = 0; + s->mfr_desc = NULL; + s->hw_desc = NULL; + s->sw_desc = NULL; + s->serial_desc = NULL; + svec_init(&s->listeners); s->fail_mode = FAIL_OPEN; - s->max_idle = 15; - s->probe_interval = 15; - s->max_backoff = 15; + s->max_idle = 0; + s->probe_interval = 0; + s->max_backoff = 0; s->update_resolv_conf = true; s->rate_limit = 0; s->burst_limit = 0; @@ -257,8 +355,7 @@ parse_options(int argc, char *argv[], struct ofsettings *s) break; case OPT_ACCEPT_VCONN: - s->accept_controller_re = (optarg[0] == '^' ? optarg - : xasprintf("^%s", optarg)); + s->accept_controller_re = optarg; break; case OPT_BR_NAME: @@ -361,12 +458,7 @@ parse_options(int argc, char *argv[], struct ofsettings *s) break; case 'l': - if (s->n_listeners >= MAX_MGMT) { - ofp_fatal(0, - "-l or --listen may be specified at most %d times", - MAX_MGMT); - } - s->listener_names[s->n_listeners++] = optarg; + svec_add(&s->listeners, optarg); break; case 'h': diff --git a/secchan/netflow.c b/secchan/netflow.c index 7f2d320f..4996bcd0 100644 --- a/secchan/netflow.c +++ b/secchan/netflow.c @@ -104,19 +104,17 @@ struct netflow_v5_record { }; BUILD_ASSERT_DECL(sizeof(struct netflow_v5_record) == 48); -#define MAX_COLLECTORS 8 - struct netflow { - const char *br_name; /* Bridge name, for reading config file. */ long long int boot_time; /* Time when netflow_create() was called. */ - int netflow_fds[MAX_COLLECTORS]; /* Sockets for NetFlow collectors. */ + int *fds; /* Sockets for NetFlow collectors. */ + size_t n_fds; /* Number of Netflow collectors. */ uint32_t netflow_cnt; /* Flow sequence number for NetFlow. */ }; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); static int -udp_open(char *dst) +open_collector(char *dst) { char *save_ptr; const char *host_name; @@ -228,14 +226,10 @@ netflow_expire(struct netflow *nf, const flow_t *flow, nf_rec.src_mask = 0; nf_rec.dst_mask = 0; - for (i=0; in_fds; i++) { struct msghdr msghdr; struct iovec iov[2]; - if (nf->netflow_fds[i] == -1) { - break; - } - iov[0].iov_base = &nf_hdr; iov[0].iov_len = sizeof nf_hdr; iov[1].iov_base = &nf_rec; @@ -247,7 +241,7 @@ netflow_expire(struct netflow *nf, const flow_t *flow, msghdr.msg_control = NULL; msghdr.msg_controllen = 0; msghdr.msg_flags = 0; - if (sendmsg(nf->netflow_fds[i], &msghdr, 0) < 0) { + if (sendmsg(nf->fds[i], &msghdr, 0) < 0) { VLOG_WARN_RL(&rl, "netflow message send failed: %s", strerror(errno)); } @@ -255,58 +249,59 @@ netflow_expire(struct netflow *nf, const flow_t *flow, nf->netflow_cnt++; } -void -netflow_reconfigure(struct netflow *nf) +static void +clear_collectors(struct netflow *nf) { - struct svec collectors; - int i, nf_idx=0; + size_t i; - /* Configure NetFlow collectors. */ - for (i=0; inetflow_fds[i] >= 0) { - close(nf->netflow_fds[i]); - nf->netflow_fds[i] = -1; - } + for (i = 0; i < nf->n_fds; i++) { + close(nf->fds[i]); } + free(nf->fds); + nf->fds = NULL; + nf->n_fds = 0; +} - svec_init(&collectors); - cfg_get_all_keys(&collectors, "netflow.%s.host", nf->br_name); - svec_sort(&collectors); - if (!svec_is_unique(&collectors)) { - VLOG_WARN("%s specified twice as netflow collector", - svec_get_duplicate(&collectors)); - svec_unique(&collectors); - } - - for (i=0; i= MAX_COLLECTORS) { - VLOG_WARN("too many netflow collectors specified, ignoring %s\n", - collectors.names[i]); - continue; - } - - nf->netflow_fds[nf_idx] = udp_open(collectors.names[i]); - if (nf->netflow_fds[nf_idx] < 0) { - VLOG_WARN("couldn't open connection to collector, ignoring %s\n", - collectors.names[i]); +int +netflow_set_collectors(struct netflow *nf, const struct svec *collectors_) +{ + struct svec collectors; + int error = 0; + size_t i; + + clear_collectors(nf); + + svec_clone(&collectors, collectors_); + svec_sort_unique(&collectors); + + nf->fds = xmalloc(sizeof *nf->fds * collectors.n); + for (i = 0; i < collectors.n; i++) { + const char *name = collectors.names[i]; + char *tmpname = xstrdup(name); + int fd = open_collector(tmpname); + free(tmpname); + if (fd >= 0) { + nf->fds[nf->n_fds++] = fd; } else { - nf_idx++; + VLOG_WARN("couldn't open connection to collector (%s), " + "ignoring %s\n", strerror(-fd), name); + if (!error) { + error = -fd; + } } } + + svec_destroy(&collectors); + return error; } struct netflow * -netflow_create(const char *br_name) +netflow_create(void) { - struct netflow *nf; - int i; - - nf = xmalloc(sizeof *nf); - nf->br_name = br_name; + struct netflow *nf = xmalloc(sizeof *nf); nf->boot_time = time_msec(); - for (i = 0; i < MAX_COLLECTORS; i++) { - nf->netflow_fds[i] = -1; - } + nf->fds = NULL; + nf->n_fds = 0; nf->netflow_cnt = 0; return nf; } @@ -315,15 +310,7 @@ void netflow_destroy(struct netflow *nf) { if (nf) { - int i; - - /* We don't own nf->br_name. */ - for (i = 0; i < MAX_COLLECTORS; i++) { - int fd = nf->netflow_fds[i]; - if (fd >= 0) { - close(fd); - } - } + clear_collectors(nf); free(nf); } } diff --git a/secchan/netflow.h b/secchan/netflow.h index 9c498777..6ccca666 100644 --- a/secchan/netflow.h +++ b/secchan/netflow.h @@ -37,10 +37,11 @@ #include "flow.h" struct odp_flow_stats; +struct svec; -struct netflow *netflow_create(const char *br_name); +struct netflow *netflow_create(void); void netflow_destroy(struct netflow *); -void netflow_reconfigure(struct netflow *); +int netflow_set_collectors(struct netflow *, const struct svec *collectors); void netflow_expire(struct netflow *, const flow_t *, const struct odp_flow_stats *, long long int created); diff --git a/secchan/ofproto.c b/secchan/ofproto.c index 38ced4cf..b14556c3 100644 --- a/secchan/ofproto.c +++ b/secchan/ofproto.c @@ -62,6 +62,7 @@ #include "svec.h" #include "timeval.h" #include "vconn.h" +#include "vconn-ssl.h" #include "xtoxll.h" #define THIS_MODULE VLM_ofproto @@ -140,10 +141,11 @@ static void queue_tx(struct ofpbuf *msg, const struct ofconn *ofconn); struct ofproto { /* Settings. */ uint64_t datapath_id; /* Datapath ID. */ - const char *mfr_desc; /* Manufacturer. */ - const char *hw_desc; /* Hardware. */ - const char *sw_desc; /* Software version. */ - const char *serial_desc; /* Serial number. */ + uint64_t fallback_dpid; /* Datapath ID if no better choice found. */ + char *manufacturer; /* Manufacturer. */ + char *hardware; /* Hardware. */ + char *software; /* Software version. */ + char *serial; /* Serial number. */ /* Datapath. */ struct dpif dpif; @@ -153,6 +155,7 @@ struct ofproto { /* Configuration. */ struct switch_status *switch_status; + struct status_category *ss_cat; struct in_band *in_band; struct discovery *discovery; struct fail_open *fail_open; @@ -168,13 +171,14 @@ struct ofproto { /* OpenFlow connections. */ struct list all_conns; struct ofconn *controller; - struct pvconn *listeners[MAX_MGMT]; + struct pvconn **listeners; size_t n_listeners; }; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); -static uint64_t pick_datapath_id(struct dpif *); +static uint64_t pick_datapath_id(struct dpif *, uint64_t fallback_dpid); +static uint64_t pick_fallback_dpid(void); static void send_packet_in_miss(struct ofpbuf *, void *ofproto); static void send_packet_in_action(struct ofpbuf *, void *ofproto); static void update_used(struct ofproto *); @@ -189,95 +193,69 @@ static void handle_openflow(struct ofconn *, struct ofproto *, static void refresh_port_group(struct ofproto *, unsigned int group); static void update_port(struct ofproto *, const char *devname); -static void init_ports(struct ofproto *); +static int init_ports(struct ofproto *); static void reinit_ports(struct ofproto *); static uint16_t odp_port_to_ofp_port(uint16_t odp_port); static uint16_t ofp_port_to_odp_port(uint16_t ofp_port); -struct ofproto * -ofproto_create(const struct ofsettings *settings) +int +ofproto_create(const char *datapath, struct ofproto **ofprotop) { struct dpifmon *dpifmon; - struct rconn *controller; struct ofproto *p; struct dpif dpif; int error; - size_t i; + + *ofprotop = NULL; /* Connect to datapath and start listening for messages. */ - error = dpif_open(settings->dp_name, &dpif); + error = dpif_open(datapath, &dpif); if (error) { - ofp_fatal(error, "Failed to open datapath %s", settings->dp_name); + VLOG_ERR("failed to open datapath %s: %s", datapath, strerror(error)); + return error; } error = dpif_set_listen_mask(&dpif, ODPL_MISS | ODPL_ACTION); if (error) { - ofp_fatal(error, "failed to listen on dpif %d", dpif.minor); + VLOG_ERR("failed to listen on datapath %s: %s", + datapath, strerror(error)); + dpif_close(&dpif); + return error; } /* Start monitoring datapath ports for status changes. */ error = dpifmon_create(&dpif, &dpifmon); if (error) { - ofp_fatal(error, "failed to starting monitoring dpif %d", dpif.minor); - } - - /* Create controller connection. */ - controller = rconn_create(settings->probe_interval, settings->max_backoff); - if (settings->controller_name) { - error = rconn_connect(controller, settings->controller_name); - if (error == EAFNOSUPPORT) { - ofp_fatal(0, "No support for %s vconn", settings->controller_name); - } + VLOG_ERR("failed to starting monitoring datapath %s: %s", + datapath, strerror(error)); + dpif_close(&dpif); + return error; } /* Initialize settings. */ p = xcalloc(1, sizeof *p); - p->datapath_id = settings->datapath_id; - if (!p->datapath_id) { - p->datapath_id = pick_datapath_id(&dpif); - } + p->fallback_dpid = pick_fallback_dpid(); + p->datapath_id = pick_datapath_id(&dpif, p->fallback_dpid); VLOG_INFO("using datapath ID %012"PRIx64, p->datapath_id); - p->mfr_desc = settings->mfr_desc; - p->hw_desc = settings->hw_desc; - p->sw_desc = settings->sw_desc; - p->serial_desc = settings->serial_desc; + p->manufacturer = xstrdup("Nicira Networks, Inc."); + p->hardware = xstrdup("Reference Implementation"); + p->software = xstrdup(VERSION BUILDNR); + p->serial = xstrdup("None"); - /* Initialize datapath information. */ + /* Initialize datapath. */ p->dpif = dpif; p->dpifmon = dpifmon; port_array_init(&p->ports); shash_init(&p->port_by_name); /* Initialize submodules. */ - p->switch_status = switch_status_create(settings); - switch_status_register_category(p->switch_status, "remote", - rconn_status_cb, controller); - if (settings->in_band) { - p->in_band = in_band_create(&dpif, p->switch_status, controller); - } - if (settings->discovery) { - p->discovery = discovery_create(settings->accept_controller_re, - settings->update_resolv_conf, &p->dpif, - p->switch_status); - } - if (settings->fail_mode == FAIL_OPEN) { - p->fail_open = fail_open_create(settings->probe_interval * 3, - p->switch_status, controller); - } - if (settings->rate_limit) { - p->miss_sched = pinsched_create(settings->rate_limit, - settings->burst_limit, - p->switch_status); - p->action_sched = pinsched_create(settings->rate_limit, - settings->burst_limit, NULL); - } - if (settings->command_acl[0]) { - p->executer = executer_create(settings->command_acl, - settings->command_dir); - } - if (settings->br_name) { - p->netflow = netflow_create(settings->br_name); - } + p->switch_status = switch_status_create(p); + p->in_band = NULL; + p->discovery = NULL; + p->fail_open = NULL; + p->miss_sched = p->action_sched = NULL; + p->executer = NULL; + p->netflow = NULL; /* Initialize flow table. */ classifier_init(&p->cls); @@ -286,25 +264,300 @@ ofproto_create(const struct ofsettings *settings) /* Initialize OpenFlow connections. */ list_init(&p->all_conns); - p->controller = ofconn_create(p, controller); + p->controller = ofconn_create(p, rconn_create(15, 15)); p->controller->pktbuf = pktbuf_create(); p->controller->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN; + p->listeners = NULL; p->n_listeners = 0; - for (i = 0; i < settings->n_listeners; i++) { - const char *name = settings->listener_names[i]; + + /* Register switch status category. */ + p->ss_cat = switch_status_register(p->switch_status, "remote", + rconn_status_cb, p->controller->rconn); + + /* Almost done... */ + error = init_ports(p); + if (error) { + ofproto_destroy(p); + return error; + } + + *ofprotop = p; + return 0; +} + +void +ofproto_set_datapath_id(struct ofproto *p, uint64_t datapath_id) +{ + uint64_t old_dpid = p->datapath_id; + p->datapath_id = (datapath_id + ? datapath_id + : pick_datapath_id(&p->dpif, p->fallback_dpid)); + if (p->datapath_id != old_dpid) { + VLOG_INFO("datapath ID changed to %012"PRIx64, p->datapath_id); + rconn_reconnect(p->controller->rconn); + } +} + +void +ofproto_set_probe_interval(struct ofproto *p, int probe_interval) +{ + if (probe_interval) { + rconn_set_probe_interval(p->controller->rconn, probe_interval); + if (p->fail_open) { + fail_open_set_trigger_duration(p->fail_open, probe_interval * 3); + } + } +} + +void +ofproto_set_max_backoff(struct ofproto *p, int max_backoff) +{ + if (max_backoff) { + rconn_set_max_backoff(p->controller->rconn, max_backoff); + } +} + +void +ofproto_set_desc(struct ofproto *p, + const char *manufacturer, const char *hardware, + const char *software, const char *serial) +{ + if (manufacturer) { + free(p->manufacturer); + p->manufacturer = xstrdup(manufacturer); + } + if (hardware) { + free(p->hardware); + p->hardware = xstrdup(hardware); + } + if (software) { + free(p->software); + p->software = xstrdup(software); + } + if (serial) { + free(p->serial); + p->serial = xstrdup(serial); + } +} + +int +ofproto_set_in_band(struct ofproto *p, bool in_band) +{ + if (in_band != (p->in_band != NULL)) { + if (in_band) { + return in_band_create(&p->dpif, p->switch_status, + p->controller->rconn, &p->in_band); + } else { + ofproto_set_discovery(p, false, NULL, true); + in_band_destroy(p->in_band); + p->in_band = NULL; + } + rconn_reconnect(p->controller->rconn); + } + return 0; +} + +int +ofproto_set_discovery(struct ofproto *p, bool discovery, + const char *re, bool update_resolv_conf) +{ + if (discovery != (p->discovery != NULL)) { + if (discovery) { + int error = ofproto_set_in_band(p, true); + if (error) { + return error; + } + error = discovery_create(re, update_resolv_conf, + &p->dpif, p->switch_status, + &p->discovery); + if (error) { + return error; + } + } else { + discovery_destroy(p->discovery); + p->discovery = NULL; + } + rconn_disconnect(p->controller->rconn); + } else if (discovery) { + discovery_set_update_resolv_conf(p->discovery, update_resolv_conf); + return discovery_set_accept_controller_re(p->discovery, re); + } + return 0; +} + +int +ofproto_set_controller(struct ofproto *ofproto, const char *controller) +{ + if (ofproto->discovery) { + return EINVAL; + } + rconn_connect(ofproto->controller->rconn, controller); + return 0; +} + +int +ofproto_set_listeners(struct ofproto *ofproto, const struct svec *listeners) +{ + int retval = 0; + size_t i; + + for (i = 0; i < ofproto->n_listeners; i++) { + pvconn_close(ofproto->listeners[i]); + } + free(ofproto->listeners); + ofproto->n_listeners = 0; + + ofproto->listeners = xmalloc(listeners->n * sizeof *ofproto->listeners); + for (i = 0; i < listeners->n; i++) { + const char *name = listeners->names[i]; struct pvconn *pvconn; + int error; error = pvconn_open(name, &pvconn); - if (error && error != EAGAIN) { - ofp_fatal(error, "opening %s", name); + if (!error) { + ofproto->listeners[ofproto->n_listeners++] = pvconn; + } else { + VLOG_ERR("failed to listen on %s: %s", name, strerror(error)); + if (!retval) { + retval = error; + } } - p->listeners[p->n_listeners++] = pvconn; } + return retval; +} - /* Retrieve initial port status. */ - init_ports(p); +int +ofproto_set_netflow(struct ofproto *ofproto, const struct svec *collectors) +{ + if (collectors && collectors->n) { + if (!ofproto->netflow) { + ofproto->netflow = netflow_create(); + } + return netflow_set_collectors(ofproto->netflow, collectors); + } else { + netflow_destroy(ofproto->netflow); + ofproto->netflow = NULL; + return 0; + } +} - return p; +void +ofproto_set_failure(struct ofproto *ofproto, bool fail_open) +{ + if (fail_open) { + struct rconn *rconn = ofproto->controller->rconn; + int trigger_duration = rconn_get_probe_interval(rconn) * 3; + if (!ofproto->fail_open) { + ofproto->fail_open = fail_open_create(trigger_duration, + ofproto->switch_status, + rconn); + } else { + fail_open_set_trigger_duration(ofproto->fail_open, + trigger_duration); + } + } else { + fail_open_destroy(ofproto->fail_open); + ofproto->fail_open = NULL; + } +} + +void +ofproto_set_rate_limit(struct ofproto *ofproto, + int rate_limit, int burst_limit) +{ + if (rate_limit) { + if (!ofproto->miss_sched) { + ofproto->miss_sched = pinsched_create(rate_limit, burst_limit, + ofproto->switch_status); + ofproto->action_sched = pinsched_create(rate_limit, burst_limit, + NULL); + } else { + pinsched_set_limits(ofproto->miss_sched, rate_limit, burst_limit); + pinsched_set_limits(ofproto->action_sched, + rate_limit, burst_limit); + } + } else { + pinsched_destroy(ofproto->miss_sched); + ofproto->miss_sched = NULL; + pinsched_destroy(ofproto->action_sched); + ofproto->action_sched = NULL; + } +} + +int +ofproto_set_stp(struct ofproto *ofproto UNUSED, bool enable_stp) +{ + /* XXX */ + if (enable_stp) { + VLOG_WARN("STP is not yet implemented"); + return EINVAL; + } else { + return 0; + } +} + +int +ofproto_set_remote_execution(struct ofproto *ofproto, const char *command_acl, + const char *command_dir) +{ + if (command_acl) { + if (!ofproto->executer) { + return executer_create(command_acl, command_dir, + &ofproto->executer); + } else { + executer_set_acl(ofproto->executer, command_acl, command_dir); + } + } else { + executer_destroy(ofproto->executer); + ofproto->executer = NULL; + } + return 0; +} + +uint64_t +ofproto_get_datapath_id(const struct ofproto *ofproto) +{ + return ofproto->datapath_id; +} + +int +ofproto_get_probe_interval(const struct ofproto *ofproto) +{ + return rconn_get_probe_interval(ofproto->controller->rconn); +} + +int +ofproto_get_max_backoff(const struct ofproto *ofproto) +{ + return rconn_get_max_backoff(ofproto->controller->rconn); +} + +bool +ofproto_get_in_band(const struct ofproto *ofproto) +{ + return ofproto->in_band != NULL; +} + +bool +ofproto_get_discovery(const struct ofproto *ofproto) +{ + return ofproto->discovery != NULL; +} + +const char * +ofproto_get_controller(const struct ofproto *ofproto) +{ + return rconn_get_name(ofproto->controller->rconn); +} + +void +ofproto_get_listeners(const struct ofproto *ofproto, struct svec *listeners) +{ + size_t i; + + for (i = 0; i < ofproto->n_listeners; i++) { + svec_add(listeners, pvconn_get_name(ofproto->listeners[i])); + } } void @@ -340,21 +593,16 @@ ofproto_destroy(struct ofproto *p) executer_destroy(p->executer); netflow_destroy(p->netflow); + switch_status_unregister(p->ss_cat); + for (i = 0; i < p->n_listeners; i++) { pvconn_close(p->listeners[i]); } + free(p->listeners); free(p); } -void -ofproto_reconfigure(struct ofproto *p) -{ - if (p->netflow) { - netflow_reconfigure(p->netflow); - } -} - void ofproto_run(struct ofproto *p) { @@ -767,7 +1015,7 @@ update_port(struct ofproto *p, const char *devname) refresh_port_groups(p); } -static void +static int init_ports(struct ofproto *p) { struct odp_port *ports; @@ -777,7 +1025,7 @@ init_ports(struct ofproto *p) error = dpif_port_list(&p->dpif, &ports, &n_ports); if (error) { - ofp_fatal(error, "failed to list datapath ports"); + return error; } for (i = 0; i < n_ports; i++) { @@ -791,6 +1039,7 @@ init_ports(struct ofproto *p) } free(ports); refresh_port_groups(p); + return 0; } static struct ofconn * @@ -1468,10 +1717,10 @@ handle_desc_stats_request(struct ofproto *p, struct ofconn *ofconn, msg = start_stats_reply(request, sizeof *ods); ods = append_stats_reply(sizeof *ods, ofconn, &msg); - strncpy(ods->mfr_desc, p->mfr_desc, sizeof ods->mfr_desc); - strncpy(ods->hw_desc, p->hw_desc, sizeof ods->hw_desc); - strncpy(ods->sw_desc, p->sw_desc, sizeof ods->sw_desc); - strncpy(ods->serial_num, p->serial_desc, sizeof ods->serial_num); + strncpy(ods->mfr_desc, p->manufacturer, sizeof ods->mfr_desc); + strncpy(ods->hw_desc, p->hardware, sizeof ods->hw_desc); + strncpy(ods->sw_desc, p->software, sizeof ods->sw_desc); + strncpy(ods->serial_num, p->serial, sizeof ods->serial_num); queue_tx(msg, ofconn); return 0; @@ -2541,7 +2790,7 @@ send_packet_in_miss(struct ofpbuf *packet, void *p_) } static uint64_t -pick_datapath_id(struct dpif *dpif) +pick_datapath_id(struct dpif *dpif, uint64_t fallback_dpid) { struct odp_port port; uint8_t ea[ETH_ADDR_LEN]; @@ -2559,10 +2808,16 @@ pick_datapath_id(struct dpif *dpif) VLOG_WARN("datapath local port query failed (%s)", strerror(error)); } + return fallback_dpid; +} + +static uint64_t +pick_fallback_dpid(void) +{ + uint8_t ea[ETH_ADDR_LEN]; eth_addr_random(ea); ea[0] = 0x00; /* Set Nicira OUI. */ ea[1] = 0x23; ea[2] = 0x20; return eth_addr_to_uint64(ea); } - diff --git a/secchan/ofproto.h b/secchan/ofproto.h index a50be0de..7a92e1c8 100644 --- a/secchan/ofproto.h +++ b/secchan/ofproto.h @@ -36,71 +36,49 @@ #include #include +#include #include "flow.h" -/* Behavior when the connection to the controller fails. */ -enum fail_mode { - FAIL_OPEN, /* Act as learning switch. */ - FAIL_CLOSED /* Drop all packets. */ -}; +struct ofproto; +struct svec; -/* Maximum number of management connection listeners. */ -#define MAX_MGMT 8 - -/* Settings that may be configured by the user. */ -struct ofsettings { - /* Configuration. */ - const char *br_name; /* Bridge name to use for configuration lookup */ - - /* Overall mode of operation. */ - bool discovery; /* Discover the controller automatically? */ - bool in_band; /* Connect to controller in-band? */ - - /* Datapath. */ - uint64_t datapath_id; /* Datapath ID. */ - const char *dp_name; /* Name of local datapath. */ - - /* Description strings. */ - const char *mfr_desc; /* Manufacturer. */ - const char *hw_desc; /* Hardware. */ - const char *sw_desc; /* Software version. */ - const char *serial_desc; /* Serial number. */ - - /* Related vconns and network devices. */ - const char *controller_name; /* Controller (if not discovery mode). */ - const char *listener_names[MAX_MGMT]; /* Listen for mgmt connections. */ - size_t n_listeners; /* Number of mgmt connection listeners. */ - - /* 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. */ - - /* Packet-in rate-limiting. */ - int rate_limit; /* Tokens added to bucket per second. */ - int burst_limit; /* Maximum number token bucket size. */ - - /* Discovery behavior. */ - const char *accept_controller_re; /* Controller vconns to accept. */ - bool update_resolv_conf; /* Update /etc/resolv.conf? */ - - /* Spanning tree protocol. */ - bool enable_stp; - - /* Remote command execution. */ - char *command_acl; /* Command white/blacklist, as shell globs. */ - char *command_dir; /* Directory that contains commands. */ -}; - -struct ofproto *ofproto_create(const struct ofsettings *); +int ofproto_create(const char *datapath, struct ofproto **ofprotop); void ofproto_destroy(struct ofproto *); -void ofproto_reconfigure(struct ofproto *); void ofproto_run(struct ofproto *); void ofproto_wait(struct ofproto *); -bool ofproto_is_alive(const struct ofproto *p); - -int ofproto_send_packet(struct ofproto *p, const flow_t *, +bool ofproto_is_alive(const struct ofproto *); + +/* Configuration. */ +void ofproto_set_datapath_id(struct ofproto *, uint64_t datapath_id); +void ofproto_set_probe_interval(struct ofproto *, int probe_interval); +void ofproto_set_max_backoff(struct ofproto *, int max_backoff); +void ofproto_set_desc(struct ofproto *, + const char *manufacturer, const char *hardware, + const char *software, const char *serial); +int ofproto_set_in_band(struct ofproto *, bool in_band); +int ofproto_set_discovery(struct ofproto *, bool discovery, + const char *accept_controller_re, + bool update_resolv_conf); +int ofproto_set_controller(struct ofproto *, const char *controller); +int ofproto_set_listeners(struct ofproto *, const struct svec *listeners); +int ofproto_set_netflow(struct ofproto *, const struct svec *collectors); +void ofproto_set_failure(struct ofproto *, bool fail_open); +void ofproto_set_rate_limit(struct ofproto *, int rate_limit, int burst_limit); +int ofproto_set_stp(struct ofproto *, bool enable_stp); +int ofproto_set_remote_execution(struct ofproto *, const char *command_acl, + const char *command_dir); + +/* Configuration querying. */ +uint64_t ofproto_get_datapath_id(const struct ofproto *); +int ofproto_get_probe_interval(const struct ofproto *); +int ofproto_get_max_backoff(const struct ofproto *); +bool ofproto_get_in_band(const struct ofproto *); +bool ofproto_get_discovery(const struct ofproto *); +const char *ofproto_get_controller(const struct ofproto *); +void ofproto_get_listeners(const struct ofproto *, struct svec *); + +/* Functions for use by ofproto implementation modules, not by clients. */ +int ofproto_send_packet(struct ofproto *, const flow_t *, const union ofp_action *, size_t n_actions, const struct ofpbuf *); void ofproto_setup_exact_flow(struct ofproto *, const flow_t *, diff --git a/secchan/pinsched.c b/secchan/pinsched.c index 09843937..ca4d5ed9 100644 --- a/secchan/pinsched.c +++ b/secchan/pinsched.c @@ -73,6 +73,9 @@ struct pinsched { unsigned long long n_limited; /* # queued for rate limiting. */ unsigned long long n_queue_dropped; /* # dropped due to queue overflow. */ unsigned long long n_tx_dropped; /* # dropped due to tx overflow. */ + + /* Switch status. */ + struct status_category *ss_cat; }; static struct ofpbuf * @@ -250,8 +253,8 @@ pinsched_create(int rate_limit, int burst_limit, struct switch_status *ss) ps->n_tx_dropped = 0; if (ss) { - switch_status_register_category(ss, "rate-limit", - pinsched_status_cb, ps); + ps->ss_cat = switch_status_register(ss, "rate-limit", + pinsched_status_cb, ps); } return ps; @@ -269,6 +272,17 @@ pinsched_destroy(struct pinsched *ps) free(queue); } port_array_destroy(&ps->queues); + switch_status_unregister(ps->ss_cat); free(ps); } } + +void +pinsched_set_limits(struct pinsched *ps, int rate_limit, int burst_limit) +{ + ps->rate_limit = rate_limit; + ps->burst_limit = burst_limit; + while (ps->n_queued > burst_limit) { + drop_packet(ps); + } +} diff --git a/secchan/pinsched.h b/secchan/pinsched.h index 237d4a2d..f67df2eb 100644 --- a/secchan/pinsched.h +++ b/secchan/pinsched.h @@ -42,6 +42,7 @@ struct switch_status; typedef void pinsched_tx_cb(struct ofpbuf *, void *aux); struct pinsched *pinsched_create(int rate_limit, int burst_limit, struct switch_status *); +void pinsched_set_limits(struct pinsched *, int rate_limit, int burst_limit); void pinsched_destroy(struct pinsched *); void pinsched_send(struct pinsched *, uint16_t port_no, struct ofpbuf *, pinsched_tx_cb *, void *aux); diff --git a/secchan/status.c b/secchan/status.c index bfe20ba2..738c6dc6 100644 --- a/secchan/status.c +++ b/secchan/status.c @@ -39,17 +39,20 @@ #include #include #include "dynamic-string.h" +#include "list.h" #include "ofpbuf.h" #include "ofproto.h" #include "openflow/nicira-ext.h" #include "rconn.h" +#include "svec.h" #include "timeval.h" #include "vconn.h" #define THIS_MODULE VLM_status #include "vlog.h" -struct switch_status_category { +struct status_category { + struct list node; char *name; void (*cb)(struct status_reply *, void *aux); void *aux; @@ -57,12 +60,13 @@ struct switch_status_category { struct switch_status { time_t booted; - struct switch_status_category *categories; - size_t n_categories, allocated_categories; + struct status_category *config_cat; + struct status_category *switch_cat; + struct list categories; }; struct status_reply { - struct switch_status_category *category; + struct status_category *category; struct ds request; struct ds output; }; @@ -71,7 +75,7 @@ int switch_status_handle_request(struct switch_status *ss, struct rconn *rconn, struct nicira_header *request) { - struct switch_status_category *c; + struct status_category *c; struct nicira_header *reply; struct status_reply sr; struct ofpbuf *b; @@ -80,7 +84,7 @@ switch_status_handle_request(struct switch_status *ss, struct rconn *rconn, sr.request.string = (void *) (request + 1); sr.request.length = ntohs(request->header.length) - sizeof *request; ds_init(&sr.output); - for (c = ss->categories; c < &ss->categories[ss->n_categories]; c++) { + LIST_FOR_EACH (c, struct status_category, node, &ss->categories) { if (!memcmp(c->name, sr.request.string, MIN(strlen(c->name), sr.request.length))) { sr.category = c; @@ -125,19 +129,28 @@ rconn_status_cb(struct status_reply *sr, void *rconn_) } static void -config_status_cb(struct status_reply *sr, void *s_) +config_status_cb(struct status_reply *sr, void *ofproto_) { - const struct ofsettings *s = s_; + const struct ofproto *ofproto = ofproto_; + struct svec listeners; + int probe_interval, max_backoff; size_t i; - for (i = 0; i < s->n_listeners; i++) { - status_reply_put(sr, "management%zu=%s", i, s->listener_names[i]); + svec_init(&listeners); + ofproto_get_listeners(ofproto, &listeners); + for (i = 0; i < listeners.n; i++) { + status_reply_put(sr, "management%zu=%s", i, listeners.names[i]); } - if (s->probe_interval) { - status_reply_put(sr, "probe-interval=%d", s->probe_interval); + svec_destroy(&listeners); + + probe_interval = ofproto_get_probe_interval(ofproto); + if (probe_interval) { + status_reply_put(sr, "probe-interval=%d", probe_interval); } - if (s->max_backoff) { - status_reply_put(sr, "max-backoff=%d", s->max_backoff); + + max_backoff = ofproto_get_max_backoff(ofproto); + if (max_backoff) { + status_reply_put(sr, "max-backoff=%d", max_backoff); } } @@ -153,13 +166,15 @@ switch_status_cb(struct status_reply *sr, void *ss_) } struct switch_status * -switch_status_create(const struct ofsettings *settings) +switch_status_create(const struct ofproto *ofproto) { struct switch_status *ss = xcalloc(1, sizeof *ss); ss->booted = time_now(); - switch_status_register_category(ss, "config", - config_status_cb, (void *) settings); - switch_status_register_category(ss, "switch", switch_status_cb, ss); + list_init(&ss->categories); + ss->config_cat = switch_status_register(ss, "config", config_status_cb, + (void *) ofproto); + ss->switch_cat = switch_status_register(ss, "switch", switch_status_cb, + ss); return ss; } @@ -167,26 +182,41 @@ void switch_status_destroy(struct switch_status *ss) { if (ss) { - free(ss->categories); + /* Orphan any remaining categories, so that unregistering them later + * won't write to bad memory. */ + struct status_category *c; + LIST_FOR_EACH (c, struct status_category, node, &ss->categories) { + list_init(&c->node); + } + switch_status_unregister(ss->config_cat); + switch_status_unregister(ss->switch_cat); free(ss); } } -void -switch_status_register_category(struct switch_status *ss, - const char *category, - void (*cb)(struct status_reply *, void *aux), - void *aux) +struct status_category * +switch_status_register(struct switch_status *ss, + const char *category, + status_cb_func *cb, void *aux) { - struct switch_status_category *c; - if (ss->n_categories >= ss->allocated_categories) { - ss->categories = x2nrealloc(ss->categories, &ss->allocated_categories, - sizeof *ss->categories); - } - c = &ss->categories[ss->n_categories++]; + struct status_category *c = xmalloc(sizeof *c); c->cb = cb; c->aux = aux; c->name = xstrdup(category); + list_push_back(&ss->categories, &c->node); + return c; +} + +void +switch_status_unregister(struct status_category *c) +{ + if (c) { + if (!list_is_empty(&c->node)) { + list_remove(&c->node); + } + free(c->name); + free(c); + } } void diff --git a/secchan/status.h b/secchan/status.h index da879be7..918a881e 100644 --- a/secchan/status.h +++ b/secchan/status.h @@ -39,20 +39,20 @@ struct nicira_header; struct rconn; struct secchan; -struct ofsettings; +struct ofproto; struct status_reply; -struct switch_status *switch_status_create(const struct ofsettings *); +struct switch_status *switch_status_create(const struct ofproto *); void switch_status_destroy(struct switch_status *); int switch_status_handle_request(struct switch_status *, struct rconn *, struct nicira_header *); -void switch_status_register_category(struct switch_status *, - const char *category, - void (*cb)(struct status_reply *, - void *aux), - void *aux); +typedef void status_cb_func(struct status_reply *, void *aux); +struct status_category *switch_status_register(struct switch_status *, + const char *category, + status_cb_func *, void *aux); +void switch_status_unregister(struct status_category *); void status_reply_put(struct status_reply *, const char *, ...) PRINTF_FORMAT(2, 3);