Add --max-idle option to secchan and controller.
authorBen Pfaff <blp@cs.stanford.edu>
Thu, 26 Jun 2008 16:25:47 +0000 (09:25 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Thu, 26 Jun 2008 16:26:50 +0000 (09:26 -0700)
controller/controller.8.in
controller/controller.c
include/learning-switch.h
include/vconn.h
lib/learning-switch.c
lib/vconn.c
secchan/secchan.8.in
secchan/secchan.c

index b3e6189..d3c9195 100644 (file)
@@ -73,6 +73,21 @@ in the network passes through the controller.
 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
index 284e2e9..e0b8b88 100644 (file)
@@ -33,6 +33,7 @@
 
 #include <errno.h>
 #include <getopt.h>
+#include <limits.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -42,6 +43,7 @@
 #include "daemon.h"
 #include "fault.h"
 #include "learning-switch.h"
+#include "openflow.h"
 #include "poll-loop.h"
 #include "rconn.h"
 #include "util.h"
@@ -66,6 +68,9 @@ static bool learn_macs = true;
 /* 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[]);
@@ -189,7 +194,8 @@ static void
 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
@@ -215,11 +221,13 @@ do_switching(struct switch_ *sw)
 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'},
@@ -254,6 +262,18 @@ parse_options(int argc, char *argv[])
             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();
 
@@ -290,6 +310,7 @@ usage(void)
            "  -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"
index 8e30446..07bb15d 100644 (file)
@@ -39,8 +39,7 @@
 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 *);
index ecaab0e..72cb879 100644 (file)
@@ -75,7 +75,8 @@ void vconn_recv_wait(struct vconn *);
 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,
index 5768fd4..874e2cb 100644 (file)
 #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. */
@@ -69,16 +73,17 @@ static void process_packet_in(struct lswitch *, struct rconn *,
  * 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;
@@ -213,11 +218,11 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn,
         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) {
index 4400584..9c7d2e2 100644 (file)
@@ -370,7 +370,7 @@ vconn_send_wait(struct vconn *vconn)
 
 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];
@@ -392,7 +392,7 @@ make_add_simple_flow(const struct flow *flow,
     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);
index 1b9d1ac..4a85021 100644 (file)
@@ -115,7 +115,35 @@ seconds.
 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
index 66993ba..0800214 100644 (file)
@@ -107,6 +107,10 @@ static enum fail_mode fail_mode = FAIL_OPEN;
  * 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;
 
@@ -409,7 +413,8 @@ local_hook(struct relay *r)
 
     /* 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);
         }
@@ -453,7 +458,7 @@ fail_open_hook(struct relay *r)
     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. */
@@ -465,9 +470,11 @@ fail_open_hook(struct relay *r)
 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'},
@@ -507,6 +514,18 @@ parse_options(int argc, char *argv[])
             }
             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;
@@ -560,6 +579,7 @@ usage(void)
            "                            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"