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
*
* 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;
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;
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 *);
{
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;
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;
}
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. */
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 *);
} 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;
}
* 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;
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;
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);
}
}
}
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)
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);
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;
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)
}
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)
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;
}
}
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;
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);
}
}
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)
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;
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. */
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
* 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);
/* 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
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 {
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);
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);
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;
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 *);
} 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;
}
{
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++;
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;
}
}
+/* 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();
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 *);
* 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;
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;
}
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++;
}
}
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)
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;
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)
}
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)
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;
}
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;
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++;
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)
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);
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
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
* 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);
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:
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)
{
if (table_idx) {
*table_idx = 0xff;
}
+ if (out_port) {
+ *out_port = OFPP_NONE;
+ }
if (priority) {
*priority = OFP_DEFAULT_PRIORITY;
}
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")) {
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);
}
{
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);
}
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);
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);
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 {
{
struct vconn *vconn;
uint16_t priority;
+ uint16_t out_port;
struct ofpbuf *buffer;
struct ofp_flow_mod *ofm;
size_t size;
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 {
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);