From 7bee00fad99b301693c11f0538cb5cff2f3a9397 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 7 Aug 2008 15:19:31 -0700 Subject: [PATCH] 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. --- datapath/datapath.c | 20 +++++++----- datapath/datapath.h | 3 +- datapath/flow.c | 15 +++++++++ datapath/flow.h | 18 +++-------- datapath/forward.c | 5 +-- datapath/hwtable_dummy/hwtable_dummy.c | 5 +-- datapath/table-hash.c | 10 +++--- datapath/table-linear.c | 6 ++-- include/openflow.h | 28 +++++++++++------ lib/ofp-print.c | 22 +++++++++++-- lib/vconn.c | 12 ++++--- switch/datapath.c | 26 ++++++++++------ switch/switch-flow.c | 23 ++++++++------ switch/switch-flow.h | 8 +++-- utilities/dpctl.8 | 19 ++++++++++-- utilities/dpctl.c | 43 ++++++++++++++++---------- 16 files changed, 174 insertions(+), 89 deletions(-) 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); -- 2.30.2