This option is most useful for debugging. It reduces switching
performance, so it should not be used in production.
+.TP
+\fB--max-idle=\fIsecs\fR|\fBpermanent\fR
+Sets \fIsecs\fR as the number of seconds that a flow set up by the
+controller will remain in the switch's flow table without any matching
+packets being seen. If \fBpermanent\fR is specified, which is not
+recommended, flows will never expire. The default is 60 seconds.
+
+This option affects only flows set up by the OpenFlow controller. In
+some configurations, the OpenFlow secure channel can set up some flows
+on its own. To set the idle time for those flows, pass
+\fB--max-idle\fR to \fBsecchan\fR(8).
+
+This option has no effect when \fB-n\fR (or \fB--noflow\fR) is in use
+(because the controller does not set up flows in that case).
+
.TP
.BR \-H ", " \-\^\-hub
By default, the controller acts as an L2 MAC-learning switch. This
#include <errno.h>
#include <getopt.h>
+#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "daemon.h"
#include "fault.h"
#include "learning-switch.h"
+#include "openflow.h"
#include "poll-loop.h"
#include "rconn.h"
#include "util.h"
/* Set up flows? (If not, every packet is processed at the controller.) */
static bool setup_flows = true;
+/* --max-idle: Maximum idle time, in seconds, before flows expire. */
+static int max_idle = 60;
+
static int do_switching(struct switch_ *);
static void new_switch(struct switch_ *, struct vconn *, const char *name);
static void parse_options(int argc, char *argv[]);
new_switch(struct switch_ *sw, struct vconn *vconn, const char *name)
{
sw->rconn = rconn_new_from_vconn(name, 128, vconn);
- sw->lswitch = lswitch_create(sw->rconn, learn_macs, setup_flows);
+ sw->lswitch = lswitch_create(sw->rconn, learn_macs,
+ setup_flows ? max_idle : -1);
}
static int
static void
parse_options(int argc, char *argv[])
{
+ enum { OPT_MAX_IDLE = UCHAR_MAX + 1 };
static struct option long_options[] = {
{"detach", no_argument, 0, 'D'},
{"pidfile", optional_argument, 0, 'P'},
{"hub", no_argument, 0, 'H'},
{"noflow", no_argument, 0, 'n'},
+ {"max-idle", required_argument, 0, OPT_MAX_IDLE},
{"verbose", optional_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'},
setup_flows = false;
break;
+ case OPT_MAX_IDLE:
+ if (!strcmp(optarg, "permanent")) {
+ max_idle = OFP_FLOW_PERMANENT;
+ } else {
+ max_idle = atoi(optarg);
+ if (max_idle < 1 || max_idle > 65535) {
+ fatal(0, "--max-idle argument must be between 1 and "
+ "65535 or the word 'permanent'");
+ }
+ }
+ break;
+
case 'h':
usage();
" -P, --pidfile[=FILE] create pidfile (default: %s/controller.pid)\n"
" -H, --hub act as hub instead of learning switch\n"
" -n, --noflow pass traffic, but don't add flows\n"
+ " --max-idle=SECS max idle time for new flows\n"
" -v, --verbose=MODULE:FACILITY:LEVEL configure logging levels\n"
" -v, --verbose set maximum verbosity level\n"
" -h, --help display this help message\n"
struct buffer;
struct rconn;
-struct lswitch *lswitch_create(struct rconn *,
- bool learn_macs, bool setup_flows);
+struct lswitch *lswitch_create(struct rconn *, bool learn_macs, int max_idle);
void lswitch_destroy(struct lswitch *);
void lswitch_process_packet(struct lswitch *, struct rconn *,
const struct buffer *);
void vconn_send_wait(struct vconn *);
struct buffer *make_add_simple_flow(const struct flow *,
- uint32_t buffer_id, uint16_t out_port);
+ uint32_t buffer_id, uint16_t out_port,
+ uint16_t max_idle);
struct buffer *make_buffered_packet_out(uint32_t buffer_id,
uint16_t in_port, uint16_t out_port);
struct buffer *make_unbuffered_packet_out(const struct buffer *packet,
#include "vlog.h"
struct lswitch {
- bool setup_flows; /* Set up flows? (or controller processes all packets) */
+ /* If nonnegative, the switch sets up flows that expire after the given
+ * number of seconds (or never expire, if the value is OFP_FLOW_PERMANENT).
+ * Otherwise, the switch processes every packet. */
+ int max_idle;
+
uint64_t datapath_id;
time_t last_features_request;
struct mac_learning *ml; /* NULL to act as hub instead of switch. */
* If 'learn_macs' is true, the new switch will learn the ports on which MAC
* addresses appear. Otherwise, the new switch will flood all packets.
*
- * If 'setup_flows' is true, the new switch will set up flows. Otherwise, the
- * new switch will process every packet.
+ * If 'max_idle' is nonnegative, the new switch will set up flows that expire
+ * after the given number of seconds (or never expire, if 'max_idle' is
+ * OFP_FLOW_PERMANENT). Otherwise, the new switch will process every packet.
*
* 'rconn' is used to send out an OpenFlow features request. */
struct lswitch *
-lswitch_create(struct rconn *rconn, bool learn_macs, bool setup_flows)
+lswitch_create(struct rconn *rconn, bool learn_macs, int max_idle)
{
struct lswitch *sw = xmalloc(sizeof *sw);
memset(sw, 0, sizeof *sw);
- sw->setup_flows = setup_flows;
+ sw->max_idle = max_idle;
sw->datapath_id = 0;
sw->last_features_request = 0;
sw->ml = learn_macs ? mac_learning_create() : NULL;
out_port = mac_learning_lookup(sw->ml, flow.dl_dst);
}
- if (sw->setup_flows && (!sw->ml || out_port != OFPP_FLOOD)) {
+ if (sw->max_idle >= 0 && (!sw->ml || out_port != OFPP_FLOOD)) {
/* The output port is known, or we always flood everything, so add a
* new flow. */
queue_tx(sw, rconn, make_add_simple_flow(&flow, ntohl(opi->buffer_id),
- out_port));
+ out_port, sw->max_idle));
/* If the switch didn't buffer the packet, we need to send a copy. */
if (ntohl(opi->buffer_id) == UINT32_MAX) {
struct buffer *
make_add_simple_flow(const struct flow *flow,
- uint32_t buffer_id, uint16_t out_port)
+ uint32_t buffer_id, uint16_t out_port, uint16_t max_idle)
{
struct ofp_flow_mod *ofm;
size_t size = sizeof *ofm + sizeof ofm->actions[0];
ofm->match.tp_src = flow->tp_src;
ofm->match.tp_dst = flow->tp_dst;
ofm->command = htons(OFPFC_ADD);
- ofm->max_idle = htons(60);
+ ofm->max_idle = htons(max_idle);
ofm->buffer_id = htonl(buffer_id);
ofm->actions[0].type = htons(OFPAT_OUTPUT);
ofm->actions[0].arg.output.max_len = htons(0);
This option has no effect when \fB--fail=closed\fR is specified.
.TP
-\fB-l\Fr, \Fb--listen=\fImethod\fR
+\fB--max-idle=\fIsecs\fR|\fBpermanent\fR
+Sets \fIsecs\fR as the number of seconds that a flow set up by the
+secure channel will remain in the switch's flow table without any
+matching packets being seen. If \fBpermanent\fR is specified, which
+is not recommended, flows set up by the secure channel will never
+expire. The default is 15 seconds.
+
+Most flows are set up by the OpenFlow controller, not by the secure
+channel. This option affects only the following flows, which the
+secure channel sets up itself:
+
+.RS
+.IP \(bu
+When \fB--fail=open\fR is specified, flows set up when the secure
+channel has not been able to contact the controller for the configured
+fail-open delay.
+
+.IP \(bu
+When in-band control is in use, flows set up to bootstrap contacting
+the controller (see \fBCONTACTING THE CONTROLLER\fR, above, for
+more information about in-band control).
+.RE
+
+.IP
+As a result, when both \fB--fail=open\fR and in-band control are not
+in use, this option has no effect.
+
+.TP
+\fB-l\fR, \fB--listen=\fImethod\fR
Configures the switch to additionally listen for incoming OpenFlow
connections for switch management with \fBdpctl\fR. The \fImethod\fR
must be given as one of the following passive OpenFlow connection
* fail_mode is FAIL_OPEN. */
static int fail_open_delay = 30;
+/* --max-idle: Idle time to assign to flows created by learning switch when in
+ * fail-open mode. */
+static int max_idle = 15;
+
static void parse_options(int argc, char *argv[]);
static void usage(void) NO_RETURN;
/* Add new flow. */
if (out_port != OFPP_FLOOD) {
- b = make_add_simple_flow(&flow, ntohl(opi->buffer_id), out_port);
+ b = make_add_simple_flow(&flow, ntohl(opi->buffer_id), out_port,
+ max_idle);
if (rconn_force_send(rc, b)) {
buffer_delete(b);
}
if (!r->lswitch) {
VLOG_WARN("Could not connect to controller for %d seconds, "
"failing open", disconnected_duration);
- r->lswitch = lswitch_create(local, true, true);
+ r->lswitch = lswitch_create(local, true, max_idle);
}
/* Do switching. */
static void
parse_options(int argc, char *argv[])
{
+ enum { OPT_MAX_IDLE = UCHAR_MAX + 1 };
static struct option long_options[] = {
{"fail", required_argument, 0, 'f'},
{"fail-open-delay", required_argument, 0, 'd'},
+ {"max-idle", required_argument, 0, OPT_MAX_IDLE},
{"listen", required_argument, 0, 'l'},
{"detach", no_argument, 0, 'D'},
{"pidfile", optional_argument, 0, 'P'},
}
break;
+ case OPT_MAX_IDLE:
+ if (!strcmp(optarg, "permanent")) {
+ max_idle = OFP_FLOW_PERMANENT;
+ } else {
+ max_idle = atoi(optarg);
+ if (max_idle < 1 || max_idle > 65535) {
+ fatal(0, "--max-idle argument must be between 1 and "
+ "65535 or the word 'permanent'");
+ }
+ }
+ break;
+
case 'D':
set_detach();
break;
" open (default): act as learning switch\n"
" -d, --fail-open-delay=SECS number of seconds after which to\n"
" fail open if --fail=open (default: 30)\n"
+ " --max-idle=SECS max idle for flows set up by secchan\n"
" -l, --listen=METHOD allow management connections on METHOD\n"
" (a passive OpenFlow connection method)\n"
"\nOther options:\n"