From f22864a699ba6a5fe175eab90951be32907b647e Mon Sep 17 00:00:00 2001 From: Justin Pettit Date: Mon, 1 Dec 2008 14:34:23 -0800 Subject: [PATCH] Add support for listing and deleting entries based on an output port. 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. --- datapath/chain.c | 11 ++++++----- datapath/chain.h | 3 ++- datapath/datapath.c | 6 +++--- datapath/flow.c | 34 ++++++++++++++++++++++++++++++++++ datapath/flow.h | 1 + datapath/forward.c | 5 +++-- datapath/table-hash.c | 29 +++++++++++++++++------------ datapath/table-linear.c | 9 ++++++--- datapath/table.h | 19 +++++++++++-------- include/openflow/openflow.h | 19 +++++++++++++++---- switch/chain.c | 11 ++++++----- switch/chain.h | 3 ++- switch/datapath.c | 11 ++++++----- switch/switch-flow.c | 28 ++++++++++++++++++++++++++++ switch/switch-flow.h | 1 + switch/table-hash.c | 30 ++++++++++++++++++++---------- switch/table-linear.c | 7 ++++++- switch/table.h | 17 ++++++++++------- utilities/dpctl.8.in | 8 ++++++++ utilities/dpctl.c | 29 ++++++++++++++++++++--------- 20 files changed, 205 insertions(+), 76 deletions(-) diff --git a/datapath/chain.c b/datapath/chain.c index cd3b6e53..c270e295 100644 --- a/datapath/chain.c +++ b/datapath/chain.c @@ -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; diff --git a/datapath/chain.h b/datapath/chain.h index 26edd47c..a78a9385 100644 --- a/datapath/chain.h +++ b/datapath/chain.h @@ -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 *); diff --git a/datapath/datapath.c b/datapath/datapath.c index 326300d9..570cdcfb 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -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; diff --git a/datapath/flow.c b/datapath/flow.c index effd0c83..a8c3368f 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -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. */ diff --git a/datapath/flow.h b/datapath/flow.h index f0f8c66e..d04d7b93 100644 --- a/datapath/flow.h +++ b/datapath/flow.h @@ -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 *); diff --git a/datapath/forward.c b/datapath/forward.c index 33b3a5e2..d54674ac 100644 --- a/datapath/forward.c +++ b/datapath/forward.c @@ -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; } diff --git a/datapath/table-hash.c b/datapath/table-hash.c index 86e19201..21a49f98 100644 --- a/datapath/table-hash.c +++ b/datapath/table-hash.c @@ -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; } diff --git a/datapath/table-linear.c b/datapath/table-linear.c index 2a0752a2..af760ac4 100644 --- a/datapath/table-linear.c +++ b/datapath/table-linear.c @@ -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; diff --git a/datapath/table.h b/datapath/table.h index 80865e6d..27a562cd 100644 --- a/datapath/table.h +++ b/datapath/table.h @@ -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); diff --git a/include/openflow/openflow.h b/include/openflow/openflow.h index e7da0d1c..1d409955 100644 --- a/include/openflow/openflow.h +++ b/include/openflow/openflow.h @@ -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); diff --git a/switch/chain.c b/switch/chain.c index d64cd12e..8f09c00e 100644 --- a/switch/chain.c +++ b/switch/chain.c @@ -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; diff --git a/switch/chain.h b/switch/chain.h index 39a9f30e..9ffe072d 100644 --- a/switch/chain.h +++ b/switch/chain.h @@ -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 *); diff --git a/switch/datapath.c b/switch/datapath.c index e05365cc..452d471b 100644 --- a/switch/datapath.c +++ b/switch/datapath.c @@ -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; diff --git a/switch/switch-flow.c b/switch/switch-flow.c index 4ea258b9..82eee55d 100644 --- a/switch/switch-flow.c +++ b/switch/switch-flow.c @@ -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(); diff --git a/switch/switch-flow.h b/switch/switch-flow.h index 38cfa6e0..ef0497c8 100644 --- a/switch/switch-flow.h +++ b/switch/switch-flow.h @@ -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 *); diff --git a/switch/table-hash.c b/switch/table-hash.c index bef7d9e1..6b5e9457 100644 --- a/switch/table-hash.c +++ b/switch/table-hash.c @@ -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; } diff --git a/switch/table-linear.c b/switch/table-linear.c index 32a0bc2f..1ec6951c 100644 --- a/switch/table-linear.c +++ b/switch/table-linear.c @@ -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); diff --git a/switch/table.h b/switch/table.h index b73a8fe5..011591c6 100644 --- a/switch/table.h +++ b/switch/table.h @@ -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); diff --git a/utilities/dpctl.8.in b/utilities/dpctl.8.in index d1f03bb6..595148e9 100644 --- a/utilities/dpctl.8.in +++ b/utilities/dpctl.8.in @@ -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: diff --git a/utilities/dpctl.c b/utilities/dpctl.c index 33847009..8fa0da85 100644 --- a/utilities/dpctl.c +++ b/utilities/dpctl.c @@ -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); -- 2.30.2