Implement OpenFlow hard timeouts.
authorBen Pfaff <blp@nicira.com>
Thu, 7 Aug 2008 22:19:31 +0000 (15:19 -0700)
committerBen Pfaff <blp@nicira.com>
Thu, 7 Aug 2008 22:19:31 +0000 (15:19 -0700)
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.

16 files changed:
datapath/datapath.c
datapath/datapath.h
datapath/flow.c
datapath/flow.h
datapath/forward.c
datapath/hwtable_dummy/hwtable_dummy.c
datapath/table-hash.c
datapath/table-linear.c
include/openflow.h
lib/ofp-print.c
lib/vconn.c
switch/datapath.c
switch/switch-flow.c
switch/switch-flow.h
utilities/dpctl.8
utilities/dpctl.c

index b320f2e..1729730 100644 (file)
@@ -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;
index aa313e8..5461e05 100644 (file)
@@ -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);
index 094d32b..9b5a87a 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/if_vlan.h>
 #include <net/llc_pdu.h>
 #include <linux/ip.h>
+#include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/tcp.h>
@@ -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)
index 28c2b8b..cd253d1 100644 (file)
@@ -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 <linux/jiffies.h>
-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++;
index 524ee14..fedcca8 100644 (file)
@@ -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;
index a80ce6b..fc797ca 100644 (file)
@@ -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);
                }
index 1627a47..1c0c6e7 100644 (file)
@@ -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;
index f0f162d..d2d7e3c 100644 (file)
@@ -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;
index 9db427b..0f99c7a 100644 (file)
@@ -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 {
index 98dfb10..cb95f1f 100644 (file)
@@ -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');
index 332f5de..9d4659d 100644 (file)
@@ -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);
index 4d52dda..d0013d8 100644 (file)
@@ -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);
index 732cea5..42ede87 100644 (file)
@@ -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;
 }
index d5e85b4..8da8534 100644 (file)
@@ -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 */
index fc4cd31..7084ad7 100644 (file)
@@ -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:
index acadb88..224c7ca 100644 (file)
@@ -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);