/* 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? */
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");
OPT_INACTIVITY_PROBE,
OPT_MAX_IDLE,
OPT_MAX_BACKOFF,
+ OPT_SNOOP,
OPT_RATE_LIMIT,
OPT_BURST_LIMIT,
OPT_BOOTSTRAP_CA_CERT,
{"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},
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;
svec_add(&s->listeners, optarg);
break;
+ case OPT_SNOOP:
+ svec_add(&s->snoops, optarg);
+ break;
+
case 'h':
usage();
" 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"
struct ofconn *controller;
struct pvconn **listeners;
size_t n_listeners;
+ struct pvconn **snoops;
+ size_t n_snoops;
/* Hooks for vswitchd. */
const struct ofhooks *ofhooks;
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;
}
}
-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) {
}
}
}
+
+ *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)
{
}
}
+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)
{
}
free(p->listeners);
+ for (i = 0; i < p->n_snoops; i++) {
+ pvconn_close(p->snoops[i]);
+ }
+ free(p->snoops);
+
free(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);
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
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);
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 *,
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
{
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;
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);
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);
}
.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).