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.
This commit is contained in:
1 parent
bf35d7b0b9
commit
75f7b917bc
20 files changed
+205
-76
No files matched your search
+6
-5
@@ -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;
|
||||
|
||||
+2
-1
@@ -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 *);
|
||||
|
||||
|
||||
+3
-3
@@ -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;
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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 *);
|
||||
|
||||
+3
-2
@@ -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;
|
||||
}
|
||||
|
||||
+17
-12
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
+11
-8
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -532,12 +532,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 {
|
||||
@@ -686,7 +691,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);
|
||||
|
||||
@@ -713,7 +721,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);
|
||||
|
||||
|
||||
+6
-5
@@ -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;
|
||||
|
||||
+2
-1
@@ -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 *);
|
||||
|
||||
|
||||
+6
-5
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 *);
|
||||
|
||||
+20
-10
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
+10
-7
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
+20
-9
@@ -762,7 +762,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)
|
||||
{
|
||||
|
||||
@@ -772,6 +772,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;
|
||||
}
|
||||
@@ -821,6 +824,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")) {
|
||||
@@ -856,12 +861,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);
|
||||
}
|
||||
@@ -871,11 +878,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);
|
||||
}
|
||||
@@ -945,7 +954,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);
|
||||
@@ -997,7 +1006,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);
|
||||
@@ -1027,7 +1036,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 {
|
||||
@@ -1051,6 +1060,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;
|
||||
@@ -1059,7 +1069,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 {
|
||||
@@ -1068,6 +1078,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);
|
||||
|
||||
|
||||
Reference in new issue
Block a user