Add ability to snoop on the primary switch<->controller OpenFlow connection.
authorBen Pfaff <blp@nicira.com>
Mon, 16 Mar 2009 17:50:42 +0000 (10:50 -0700)
committerBen Pfaff <blp@nicira.com>
Mon, 16 Mar 2009 17:50:42 +0000 (10:50 -0700)
This is useful for debugging problems that may be in the switch or in the
controller.

secchan/main.c
secchan/ofproto.c
secchan/ofproto.h
secchan/secchan.8.in
vswitchd/bridge.c
vswitchd/vswitchd.conf.5.in

index 5974f1611ba334a7f83e035e1bf50ae4dd090916..58d4210be3f9029a3f4308ce2e7342325031e16b 100644 (file)
@@ -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"
index f3dce1912d0051838ae7179b7c7f076b373b8faf..c2a3183792943e28e28054208746cc4ca01f0b6f 100644 (file)
@@ -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
index b7995a2f8333bedd2b66f174dc7554f520a39081..584bd1bc7cdc50d4ce6812f8888719ce477a3051 100644 (file)
@@ -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 *,
index bcebe330b0bdb11c6eb0b004740a910c1b14c4d9..031e028319964f9a5de2da3150cf20349cf0dac8 100644 (file)
@@ -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
index 1b5f279ac5d550f8f7542b2914ac3cef33f71bc2..faa400d144fb84b3ed40581591382f1e34ca63a1 100644 (file)
@@ -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);
 }
 
index 61b5dbb055847c82005f08c5119c74803cb1ff7d..766f3a7048d1f439c73e9e76e06a785e459c2404 100644 (file)
@@ -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).