From 5a6642496d83615988883a2e960edabd5f9ee927 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Mon, 16 Mar 2009 10:50:42 -0700 Subject: [PATCH] Add ability to snoop on the primary switch<->controller OpenFlow connection. This is useful for debugging problems that may be in the switch or in the controller. --- secchan/main.c | 15 ++++++++ secchan/ofproto.c | 73 ++++++++++++++++++++++++++++++++----- secchan/ofproto.h | 2 + secchan/secchan.8.in | 14 +++++++ vswitchd/bridge.c | 24 ++++++++++++ vswitchd/vswitchd.conf.5.in | 19 ++++++++++ 6 files changed, 137 insertions(+), 10 deletions(-) diff --git a/secchan/main.c b/secchan/main.c index 5974f161..58d4210b 100644 --- a/secchan/main.c +++ b/secchan/main.c @@ -95,6 +95,7 @@ struct ofsettings { /* Related vconns and network devices. */ const char *controller_name; /* Controller (if not discovery mode). */ struct svec listeners; /* Listen for management connections. */ + struct svec snoops; /* Listen for controller snooping conns. */ /* Failure behavior. */ enum fail_mode fail_mode; /* Act as learning switch if no controller? */ @@ -178,6 +179,11 @@ main(int argc, char *argv[]) if (error) { ofp_fatal(error, "failed to configure management connections"); } + error = ofproto_set_snoops(ofproto, &s.snoops); + if (error) { + ofp_fatal(error, + "failed to configure controller snooping connections"); + } error = ofproto_set_netflow(ofproto, &s.netflow); if (error) { ofp_fatal(error, "failed to configure NetFlow collectors"); @@ -232,6 +238,7 @@ parse_options(int argc, char *argv[], struct ofsettings *s) OPT_INACTIVITY_PROBE, OPT_MAX_IDLE, OPT_MAX_BACKOFF, + OPT_SNOOP, OPT_RATE_LIMIT, OPT_BURST_LIMIT, OPT_BOOTSTRAP_CA_CERT, @@ -261,6 +268,7 @@ parse_options(int argc, char *argv[], struct ofsettings *s) {"max-idle", required_argument, 0, OPT_MAX_IDLE}, {"max-backoff", required_argument, 0, OPT_MAX_BACKOFF}, {"listen", required_argument, 0, 'l'}, + {"snoop", required_argument, 0, OPT_SNOOP}, {"rate-limit", optional_argument, 0, OPT_RATE_LIMIT}, {"burst-limit", required_argument, 0, OPT_BURST_LIMIT}, {"stp", no_argument, 0, OPT_STP}, @@ -292,6 +300,7 @@ parse_options(int argc, char *argv[], struct ofsettings *s) s->sw_desc = NULL; s->serial_desc = NULL; svec_init(&s->listeners); + svec_init(&s->snoops); s->fail_mode = FAIL_OPEN; s->max_idle = 0; s->probe_interval = 0; @@ -454,6 +463,10 @@ parse_options(int argc, char *argv[], struct ofsettings *s) svec_add(&s->listeners, optarg); break; + case OPT_SNOOP: + svec_add(&s->snoops, optarg); + break; + case 'h': usage(); @@ -553,6 +566,8 @@ usage(void) " attempts (default: 15 seconds)\n" " -l, --listen=METHOD allow management connections on METHOD\n" " (a passive OpenFlow connection method)\n" + " --snoop=METHOD allow controller snooping on METHOD\n" + " (a passive OpenFlow connection method)\n" " --out-of-band controller connection is out-of-band\n" " --stp enable 802.1D Spanning Tree Protocol\n" " --no-stp disable 802.1D Spanning Tree Protocol\n" diff --git a/secchan/ofproto.c b/secchan/ofproto.c index f3dce191..c2a31837 100644 --- a/secchan/ofproto.c +++ b/secchan/ofproto.c @@ -174,6 +174,8 @@ struct ofproto { struct ofconn *controller; struct pvconn **listeners; size_t n_listeners; + struct pvconn **snoops; + size_t n_snoops; /* Hooks for vswitchd. */ const struct ofhooks *ofhooks; @@ -276,6 +278,8 @@ ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux, p->controller->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN; p->listeners = NULL; p->n_listeners = 0; + p->snoops = NULL; + p->n_snoops = 0; /* Initialize hooks. */ p->ofhooks = ofhooks ? ofhooks : &null_ofhooks; @@ -417,27 +421,30 @@ ofproto_set_controller(struct ofproto *ofproto, const char *controller) } } -int -ofproto_set_listeners(struct ofproto *ofproto, const struct svec *listeners) +static int +set_pvconns(struct pvconn ***pvconnsp, size_t *n_pvconnsp, + const struct svec *svec) { + struct pvconn **pvconns = *pvconnsp; + size_t n_pvconns = *n_pvconnsp; int retval = 0; size_t i; - for (i = 0; i < ofproto->n_listeners; i++) { - pvconn_close(ofproto->listeners[i]); + for (i = 0; i < n_pvconns; i++) { + pvconn_close(pvconns[i]); } - free(ofproto->listeners); - ofproto->n_listeners = 0; + free(pvconns); - ofproto->listeners = xmalloc(listeners->n * sizeof *ofproto->listeners); - for (i = 0; i < listeners->n; i++) { - const char *name = listeners->names[i]; + pvconns = xmalloc(svec->n * sizeof *pvconns); + n_pvconns = 0; + for (i = 0; i < svec->n; i++) { + const char *name = svec->names[i]; struct pvconn *pvconn; int error; error = pvconn_open(name, &pvconn); if (!error) { - ofproto->listeners[ofproto->n_listeners++] = pvconn; + pvconns[n_pvconns++] = pvconn; } else { VLOG_ERR("failed to listen on %s: %s", name, strerror(error)); if (!retval) { @@ -445,9 +452,25 @@ ofproto_set_listeners(struct ofproto *ofproto, const struct svec *listeners) } } } + + *pvconnsp = pvconns; + *n_pvconnsp = n_pvconns; + return retval; } +int +ofproto_set_listeners(struct ofproto *ofproto, const struct svec *listeners) +{ + return set_pvconns(&ofproto->listeners, &ofproto->n_listeners, listeners); +} + +int +ofproto_set_snoops(struct ofproto *ofproto, const struct svec *snoops) +{ + return set_pvconns(&ofproto->snoops, &ofproto->n_snoops, snoops); +} + int ofproto_set_netflow(struct ofproto *ofproto, const struct svec *collectors) { @@ -582,6 +605,16 @@ ofproto_get_listeners(const struct ofproto *ofproto, struct svec *listeners) } } +void +ofproto_get_snoops(const struct ofproto *ofproto, struct svec *snoops) +{ + size_t i; + + for (i = 0; i < ofproto->n_snoops; i++) { + svec_add(snoops, pvconn_get_name(ofproto->snoops[i])); + } +} + void ofproto_destroy(struct ofproto *p) { @@ -625,6 +658,11 @@ ofproto_destroy(struct ofproto *p) } free(p->listeners); + for (i = 0; i < p->n_snoops; i++) { + pvconn_close(p->snoops[i]); + } + free(p->snoops); + free(p); } @@ -718,6 +756,18 @@ ofproto_run1(struct ofproto *p) } } + for (i = 0; i < p->n_snoops; i++) { + struct vconn *vconn; + int retval; + + retval = pvconn_accept(p->snoops[i], OFP_VERSION, &vconn); + if (!retval) { + rconn_add_monitor(p->controller->rconn, vconn); + } else if (retval != EAGAIN) { + VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval)); + } + } + if (time_msec() >= p->next_expiration) { p->next_expiration = time_msec() + 1000; update_used(p); @@ -798,6 +848,9 @@ ofproto_wait(struct ofproto *p) for (i = 0; i < p->n_listeners; i++) { pvconn_wait(p->listeners[i]); } + for (i = 0; i < p->n_snoops; i++) { + pvconn_wait(p->snoops[i]); + } } bool diff --git a/secchan/ofproto.h b/secchan/ofproto.h index b7995a2f..584bd1bc 100644 --- a/secchan/ofproto.h +++ b/secchan/ofproto.h @@ -79,6 +79,7 @@ int ofproto_set_discovery(struct ofproto *, bool discovery, 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_snoops(struct ofproto *, const struct svec *snoops); 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); @@ -94,6 +95,7 @@ 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 *); +void ofproto_get_snoops(const struct ofproto *, struct svec *); /* Functions for use by ofproto implementation modules, not by clients. */ int ofproto_send_packet(struct ofproto *, const flow_t *, diff --git a/secchan/secchan.8.in b/secchan/secchan.8.in index bcebe330..031e0283 100644 --- a/secchan/secchan.8.in +++ b/secchan/secchan.8.in @@ -321,6 +321,20 @@ Listens for TCP connections on \fIport\fR (default: 6633). Listens for connections on Unix domain server socket named \fIfile\fR. .RE +.TP +\fB--snoop=\fImethod\fR +Configures the switch to additionally listen for incoming OpenFlow +connections for controller connection snooping. The \fImethod\fR must +be given as one of the passive OpenFlow connection methods listed +under the \fB--listen\fR option above. This option may be specified +multiple times to listen to multiple connection methods. + +If \fBdpctl monitor\fR is used to connect to \fImethod\fR specified on +\fB--snoop\fR, it will display all the OpenFlow messages traveling +between the switch and its controller on the primary OpenFlow +connection. This can be useful for debugging switch and controller +problems. + .TP \fB--in-band\fR, \fB--out-of-band\fR Configures \fBsecchan\fR to operate in in-band or out-of-band control diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 1b5f279a..faa400d1 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -757,6 +757,7 @@ bridge_reconfigure_one(struct bridge *br) { struct svec old_ports, new_ports, ifaces; struct svec listeners, old_listeners; + struct svec snoops, old_snoops; const char *controller; size_t i, j; char *ctl, *pfx; @@ -959,6 +960,8 @@ bridge_reconfigure_one(struct bridge *br) if (!listeners.n) { svec_add_nocopy(&listeners, xasprintf("punix:%s/%s.mgmt", ofp_rundir, br->name)); + } else if (listeners.n == 1 && !strcmp(listeners.names[0], "none")) { + svec_clear(&listeners); } svec_sort_unique(&listeners); @@ -972,6 +975,27 @@ bridge_reconfigure_one(struct bridge *br) svec_destroy(&listeners); svec_destroy(&old_listeners); + /* Configure OpenFlow controller connection snooping. */ + svec_init(&snoops); + cfg_get_all_strings(&snoops, "bridge.%s.openflow.snoops", br->name); + if (!snoops.n) { + svec_add_nocopy(&snoops, xasprintf("punix:%s/%s.snoop", + ofp_rundir, br->name)); + } else if (snoops.n == 1 && !strcmp(snoops.names[0], "none")) { + svec_clear(&snoops); + } + svec_sort_unique(&snoops); + + svec_init(&old_snoops); + ofproto_get_snoops(br->ofproto, &old_snoops); + svec_sort_unique(&old_snoops); + + if (!svec_equal(&snoops, &old_snoops)) { + ofproto_set_snoops(br->ofproto, &snoops); + } + svec_destroy(&snoops); + svec_destroy(&old_snoops); + mirror_reconfigure(br); } diff --git a/vswitchd/vswitchd.conf.5.in b/vswitchd/vswitchd.conf.5.in index 61b5dbb0..766f3a70 100644 --- a/vswitchd/vswitchd.conf.5.in +++ b/vswitchd/vswitchd.conf.5.in @@ -611,5 +611,24 @@ above). .IP "\fBptcp:\fR[\fIport\fR]" Listens for TCP connections on \fIport\fR (default: 6633). .RE +To entirely disable listening for management connections, set +\fBbridge.\fIname\fB.openflow.listeners\fR to the single value +.SS "OpenFlow Controller Connection Snooping" +By default, each bridge \fIname\fR listens for OpenFlow controller +connection snooping connections on a Unix domain socket named +\fB@RUNDIR@/\fIname\fB.snoop\fR. A client that connects to this +socket, e.g. \fBdpctl monitor unix:@RUNDIR@/\fIname\fB.snoop\fR, will +receive a copy of every OpenFlow message sent by the switch to the +controller, or vice versa, on the primary OpenFlow controller +connection. +.PP +If \fBbridge.\fIname\fB.openflow.snoops\fR is set to one or more +values, \fBvswitchd\fR instead listens on the specified connection +methods. The acceptable connection methods are the same as for +OpenFlow management connections (see above). +.PP +To entirely disable controller connection snooping, set +\fBbridge.\fIname\fB.openflow.snoops\fR to the single value +\fBnone\fR. .SH "SEE ALSO" .BR vswitchd (8). -- 2.30.2