Added OFPFC_MODIFY_STRICT flow mod command.
authorJustin Pettit <jpettit@nicira.com>
Tue, 16 Sep 2008 23:07:25 +0000 (16:07 -0700)
committerJustin Pettit <jpettit@nicira.com>
Tue, 16 Sep 2008 23:07:41 +0000 (16:07 -0700)
This cleans up the handling of modifying existing flows.  There now exists
OFPFC_MODIFY and OFPFC_MODIFY_STRICT commands, which have the same matching
characteristics to their OFPFC_DELETE* counterparts.  When adding a flow that
already exists, the counters and timers are now reset (ie, its treated as a
new flow).  This commit also adds the "--strict" option to dpctl to allow
strict matching for "mod-flows" and "del-flows".

21 files changed:
datapath/chain.c
datapath/chain.h
datapath/flow.c
datapath/flow.h
datapath/forward.c
datapath/hwtable_dummy/hwtable_dummy.c
datapath/table-hash.c
datapath/table-linear.c
datapath/table.h
include/openflow.h
lib/ofp-print.c
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
utilities/dpctl.c

index 36749f306cfacbe36609c638abef3b7bff825d21..865f908d7d00cf468cc52861b5427b2e3049d9bb 100644 (file)
@@ -101,14 +101,15 @@ int chain_insert(struct sw_chain *chain, struct sw_flow *flow)
        return -ENOBUFS;
 }
 
        return -ENOBUFS;
 }
 
-/* Modifies actions in 'chain' that match 'key'.  Returns the number of 
- * flows that were modified.
+/* Modifies actions in 'chain' that match 'key'.  If 'strict' set, wildcards 
+ * and priority must match.  Returns the number of flows that were modified.
  *
  * 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_modify(struct sw_chain *chain, const struct sw_flow_key *key, 
  *
  * 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_modify(struct sw_chain *chain, const struct sw_flow_key *key, 
+               uint16_t priority, int strict,
                const struct ofp_action *actions, int n_actions)
 {
        int count = 0;
                const struct ofp_action *actions, int n_actions)
 {
        int count = 0;
@@ -116,14 +117,15 @@ chain_modify(struct sw_chain *chain, const struct sw_flow_key *key,
 
        for (i = 0; i < chain->n_tables; i++) {
                struct sw_table *t = chain->tables[i];
 
        for (i = 0; i < chain->n_tables; i++) {
                struct sw_table *t = chain->tables[i];
-               count += t->modify(t, key, actions, n_actions);
+               count += t->modify(t, key, priority, strict, actions, n_actions);
        }
 
        return count;
 }
 
        }
 
        return count;
 }
 
-/* Deletes from 'chain' any and all flows that match 'key'.  Returns the number
- * of flows that were deleted.
+/* 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.
  *
  * Expensive in the general case as currently implemented, since it requires
  * iterating through the entire contents of each table for keys that contain
  *
  * Expensive in the general case as currently implemented, since it requires
  * iterating through the entire contents of each table for keys that contain
index dbbae416ac88e3cece7cb77a9494e713a950a15f..a7d044eee612273fc338f146b20a7b05bd36d75f 100644 (file)
@@ -26,7 +26,7 @@ struct sw_chain *chain_create(struct datapath *);
 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 *, 
 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 *, 
-               const struct ofp_action *, int);
+               uint16_t, int, const struct ofp_action *, int);
 int chain_delete(struct sw_chain *, const struct sw_flow_key *, uint16_t, int);
 int chain_timeout(struct sw_chain *);
 void chain_destroy(struct sw_chain *);
 int chain_delete(struct sw_chain *, const struct sw_flow_key *, uint16_t, int);
 int chain_timeout(struct sw_chain *);
 void chain_destroy(struct sw_chain *);
index 2093dbba276d90eb3c69ffab312cf115f64a6d84..ae2f78792d8bc6a0b3c4c90b481d9a0873ea344b 100644 (file)
@@ -66,17 +66,18 @@ int flow_matches_2wild(const struct sw_flow_key *a,
 EXPORT_SYMBOL(flow_matches_2wild);
 
 /* Returns nonzero if 't' (the table entry's key) and 'd' (the key
 EXPORT_SYMBOL(flow_matches_2wild);
 
 /* Returns nonzero if 't' (the table entry's key) and 'd' (the key
- * describing the deletion) match, that is, if their fields are
+ * describing the match) match, that is, if their fields are
  * equal modulo wildcards, zero otherwise.  If 'strict' is nonzero, the
  * wildcards must match in both 't_key' and 'd_key'.  Note that the
  * table's wildcards are ignored unless 'strict' is set. */
  * equal modulo wildcards, zero otherwise.  If 'strict' is nonzero, the
  * wildcards must match in both 't_key' and 'd_key'.  Note that the
  * table's wildcards are ignored unless 'strict' is set. */
-int flow_del_matches(const struct sw_flow_key *t, const struct sw_flow_key *d, int strict)
+int flow_matches_desc(const struct sw_flow_key *t, const struct sw_flow_key *d, 
+               int strict)
 {
        if (strict && d->wildcards != t->wildcards)
                return 0;
        return flow_matches_1wild(t, d);
 }
 {
        if (strict && d->wildcards != t->wildcards)
                return 0;
        return flow_matches_1wild(t, d);
 }
-EXPORT_SYMBOL(flow_del_matches);
+EXPORT_SYMBOL(flow_matches_desc);
 
 static uint32_t make_nw_mask(int n_wild_bits)
 {
 
 static uint32_t make_nw_mask(int n_wild_bits)
 {
index 3b25ad5d63f3b7b3efc6ee7e7470c1b242db8964..8c0c27feed228374b5fb18bf551749b5ea3b53cf 100644 (file)
@@ -99,7 +99,7 @@ struct sw_flow {
 
 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_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_del_matches(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);
 struct sw_flow *flow_alloc(int n_actions, gfp_t flags);
 void flow_free(struct sw_flow *);
                int);
 struct sw_flow *flow_alloc(int n_actions, gfp_t flags);
 void flow_free(struct sw_flow *);
index 7d7757167d7c4cd525491fdf79b1dfdb9df035d8..b63dfefb079169e78a37b6518a12983fe934b1ab 100644 (file)
@@ -495,6 +495,8 @@ mod_flow(struct sw_chain *chain, const struct ofp_flow_mod *ofm)
        int i;
        int n_actions;
        struct sw_flow_key key;
        int i;
        int n_actions;
        struct sw_flow_key key;
+       uint16_t priority;
+       int strict;
 
        /* To prevent loops, make sure there's no action to send to the
         * OFP_TABLE virtual port.
 
        /* To prevent loops, make sure there's no action to send to the
         * OFP_TABLE virtual port.
@@ -514,7 +516,9 @@ mod_flow(struct sw_chain *chain, const struct ofp_flow_mod *ofm)
        }
 
        flow_extract_match(&key, &ofm->match);
        }
 
        flow_extract_match(&key, &ofm->match);
-       chain_modify(chain, &key, ofm->actions, n_actions);
+       priority = key.wildcards ? ntohs(ofm->priority) : -1;
+       strict = (ofm->command == htons(OFPFC_MODIFY_STRICT)) ? 1 : 0;
+       chain_modify(chain, &key, priority, strict, ofm->actions, n_actions);
 
        if (ntohl(ofm->buffer_id) != (uint32_t) -1) {
                struct sk_buff *skb = retrieve_skb(ntohl(ofm->buffer_id));
 
        if (ntohl(ofm->buffer_id) != (uint32_t) -1) {
                struct sk_buff *skb = retrieve_skb(ntohl(ofm->buffer_id));
@@ -543,7 +547,7 @@ recv_flow(struct sw_chain *chain, const struct sender *sender, const void *msg)
 
        if (command == OFPFC_ADD) {
                return add_flow(chain, ofm);
 
        if (command == OFPFC_ADD) {
                return add_flow(chain, ofm);
-       } else if (command == OFPFC_MODIFY) {
+       } else if ((command == OFPFC_MODIFY) || (command == OFPFC_MODIFY_STRICT)) {
                return mod_flow(chain, ofm);
        }  else if (command == OFPFC_DELETE) {
                struct sw_flow_key key;
                return mod_flow(chain, ofm);
        }  else if (command == OFPFC_DELETE) {
                struct sw_flow_key key;
index b78d364315efe07eb14cef5f31155c23572cdd4b..1edb83af4898a8cca9f5092ea01c8f79382c3048 100644 (file)
@@ -98,7 +98,7 @@ static int table_dummy_insert(struct sw_table *swt, struct sw_flow *flow)
 }
 
 static int table_dummy_modify(struct sw_table *swt, 
 }
 
 static int table_dummy_modify(struct sw_table *swt, 
-               const struct sw_flow_key *key,
+               const struct sw_flow_key *key, uint16_t priority, int strict,
                const struct ofp_action *actions, int n_actions)
 {
        struct sw_table_dummy *td = (struct sw_table_dummy *) swt;
                const struct ofp_action *actions, int n_actions)
 {
        struct sw_table_dummy *td = (struct sw_table_dummy *) swt;
@@ -106,7 +106,8 @@ static int table_dummy_modify(struct sw_table *swt,
        unsigned int count = 0;
 
        list_for_each_entry (flow, &td->flows, node) {
        unsigned int count = 0;
 
        list_for_each_entry (flow, &td->flows, node) {
-               if (flow_matches_1wild(&flow->key, key)) {
+               if (flow_matches_desc(&flow->key, key, strict)
+                               && (!strict || (flow->priority == priority))) {
                        flow_replace_acts(flow, actions, n_actions);
                        /* xxx Do whatever is necessary to modify the entry in hardware */
                        count++;
                        flow_replace_acts(flow, actions, n_actions);
                        /* xxx Do whatever is necessary to modify the entry in hardware */
                        count++;
@@ -135,7 +136,7 @@ static int table_dummy_delete(struct sw_table *swt,
        unsigned int count = 0;
 
        list_for_each_entry (flow, &td->flows, node) {
        unsigned int count = 0;
 
        list_for_each_entry (flow, &td->flows, node) {
-               if (flow_del_matches(&flow->key, key, strict)
+               if (flow_matches_desc(&flow->key, key, strict)
                    && (!strict || (flow->priority == priority)))
                        count += do_delete(swt, flow);
        }
                    && (!strict || (flow->priority == priority)))
                        count += do_delete(swt, flow);
        }
index f6a41de5c5424070b6c91952585434f92f4778fc..29d205943acb68955e4a500f3017f348c43a5ed8 100644 (file)
@@ -60,11 +60,6 @@ static int table_hash_insert(struct sw_table *swt, struct sw_flow *flow)
        } else {
                struct sw_flow *old_flow = *bucket;
                if (flow_keys_equal(&old_flow->key, &flow->key)) {
        } else {
                struct sw_flow *old_flow = *bucket;
                if (flow_keys_equal(&old_flow->key, &flow->key)) {
-                       /* Keep stats from the original flow */
-                       flow->init_time = old_flow->init_time;
-                       flow->packet_count = old_flow->packet_count;
-                       flow->byte_count = old_flow->byte_count;
-
                        rcu_assign_pointer(*bucket, flow);
                        flow_deferred_free(old_flow);
                        retval = 1;
                        rcu_assign_pointer(*bucket, flow);
                        flow_deferred_free(old_flow);
                        retval = 1;
@@ -76,7 +71,7 @@ static int table_hash_insert(struct sw_table *swt, struct sw_flow *flow)
 }
 
 static int table_hash_modify(struct sw_table *swt, 
 }
 
 static int table_hash_modify(struct sw_table *swt, 
-               const struct sw_flow_key *key, 
+               const struct sw_flow_key *key, uint16_t priority, int strict,
                const struct ofp_action *actions, int n_actions) 
 {
        struct sw_table_hash *th = (struct sw_table_hash *) swt;
                const struct ofp_action *actions, int n_actions) 
 {
        struct sw_table_hash *th = (struct sw_table_hash *) swt;
@@ -85,7 +80,8 @@ static int table_hash_modify(struct sw_table *swt,
        if (key->wildcards == 0) {
                struct sw_flow **bucket = find_bucket(swt, key);
                struct sw_flow *flow = *bucket;
        if (key->wildcards == 0) {
                struct sw_flow **bucket = find_bucket(swt, key);
                struct sw_flow *flow = *bucket;
-               if (flow && flow_matches_1wild(&flow->key, key)) {
+               if (flow && flow_matches_desc(&flow->key, key, strict)
+                               && (!strict || (flow->priority == priority))) {
                        flow_replace_acts(flow, actions, n_actions);
                        count = 1;
                }
                        flow_replace_acts(flow, actions, n_actions);
                        count = 1;
                }
@@ -95,7 +91,8 @@ static int table_hash_modify(struct sw_table *swt,
                for (i = 0; i <= th->bucket_mask; i++) {
                        struct sw_flow **bucket = &th->buckets[i];
                        struct sw_flow *flow = *bucket;
                for (i = 0; i <= th->bucket_mask; i++) {
                        struct sw_flow **bucket = &th->buckets[i];
                        struct sw_flow *flow = *bucket;
-                       if (flow && flow_matches_1wild(&flow->key, key)) {
+                       if (flow && flow_matches_desc(&flow->key, key, strict)
+                                       && (!strict || (flow->priority == priority))) {
                                flow_replace_acts(flow, actions, n_actions);
                                count++;
                        }
                                flow_replace_acts(flow, actions, n_actions);
                                count++;
                        }
@@ -133,7 +130,7 @@ 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;
                for (i = 0; i <= th->bucket_mask; i++) {
                        struct sw_flow **bucket = &th->buckets[i];
                        struct sw_flow *flow = *bucket;
-                       if (flow && flow_del_matches(&flow->key, key, strict))
+                       if (flow && flow_matches_desc(&flow->key, key, strict))
                                count += do_delete(bucket, flow);
                }
        }
                                count += do_delete(bucket, flow);
                }
        }
@@ -291,12 +288,14 @@ static int table_hash2_insert(struct sw_table *swt, struct sw_flow *flow)
 }
 
 static int table_hash2_modify(struct sw_table *swt, 
 }
 
 static int table_hash2_modify(struct sw_table *swt, 
-               const struct sw_flow_key *key,
+               const struct sw_flow_key *key, uint16_t priority, int strict,
                const struct ofp_action *actions, int n_actions)
 {
        struct sw_table_hash2 *t2 = (struct sw_table_hash2 *) swt;
                const struct ofp_action *actions, int n_actions)
 {
        struct sw_table_hash2 *t2 = (struct sw_table_hash2 *) swt;
-       return (table_hash_modify(t2->subtable[0], key, actions, n_actions)
-                       + table_hash_modify(t2->subtable[1], key, actions, n_actions));
+       return (table_hash_modify(t2->subtable[0], key, priority, strict, 
+                                       actions, n_actions)
+                       + table_hash_modify(t2->subtable[1], key, priority, strict, 
+                                       actions, n_actions));
 }
 
 static int table_hash2_delete(struct sw_table *swt,
 }
 
 static int table_hash2_delete(struct sw_table *swt,
index 4a10df550373a2cee35d84734a66404218d74f32..89f5c3c4cb3814f4ae0d96e740f6c5acda65bb73 100644 (file)
@@ -48,11 +48,6 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow)
                if (f->priority == flow->priority
                                && f->key.wildcards == flow->key.wildcards
                                && flow_matches_2wild(&f->key, &flow->key)) {
                if (f->priority == flow->priority
                                && f->key.wildcards == flow->key.wildcards
                                && flow_matches_2wild(&f->key, &flow->key)) {
-                       /* Keep stats from the original flow */
-                       flow->init_time = f->init_time;
-                       flow->packet_count = f->packet_count;
-                       flow->byte_count = f->byte_count;
-
                        flow->serial = f->serial;
                        list_replace_rcu(&f->node, &flow->node);
                        list_replace_rcu(&f->iter_node, &flow->iter_node);
                        flow->serial = f->serial;
                        list_replace_rcu(&f->node, &flow->node);
                        list_replace_rcu(&f->iter_node, &flow->iter_node);
@@ -78,7 +73,7 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow)
 }
 
 static int table_linear_modify(struct sw_table *swt,
 }
 
 static int table_linear_modify(struct sw_table *swt,
-                               const struct sw_flow_key *key,
+                               const struct sw_flow_key *key, uint16_t priority, int strict,
                                const struct ofp_action *actions, int n_actions)
 {
        struct sw_table_linear *tl = (struct sw_table_linear *) swt;
                                const struct ofp_action *actions, int n_actions)
 {
        struct sw_table_linear *tl = (struct sw_table_linear *) swt;
@@ -86,7 +81,8 @@ static int table_linear_modify(struct sw_table *swt,
        unsigned int count = 0;
 
        list_for_each_entry (flow, &tl->flows, node) {
        unsigned int count = 0;
 
        list_for_each_entry (flow, &tl->flows, node) {
-               if (flow_matches_1wild(&flow->key, key)) {
+               if (flow_matches_desc(&flow->key, key, strict)
+                               && (!strict || (flow->priority == priority))) {
                        flow_replace_acts(flow, actions, n_actions);
                        count++;
                }
                        flow_replace_acts(flow, actions, n_actions);
                        count++;
                }
@@ -110,7 +106,7 @@ static int table_linear_delete(struct sw_table *swt,
        unsigned int count = 0;
 
        list_for_each_entry (flow, &tl->flows, node) {
        unsigned int count = 0;
 
        list_for_each_entry (flow, &tl->flows, node) {
-               if (flow_del_matches(&flow->key, key, strict)
+               if (flow_matches_desc(&flow->key, key, strict)
                                && (!strict || (flow->priority == priority)))
                        count += do_delete(swt, flow);
        }
                                && (!strict || (flow->priority == priority)))
                        count += do_delete(swt, flow);
        }
index 8cbcfb7a6d0880d7d4013748923e88b47e04cd20..3dda2769bd31b04359b1348d8abfd3364a654bb6 100644 (file)
@@ -55,9 +55,11 @@ struct sw_table {
         * retained by the caller. */
        int (*insert)(struct sw_table *table, struct sw_flow *flow);
 
         * retained by the caller. */
        int (*insert)(struct sw_table *table, struct sw_flow *flow);
 
-       /* Modifies the actions in 'table' that match 'key'.  Returns the
-        * number of flows that were modified. */
+       /* Modifies the actions in 'table' that match 'key'.  If 'strict'
+        * set, wildcards and priority must match.  Returns the number of flows 
+        * that were modified. */
        int (*modify)(struct sw_table *table, const struct sw_flow_key *key,
        int (*modify)(struct sw_table *table, const struct sw_flow_key *key,
+                       uint16_t priority, int strict,
                        const struct ofp_action *actions, int n_actions);
 
        /* Deletes from 'table' any and all flows that match 'key' from
                        const struct ofp_action *actions, int n_actions);
 
        /* Deletes from 'table' any and all flows that match 'key' from
index e04cfbedb8f33af53052e48911cfe728969d9785..528efeb89fe791c63341dabd784def53cfa66d52 100644 (file)
@@ -353,6 +353,7 @@ OFP_ASSERT(sizeof(struct ofp_packet_out) == 16);
 enum ofp_flow_mod_command {
     OFPFC_ADD,              /* New flow. */
     OFPFC_MODIFY,           /* Modify all matching flows. */
 enum ofp_flow_mod_command {
     OFPFC_ADD,              /* New flow. */
     OFPFC_MODIFY,           /* Modify all matching flows. */
+    OFPFC_MODIFY_STRICT,    /* Modify entry strictly matching wildcards */
     OFPFC_DELETE,           /* Delete all matching flows. */
     OFPFC_DELETE_STRICT     /* Strictly match wildcards and priority. */
 };
     OFPFC_DELETE,           /* Delete all matching flows. */
     OFPFC_DELETE_STRICT     /* Strictly match wildcards and priority. */
 };
index bbe4d49b3b7cbfa4f9754df84bfe18aaed45a4fa..65c7d07219844d48a57ea842d52cba422d9ce220 100644 (file)
@@ -565,6 +565,9 @@ ofp_print_flow_mod(struct ds *string, const void *oh, size_t len,
     case OFPFC_MODIFY:
         ds_put_cstr(string, " MOD: ");
         break;
     case OFPFC_MODIFY:
         ds_put_cstr(string, " MOD: ");
         break;
+    case OFPFC_MODIFY_STRICT:
+        ds_put_cstr(string, " MOD_STRICT: ");
+        break;
     case OFPFC_DELETE:
         ds_put_cstr(string, " DEL: ");
         break;
     case OFPFC_DELETE:
         ds_put_cstr(string, " DEL: ");
         break;
index 0388951da8ff9ff9dd0f7ec180470b1582562435..c591cdbfc2e7c238436340c237f107a0db548fc1 100644 (file)
@@ -114,14 +114,15 @@ chain_insert(struct sw_chain *chain, struct sw_flow *flow)
     return -ENOBUFS;
 }
 
     return -ENOBUFS;
 }
 
-/* Modifies actions in 'chain' that match 'key'.  Returns the number of 
- * flows that were modified.
+/* Modifies actions in 'chain' that match 'key'.  If 'strict' set, wildcards 
+ * and priority must match.  Returns the number of flows that were modified.
  *
  * 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_modify(struct sw_chain *chain, const struct sw_flow_key *key,
  *
  * 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_modify(struct sw_chain *chain, const struct sw_flow_key *key,
+        uint16_t priority, int strict,
         const struct ofp_action *actions, int n_actions)
 {
     int count = 0;
         const struct ofp_action *actions, int n_actions)
 {
     int count = 0;
@@ -129,14 +130,15 @@ chain_modify(struct sw_chain *chain, const struct sw_flow_key *key,
 
     for (i = 0; i < chain->n_tables; i++) {
         struct sw_table *t = chain->tables[i];
 
     for (i = 0; i < chain->n_tables; i++) {
         struct sw_table *t = chain->tables[i];
-        count += t->modify(t, key, actions, n_actions);
+        count += t->modify(t, key, priority, strict, actions, n_actions);
     }
 
     return count;
 }
 
     }
 
     return count;
 }
 
-/* Deletes from 'chain' any and all flows that match 'key'.  Returns the number
- * of flows that were deleted.
+/* 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.
  *
  * Expensive in the general case as currently implemented, since it requires
  * iterating through the entire contents of each table for keys that contain
  *
  * Expensive in the general case as currently implemented, since it requires
  * iterating through the entire contents of each table for keys that contain
index eaccad41f0eeb35b2ff35eafc765362ba242c1f5..9ad54d0eee779f685fcf0cc14f100d7e4e146036 100644 (file)
@@ -57,7 +57,7 @@ struct sw_chain *chain_create(void);
 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 *, 
 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 *, 
-        const struct ofp_action *, int);
+        uint16_t, int, const struct ofp_action *, int);
 int chain_delete(struct sw_chain *, const struct sw_flow_key *, uint16_t, int);
 void chain_timeout(struct sw_chain *, struct list *deleted);
 void chain_destroy(struct sw_chain *);
 int chain_delete(struct sw_chain *, const struct sw_flow_key *, uint16_t, int);
 void chain_timeout(struct sw_chain *, struct list *deleted);
 void chain_destroy(struct sw_chain *);
index e5a2440f486871d68b7a4afa293876f3af9ad451..032b7ec7afbe00bfcb2a076351a06fded31b52f6 100644 (file)
@@ -1224,6 +1224,8 @@ mod_flow(struct datapath *dp, const struct ofp_flow_mod *ofm)
     int n_actions;
     int i;
     struct sw_flow_key key;
     int n_actions;
     int i;
     struct sw_flow_key key;
+    uint16_t priority;
+    int strict;
 
 
     /* To prevent loops, make sure there's no action to send to the
 
 
     /* To prevent loops, make sure there's no action to send to the
@@ -1244,7 +1246,9 @@ mod_flow(struct datapath *dp, const struct ofp_flow_mod *ofm)
     }
 
     flow_extract_match(&key, &ofm->match);
     }
 
     flow_extract_match(&key, &ofm->match);
-    chain_modify(dp->chain, &key, ofm->actions, n_actions);
+    priority = key.wildcards ? ntohs(ofm->priority) : -1;
+    strict = (ofm->command == htons(OFPFC_MODIFY_STRICT)) ? 1 : 0;
+    chain_modify(dp->chain, &key, priority, strict, ofm->actions, n_actions);
 
     if (ntohl(ofm->buffer_id) != UINT32_MAX) {
         struct ofpbuf *buffer = retrieve_buffer(ntohl(ofm->buffer_id));
 
     if (ntohl(ofm->buffer_id) != UINT32_MAX) {
         struct ofpbuf *buffer = retrieve_buffer(ntohl(ofm->buffer_id));
@@ -1275,7 +1279,7 @@ recv_flow(struct datapath *dp, const struct sender *sender UNUSED,
 
     if (command == OFPFC_ADD) {
         return add_flow(dp, ofm);
 
     if (command == OFPFC_ADD) {
         return add_flow(dp, ofm);
-    } else if (command == OFPFC_MODIFY) {
+    } else if ((command == OFPFC_MODIFY) || (command == OFPFC_MODIFY_STRICT)) {
         return mod_flow(dp, ofm);
     }  else if (command == OFPFC_DELETE) {
         struct sw_flow_key key;
         return mod_flow(dp, ofm);
     }  else if (command == OFPFC_DELETE) {
         struct sw_flow_key key;
index e0410afc445323db4adee3d98dcb1e4058189d68..a42d00fb32e60da9cd9b335b942423724723da87 100644 (file)
@@ -85,12 +85,13 @@ flow_matches_2wild(const struct sw_flow_key *a, const struct sw_flow_key *b)
 }
 
 /* Returns nonzero if 't' (the table entry's key) and 'd' (the key 
 }
 
 /* Returns nonzero if 't' (the table entry's key) and 'd' (the key 
- * describing the deletion) match, that is, if their fields are 
+ * describing the match) match, that is, if their fields are 
  * equal modulo wildcards, zero otherwise.  If 'strict' is nonzero, the
  * wildcards must match in both 't_key' and 'd_key'.  Note that the
  * table's wildcards are ignored unless 'strict' is set. */
 int
  * equal modulo wildcards, zero otherwise.  If 'strict' is nonzero, the
  * wildcards must match in both 't_key' and 'd_key'.  Note that the
  * table's wildcards are ignored unless 'strict' is set. */
 int
-flow_del_matches(const struct sw_flow_key *t, const struct sw_flow_key *d, int strict)
+flow_matches_desc(const struct sw_flow_key *t, const struct sw_flow_key *d, 
+        int strict)
 {
     if (strict && d->wildcards != t->wildcards) {
         return 0;
 {
     if (strict && d->wildcards != t->wildcards) {
         return 0;
index 16066cbf349db7de66d4d3a949380daf973ff27e..817998c7f7017cce0391214ce4df6a1944f71dd1 100644 (file)
@@ -76,7 +76,7 @@ struct sw_flow {
 
 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_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_del_matches(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);
 struct sw_flow *flow_alloc(int n_actions);
 void flow_free(struct sw_flow *);
                      int);
 struct sw_flow *flow_alloc(int n_actions);
 void flow_free(struct sw_flow *);
index 34cba7ad564e69f05ada0bf8cde9a8e495b5230f..0960082b3cc2cfb8642247925cb75349412df1e9 100644 (file)
@@ -82,12 +82,6 @@ static int table_hash_insert(struct sw_table *swt, struct sw_flow *flow)
     } else {
         struct sw_flow *old_flow = *bucket;
         if (!flow_compare(&old_flow->key.flow, &flow->key.flow)) {
     } else {
         struct sw_flow *old_flow = *bucket;
         if (!flow_compare(&old_flow->key.flow, &flow->key.flow)) {
-            /* Keep stats from the original flow */
-            flow->used = old_flow->used;
-            flow->created = old_flow->created;
-            flow->packet_count = old_flow->packet_count;
-            flow->byte_count = old_flow->byte_count;
-
             *bucket = flow;
             flow_free(old_flow);
             retval = 1;
             *bucket = flow;
             flow_free(old_flow);
             retval = 1;
@@ -99,7 +93,7 @@ static int table_hash_insert(struct sw_table *swt, struct sw_flow *flow)
 }
 
 static int table_hash_modify(struct sw_table *swt, 
 }
 
 static int table_hash_modify(struct sw_table *swt, 
-        const struct sw_flow_key *key,
+        const struct sw_flow_key *key, uint16_t priority, int strict,
         const struct ofp_action *actions, int n_actions) 
 {
     struct sw_table_hash *th = (struct sw_table_hash *) swt;
         const struct ofp_action *actions, int n_actions) 
 {
     struct sw_table_hash *th = (struct sw_table_hash *) swt;
@@ -108,7 +102,8 @@ static int table_hash_modify(struct sw_table *swt,
     if (key->wildcards == 0) {
         struct sw_flow **bucket = find_bucket(swt, key);
         struct sw_flow *flow = *bucket;
     if (key->wildcards == 0) {
         struct sw_flow **bucket = find_bucket(swt, key);
         struct sw_flow *flow = *bucket;
-        if (flow && flow_matches_1wild(&flow->key, key)) {
+        if (flow && flow_matches_desc(&flow->key, key, strict)
+                && (!strict || (flow->priority == priority))) {
             flow_replace_acts(flow, actions, n_actions);
             count = 1;
         }
             flow_replace_acts(flow, actions, n_actions);
             count = 1;
         }
@@ -118,7 +113,8 @@ static int table_hash_modify(struct sw_table *swt,
         for (i = 0; i <= th->bucket_mask; i++) {
             struct sw_flow **bucket = &th->buckets[i];
             struct sw_flow *flow = *bucket;
         for (i = 0; i <= th->bucket_mask; i++) {
             struct sw_flow **bucket = &th->buckets[i];
             struct sw_flow *flow = *bucket;
-            if (flow && flow_matches_1wild(&flow->key, key)) {
+            if (flow && flow_matches_desc(&flow->key, key, strict)
+                    && (!strict || (flow->priority == priority))) {
                 flow_replace_acts(flow, actions, n_actions);
                 count++;
             }
                 flow_replace_acts(flow, actions, n_actions);
                 count++;
             }
@@ -158,7 +154,7 @@ 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;
         for (i = 0; i <= th->bucket_mask; i++) {
             struct sw_flow **bucket = &th->buckets[i];
             struct sw_flow *flow = *bucket;
-            if (flow && flow_del_matches(&flow->key, key, strict)) {
+            if (flow && flow_matches_desc(&flow->key, key, strict)) {
                 do_delete(bucket);
                 count++;
             }
                 do_delete(bucket);
                 count++;
             }
@@ -307,12 +303,14 @@ static int table_hash2_insert(struct sw_table *swt, struct sw_flow *flow)
 }
 
 static int table_hash2_modify(struct sw_table *swt, 
 }
 
 static int table_hash2_modify(struct sw_table *swt, 
-        const struct sw_flow_key *key,
+        const struct sw_flow_key *key, uint16_t priority, int strict,
         const struct ofp_action *actions, int n_actions) 
 {
     struct sw_table_hash2 *t2 = (struct sw_table_hash2 *) swt;
         const struct ofp_action *actions, int n_actions) 
 {
     struct sw_table_hash2 *t2 = (struct sw_table_hash2 *) swt;
-    return (table_hash_modify(t2->subtable[0], key, actions, n_actions)
-            + table_hash_modify(t2->subtable[1], key, actions, n_actions));
+    return (table_hash_modify(t2->subtable[0], key, priority, strict,
+                    actions, n_actions)
+            + table_hash_modify(t2->subtable[1], key, priority, strict,
+                    actions, n_actions));
 }
 
 static int table_hash2_delete(struct sw_table *swt,
 }
 
 static int table_hash2_delete(struct sw_table *swt,
index d1a040389a375d61cc1f6dc9ab18b4228366227f..9a7a06cc855cafb69c36f9c1c9ce67b2259f478f 100644 (file)
@@ -75,12 +75,6 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow)
         if (f->priority == flow->priority
                 && f->key.wildcards == flow->key.wildcards
                 && flow_matches_2wild(&f->key, &flow->key)) {
         if (f->priority == flow->priority
                 && f->key.wildcards == flow->key.wildcards
                 && flow_matches_2wild(&f->key, &flow->key)) {
-            /* Keep stats from the original flow */
-            flow->used = f->used;
-            flow->created = f->created;
-            flow->packet_count = f->packet_count;
-            flow->byte_count = f->byte_count;
-
             flow->serial = f->serial;
             list_replace(&flow->node, &f->node);
             list_replace(&flow->iter_node, &f->iter_node);
             flow->serial = f->serial;
             list_replace(&flow->node, &f->node);
             list_replace(&flow->iter_node, &f->iter_node);
@@ -107,7 +101,7 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow)
 }
 
 static int table_linear_modify(struct sw_table *swt,
 }
 
 static int table_linear_modify(struct sw_table *swt,
-                const struct sw_flow_key *key,
+                const struct sw_flow_key *key, uint16_t priority, int strict,
                 const struct ofp_action *actions, int n_actions)
 {
     struct sw_table_linear *tl = (struct sw_table_linear *) swt;
                 const struct ofp_action *actions, int n_actions)
 {
     struct sw_table_linear *tl = (struct sw_table_linear *) swt;
@@ -115,7 +109,8 @@ static int table_linear_modify(struct sw_table *swt,
     unsigned int count = 0;
 
     LIST_FOR_EACH (flow, struct sw_flow, node, &tl->flows) {
     unsigned int count = 0;
 
     LIST_FOR_EACH (flow, struct sw_flow, node, &tl->flows) {
-        if (flow_matches_1wild(&flow->key, key)) {
+        if (flow_matches_desc(&flow->key, key, strict)
+                && (!strict || (flow->priority == priority))) {
             flow_replace_acts(flow, actions, n_actions);
             count++;
         }
             flow_replace_acts(flow, actions, n_actions);
             count++;
         }
@@ -140,7 +135,7 @@ static int table_linear_delete(struct sw_table *swt,
     unsigned int count = 0;
 
     LIST_FOR_EACH_SAFE (flow, n, struct sw_flow, node, &tl->flows) {
     unsigned int count = 0;
 
     LIST_FOR_EACH_SAFE (flow, n, struct sw_flow, node, &tl->flows) {
-        if (flow_del_matches(&flow->key, key, strict)
+        if (flow_matches_desc(&flow->key, key, strict)
                 && (!strict || (flow->priority == priority))) {
             do_delete(flow);
             count++;
                 && (!strict || (flow->priority == priority))) {
             do_delete(flow);
             count++;
index 9a48c4b5d1705d56e997be132e2027aab05077a5..aacaa63fd280ee118c876e5cdac2e0eb5b5aeb4c 100644 (file)
@@ -84,9 +84,11 @@ struct sw_table {
      * retained by the caller. */
     int (*insert)(struct sw_table *table, struct sw_flow *flow);
 
      * retained by the caller. */
     int (*insert)(struct sw_table *table, struct sw_flow *flow);
 
-    /* Modifies entries in 'table' that match 'key'.  Returns the
-     * number of flows that were modified. */
+    /* Modifies the actions in 'table' that match 'key'.  If 'strict'
+     * set, wildcards and priority must match.  Returns the number of flows 
+     * that were modified. */
     int (*modify)(struct sw_table *table, const struct sw_flow_key *key,
     int (*modify)(struct sw_table *table, const struct sw_flow_key *key,
+            uint16_t priority, int strict,
             const struct ofp_action *actions, int n_actions);
 
     /* Deletes from 'table' any and all flows that match 'key' from
             const struct ofp_action *actions, int n_actions);
 
     /* Deletes from 'table' any and all flows that match 'key' from
index 870ffb907210d0c574dd4ca1ba649fb387f1e0af..014b2b1ba4f5301103634526468c2910b9d4cba3 100644 (file)
@@ -172,15 +172,18 @@ described in \fBFLOW SYNTAX\fR, below.
 .TP
 \fBmod-flows \fIswitch flow\fR
 Modify the actions in entries from the datapath \fIswitch\fR's tables 
 .TP
 \fBmod-flows \fIswitch flow\fR
 Modify the actions in entries from the datapath \fIswitch\fR's tables 
-that match \fIflow\fR.  See \fBFLOW SYNTAX\fR, below, for the syntax 
-of \fIflows\fR.
+that match \fIflow\fR.  When invoked with the \fB--strict\fR option,
+wildcards are not treated as active for matching purposes.  See 
+\fBFLOW SYNTAX\fR, below, for the syntax of \fIflows\fR.
 
 .TP
 \fBdel-flows \fIswitch \fR[\fIflow\fR]
 Deletes entries from the datapath \fIswitch\fR's tables that match
 
 .TP
 \fBdel-flows \fIswitch \fR[\fIflow\fR]
 Deletes entries from the datapath \fIswitch\fR's tables that match
-\fIflow\fR.  If \fIflow\fR is omitted, all flows in the datapath's
-tables are removed.  See \fBFLOW SYNTAX\fR, below, for the syntax of
-\fIflows\fR.
+\fIflow\fR.  When invoked with the \fB--strict\fR option, wildcards are 
+not treated as active for matching purposes.  If \fIflow\fR is 
+omitted and the \fB--strict\fR option is not used, all flows in the 
+datapath's tables are removed.  See \fBFLOW SYNTAX\fR, below, for the 
+syntax of \fIflows\fR.
 
 .TP
 \fBmonitor \fIswitch\fR
 
 .TP
 \fBmonitor \fIswitch\fR
@@ -388,6 +391,10 @@ If this field is not specified, or if \fInumber\fR is given as
 \fB255\fR, statistics are gathered about flows from all tables.
 
 .SH OPTIONS
 \fB255\fR, statistics are gathered about flows from all tables.
 
 .SH OPTIONS
+.TP
+\fB--strict\fR
+Uses strict matching when running flow modification commands.
+
 .TP
 \fB-t\fR, \fB--timeout=\fIsecs\fR
 Limits \fBdpctl\fR runtime to approximately \fIsecs\fR seconds.  If
 .TP
 \fB-t\fR, \fB--timeout=\fIsecs\fR
 Limits \fBdpctl\fR runtime to approximately \fIsecs\fR seconds.  If
index f957b8225f3c8adee01e2ffe49fc1e2412c69638..58e5e56bf15ab49da7a4270cbcfb70e1cbe323e9 100644 (file)
 #define MOD_PORT_CMD_FLOOD   "flood"
 #define MOD_PORT_CMD_NOFLOOD "noflood"
 
 #define MOD_PORT_CMD_FLOOD   "flood"
 #define MOD_PORT_CMD_NOFLOOD "noflood"
 
+
+/* Settings that may be configured by the user. */
+struct settings {
+    bool strict;        /* Use strict matching for flow mod commands */
+};
+
 struct command {
     const char *name;
     int min_args;
     int max_args;
 struct command {
     const char *name;
     int min_args;
     int max_args;
-    void (*handler)(int argc, char *argv[]);
+    void (*handler)(const struct settings *, int argc, char *argv[]);
 };
 
 static struct command all_commands[];
 
 static void usage(void) NO_RETURN;
 };
 
 static struct command all_commands[];
 
 static void usage(void) NO_RETURN;
-static void parse_options(int argc, char *argv[]);
+static void parse_options(int argc, char *argv[], struct settings *);
 
 int main(int argc, char *argv[])
 {
 
 int main(int argc, char *argv[])
 {
+    struct settings s;
     struct command *p;
 
     set_program_name(argv[0]);
     time_init();
     vlog_init();
     struct command *p;
 
     set_program_name(argv[0]);
     time_init();
     vlog_init();
-    parse_options(argc, argv);
+    parse_options(argc, argv, &s);
     signal(SIGPIPE, SIG_IGN);
 
     argc -= optind;
     signal(SIGPIPE, SIG_IGN);
 
     argc -= optind;
@@ -113,7 +120,7 @@ int main(int argc, char *argv[])
                 ofp_fatal(0, "'%s' command takes at most %d arguments",
                           p->name, p->max_args);
             else {
                 ofp_fatal(0, "'%s' command takes at most %d arguments",
                           p->name, p->max_args);
             else {
-                p->handler(argc, argv);
+                p->handler(&s, argc, argv);
                 exit(0);
             }
         }
                 exit(0);
             }
         }
@@ -124,11 +131,15 @@ int main(int argc, char *argv[])
 }
 
 static void
 }
 
 static void
-parse_options(int argc, char *argv[])
+parse_options(int argc, char *argv[], struct settings *s)
 {
 {
+    enum {
+        OPT_STRICT = UCHAR_MAX + 1
+    };
     static struct option long_options[] = {
         {"timeout", required_argument, 0, 't'},
         {"verbose", optional_argument, 0, 'v'},
     static struct option long_options[] = {
         {"timeout", required_argument, 0, 't'},
         {"verbose", optional_argument, 0, 'v'},
+        {"strict", no_argument, 0, OPT_STRICT},
         {"help", no_argument, 0, 'h'},
         {"version", no_argument, 0, 'V'},
         VCONN_SSL_LONG_OPTIONS
         {"help", no_argument, 0, 'h'},
         {"version", no_argument, 0, 'V'},
         VCONN_SSL_LONG_OPTIONS
@@ -136,6 +147,9 @@ parse_options(int argc, char *argv[])
     };
     char *short_options = long_options_to_short_options(long_options);
 
     };
     char *short_options = long_options_to_short_options(long_options);
 
+    /* Set defaults that we can figure out before parsing options. */
+    s->strict = false;
+
     for (;;) {
         unsigned long int timeout;
         int c;
     for (;;) {
         unsigned long int timeout;
         int c;
@@ -167,6 +181,10 @@ parse_options(int argc, char *argv[])
             vlog_set_verbosity(optarg);
             break;
 
             vlog_set_verbosity(optarg);
             break;
 
+        case OPT_STRICT:
+            s->strict = true;
+            break;
+
         VCONN_SSL_OPTION_HANDLERS
 
         case '?':
         VCONN_SSL_OPTION_HANDLERS
 
         case '?':
@@ -215,6 +233,7 @@ usage(void)
            program_name, program_name);
     vconn_usage(true, false, false);
     printf("\nOptions:\n"
            program_name, program_name);
     vconn_usage(true, false, false);
     printf("\nOptions:\n"
+           "  --strict                    use strict match for flow commands\n"
            "  -t, --timeout=SECS          give up after SECS seconds\n"
            "  -v, --verbose=MODULE[:FACILITY[:LEVEL]]  set logging levels\n"
            "  -v, --verbose               set maximum verbosity level\n"
            "  -t, --timeout=SECS          give up after SECS seconds\n"
            "  -v, --verbose=MODULE[:FACILITY[:LEVEL]]  set logging levels\n"
            "  -v, --verbose               set maximum verbosity level\n"
@@ -271,7 +290,7 @@ static void open_nl_vconn(const char *name, bool subscribe, struct dpif *dpif)
     run(dpif_open(atoi(name + 3), subscribe, dpif), "opening datapath");
 }
 
     run(dpif_open(atoi(name + 3), subscribe, dpif), "opening datapath");
 }
 
-static void do_add_dp(int argc UNUSED, char *argv[])
+static void do_add_dp(const struct settings *s, int argc UNUSED, char *argv[])
 {
     struct dpif dp;
     open_nl_vconn(argv[1], false, &dp);
 {
     struct dpif dp;
     open_nl_vconn(argv[1], false, &dp);
@@ -279,7 +298,7 @@ static void do_add_dp(int argc UNUSED, char *argv[])
     dpif_close(&dp);
 }
 
     dpif_close(&dp);
 }
 
-static void do_del_dp(int argc UNUSED, char *argv[])
+static void do_del_dp(const struct settings *s, int argc UNUSED, char *argv[])
 {
     struct dpif dp;
     open_nl_vconn(argv[1], false, &dp);
 {
     struct dpif dp;
     open_nl_vconn(argv[1], false, &dp);
@@ -316,12 +335,14 @@ static int ifup_and_add_port(struct dpif *dpif, const char *netdev)
     return retval ? retval : dpif_add_port(dpif, netdev);
 }
 
     return retval ? retval : dpif_add_port(dpif, netdev);
 }
 
-static void do_add_port(int argc UNUSED, char *argv[])
+static void do_add_port(const struct settings *s, int argc UNUSED, 
+        char *argv[])
 {
     add_del_ports(argc, argv, ifup_and_add_port, "add", "to");
 }
 
 {
     add_del_ports(argc, argv, ifup_and_add_port, "add", "to");
 }
 
-static void do_del_port(int argc UNUSED, char *argv[])
+static void do_del_port(const struct settings *s, int argc UNUSED, 
+        char *argv[])
 {
     add_del_ports(argc, argv, dpif_del_port, "remove", "from");
 }
 {
     add_del_ports(argc, argv, dpif_del_port, "remove", "from");
 }
@@ -414,14 +435,14 @@ dump_trivial_stats_transaction(const char *vconn_name, uint8_t stats_type)
 }
 
 static void
 }
 
 static void
-do_show(int argc UNUSED, char *argv[])
+do_show(const struct settings *s, int argc UNUSED, char *argv[])
 {
     dump_trivial_transaction(argv[1], OFPT_FEATURES_REQUEST);
     dump_trivial_transaction(argv[1], OFPT_GET_CONFIG_REQUEST);
 }
 
 static void
 {
     dump_trivial_transaction(argv[1], OFPT_FEATURES_REQUEST);
     dump_trivial_transaction(argv[1], OFPT_GET_CONFIG_REQUEST);
 }
 
 static void
-do_status(int argc, char *argv[])
+do_status(const struct settings *s, int argc, char *argv[])
 {
     struct nicira_header *request, *reply;
     struct vconn *vconn;
 {
     struct nicira_header *request, *reply;
     struct vconn *vconn;
@@ -452,13 +473,13 @@ do_status(int argc, char *argv[])
 }
 
 static void
 }
 
 static void
-do_dump_desc(int argc, char *argv[])
+do_dump_desc(const struct settings *s, int argc, char *argv[])
 {
     dump_trivial_stats_transaction(argv[1], OFPST_DESC);
 }
 
 static void
 {
     dump_trivial_stats_transaction(argv[1], OFPST_DESC);
 }
 
 static void
-do_dump_tables(int argc, char *argv[])
+do_dump_tables(const struct settings *s, int argc, char *argv[])
 {
     dump_trivial_stats_transaction(argv[1], OFPST_TABLE);
 }
 {
     dump_trivial_stats_transaction(argv[1], OFPST_TABLE);
 }
@@ -767,7 +788,7 @@ str_to_flow(char *string, struct ofp_match *match,
     match->wildcards = htonl(wildcards);
 }
 
     match->wildcards = htonl(wildcards);
 }
 
-static void do_dump_flows(int argc, char *argv[])
+static void do_dump_flows(const struct settings *s, int argc, char *argv[])
 {
     struct ofp_flow_stats_request *req;
     struct ofpbuf *request;
 {
     struct ofp_flow_stats_request *req;
     struct ofpbuf *request;
@@ -780,7 +801,8 @@ static void do_dump_flows(int argc, char *argv[])
     dump_stats_transaction(argv[1], request);
 }
 
     dump_stats_transaction(argv[1], request);
 }
 
-static void do_dump_aggregate(int argc, char *argv[])
+static void do_dump_aggregate(const struct settings *s, int argc, 
+        char *argv[])
 {
     struct ofp_aggregate_stats_request *req;
     struct ofpbuf *request;
 {
     struct ofp_aggregate_stats_request *req;
     struct ofpbuf *request;
@@ -793,7 +815,7 @@ static void do_dump_aggregate(int argc, char *argv[])
     dump_stats_transaction(argv[1], request);
 }
 
     dump_stats_transaction(argv[1], request);
 }
 
-static void do_add_flow(int argc, char *argv[])
+static void do_add_flow(const struct settings *s, int argc, char *argv[])
 {
     struct vconn *vconn;
     struct ofpbuf *buffer;
 {
     struct vconn *vconn;
     struct ofpbuf *buffer;
@@ -822,7 +844,7 @@ static void do_add_flow(int argc, char *argv[])
     vconn_close(vconn);
 }
 
     vconn_close(vconn);
 }
 
-static void do_add_flows(int argc, char *argv[])
+static void do_add_flows(const struct settings *s, int argc, char *argv[])
 {
     struct vconn *vconn;
     FILE *file;
 {
     struct vconn *vconn;
     FILE *file;
@@ -875,9 +897,9 @@ static void do_add_flows(int argc, char *argv[])
     fclose(file);
 }
 
     fclose(file);
 }
 
-static void do_mod_flows(int argc, char *argv[])
+static void do_mod_flows(const struct settings *s, int argc, char *argv[])
 {
 {
-    uint16_t idle_timeout, hard_timeout;
+    uint16_t priority, idle_timeout, hard_timeout;
     struct vconn *vconn;
     struct ofpbuf *buffer;
     struct ofp_flow_mod *ofm;
     struct vconn *vconn;
     struct ofpbuf *buffer;
     struct ofp_flow_mod *ofm;
@@ -888,12 +910,16 @@ static void do_mod_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(argv[2], &ofm->match, &ofm->actions[0], &n_actions, 
     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, NULL, &idle_timeout, &hard_timeout);
-    ofm->command = htons(OFPFC_MODIFY);
+                NULL, &priority, &idle_timeout, &hard_timeout);
+    if (s->strict) {
+        ofm->command = htons(OFPFC_MODIFY_STRICT);
+    } else {
+        ofm->command = htons(OFPFC_MODIFY);
+    }
     ofm->idle_timeout = htons(idle_timeout);
     ofm->hard_timeout = htons(hard_timeout);
     ofm->buffer_id = htonl(UINT32_MAX);
     ofm->idle_timeout = htons(idle_timeout);
     ofm->hard_timeout = htons(hard_timeout);
     ofm->buffer_id = htonl(UINT32_MAX);
-    ofm->priority = htons(0);
+    ofm->priority = htons(priority);
     ofm->reserved = htonl(0);
 
     /* xxx Should we use the buffer library? */
     ofm->reserved = htonl(0);
 
     /* xxx Should we use the buffer library? */
@@ -904,7 +930,7 @@ static void do_mod_flows(int argc, char *argv[])
     vconn_close(vconn);
 }
 
     vconn_close(vconn);
 }
 
-static void do_del_flows(int argc, char *argv[])
+static void do_del_flows(const struct settings *s, int argc, char *argv[])
 {
     struct vconn *vconn;
     uint16_t priority;
 {
     struct vconn *vconn;
     uint16_t priority;
@@ -917,7 +943,11 @@ static void do_del_flows(int argc, char *argv[])
     ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
     str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, 0, NULL, 
                 &priority, NULL, NULL);
     ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
     str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, 0, NULL, 
                 &priority, NULL, NULL);
-    ofm->command = htons(OFPFC_DELETE);
+    if (s->strict) {
+        ofm->command = htons(OFPFC_DELETE_STRICT);
+    } else {
+        ofm->command = htons(OFPFC_DELETE);
+    }
     ofm->idle_timeout = htons(0);
     ofm->hard_timeout = htons(0);
     ofm->buffer_id = htonl(UINT32_MAX);
     ofm->idle_timeout = htons(0);
     ofm->hard_timeout = htons(0);
     ofm->buffer_id = htonl(UINT32_MAX);
@@ -930,7 +960,7 @@ static void do_del_flows(int argc, char *argv[])
 }
 
 static void
 }
 
 static void
-do_monitor(int argc UNUSED, char *argv[])
+do_monitor(const struct settings *s, int argc UNUSED, char *argv[])
 {
     struct vconn *vconn;
     const char *name;
 {
     struct vconn *vconn;
     const char *name;
@@ -953,13 +983,13 @@ do_monitor(int argc UNUSED, char *argv[])
 }
 
 static void
 }
 
 static void
-do_dump_ports(int argc, char *argv[])
+do_dump_ports(const struct settings *s, int argc, char *argv[])
 {
     dump_trivial_stats_transaction(argv[1], OFPST_PORT);
 }
 
 static void
 {
     dump_trivial_stats_transaction(argv[1], OFPST_PORT);
 }
 
 static void
-do_probe(int argc, char *argv[])
+do_probe(const struct settings *s, int argc, char *argv[])
 {
     struct ofpbuf *request;
     struct vconn *vconn;
 {
     struct ofpbuf *request;
     struct vconn *vconn;
@@ -976,7 +1006,7 @@ do_probe(int argc, char *argv[])
 }
 
 static void
 }
 
 static void
-do_mod_port(int argc, char *argv[])
+do_mod_port(const struct settings *s, int argc, char *argv[])
 {
     struct ofpbuf *request, *reply;
     struct ofp_switch_features *osf;
 {
     struct ofpbuf *request, *reply;
     struct ofp_switch_features *osf;
@@ -1054,7 +1084,7 @@ do_mod_port(int argc, char *argv[])
 }
 
 static void
 }
 
 static void
-do_ping(int argc, char *argv[])
+do_ping(const struct settings *s, int argc, char *argv[])
 {
     size_t max_payload = 65535 - sizeof(struct ofp_header);
     unsigned int payload;
 {
     size_t max_payload = 65535 - sizeof(struct ofp_header);
     unsigned int payload;
@@ -1101,7 +1131,7 @@ do_ping(int argc, char *argv[])
 }
 
 static void
 }
 
 static void
-do_benchmark(int argc, char *argv[])
+do_benchmark(const struct settings *s, int argc, char *argv[])
 {
     size_t max_payload = 65535 - sizeof(struct ofp_header);
     struct timeval start, end;
 {
     size_t max_payload = 65535 - sizeof(struct ofp_header);
     struct timeval start, end;
@@ -1143,7 +1173,8 @@ do_benchmark(int argc, char *argv[])
            count * message_size / (duration / 1000.0));
 }
 
            count * message_size / (duration / 1000.0));
 }
 
-static void do_help(int argc UNUSED, char *argv[] UNUSED)
+static void do_help(const struct settings *s, int argc UNUSED, 
+        char *argv[] UNUSED)
 {
     usage();
 }
 {
     usage();
 }