From e4cf5c4b82ce35b7702b24cb690f005cdd0a42d0 Mon Sep 17 00:00:00 2001 From: Justin Pettit Date: Tue, 16 Sep 2008 16:07:25 -0700 Subject: [PATCH] Added OFPFC_MODIFY_STRICT flow mod command. 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". --- datapath/chain.c | 12 ++-- datapath/chain.h | 2 +- datapath/flow.c | 7 +- datapath/flow.h | 2 +- datapath/forward.c | 8 ++- datapath/hwtable_dummy/hwtable_dummy.c | 7 +- datapath/table-hash.c | 23 +++---- datapath/table-linear.c | 12 ++-- datapath/table.h | 6 +- include/openflow.h | 1 + lib/ofp-print.c | 3 + switch/chain.c | 12 ++-- switch/chain.h | 2 +- switch/datapath.c | 8 ++- switch/switch-flow.c | 5 +- switch/switch-flow.h | 2 +- switch/table-hash.c | 24 +++---- switch/table-linear.c | 13 ++-- switch/table.h | 6 +- utilities/dpctl.8 | 17 +++-- utilities/dpctl.c | 93 +++++++++++++++++--------- 21 files changed, 157 insertions(+), 108 deletions(-) diff --git a/datapath/chain.c b/datapath/chain.c index 36749f30..865f908d 100644 --- a/datapath/chain.c +++ b/datapath/chain.c @@ -101,14 +101,15 @@ int chain_insert(struct sw_chain *chain, struct sw_flow *flow) 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, + uint16_t priority, int strict, 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]; - count += t->modify(t, key, actions, n_actions); + count += t->modify(t, key, priority, strict, actions, n_actions); } 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 diff --git a/datapath/chain.h b/datapath/chain.h index dbbae416..a7d044ee 100644 --- a/datapath/chain.h +++ b/datapath/chain.h @@ -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 *, - 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 *); diff --git a/datapath/flow.c b/datapath/flow.c index 2093dbba..ae2f7879 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -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 - * 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 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); } -EXPORT_SYMBOL(flow_del_matches); +EXPORT_SYMBOL(flow_matches_desc); static uint32_t make_nw_mask(int n_wild_bits) { diff --git a/datapath/flow.h b/datapath/flow.h index 3b25ad5d..8c0c27fe 100644 --- a/datapath/flow.h +++ b/datapath/flow.h @@ -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_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 *); diff --git a/datapath/forward.c b/datapath/forward.c index 7d775716..b63dfefb 100644 --- a/datapath/forward.c +++ b/datapath/forward.c @@ -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; + uint16_t priority; + int strict; /* 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); - 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)); @@ -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); - } 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; diff --git a/datapath/hwtable_dummy/hwtable_dummy.c b/datapath/hwtable_dummy/hwtable_dummy.c index b78d3643..1edb83af 100644 --- a/datapath/hwtable_dummy/hwtable_dummy.c +++ b/datapath/hwtable_dummy/hwtable_dummy.c @@ -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, - 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; @@ -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) { - 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++; @@ -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) { - 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); } diff --git a/datapath/table-hash.c b/datapath/table-hash.c index f6a41de5..29d20594 100644 --- a/datapath/table-hash.c +++ b/datapath/table-hash.c @@ -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)) { - /* 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; @@ -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, - 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; @@ -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 (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; } @@ -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; - 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++; } @@ -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; - if (flow && flow_del_matches(&flow->key, key, strict)) + if (flow && flow_matches_desc(&flow->key, key, strict)) 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, - 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; - 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, diff --git a/datapath/table-linear.c b/datapath/table-linear.c index 4a10df55..89f5c3c4 100644 --- a/datapath/table-linear.c +++ b/datapath/table-linear.c @@ -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)) { - /* 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); @@ -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, - 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; @@ -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) { - 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++; } @@ -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) { - 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); } diff --git a/datapath/table.h b/datapath/table.h index 8cbcfb7a..3dda2769 100644 --- a/datapath/table.h +++ b/datapath/table.h @@ -55,9 +55,11 @@ struct sw_table { * 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, + uint16_t priority, int strict, const struct ofp_action *actions, int n_actions); /* Deletes from 'table' any and all flows that match 'key' from diff --git a/include/openflow.h b/include/openflow.h index e04cfbed..528efeb8 100644 --- a/include/openflow.h +++ b/include/openflow.h @@ -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. */ + OFPFC_MODIFY_STRICT, /* Modify entry strictly matching wildcards */ OFPFC_DELETE, /* Delete all matching flows. */ OFPFC_DELETE_STRICT /* Strictly match wildcards and priority. */ }; diff --git a/lib/ofp-print.c b/lib/ofp-print.c index bbe4d49b..65c7d072 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -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_STRICT: + ds_put_cstr(string, " MOD_STRICT: "); + break; case OFPFC_DELETE: ds_put_cstr(string, " DEL: "); break; diff --git a/switch/chain.c b/switch/chain.c index 0388951d..c591cdbf 100644 --- a/switch/chain.c +++ b/switch/chain.c @@ -114,14 +114,15 @@ chain_insert(struct sw_chain *chain, struct sw_flow *flow) 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, + uint16_t priority, int strict, 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]; - count += t->modify(t, key, actions, n_actions); + count += t->modify(t, key, priority, strict, actions, n_actions); } 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 diff --git a/switch/chain.h b/switch/chain.h index eaccad41..9ad54d0e 100644 --- a/switch/chain.h +++ b/switch/chain.h @@ -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 *, - 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 *); diff --git a/switch/datapath.c b/switch/datapath.c index e5a2440f..032b7ec7 100644 --- a/switch/datapath.c +++ b/switch/datapath.c @@ -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; + uint16_t priority; + int strict; /* 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); - 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)); @@ -1275,7 +1279,7 @@ recv_flow(struct datapath *dp, const struct sender *sender UNUSED, 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; diff --git a/switch/switch-flow.c b/switch/switch-flow.c index e0410afc..a42d00fb 100644 --- a/switch/switch-flow.c +++ b/switch/switch-flow.c @@ -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 - * 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 -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; diff --git a/switch/switch-flow.h b/switch/switch-flow.h index 16066cbf..817998c7 100644 --- a/switch/switch-flow.h +++ b/switch/switch-flow.h @@ -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_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 *); diff --git a/switch/table-hash.c b/switch/table-hash.c index 34cba7ad..0960082b 100644 --- a/switch/table-hash.c +++ b/switch/table-hash.c @@ -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)) { - /* 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; @@ -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, - 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; @@ -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 (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; } @@ -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; - 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++; } @@ -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; - if (flow && flow_del_matches(&flow->key, key, strict)) { + if (flow && flow_matches_desc(&flow->key, key, strict)) { 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, - 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; - 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, diff --git a/switch/table-linear.c b/switch/table-linear.c index d1a04038..9a7a06cc 100644 --- a/switch/table-linear.c +++ b/switch/table-linear.c @@ -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)) { - /* 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); @@ -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, - 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; @@ -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) { - 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++; } @@ -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) { - if (flow_del_matches(&flow->key, key, strict) + if (flow_matches_desc(&flow->key, key, strict) && (!strict || (flow->priority == priority))) { do_delete(flow); count++; diff --git a/switch/table.h b/switch/table.h index 9a48c4b5..aacaa63f 100644 --- a/switch/table.h +++ b/switch/table.h @@ -84,9 +84,11 @@ struct sw_table { * 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, + uint16_t priority, int strict, const struct ofp_action *actions, int n_actions); /* Deletes from 'table' any and all flows that match 'key' from diff --git a/utilities/dpctl.8 b/utilities/dpctl.8 index 870ffb90..014b2b1b 100644 --- a/utilities/dpctl.8 +++ b/utilities/dpctl.8 @@ -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 -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 -\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 @@ -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 +.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 diff --git a/utilities/dpctl.c b/utilities/dpctl.c index f957b822..58e5e56b 100644 --- a/utilities/dpctl.c +++ b/utilities/dpctl.c @@ -76,26 +76,33 @@ #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; - 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 void parse_options(int argc, char *argv[]); +static void parse_options(int argc, char *argv[], struct settings *); int main(int argc, char *argv[]) { + struct settings s; 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; @@ -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 { - p->handler(argc, argv); + p->handler(&s, argc, argv); exit(0); } } @@ -124,11 +131,15 @@ int main(int argc, char *argv[]) } 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'}, + {"strict", no_argument, 0, OPT_STRICT}, {"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); + /* Set defaults that we can figure out before parsing options. */ + s->strict = false; + for (;;) { unsigned long int timeout; int c; @@ -167,6 +181,10 @@ parse_options(int argc, char *argv[]) vlog_set_verbosity(optarg); break; + case OPT_STRICT: + s->strict = true; + break; + VCONN_SSL_OPTION_HANDLERS case '?': @@ -215,6 +233,7 @@ usage(void) 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" @@ -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"); } -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); @@ -279,7 +298,7 @@ static void do_add_dp(int argc UNUSED, char *argv[]) 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); @@ -316,12 +335,14 @@ static int ifup_and_add_port(struct dpif *dpif, const char *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"); } -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"); } @@ -414,14 +435,14 @@ dump_trivial_stats_transaction(const char *vconn_name, uint8_t stats_type) } 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 -do_status(int argc, char *argv[]) +do_status(const struct settings *s, int argc, char *argv[]) { struct nicira_header *request, *reply; struct vconn *vconn; @@ -452,13 +473,13 @@ do_status(int argc, char *argv[]) } 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 -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); } @@ -767,7 +788,7 @@ str_to_flow(char *string, struct ofp_match *match, 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; @@ -780,7 +801,8 @@ static void do_dump_flows(int argc, char *argv[]) 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; @@ -793,7 +815,7 @@ static void do_dump_aggregate(int argc, char *argv[]) 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; @@ -822,7 +844,7 @@ static void do_add_flow(int argc, char *argv[]) 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; @@ -875,9 +897,9 @@ static void do_add_flows(int argc, char *argv[]) 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; @@ -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, - 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->priority = htons(0); + ofm->priority = htons(priority); 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); } -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; @@ -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->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); @@ -930,7 +960,7 @@ static void do_del_flows(int argc, char *argv[]) } 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; @@ -953,13 +983,13 @@ do_monitor(int argc UNUSED, char *argv[]) } 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 -do_probe(int argc, char *argv[]) +do_probe(const struct settings *s, int argc, char *argv[]) { struct ofpbuf *request; struct vconn *vconn; @@ -976,7 +1006,7 @@ do_probe(int argc, char *argv[]) } 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; @@ -1054,7 +1084,7 @@ do_mod_port(int argc, char *argv[]) } 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; @@ -1101,7 +1131,7 @@ do_ping(int argc, char *argv[]) } 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; @@ -1143,7 +1173,8 @@ do_benchmark(int argc, char *argv[]) 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(); } -- 2.30.2