From 5aca80fab98fa4208981ca89060f6ba331baac98 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 26 Jun 2008 09:25:47 -0700 Subject: [PATCH 1/1] Add --max-idle option to secchan and controller. --- controller/controller.8.in | 15 +++++++++++++++ controller/controller.c | 23 ++++++++++++++++++++++- include/learning-switch.h | 3 +-- include/vconn.h | 3 ++- lib/learning-switch.c | 19 ++++++++++++------- lib/vconn.c | 4 ++-- secchan/secchan.8.in | 30 +++++++++++++++++++++++++++++- secchan/secchan.c | 24 ++++++++++++++++++++++-- 8 files changed, 105 insertions(+), 16 deletions(-) diff --git a/controller/controller.8.in b/controller/controller.8.in index b3e618927..d3c91957d 100644 --- a/controller/controller.8.in +++ b/controller/controller.8.in @@ -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 diff --git a/controller/controller.c b/controller/controller.c index 284e2e9ff..e0b8b88d8 100644 --- a/controller/controller.c +++ b/controller/controller.c @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -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" diff --git a/include/learning-switch.h b/include/learning-switch.h index 8e3044613..07bb15d7b 100644 --- a/include/learning-switch.h +++ b/include/learning-switch.h @@ -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 *); diff --git a/include/vconn.h b/include/vconn.h index ecaab0ec7..72cb8791b 100644 --- a/include/vconn.h +++ b/include/vconn.h @@ -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, diff --git a/lib/learning-switch.c b/lib/learning-switch.c index 5768fd4d6..874e2cbe4 100644 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@ -53,7 +53,11 @@ #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) { diff --git a/lib/vconn.c b/lib/vconn.c index 440058476..9c7d2e27f 100644 --- a/lib/vconn.c +++ b/lib/vconn.c @@ -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); diff --git a/secchan/secchan.8.in b/secchan/secchan.8.in index 1b9d1ac80..4a8502194 100644 --- a/secchan/secchan.8.in +++ b/secchan/secchan.8.in @@ -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 diff --git a/secchan/secchan.c b/secchan/secchan.c index 66993ba8e..080021431 100644 --- a/secchan/secchan.c +++ b/secchan/secchan.c @@ -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" -- 2.43.0