Add support for listing and deleting entries based on an output port.
authorJustin Pettit <jpettit@nicira.com>
Mon, 1 Dec 2008 22:34:23 +0000 (14:34 -0800)
committerJustin Pettit <jpettit@nicira.com>
Mon, 1 Dec 2008 22:34:23 +0000 (14:34 -0800)
To support this, an "out_port" field has been added to the "ofp_flow_mod",
"ofp_flow_stats_request", and "ofp_aggregate_stats_request" messages.  If an
"out_port" contains a value other than "OFPP_NONE", it introduces a constraint
when matching.  This constraint is that the rule must contain an output action
directed at that port.  Other constraints such as ofp_match structs and
priorities are still used; this is purely an *additional* constraint.  Note
that to get previous behavior, though, "out_port" must be set to "OFPP_NONE",
since "0" is a valid port id.  This only applies to the delete and
delete_strict flow mod commands; the field is ignored by add, modify, and
modify_strict.

20 files changed:
datapath/chain.c
datapath/chain.h
datapath/datapath.c
datapath/flow.c
datapath/flow.h
datapath/forward.c
datapath/table-hash.c
datapath/table-linear.c
datapath/table.h
include/openflow/openflow.h
switch/chain.c
switch/chain.h
switch/datapath.c
switch/switch-flow.c
switch/switch-flow.h
switch/table-hash.c
switch/table-linear.c
switch/table.h
utilities/dpctl.8.in
utilities/dpctl.c

index cd3b6e5..c270e29 100644 (file)
@@ -124,9 +124,10 @@ chain_modify(struct sw_chain *chain, const struct sw_flow_key *key,
        return count;
 }
 
-/* Deletes from 'chain' any and all flows that match 'key'.  If 'strict' set, 
- * wildcards and priority must match.  Returns the number of flows that were 
- * deleted.
+/* Deletes from 'chain' any and all flows that match 'key'.  If 'out_port' 
+ * is not OFPP_NONE, then matching entries must have that port as an 
+ * argument for an output action.  If 'strict" is set, then wildcards and 
+ * priority must match.  Returns the number of flows that were deleted.
  *
  * Expensive in the general case as currently implemented, since it requires
  * iterating through the entire contents of each table for keys that contain
@@ -134,7 +135,7 @@ chain_modify(struct sw_chain *chain, const struct sw_flow_key *key,
  *
  * Caller must hold dp_mutex. */
 int chain_delete(struct sw_chain *chain, const struct sw_flow_key *key, 
-               uint16_t priority, int strict)
+               uint16_t out_port, uint16_t priority, int strict)
 {
        int count = 0;
        int i;
@@ -142,7 +143,7 @@ int chain_delete(struct sw_chain *chain, const struct sw_flow_key *key,
        might_sleep();
        for (i = 0; i < chain->n_tables; i++) {
                struct sw_table *t = chain->tables[i];
-               count += t->delete(t, key, priority, strict);
+               count += t->delete(t, key, out_port, priority, strict);
        }
 
        return count;
index 26edd47..a78a938 100644 (file)
@@ -27,7 +27,8 @@ struct sw_flow *chain_lookup(struct sw_chain *, const struct sw_flow_key *);
 int chain_insert(struct sw_chain *, struct sw_flow *);
 int chain_modify(struct sw_chain *, const struct sw_flow_key *, 
                uint16_t, int, const struct ofp_action_header *, size_t);
-int chain_delete(struct sw_chain *, const struct sw_flow_key *, uint16_t, int);
+int chain_delete(struct sw_chain *, const struct sw_flow_key *, uint16_t, 
+               uint16_t, int);
 int chain_timeout(struct sw_chain *);
 void chain_destroy(struct sw_chain *);
 
index 326300d..570cdcf 100644 (file)
@@ -1382,8 +1382,8 @@ static int flow_stats_dump(struct datapath *dp, void *state,
        {
                struct sw_table *table = dp->chain->tables[s->table_idx];
 
-               error = table->iterate(table, &match_key, &s->position,
-                                      flow_stats_dump_callback, s);
+               error = table->iterate(table, &match_key, s->rq->out_port, 
+                               &s->position, flow_stats_dump_callback, s);
                if (error)
                        break;
 
@@ -1447,7 +1447,7 @@ static int aggregate_stats_dump(struct datapath *dp, void *state,
                struct sw_table *table = dp->chain->tables[table_idx];
                int error;
 
-               error = table->iterate(table, &match_key, &position,
+               error = table->iterate(table, &match_key, rq->out_port, &position,
                                       aggregate_stats_dump_callback, rpy);
                if (error)
                        return error;
index effd0c8..a8c3368 100644 (file)
@@ -167,6 +167,40 @@ int flow_timeout(struct sw_flow *flow)
 }
 EXPORT_SYMBOL(flow_timeout);
 
+/* Returns nonzero if 'flow' contains an output action to 'out_port' or
+ * has the value OFPP_NONE. 'out_port' is in network-byte order. */
+int flow_has_out_port(struct sw_flow *flow, uint16_t out_port)
+{
+       struct sw_flow_actions *sf_acts;
+       size_t actions_len;
+       uint8_t *p;
+
+       if (out_port == htons(OFPP_NONE))
+               return 1;
+
+       sf_acts = rcu_dereference(flow->sf_acts);
+
+       actions_len = sf_acts->actions_len;
+       p = (uint8_t *)sf_acts->actions;
+
+       while (actions_len > 0) {
+               struct ofp_action_header *ah = (struct ofp_action_header *)p;
+               size_t len = ntohs(ah->len);
+
+               if (ah->type == htons(OFPAT_OUTPUT)) {
+                       struct ofp_action_output *oa = (struct ofp_action_output *)p;
+                       if (oa->port == out_port)
+                               return 1;
+               }
+
+               p += len;
+               actions_len -= len;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(flow_has_out_port);
+
 /* Allocates and returns a new flow with room for 'actions_len' actions, 
  * using allocation flags 'flags'.  Returns the new flow or a null pointer 
  * on failure. */
index f0f8c66..d04d7b9 100644 (file)
@@ -106,6 +106,7 @@ int flow_matches_1wild(const struct sw_flow_key *, const struct sw_flow_key *);
 int flow_matches_2wild(const struct sw_flow_key *, const struct sw_flow_key *);
 int flow_matches_desc(const struct sw_flow_key *, const struct sw_flow_key *, 
                int);
+int flow_has_out_port(struct sw_flow *, uint16_t);
 struct sw_flow *flow_alloc(size_t actions_len, gfp_t flags);
 void flow_free(struct sw_flow *);
 void flow_deferred_free(struct sw_flow *);
index 33b3a5e..d54674a 100644 (file)
@@ -322,13 +322,14 @@ recv_flow(struct sw_chain *chain, const struct sender *sender, const void *msg)
        }  else if (command == OFPFC_DELETE) {
                struct sw_flow_key key;
                flow_extract_match(&key, &ofm->match);
-               return chain_delete(chain, &key, 0, 0) ? 0 : -ESRCH;
+               return chain_delete(chain, &key, ofm->out_port, 0, 0) ? 0 : -ESRCH;
        } else if (command == OFPFC_DELETE_STRICT) {
                struct sw_flow_key key;
                uint16_t priority;
                flow_extract_match(&key, &ofm->match);
                priority = key.wildcards ? ntohs(ofm->priority) : -1;
-               return chain_delete(chain, &key, priority, 1) ? 0 : -ESRCH;
+               return chain_delete(chain, &key, ofm->out_port, 
+                               priority, 1) ? 0 : -ESRCH;
        } else {
                return -ENOTSUPP;
        }
index 86e1920..21a49f9 100644 (file)
@@ -113,8 +113,8 @@ static int do_delete(struct sw_flow **bucket, struct sw_flow *flow)
  * argument, since all exact-match entries are the same (highest)
  * priority. */
 static int table_hash_delete(struct sw_table *swt,
-                                                        const struct sw_flow_key *key
-                                                        uint16_t priority, int strict)
+                                       const struct sw_flow_key *key,  uint16_t out_port
+                                       uint16_t priority, int strict)
 {
        struct sw_table_hash *th = (struct sw_table_hash *) swt;
        unsigned int count = 0;
@@ -122,7 +122,8 @@ static int table_hash_delete(struct sw_table *swt,
        if (key->wildcards == 0) {
                struct sw_flow **bucket = find_bucket(swt, key);
                struct sw_flow *flow = *bucket;
-               if (flow && flow_keys_equal(&flow->key, key))
+               if (flow && flow_keys_equal(&flow->key, key)
+                               && flow_has_out_port(flow, out_port))
                        count = do_delete(bucket, flow);
        } else {
                unsigned int i;
@@ -130,7 +131,8 @@ static int table_hash_delete(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_matches_desc(&flow->key, key, strict))
+                       if (flow && flow_matches_desc(&flow->key, key, strict)
+                                       && flow_has_out_port(flow, out_port))
                                count += do_delete(bucket, flow);
                }
        }
@@ -174,7 +176,7 @@ static void table_hash_destroy(struct sw_table *swt)
 }
 
 static int table_hash_iterate(struct sw_table *swt,
-                             const struct sw_flow_key *key,
+                             const struct sw_flow_key *key, uint16_t out_port,
                              struct sw_table_position *position,
                              int (*callback)(struct sw_flow *, void *private),
                              void *private) 
@@ -189,7 +191,7 @@ static int table_hash_iterate(struct sw_table *swt,
                int error;
 
                flow = table_hash_lookup(swt, key);
-               if (!flow)
+               if (!flow || !flow_has_out_port(flow, out_port))
                        return 0;
 
                error = callback(flow, private);
@@ -201,7 +203,8 @@ static int table_hash_iterate(struct sw_table *swt,
 
                for (i = position->private[0]; i <= th->bucket_mask; i++) {
                        struct sw_flow *flow = th->buckets[i];
-                       if (flow && flow_matches_1wild(&flow->key, key)) {
+                       if (flow && flow_matches_1wild(&flow->key, key)
+                                       && flow_has_out_port(flow, out_port)) {
                                int error = callback(flow, private);
                                if (error) {
                                        position->private[0] = i;
@@ -301,11 +304,13 @@ static int table_hash2_modify(struct sw_table *swt,
 
 static int table_hash2_delete(struct sw_table *swt,
                                                          const struct sw_flow_key *key, 
+                                                         uint16_t out_port,
                                                          uint16_t priority, int strict)
 {
        struct sw_table_hash2 *t2 = (struct sw_table_hash2 *) swt;
-       return (table_hash_delete(t2->subtable[0], key, priority, strict)
-                       + table_hash_delete(t2->subtable[1], key, priority, strict));
+       return (table_hash_delete(t2->subtable[0], key, out_port, priority, strict)
+                       + table_hash_delete(t2->subtable[1], key, out_port, 
+                               priority, strict));
 }
 
 static int table_hash2_timeout(struct datapath *dp, struct sw_table *swt)
@@ -324,7 +329,7 @@ static void table_hash2_destroy(struct sw_table *swt)
 }
 
 static int table_hash2_iterate(struct sw_table *swt,
-                              const struct sw_flow_key *key,
+                              const struct sw_flow_key *key, uint16_t out_port,
                               struct sw_table_position *position,
                               int (*callback)(struct sw_flow *, void *),
                               void *private)
@@ -333,8 +338,8 @@ static int table_hash2_iterate(struct sw_table *swt,
        int i;
 
        for (i = position->private[1]; i < 2; i++) {
-               int error = table_hash_iterate(t2->subtable[i], key, position,
-                                              callback, private);
+               int error = table_hash_iterate(t2->subtable[i], key, out_port, 
+                               position, callback, private);
                if (error) {
                        return error;
                }
index 2a0752a..af760ac 100644 (file)
@@ -99,7 +99,8 @@ static int do_delete(struct sw_table *swt, struct sw_flow *flow)
 }
 
 static int table_linear_delete(struct sw_table *swt,
-                               const struct sw_flow_key *key, uint16_t priority, int strict)
+                               const struct sw_flow_key *key, uint16_t out_port,
+                               uint16_t priority, int strict)
 {
        struct sw_table_linear *tl = (struct sw_table_linear *) swt;
        struct sw_flow *flow;
@@ -107,6 +108,7 @@ static int table_linear_delete(struct sw_table *swt,
 
        list_for_each_entry (flow, &tl->flows, node) {
                if (flow_matches_desc(&flow->key, key, strict)
+                               && flow_has_out_port(flow, out_port)
                                && (!strict || (flow->priority == priority)))
                        count += do_delete(swt, flow);
        }
@@ -147,7 +149,7 @@ static void table_linear_destroy(struct sw_table *swt)
 }
 
 static int table_linear_iterate(struct sw_table *swt,
-                               const struct sw_flow_key *key,
+                               const struct sw_flow_key *key, uint16_t out_port,
                                struct sw_table_position *position,
                                int (*callback)(struct sw_flow *, void *),
                                void *private)
@@ -159,7 +161,8 @@ static int table_linear_iterate(struct sw_table *swt,
        start = position->private[0];
        list_for_each_entry (flow, &tl->iter_flows, iter_node) {
                if (flow->serial >= start
-                   && flow_matches_2wild(key, &flow->key)) {
+                               && flow_matches_2wild(key, &flow->key)
+                               && flow_has_out_port(flow, out_port)) {
                        int error = callback(flow, private);
                        if (error) {
                                position->private[0] = flow->serial;
index 80865e6..27a562c 100644 (file)
@@ -65,10 +65,12 @@ struct sw_table {
                        const struct ofp_action_header *actions, size_t actions_len);
 
        /* Deletes from 'table' any and all flows that match 'key' from
-        * 'table'.  If 'strict' set, wildcards and priority must match.  
-        * Returns the number of flows that were deleted. */
+        * 'table'.  If 'out_port' is not OFPP_NONE, then matching entries
+        * must have that port as an argument for an output action.  If 
+        * 'strict' is set, wildcards and priority must match.  Returns the 
+        * number of flows that were deleted. */
        int (*delete)(struct sw_table *table, const struct sw_flow_key *key, 
-                       uint16_t priority, int strict);
+                       uint16_t out_port, uint16_t priority, int strict);
 
        /* Performs timeout processing on all the flow entries in 'table'.
         * Returns the number of flow entries deleted through expiration. */
@@ -78,10 +80,11 @@ struct sw_table {
        void (*destroy)(struct sw_table *table);
 
        /* Iterates through the flow entries in 'table', passing each one
-        * matches 'key' to 'callback'.  The callback function should return 0
-        * to continue iteration or a nonzero error code to stop.  The iterator
-        * function returns either 0 if the table iteration completed or the
-        * value returned by the callback function otherwise.
+        * matches 'key' and output port 'out_port' to 'callback'.  The 
+        * callback function should return 0 to continue iteration or a 
+        * nonzero error code to stop.  The iterator function returns either 
+        * 0 if the table iteration completed or the value returned by the 
+        * callback function otherwise.
         *
         * The iteration starts at 'position', which may be initialized to
         * all-zero-bits to iterate from the beginning of the table.  If the
@@ -91,7 +94,7 @@ struct sw_table {
         * that caused the error (assuming that that flow entry has not been
         * deleted in the meantime). */
        int (*iterate)(struct sw_table *table,
-                      const struct sw_flow_key *key,
+                      const struct sw_flow_key *key, uint16_t out_port,
                       struct sw_table_position *position,
                       int (*callback)(struct sw_flow *flow, void *private),
                       void *private);
index e7da0d1..1d40995 100644 (file)
@@ -63,7 +63,7 @@
 /* The most significant bit being set in the version field indicates an
  * experimental OpenFlow version.  
  */
-#define OFP_VERSION   0x96
+#define OFP_VERSION   0x97
 
 #define OFP_MAX_TABLE_NAME_LEN 32
 #define OFP_MAX_PORT_NAME_LEN  16
@@ -531,12 +531,17 @@ struct ofp_flow_mod {
     uint16_t priority;            /* Priority level of flow entry. */
     uint32_t buffer_id;           /* Buffered packet to apply to (or -1). 
                                      Not meaningful for OFPFC_DELETE*. */
+    uint16_t out_port;            /* For OFPFC_DELETE* commands, require 
+                                     matching entries to include this as an 
+                                     output port.  A value of OFPP_NONE 
+                                     indicates no restriction. */
+    uint8_t pad[2];               /* Align to 32-bits. */
     uint32_t reserved;            /* Reserved for future use. */
     struct ofp_action_header actions[0]; /* The action length is inferred 
                                             from the length field in the 
                                             header. */
 };
-OFP_ASSERT(sizeof(struct ofp_flow_mod) == 60);
+OFP_ASSERT(sizeof(struct ofp_flow_mod) == 64);
 
 /* Why did this flow expire? */
 enum ofp_flow_expired_reason {
@@ -685,7 +690,10 @@ struct ofp_flow_stats_request {
     struct ofp_match match;   /* Fields to match */
     uint8_t table_id;         /* ID of table to read (from ofp_table_stats)
                                  or 0xff for all tables. */
-    uint8_t pad[3];           /* Align to 32 bits. */
+    uint8_t pad;              /* Align to 32 bits. */
+    uint16_t out_port;        /* Require matching entries to include this 
+                                 as an output port.  A value of OFPP_NONE 
+                                 indicates no restriction. */
 };
 OFP_ASSERT(sizeof(struct ofp_flow_stats_request) == 40);
 
@@ -712,7 +720,10 @@ struct ofp_aggregate_stats_request {
     struct ofp_match match;   /* Fields to match */
     uint8_t table_id;         /* ID of table to read (from ofp_table_stats)
                                  or 0xff for all tables. */
-    uint8_t pad[3];           /* Align to 32 bits. */
+    uint8_t pad;              /* Align to 32 bits. */
+    uint16_t out_port;        /* Require matching entries to include this 
+                                 as an output port.  A value of OFPP_NONE 
+                                 indicates no restriction. */
 };
 OFP_ASSERT(sizeof(struct ofp_aggregate_stats_request) == 40);
 
index d64cd12..8f09c00 100644 (file)
@@ -137,23 +137,24 @@ chain_modify(struct sw_chain *chain, const struct sw_flow_key *key,
     return count;
 }
 
-/* Deletes from 'chain' any and all flows that match 'key'.   If 'strict' set, 
- * wildcards and priority must match.  Returns the number of flows that were 
- * deleted.
+/* Deletes from 'chain' any and all flows that match 'key'.  If 'out_port' 
+ * is not OFPP_NONE, then matching entries must have that port as an 
+ * argument for an output action.  If 'strict" is set, then wildcards and 
+ * priority must match.  Returns the number of flows that were deleted.
  *
  * Expensive in the general case as currently implemented, since it requires
  * iterating through the entire contents of each table for keys that contain
  * wildcards.  Relatively cheap for fully specified keys. */
 int
 chain_delete(struct sw_chain *chain, const struct sw_flow_key *key, 
-             uint16_t priority, int strict)
+             uint16_t out_port, uint16_t priority, int strict)
 {
     int count = 0;
     int i;
 
     for (i = 0; i < chain->n_tables; i++) {
         struct sw_table *t = chain->tables[i];
-        count += t->delete(t, key, priority, strict);
+        count += t->delete(t, key, out_port, priority, strict);
     }
 
     return count;
index 39a9f30..9ffe072 100644 (file)
@@ -59,7 +59,8 @@ struct sw_flow *chain_lookup(struct sw_chain *, const struct sw_flow_key *);
 int chain_insert(struct sw_chain *, struct sw_flow *);
 int chain_modify(struct sw_chain *, const struct sw_flow_key *, 
         uint16_t, int, const struct ofp_action_header *, size_t);
-int chain_delete(struct sw_chain *, const struct sw_flow_key *, uint16_t, int);
+int chain_delete(struct sw_chain *, const struct sw_flow_key *, uint16_t, 
+        uint16_t, int);
 void chain_timeout(struct sw_chain *, struct list *deleted);
 void chain_destroy(struct sw_chain *);
 
index e05365c..452d471 100644 (file)
@@ -1098,13 +1098,14 @@ recv_flow(struct datapath *dp, const struct sender *sender,
     }  else if (command == OFPFC_DELETE) {
         struct sw_flow_key key;
         flow_extract_match(&key, &ofm->match);
-        return chain_delete(dp->chain, &key, 0, 0) ? 0 : -ESRCH;
+        return chain_delete(dp->chain, &key, ofm->out_port, 0, 0) ? 0 : -ESRCH;
     } else if (command == OFPFC_DELETE_STRICT) {
         struct sw_flow_key key;
         uint16_t priority;
         flow_extract_match(&key, &ofm->match);
         priority = key.wildcards ? ntohs(ofm->priority) : -1;
-        return chain_delete(dp->chain, &key, priority, 1) ? 0 : -ESRCH;
+        return chain_delete(dp->chain, &key, ofm->out_port, 
+                priority, 1) ? 0 : -ESRCH;
     } else {
         return -ENODEV;
     }
@@ -1167,8 +1168,8 @@ static int flow_stats_dump(struct datapath *dp, void *state,
     {
         struct sw_table *table = dp->chain->tables[s->table_idx];
 
-        if (table->iterate(table, &match_key, &s->position,
-                           flow_stats_dump_callback, s))
+        if (table->iterate(table, &match_key, s->rq.out_port,
+                    &s->position, flow_stats_dump_callback, s))
             break;
 
         s->table_idx++;
@@ -1227,7 +1228,7 @@ static int aggregate_stats_dump(struct datapath *dp, void *state,
         struct sw_table *table = dp->chain->tables[table_idx];
         int error;
 
-        error = table->iterate(table, &match_key, &position,
+        error = table->iterate(table, &match_key, rq->out_port, &position,
                                aggregate_stats_dump_callback, rpy);
         if (error)
             return error;
index 4ea258b..82eee55 100644 (file)
@@ -262,6 +262,34 @@ bool flow_timeout(struct sw_flow *flow)
     }
 }
 
+/* Returns nonzero if 'flow' contains an output action to 'out_port' or
+ * has the value OFPP_NONE. 'out_port' is in network-byte order. */
+int flow_has_out_port(struct sw_flow *flow, uint16_t out_port)
+{
+    struct sw_flow_actions *sf_acts = flow->sf_acts;
+    size_t actions_len = sf_acts->actions_len;
+    uint8_t *p = (uint8_t *)sf_acts->actions;
+
+    if (out_port == htons(OFPP_NONE))
+        return 1;
+
+    while (actions_len > 0) {
+        struct ofp_action_header *ah = (struct ofp_action_header *)p;
+        size_t len = ntohs(ah->len);
+
+        if (ah->type == htons(OFPAT_OUTPUT)) {
+            struct ofp_action_output *oa = (struct ofp_action_output *)p;
+            if (oa->port == out_port) {
+                return 1;
+            }
+        }
+        p += len;
+        actions_len -= len;
+    }
+
+    return 0;
+}
+
 void flow_used(struct sw_flow *flow, struct ofpbuf *buffer)
 {
     flow->used = time_now();
index 38cfa6e..ef0497c 100644 (file)
@@ -78,6 +78,7 @@ int flow_matches_1wild(const struct sw_flow_key *, const struct sw_flow_key *);
 int flow_matches_2wild(const struct sw_flow_key *, const struct sw_flow_key *);
 int flow_matches_desc(const struct sw_flow_key *, const struct sw_flow_key *, 
                      int);
+int flow_has_out_port(struct sw_flow *flow, uint16_t out_port);
 struct sw_flow *flow_alloc(size_t);
 void flow_free(struct sw_flow *);
 void flow_deferred_free(struct sw_flow *);
index bef7d9e..6b5e945 100644 (file)
@@ -136,6 +136,7 @@ do_delete(struct sw_flow **bucket)
  * priority. */
 static int table_hash_delete(struct sw_table *swt,
                              const struct sw_flow_key *key, 
+                             uint16_t out_port,
                              uint16_t priority, int strict)
 {
     struct sw_table_hash *th = (struct sw_table_hash *) swt;
@@ -144,7 +145,8 @@ static int table_hash_delete(struct sw_table *swt,
     if (key->wildcards == 0) {
         struct sw_flow **bucket = find_bucket(swt, key);
         struct sw_flow *flow = *bucket;
-        if (flow && !flow_compare(&flow->key.flow, &key->flow)) {
+        if (flow && !flow_compare(&flow->key.flow, &key->flow)
+                && flow_has_out_port(flow, out_port)) {
             do_delete(bucket);
             count = 1;
         }
@@ -154,7 +156,8 @@ static int table_hash_delete(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_matches_desc(&flow->key, key, strict)) {
+            if (flow && flow_matches_desc(&flow->key, key, strict)
+                    && flow_has_out_port(flow, out_port)) {
                 do_delete(bucket);
                 count++;
             }
@@ -194,7 +197,7 @@ static void table_hash_destroy(struct sw_table *swt)
 }
 
 static int table_hash_iterate(struct sw_table *swt,
-                              const struct sw_flow_key *key,
+                              const struct sw_flow_key *key, uint16_t out_port,
                               struct sw_table_position *position,
                               int (*callback)(struct sw_flow *, void *private),
                               void *private) 
@@ -207,13 +210,17 @@ static int table_hash_iterate(struct sw_table *swt,
     if (key->wildcards == 0) {
         struct sw_flow *flow = table_hash_lookup(swt, key);
         position->private[0] = -1;
-        return flow ? callback(flow, private) : 0;
+        if (!flow || !flow_has_out_port(flow, out_port)) {
+            return 0;
+        }
+        return callback(flow, private);
     } else {
         int i;
 
         for (i = position->private[0]; i <= th->bucket_mask; i++) {
             struct sw_flow *flow = th->buckets[i];
-            if (flow && flow_matches_1wild(&flow->key, key)) {
+            if (flow && flow_matches_1wild(&flow->key, key)
+                    && flow_has_out_port(flow, out_port)) {
                 int error = callback(flow, private);
                 if (error) {
                     position->private[0] = i + 1;
@@ -316,11 +323,13 @@ static int table_hash2_modify(struct sw_table *swt,
 
 static int table_hash2_delete(struct sw_table *swt,
                               const struct sw_flow_key *key, 
+                              uint16_t out_port,
                               uint16_t priority, int strict)
 {
     struct sw_table_hash2 *t2 = (struct sw_table_hash2 *) swt;
-    return (table_hash_delete(t2->subtable[0], key, priority, strict)
-            + table_hash_delete(t2->subtable[1], key, priority, strict));
+    return (table_hash_delete(t2->subtable[0], key, out_port, priority, strict)
+            + table_hash_delete(t2->subtable[1], key, out_port, priority, 
+                strict));
 }
 
 static void table_hash2_timeout(struct sw_table *swt, struct list *deleted)
@@ -339,7 +348,8 @@ static void table_hash2_destroy(struct sw_table *swt)
 }
 
 static int table_hash2_iterate(struct sw_table *swt,
-                               const struct sw_flow_key *key,
+                               const struct sw_flow_key *key, 
+                               uint16_t out_port,
                                struct sw_table_position *position,
                                int (*callback)(struct sw_flow *, void *),
                                void *private)
@@ -348,8 +358,8 @@ static int table_hash2_iterate(struct sw_table *swt,
     int i;
 
     for (i = position->private[1]; i < 2; i++) {
-        int error = table_hash_iterate(t2->subtable[i], key, position,
-                                       callback, private);
+        int error = table_hash_iterate(t2->subtable[i], key, out_port, 
+                                       position, callback, private);
         if (error) {
             return error;
         }
index 32a0bc2..1ec6951 100644 (file)
@@ -128,6 +128,7 @@ do_delete(struct sw_flow *flow)
 
 static int table_linear_delete(struct sw_table *swt,
                                const struct sw_flow_key *key, 
+                               uint16_t out_port,
                                uint16_t priority, int strict)
 {
     struct sw_table_linear *tl = (struct sw_table_linear *) swt;
@@ -136,6 +137,7 @@ static int table_linear_delete(struct sw_table *swt,
 
     LIST_FOR_EACH_SAFE (flow, n, struct sw_flow, node, &tl->flows) {
         if (flow_matches_desc(&flow->key, key, strict)
+                && flow_has_out_port(flow, out_port)
                 && (!strict || (flow->priority == priority))) {
             do_delete(flow);
             count++;
@@ -175,6 +177,7 @@ static void table_linear_destroy(struct sw_table *swt)
 
 static int table_linear_iterate(struct sw_table *swt,
                                 const struct sw_flow_key *key,
+                                uint16_t out_port,
                                 struct sw_table_position *position,
                                 int (*callback)(struct sw_flow *, void *),
                                 void *private)
@@ -185,7 +188,9 @@ static int table_linear_iterate(struct sw_table *swt,
 
     start = ~position->private[0];
     LIST_FOR_EACH (flow, struct sw_flow, iter_node, &tl->iter_flows) {
-        if (flow->serial <= start && flow_matches_2wild(key, &flow->key)) {
+        if (flow->serial <= start 
+                && flow_matches_2wild(key, &flow->key)
+                && flow_has_out_port(flow, out_port)) {
             int error = callback(flow, private);
             if (error) {
                 position->private[0] = ~(flow->serial - 1);
index b73a8fe..011591c 100644 (file)
@@ -95,10 +95,12 @@ struct sw_table {
             const struct ofp_action_header *actions, size_t actions_len);
 
     /* Deletes from 'table' any and all flows that match 'key' from
-     * 'table'.  If 'strict' set, wildcards must match.  Returns the 
+     * 'table'.  If 'out_port' is not OFPP_NONE, then matching entries
+     * must have that port as an argument for an output action.  If 
+     * 'strict' is set, wildcards and priority must match.  Returns the 
      * number of flows that were deleted. */
     int (*delete)(struct sw_table *table, const struct sw_flow_key *key, 
-                  uint16_t priority, int strict);
+                  uint16_t out_port, uint16_t priority, int strict);
 
     /* Performs timeout processing on all the flow entries in 'table'.
      * Appends all the flow entries removed from 'table' to 'deleted' for the
@@ -109,10 +111,11 @@ struct sw_table {
     void (*destroy)(struct sw_table *table);
 
     /* Iterates through the flow entries in 'table', passing each one
-     * matches 'key' to 'callback'.  The callback function should return 0
-     * to continue iteration or a nonzero error code to stop.  The iterator
-     * function returns either 0 if the table iteration completed or the
-     * value returned by the callback function otherwise.
+     * matches 'key' and output port 'out_port' to 'callback'.  The 
+     * callback function should return 0 to continue iteration or a 
+     * nonzero error code to stop.  The iterator function returns either 
+     * 0 if the table iteration completed or the value returned by the 
+     * callback function otherwise.
      *
      * The iteration starts at 'position', which may be initialized to
      * all-zero-bits to iterate from the beginning of the table.  If the
@@ -121,7 +124,7 @@ struct sw_table {
      * iterator function to resume iteration later with the following
      * flow. */
     int (*iterate)(struct sw_table *table,
-               const struct sw_flow_key *key,
+               const struct sw_flow_key *key, uint16_t out_port,
                struct sw_table_position *position,
                int (*callback)(struct sw_flow *flow, void *private),
                void *private);
index d1f03bb..595148e 100644 (file)
@@ -403,6 +403,14 @@ 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, \fBdump-aggregate\fR, \fBdel-flow\fR 
+and \fBdel-flows\fR commands support the additional optional field:
+
+.TP
+\fBout_port=\fIport\fR
+If set, a matching flow must include an output action to \fIport\fR.
+
 .PP
 The \fBdump-flows\fR and \fBdump-aggregate\fR commands support an
 additional optional field:
index 3384700..8fa0da8 100644 (file)
@@ -760,7 +760,7 @@ parse_field(const char *name, const struct field **f_out)
 static void
 str_to_flow(char *string, struct ofp_match *match, 
             struct ofp_action_header *actions, size_t *actions_len, 
-            uint8_t *table_idx, uint16_t *priority, 
+            uint8_t *table_idx, uint16_t *out_port, uint16_t *priority, 
             uint16_t *idle_timeout, uint16_t *hard_timeout)
 {
 
@@ -770,6 +770,9 @@ str_to_flow(char *string, struct ofp_match *match,
     if (table_idx) {
         *table_idx = 0xff;
     }
+    if (out_port) {
+        *out_port = OFPP_NONE;
+    }
     if (priority) {
         *priority = OFP_DEFAULT_PRIORITY;
     }
@@ -819,6 +822,8 @@ str_to_flow(char *string, struct ofp_match *match,
         
             if (table_idx && !strcmp(name, "table")) {
                 *table_idx = atoi(value);
+            } else if (out_port && !strcmp(name, "out_port")) {
+                *out_port = atoi(value);
             } else if (priority && !strcmp(name, "priority")) {
                 *priority = atoi(value);
             } else if (idle_timeout && !strcmp(name, "idle_timeout")) {
@@ -854,12 +859,14 @@ str_to_flow(char *string, struct ofp_match *match,
 static void do_dump_flows(const struct settings *s, int argc, char *argv[])
 {
     struct ofp_flow_stats_request *req;
+    uint16_t out_port;
     struct ofpbuf *request;
 
     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, NULL);
-    memset(req->pad, 0, sizeof req->pad);
+                &req->table_id, &out_port, NULL, NULL, NULL);
+    memset(&req->pad, 0, sizeof req->pad);
+    req->out_port = htons(out_port);
 
     dump_stats_transaction(argv[1], request);
 }
@@ -869,11 +876,13 @@ static void do_dump_aggregate(const struct settings *s, int argc,
 {
     struct ofp_aggregate_stats_request *req;
     struct ofpbuf *request;
+    uint16_t out_port;
 
     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, NULL);
-    memset(req->pad, 0, sizeof req->pad);
+                &req->table_id, &out_port, NULL, NULL, NULL);
+    memset(&req->pad, 0, sizeof req->pad);
+    req->out_port = htons(out_port);
 
     dump_stats_transaction(argv[1], request);
 }
@@ -943,7 +952,7 @@ static void do_add_flow(const struct settings *s, int argc, char *argv[])
     size = sizeof *ofm + actions_len;
     ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
     str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &actions_len, 
-                NULL, &priority, &idle_timeout, &hard_timeout);
+                NULL, NULL, &priority, &idle_timeout, &hard_timeout);
     ofm->command = htons(OFPFC_ADD);
     ofm->idle_timeout = htons(idle_timeout);
     ofm->hard_timeout = htons(hard_timeout);
@@ -995,7 +1004,7 @@ static void do_add_flows(const struct settings *s, int argc, char *argv[])
         size = sizeof *ofm + actions_len;
         ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
         str_to_flow(line, &ofm->match, &ofm->actions[0], &actions_len, 
-                    NULL, &priority, &idle_timeout, &hard_timeout);
+                    NULL, NULL, &priority, &idle_timeout, &hard_timeout);
         ofm->command = htons(OFPFC_ADD);
         ofm->idle_timeout = htons(idle_timeout);
         ofm->hard_timeout = htons(hard_timeout);
@@ -1025,7 +1034,7 @@ static void do_mod_flows(const struct settings *s, int argc, char *argv[])
     size = sizeof *ofm + actions_len;
     ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
     str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &actions_len, 
-                NULL, &priority, &idle_timeout, &hard_timeout);
+                NULL, NULL, &priority, &idle_timeout, &hard_timeout);
     if (s->strict) {
         ofm->command = htons(OFPFC_MODIFY_STRICT);
     } else {
@@ -1049,6 +1058,7 @@ static void do_del_flows(const struct settings *s, int argc, char *argv[])
 {
     struct vconn *vconn;
     uint16_t priority;
+    uint16_t out_port;
     struct ofpbuf *buffer;
     struct ofp_flow_mod *ofm;
     size_t size;
@@ -1057,7 +1067,7 @@ static void do_del_flows(const struct settings *s, 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, NULL);
+                &out_port, &priority, NULL, NULL);
     if (s->strict) {
         ofm->command = htons(OFPFC_DELETE_STRICT);
     } else {
@@ -1066,6 +1076,7 @@ static void do_del_flows(const struct settings *s, int argc, char *argv[])
     ofm->idle_timeout = htons(0);
     ofm->hard_timeout = htons(0);
     ofm->buffer_id = htonl(UINT32_MAX);
+    ofm->out_port = htons(out_port);
     ofm->priority = htons(priority);
     ofm->reserved = htonl(0);