From: Ben Pfaff Date: Thu, 7 Aug 2008 22:19:31 +0000 (-0700) Subject: Implement OpenFlow hard timeouts. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7bee00fad99b301693c11f0538cb5cff2f3a9397;p=openvswitch Implement OpenFlow hard timeouts. This updates the OpenFlow protocol version and changes the names of structure members, so any software that uses OpenFlow will need to be updated to match. --- diff --git a/datapath/datapath.c b/datapath/datapath.c index b320f2e5..1729730c 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -756,11 +756,14 @@ send_port_status(struct net_bridge_port *p, uint8_t status) } int -dp_send_flow_expired(struct datapath *dp, struct sw_flow *flow) +dp_send_flow_expired(struct datapath *dp, struct sw_flow *flow, + enum ofp_flow_expired_reason reason) { struct sk_buff *skb; struct ofp_flow_expired *ofe; - unsigned long duration_j; + + if (!(dp->flags & OFPC_SEND_FLOW_EXP)) + return 0; ofe = alloc_openflow_skb(dp, sizeof *ofe, OFPT_FLOW_EXPIRED, 0, &skb); if (!ofe) @@ -768,11 +771,12 @@ dp_send_flow_expired(struct datapath *dp, struct sw_flow *flow) flow_fill_match(&ofe->match, &flow->key); - memset(ofe->pad, 0, sizeof ofe->pad); ofe->priority = htons(flow->priority); + ofe->reason = reason; + memset(ofe->pad, 0, sizeof ofe->pad); - duration_j = (flow->timeout - HZ * flow->max_idle) - flow->init_time; - ofe->duration = htonl(duration_j / HZ); + ofe->duration = htonl((jiffies - flow->init_time) / HZ); + memset(ofe->pad2, 0, sizeof ofe->pad2); ofe->packet_count = cpu_to_be64(flow->packet_count); ofe->byte_count = cpu_to_be64(flow->byte_count); @@ -1092,10 +1096,12 @@ static int flow_stats_dump_callback(struct sw_flow *flow, void *private) ofs->match.tp_src = flow->key.tp_src; ofs->match.tp_dst = flow->key.tp_dst; ofs->duration = htonl((jiffies - flow->init_time) / HZ); + ofs->priority = htons(flow->priority); + ofs->idle_timeout = htons(flow->idle_timeout); + ofs->hard_timeout = htons(flow->hard_timeout); + memset(ofs->pad2, 0, sizeof ofs->pad2); ofs->packet_count = cpu_to_be64(flow->packet_count); ofs->byte_count = cpu_to_be64(flow->byte_count); - ofs->priority = htons(flow->priority); - ofs->max_idle = htons(flow->max_idle); memcpy(ofs->actions, flow->actions, actions_length); s->bytes_used += length; diff --git a/datapath/datapath.h b/datapath/datapath.h index aa313e81..5461e051 100644 --- a/datapath/datapath.h +++ b/datapath/datapath.h @@ -76,7 +76,8 @@ int dp_output_control(struct datapath *, struct sk_buff *, uint32_t, int dp_set_origin(struct datapath *, uint16_t, struct sk_buff *); int dp_send_features_reply(struct datapath *, const struct sender *); int dp_send_config_reply(struct datapath *, const struct sender *); -int dp_send_flow_expired(struct datapath *, struct sw_flow *); +int dp_send_flow_expired(struct datapath *, struct sw_flow *, + enum ofp_flow_expired_reason); int dp_send_error_msg(struct datapath *, const struct sender *, uint16_t, uint16_t, const uint8_t *, size_t); int dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp); diff --git a/datapath/flow.c b/datapath/flow.c index 094d32b5..9b5a87ab 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -126,6 +127,20 @@ void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from) memset(to->pad, '\0', sizeof(to->pad)); } +int flow_timeout(struct sw_flow *flow) +{ + if (flow->idle_timeout != OFP_FLOW_PERMANENT + && time_after(jiffies, flow->used + flow->idle_timeout * HZ)) + return OFPER_IDLE_TIMEOUT; + else if (flow->hard_timeout != OFP_FLOW_PERMANENT + && time_after(jiffies, + flow->init_time + flow->hard_timeout * HZ)) + return OFPER_HARD_TIMEOUT; + else + return -1; +} +EXPORT_SYMBOL(flow_timeout); + /* Allocates and returns a new flow with 'n_actions' action, using allocation * flags 'flags'. Returns the new flow or a null pointer on failure. */ struct sw_flow *flow_alloc(int n_actions, gfp_t flags) diff --git a/datapath/flow.h b/datapath/flow.h index 28c2b8b0..cd253d15 100644 --- a/datapath/flow.h +++ b/datapath/flow.h @@ -55,9 +55,10 @@ static inline void check_key_align(void) struct sw_flow { struct sw_flow_key key; - uint16_t max_idle; /* Idle time before discarding (seconds). */ uint16_t priority; /* Only used on entries with wildcards. */ - unsigned long timeout; /* Expiration time (in jiffies). */ + uint16_t idle_timeout; /* Idle time before discarding (seconds). */ + uint16_t hard_timeout; /* Hard expiration time (seconds) */ + unsigned long used; /* Last used time (in jiffies). */ /* FIXME? Probably most flows have only a single action. */ unsigned int n_actions; @@ -86,24 +87,15 @@ void flow_deferred_free(struct sw_flow *); int flow_extract(struct sk_buff *, uint16_t in_port, struct sw_flow_key *); void flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from); void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from); +int flow_timeout(struct sw_flow *); void print_flow(const struct sw_flow_key *); -#include -static inline int flow_timeout(struct sw_flow *flow) -{ - if (flow->max_idle == OFP_FLOW_PERMANENT) - return 0; - - return time_after(jiffies, flow->timeout); -} - static inline void flow_used(struct sw_flow *flow, struct sk_buff *skb) { unsigned long flags; - if (flow->max_idle != OFP_FLOW_PERMANENT) - flow->timeout = jiffies + HZ * flow->max_idle; + flow->used = jiffies; spin_lock_irqsave(&flow->lock, flags); flow->packet_count++; diff --git a/datapath/forward.c b/datapath/forward.c index 524ee14f..fedcca83 100644 --- a/datapath/forward.c +++ b/datapath/forward.c @@ -425,9 +425,10 @@ add_flow(struct sw_chain *chain, const struct ofp_flow_mod *ofm) /* Fill out flow. */ flow_extract_match(&flow->key, &ofm->match); - flow->max_idle = ntohs(ofm->max_idle); flow->priority = flow->key.wildcards ? ntohs(ofm->priority) : -1; - flow->timeout = jiffies + flow->max_idle * HZ; + flow->idle_timeout = ntohs(ofm->idle_timeout); + flow->hard_timeout = ntohs(ofm->hard_timeout); + flow->used = jiffies; flow->n_actions = n_acts; flow->init_time = jiffies; flow->byte_count = 0; diff --git a/datapath/hwtable_dummy/hwtable_dummy.c b/datapath/hwtable_dummy/hwtable_dummy.c index a80ce6b7..fc797caf 100644 --- a/datapath/hwtable_dummy/hwtable_dummy.c +++ b/datapath/hwtable_dummy/hwtable_dummy.c @@ -145,11 +145,12 @@ static int table_dummy_timeout(struct datapath *dp, struct sw_table *swt) flow->timeout = jiffies + HZ * flow->max_idle; } - if (flow_timeout(flow)) { + reason = flow_timeout(flow); + if (reason >= 0) { if (dp->flags & OFPC_SEND_FLOW_EXP) { /* xxx Get byte count */ flow->byte_count = 0; - dp_send_flow_expired(dp, flow); + dp_send_flow_expired(dp, flow, reason); } del_count += do_delete(swt, flow); } diff --git a/datapath/table-hash.c b/datapath/table-hash.c index 1627a471..1c0c6e73 100644 --- a/datapath/table-hash.c +++ b/datapath/table-hash.c @@ -116,10 +116,12 @@ static int table_hash_timeout(struct datapath *dp, struct sw_table *swt) for (i = 0; i <= th->bucket_mask; i++) { struct sw_flow **bucket = &th->buckets[i]; struct sw_flow *flow = *bucket; - if (flow && flow_timeout(flow)) { - count += do_delete(bucket, flow); - if (dp->flags & OFPC_SEND_FLOW_EXP) - dp_send_flow_expired(dp, flow); + if (flow) { + int reason = flow_timeout(flow); + if (reason >= 0) { + count += do_delete(bucket, flow); + dp_send_flow_expired(dp, flow, reason); + } } } th->n_flows -= count; diff --git a/datapath/table-linear.c b/datapath/table-linear.c index f0f162db..d2d7e3c7 100644 --- a/datapath/table-linear.c +++ b/datapath/table-linear.c @@ -104,10 +104,10 @@ static int table_linear_timeout(struct datapath *dp, struct sw_table *swt) mutex_lock(&dp_mutex); list_for_each_entry (flow, &tl->flows, node) { - if (flow_timeout(flow)) { + int reason = flow_timeout(flow); + if (reason >= 0) { count += do_delete(swt, flow); - if (dp->flags & OFPC_SEND_FLOW_EXP) - dp_send_flow_expired(dp, flow); + dp_send_flow_expired(dp, flow, reason); } } tl->n_flows -= count; diff --git a/include/openflow.h b/include/openflow.h index 9db427b4..0f99c7a0 100644 --- a/include/openflow.h +++ b/include/openflow.h @@ -68,7 +68,7 @@ /* The most significant bit being set in the version field indicates an * experimental OpenFlow version. */ -#define OFP_VERSION 0x85 +#define OFP_VERSION 0x86 #define OFP_MAX_TABLE_NAME_LEN 32 #define OFP_MAX_PORT_NAME_LEN 16 @@ -240,7 +240,7 @@ struct ofp_port_mod { OFP_ASSERT(sizeof(struct ofp_port_mod) == 44); /* Why is this packet being sent to the controller? */ -enum ofp_reason { +enum ofp_packet_in_reason { OFPR_NO_MATCH, /* No matching flow. */ OFPR_ACTION /* Action explicitly output to controller. */ }; @@ -363,7 +363,8 @@ struct ofp_match { }; OFP_ASSERT(sizeof(struct ofp_match) == 36); -/* Value used in "max_idle" to indicate that the entry is permanent */ +/* Value used in "idle_timeout" and "hard_timeout" to indicate that the entry + * is permanent. */ #define OFP_FLOW_PERMANENT 0 /* By default, choose a priority in the middle */ @@ -376,23 +377,30 @@ struct ofp_flow_mod { /* Flow actions. */ uint16_t command; /* One of OFPFC_*. */ - uint16_t max_idle; /* Idle time before discarding (seconds). */ - uint32_t buffer_id; /* Buffered packet to apply to (or -1). */ + uint16_t idle_timeout; /* Idle time before discarding (seconds). */ + uint16_t hard_timeout; /* Max time before discarding (seconds). */ uint16_t priority; /* Priority level of flow entry. */ - uint8_t pad[2]; /* Align to 32-bits. */ + uint32_t buffer_id; /* Buffered packet to apply to (or -1). */ uint32_t reserved; /* Reserved for future use. */ struct ofp_action actions[0]; /* The number of actions is inferred from the length field in the header. */ }; OFP_ASSERT(sizeof(struct ofp_flow_mod) == 60); +/* Why did this flow expire? */ +enum ofp_flow_expired_reason { + OFPER_IDLE_TIMEOUT, /* Flow idle time exceeded idle_timeout. */ + OFPER_HARD_TIMEOUT /* Time exceeded hard_timeout. */ +}; + /* Flow expiration (datapath -> controller). */ struct ofp_flow_expired { struct ofp_header header; struct ofp_match match; /* Description of fields */ uint16_t priority; /* Priority level of flow entry. */ - uint8_t pad[2]; /* Align to 32-bits. */ + uint8_t reason; /* One of OFPER_*. */ + uint8_t pad[1]; /* Align to 32-bits. */ uint32_t duration; /* Time flow was alive in seconds. */ uint8_t pad2[4]; /* Align to 64-bits. */ @@ -472,12 +480,14 @@ struct ofp_flow_stats { uint32_t duration; /* Time flow has been alive in seconds. */ uint16_t priority; /* Priority of the entry. Only meaningful when this is not an exact-match entry. */ - uint16_t max_idle; /* Number of seconds idle before expiration. */ + uint16_t idle_timeout; /* Number of seconds idle before expiration. */ + uint16_t hard_timeout; /* Number of seconds before expiration. */ + uint16_t pad2[3]; /* Pad to 64 bits. */ uint64_t packet_count; /* Number of packets in flow. */ uint64_t byte_count; /* Number of bytes in flow. */ struct ofp_action actions[0]; /* Actions. */ }; -OFP_ASSERT(sizeof(struct ofp_flow_stats) == 64); +OFP_ASSERT(sizeof(struct ofp_flow_stats) == 72); /* Body for ofp_stats_request of type OFPST_AGGREGATE. */ struct ofp_aggregate_stats_request { diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 98dfb104..cb95f1fe 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -475,8 +475,9 @@ ofp_print_flow_mod(struct ds *string, const void *oh, size_t len, const struct ofp_flow_mod *ofm = oh; ofp_print_match(string, &ofm->match, verbosity); - ds_put_format(string, " cmd:%d idle:%d pri:%d buf:%#x", - ntohs(ofm->command), ntohs(ofm->max_idle), + ds_put_format(string, " cmd:%d idle:%d hard:%d pri:%d buf:%#x", + ntohs(ofm->command), ntohs(ofm->idle_timeout), + ntohs(ofm->hard_timeout), ofm->match.wildcards ? ntohs(ofm->priority) : (uint16_t)-1, ntohl(ofm->buffer_id)); ofp_print_actions(string, ofm->actions, @@ -493,6 +494,18 @@ ofp_print_flow_expired(struct ds *string, const void *oh, size_t len, const struct ofp_flow_expired *ofe = oh; ofp_print_match(string, &ofe->match, verbosity); + ds_put_cstr(string, " reason="); + switch (ofe->reason) { + case OFPER_IDLE_TIMEOUT: + ds_put_cstr(string, "idle"); + break; + case OFPER_HARD_TIMEOUT: + ds_put_cstr(string, "hard"); + break; + default: + ds_put_format(string, "**%"PRIu8"**", ofe->reason); + break; + } ds_put_format(string, " pri%"PRIu16" secs%"PRIu32" pkts%"PRIu64" bytes%"PRIu64"\n", ofe->match.wildcards ? ntohs(ofe->priority) : (uint16_t)-1, @@ -594,7 +607,10 @@ ofp_flow_stats_reply(struct ds *string, const void *body_, size_t len, ds_put_format(string, "n_packets=%"PRIu64", ", ntohll(fs->packet_count)); ds_put_format(string, "n_bytes=%"PRIu64", ", ntohll(fs->byte_count)); - ds_put_format(string, "max_idle=%"PRIu16",", ntohs(fs->max_idle)); + ds_put_format(string, "idle_timeout=%"PRIu16",", + ntohs(fs->idle_timeout)); + ds_put_format(string, "hard_timeout=%"PRIu16",", + ntohs(fs->hard_timeout)); ofp_print_match(string, &fs->match, verbosity); ofp_print_actions(string, fs->actions, length - sizeof *fs); ds_put_char(string, '\n'); diff --git a/lib/vconn.c b/lib/vconn.c index 332f5de7..9d4659df 100644 --- a/lib/vconn.c +++ b/lib/vconn.c @@ -488,8 +488,8 @@ update_openflow_length(struct buffer *buffer) } struct buffer * -make_add_flow(const struct flow *flow, uint32_t buffer_id, uint16_t max_idle, - size_t n_actions) +make_add_flow(const struct flow *flow, uint32_t buffer_id, + uint16_t idle_timeout, size_t n_actions) { struct ofp_flow_mod *ofm; size_t size = sizeof *ofm + n_actions * sizeof ofm->actions[0]; @@ -511,16 +511,18 @@ make_add_flow(const struct flow *flow, uint32_t buffer_id, uint16_t max_idle, ofm->match.tp_src = flow->tp_src; ofm->match.tp_dst = flow->tp_dst; ofm->command = htons(OFPFC_ADD); - ofm->max_idle = htons(max_idle); + ofm->idle_timeout = htons(idle_timeout); + ofm->hard_timeout = htons(OFP_FLOW_PERMANENT); ofm->buffer_id = htonl(buffer_id); return out; } struct buffer * make_add_simple_flow(const struct flow *flow, - uint32_t buffer_id, uint16_t out_port, uint16_t max_idle) + uint32_t buffer_id, uint16_t out_port, + uint16_t idle_timeout) { - struct buffer *buffer = make_add_flow(flow, buffer_id, max_idle, 1); + struct buffer *buffer = make_add_flow(flow, buffer_id, idle_timeout, 1); struct ofp_flow_mod *ofm = buffer->data; ofm->actions[0].type = htons(OFPAT_OUTPUT); ofm->actions[0].arg.output.max_len = htons(0); diff --git a/switch/datapath.c b/switch/datapath.c index 4d52dda9..d0013d84 100644 --- a/switch/datapath.c +++ b/switch/datapath.c @@ -132,7 +132,8 @@ void dp_output_port(struct datapath *, struct buffer *, void dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp); void dp_output_control(struct datapath *, struct buffer *, int in_port, size_t max_len, int reason); -static void send_flow_expired(struct datapath *, struct sw_flow *); +static void send_flow_expired(struct datapath *, struct sw_flow *, + enum ofp_flow_expired_reason); static void send_port_status(struct sw_port *p, uint8_t status); static void del_switch_port(struct sw_port *p); static void execute_actions(struct datapath *, struct buffer *, @@ -279,7 +280,7 @@ dp_run(struct datapath *dp) chain_timeout(dp->chain, &deleted); LIST_FOR_EACH_SAFE (f, n, struct sw_flow, node, &deleted) { - send_flow_expired(dp, f); + send_flow_expired(dp, f, f->reason); list_remove(&f->node); flow_free(f); } @@ -684,17 +685,20 @@ send_port_status(struct sw_port *p, uint8_t status) } void -send_flow_expired(struct datapath *dp, struct sw_flow *flow) +send_flow_expired(struct datapath *dp, struct sw_flow *flow, + enum ofp_flow_expired_reason reason) { struct buffer *buffer; struct ofp_flow_expired *ofe; ofe = make_openflow_xid(sizeof *ofe, OFPT_FLOW_EXPIRED, 0, &buffer); flow_fill_match(&ofe->match, &flow->key); - memset(ofe->pad, 0, sizeof ofe->pad); ofe->priority = htons(flow->priority); + ofe->reason = reason; + memset(ofe->pad, 0, sizeof ofe->pad); - ofe->duration = htonl(flow->timeout - flow->max_idle - flow->created); + ofe->duration = htonl(time(0) - flow->created); + memset(ofe->pad2, 0, sizeof ofe->pad2); ofe->packet_count = htonll(flow->packet_count); ofe->byte_count = htonll(flow->byte_count); send_openflow_buffer(dp, buffer, NULL); @@ -737,10 +741,12 @@ fill_flow_stats(struct buffer *buffer, struct sw_flow *flow, ofs->match.tp_src = flow->key.flow.tp_src; ofs->match.tp_dst = flow->key.flow.tp_dst; ofs->duration = htonl(now - flow->created); + ofs->priority = htons(flow->priority); + ofs->idle_timeout = htons(flow->idle_timeout); + ofs->hard_timeout = htons(flow->hard_timeout); + memset(ofs->pad2, 0, sizeof ofs->pad2); ofs->packet_count = htonll(flow->packet_count); ofs->byte_count = htonll(flow->byte_count); - ofs->priority = htons(flow->priority); - ofs->max_idle = htons(flow->max_idle); memcpy(ofs->actions, flow->actions, sizeof *ofs->actions * flow->n_actions); } @@ -1069,11 +1075,11 @@ add_flow(struct datapath *dp, const struct ofp_flow_mod *ofm) /* Fill out flow. */ flow_extract_match(&flow->key, &ofm->match); - flow->max_idle = ntohs(ofm->max_idle); flow->priority = flow->key.wildcards ? ntohs(ofm->priority) : -1; - flow->timeout = time(0) + flow->max_idle; /* FIXME */ + flow->idle_timeout = ntohs(ofm->idle_timeout); + flow->hard_timeout = ntohs(ofm->hard_timeout); + flow->used = flow->created = time(0); flow->n_actions = n_acts; - flow->created = time(0); /* FIXME */ flow->byte_count = 0; flow->packet_count = 0; memcpy(flow->actions, ofm->actions, n_acts * sizeof *flow->actions); diff --git a/switch/switch-flow.c b/switch/switch-flow.c index 732cea54..42ede871 100644 --- a/switch/switch-flow.c +++ b/switch/switch-flow.c @@ -192,20 +192,25 @@ void print_flow(const struct sw_flow_key *key) ntohs(f->tp_src), ntohs(f->tp_dst)); } -int flow_timeout(struct sw_flow *flow) +bool flow_timeout(struct sw_flow *flow) { - if (flow->max_idle == OFP_FLOW_PERMANENT) - return 0; - - /* FIXME */ - return time(0) > flow->timeout; + time_t now = time(0); + if (flow->idle_timeout != OFP_FLOW_PERMANENT + && now > flow->used + flow->idle_timeout) { + flow->reason = OFPER_IDLE_TIMEOUT; + return true; + } else if (flow->hard_timeout != OFP_FLOW_PERMANENT + && now > flow->created + flow->hard_timeout) { + flow->reason = OFPER_HARD_TIMEOUT; + return true; + } else { + return false; + } } void flow_used(struct sw_flow *flow, struct buffer *buffer) { - if (flow->max_idle != OFP_FLOW_PERMANENT) - flow->timeout = time(0) + flow->max_idle; - + flow->used = time(0); flow->packet_count++; flow->byte_count += buffer->size; } diff --git a/switch/switch-flow.h b/switch/switch-flow.h index d5e85b41..8da85344 100644 --- a/switch/switch-flow.h +++ b/switch/switch-flow.h @@ -49,12 +49,14 @@ struct sw_flow_key { struct sw_flow { struct sw_flow_key key; - uint16_t max_idle; /* Idle time before discarding (seconds). */ uint16_t priority; /* Only used on entries with wildcards. */ + uint16_t idle_timeout; /* Idle time before discarding (seconds). */ + uint16_t hard_timeout; /* Hard expiration time (seconds) */ + time_t used; /* Last used time. */ time_t created; /* When the flow was created. */ - time_t timeout; /* When the flow expires (if idle). */ uint64_t packet_count; /* Number of packets seen. */ uint64_t byte_count; /* Number of bytes seen. */ + uint8_t reason; /* Reason flow expired (one of OFPER_*). */ /* Private to table implementations. */ struct list node; @@ -76,7 +78,7 @@ void flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from); void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from); void print_flow(const struct sw_flow_key *); -int flow_timeout(struct sw_flow *flow); +bool flow_timeout(struct sw_flow *flow); void flow_used(struct sw_flow *flow, struct buffer *buffer); #endif /* switch-flow.h */ diff --git a/utilities/dpctl.8 b/utilities/dpctl.8 index fc4cd311..7084ad7e 100644 --- a/utilities/dpctl.8 +++ b/utilities/dpctl.8 @@ -277,14 +277,29 @@ implemented by all OpenFlow switches.) not yet expose to the user.) .PP -The \fBadd-flows\fR and \fBdel-flows\fR commands support an additional -optional field: +The \fBadd-flow\fR, \fBadd-flows\fR, and \fBdel-flows\fR commands +support an additional optional field: .IP \fBpriority=\fIvalue\fR Sets the priority of the flow to be added or deleted to \fIvalue\fR, which should be a number between 0 and 65535, inclusive. If this field is not specified, it defaults to 32768. +.PP +The \fBadd-flow\fR and \fBadd-flows\fR commands support additional +optional fields: + +.TP +\fBidle_timeout=\fIseconds\fR +Causes the flow to expire after the given number of seconds of +inactivity. A value of 0 prevents a flow from expiring due to +inactivity. The default is 60 seconds. + +.IP \fBhard_timeout=\fIseconds\fR +Causes the flow to expire after the given number of seconds, +regardless of activity. A value of 0 (the default) gives the flow no +hard expiration deadline. + .PP The \fBdump-flows\fR and \fBdump-aggregate\fR commands support an additional optional field: diff --git a/utilities/dpctl.c b/utilities/dpctl.c index acadb88d..224c7caf 100644 --- a/utilities/dpctl.c +++ b/utilities/dpctl.c @@ -62,7 +62,7 @@ #include "vlog.h" #define THIS_MODULE VLM_dpctl -#define DEFAULT_MAX_IDLE 60 +#define DEFAULT_IDLE_TIMEOUT 60 #define MAX_ADD_ACTS 5 static const char* ifconfigbin = "/sbin/ifconfig"; @@ -499,7 +499,7 @@ str_to_action(char *str, struct ofp_action *action, int *n_actions) static void str_to_flow(char *string, struct ofp_match *match, struct ofp_action *action, int *n_actions, uint8_t *table_idx, - uint16_t *priority, uint16_t *max_idle) + uint16_t *priority, uint16_t *idle_timeout, uint16_t *hard_timeout) { struct field { const char *name; @@ -532,8 +532,11 @@ str_to_flow(char *string, struct ofp_match *match, if (priority) { *priority = OFP_DEFAULT_PRIORITY; } - if (max_idle) { - *max_idle = DEFAULT_MAX_IDLE; + if (idle_timeout) { + *idle_timeout = DEFAULT_IDLE_TIMEOUT; + } + if (hard_timeout) { + *hard_timeout = OFP_FLOW_PERMANENT; } if (action) { act_str = strstr(string, "action"); @@ -570,8 +573,13 @@ str_to_flow(char *string, struct ofp_match *match, continue; } - if (max_idle && !strcmp(name, "max_idle")) { - *max_idle = atoi(value); + if (idle_timeout && !strcmp(name, "idle_timeout")) { + *idle_timeout = atoi(value); + continue; + } + + if (hard_timeout && !strcmp(name, "hard_timeout")) { + *hard_timeout = atoi(value); continue; } @@ -623,7 +631,7 @@ static void do_dump_flows(int argc, char *argv[]) req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request); str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, 0, - &req->table_id, NULL, NULL); + &req->table_id, NULL, NULL, NULL); memset(req->pad, 0, sizeof req->pad); dump_stats_transaction(argv[1], request); @@ -636,7 +644,7 @@ static void do_dump_aggregate(int argc, char *argv[]) req = alloc_stats_request(sizeof *req, OFPST_AGGREGATE, &request); str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, 0, - &req->table_id, NULL, NULL); + &req->table_id, NULL, NULL, NULL); memset(req->pad, 0, sizeof req->pad); dump_stats_transaction(argv[1], request); @@ -647,7 +655,7 @@ static void do_add_flow(int argc, char *argv[]) struct vconn *vconn; struct buffer *buffer; struct ofp_flow_mod *ofm; - uint16_t priority, max_idle; + uint16_t priority, idle_timeout, hard_timeout; size_t size; int n_actions = MAX_ADD_ACTS; @@ -657,9 +665,10 @@ static void do_add_flow(int argc, char *argv[]) size = sizeof *ofm + (sizeof ofm->actions[0] * MAX_ADD_ACTS); ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer); str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &n_actions, - NULL, &priority, &max_idle); + NULL, &priority, &idle_timeout, &hard_timeout); ofm->command = htons(OFPFC_ADD); - ofm->max_idle = htons(max_idle); + ofm->idle_timeout = htons(idle_timeout); + ofm->hard_timeout = htons(hard_timeout); ofm->buffer_id = htonl(UINT32_MAX); ofm->priority = htons(priority); ofm->reserved = htonl(0); @@ -687,7 +696,7 @@ static void do_add_flows(int argc, char *argv[]) while (fgets(line, sizeof line, file)) { struct buffer *buffer; struct ofp_flow_mod *ofm; - uint16_t priority, max_idle; + uint16_t priority, idle_timeout, hard_timeout; size_t size; int n_actions = MAX_ADD_ACTS; @@ -708,9 +717,10 @@ static void do_add_flows(int argc, char *argv[]) size = sizeof *ofm + (sizeof ofm->actions[0] * MAX_ADD_ACTS); ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer); str_to_flow(line, &ofm->match, &ofm->actions[0], &n_actions, - NULL, &priority, &max_idle); + NULL, &priority, &idle_timeout, &hard_timeout); ofm->command = htons(OFPFC_ADD); - ofm->max_idle = htons(max_idle); + ofm->idle_timeout = htons(idle_timeout); + ofm->hard_timeout = htons(hard_timeout); ofm->buffer_id = htonl(UINT32_MAX); ofm->priority = htons(priority); ofm->reserved = htonl(0); @@ -739,9 +749,10 @@ static void do_del_flows(int argc, char *argv[]) size = sizeof *ofm; ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer); str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, 0, NULL, - &priority, NULL); + &priority, NULL, NULL); ofm->command = htons(OFPFC_DELETE); - ofm->max_idle = htons(0); + ofm->idle_timeout = htons(0); + ofm->hard_timeout = htons(0); ofm->buffer_id = htonl(UINT32_MAX); ofm->priority = htons(priority); ofm->reserved = htonl(0);