return -ENOBUFS;
}
+/* Modifies actions in 'chain' that match 'key'. Returns the number of
+ * flows that were modified.
+ *
+ * Expensive in the general case as currently implemented, since it requires
+ * iterating through the entire contents of each table for keys that contain
+ * wildcards. Relatively cheap for fully specified keys. */
+int
+chain_modify(struct sw_chain *chain, const struct sw_flow_key *key,
+ const struct ofp_action *actions, int n_actions)
+{
+ int count = 0;
+ int i;
+
+ for (i = 0; i < chain->n_tables; i++) {
+ struct sw_table *t = chain->tables[i];
+ count += t->modify(t, key, actions, n_actions);
+ }
+
+ return count;
+}
+
/* Deletes from 'chain' any and all flows that match 'key'. Returns the number
* of flows that were deleted.
*
struct sw_flow;
struct sw_flow_key;
+struct ofp_action;
struct datapath;
struct sw_chain *chain_create(struct datapath *);
struct sw_flow *chain_lookup(struct sw_chain *, const struct sw_flow_key *);
int chain_insert(struct sw_chain *, struct sw_flow *);
+int chain_modify(struct sw_chain *, const struct sw_flow_key *,
+ const struct ofp_action *, int);
int chain_delete(struct sw_chain *, const struct sw_flow_key *, uint16_t, int);
int chain_timeout(struct sw_chain *);
void chain_destroy(struct sw_chain *);
static int flow_stats_dump_callback(struct sw_flow *flow, void *private)
{
+ struct sw_flow_actions *sf_acts = rcu_dereference(flow->sf_acts);
struct flow_stats_state *s = private;
struct ofp_flow_stats *ofs;
int actions_length;
int length;
- actions_length = sizeof *ofs->actions * flow->n_actions;
- length = sizeof *ofs + sizeof *ofs->actions * flow->n_actions;
+ actions_length = sizeof *ofs->actions * sf_acts->n_actions;
+ length = sizeof *ofs + actions_length;
if (length + s->bytes_used > s->bytes_allocated)
return 1;
memset(ofs->pad2, 0, sizeof ofs->pad2);
ofs->packet_count = cpu_to_be64(flow->packet_count);
ofs->byte_count = cpu_to_be64(flow->byte_count);
- memcpy(ofs->actions, flow->actions, actions_length);
+ memcpy(ofs->actions, sf_acts->actions, actions_length);
s->bytes_used += length;
return 0;
memcpy(to->dl_src, from->dl_src, ETH_ALEN);
memcpy(to->dl_dst, from->dl_dst, ETH_ALEN);
to->dl_type = from->dl_type;
- to->nw_src = from->nw_src;
- to->nw_dst = from->nw_dst;
+ to->nw_src = from->nw_src;
+ to->nw_dst = from->nw_dst;
to->nw_proto = from->nw_proto;
- to->tp_src = from->tp_src;
- to->tp_dst = from->tp_dst;
- to->pad = 0;
+ to->tp_src = from->tp_src;
+ to->tp_dst = from->tp_dst;
+ to->pad = 0;
}
int flow_timeout(struct sw_flow *flow)
* flags 'flags'. Returns the new flow or a null pointer on failure. */
struct sw_flow *flow_alloc(int n_actions, gfp_t flags)
{
+ struct sw_flow_actions *sfa;
+ int size = sizeof *sfa + (n_actions * sizeof sfa->actions[0]);
struct sw_flow *flow = kmem_cache_alloc(flow_cache, flags);
if (unlikely(!flow))
return NULL;
- flow->n_actions = n_actions;
- flow->actions = kmalloc(n_actions * sizeof *flow->actions,
- flags);
- if (unlikely(!flow->actions) && n_actions > 0) {
+ sfa = kmalloc(size, flags);
+ if (unlikely(!sfa)) {
kmem_cache_free(flow_cache, flow);
return NULL;
}
+ sfa->n_actions = n_actions;
+ flow->sf_acts = sfa;
+
return flow;
}
{
if (unlikely(!flow))
return;
- if (flow->actions)
- kfree(flow->actions);
+ kfree(flow->sf_acts);
kmem_cache_free(flow_cache, flow);
}
EXPORT_SYMBOL(flow_free);
/* RCU callback used by flow_deferred_free. */
-static void rcu_callback(struct rcu_head *rcu)
+static void rcu_free_flow_callback(struct rcu_head *rcu)
{
struct sw_flow *flow = container_of(rcu, struct sw_flow, rcu);
flow_free(flow);
* The caller must hold rcu_read_lock for this to be sensible. */
void flow_deferred_free(struct sw_flow *flow)
{
- call_rcu(&flow->rcu, rcu_callback);
+ call_rcu(&flow->rcu, rcu_free_flow_callback);
}
EXPORT_SYMBOL(flow_deferred_free);
+/* RCU callback used by flow_deferred_free_acts. */
+static void rcu_free_acts_callback(struct rcu_head *rcu)
+{
+ struct sw_flow_actions *sf_acts = container_of(rcu,
+ struct sw_flow_actions, rcu);
+ kfree(sf_acts);
+}
+
+/* Schedules 'sf_acts' to be freed after the next RCU grace period.
+ * The caller must hold rcu_read_lock for this to be sensible. */
+void flow_deferred_free_acts(struct sw_flow_actions *sf_acts)
+{
+ call_rcu(&sf_acts->rcu, rcu_free_acts_callback);
+}
+EXPORT_SYMBOL(flow_deferred_free_acts);
+
+/* Copies 'actions' into a newly allocated structure for use by 'flow'
+ * and safely frees the structure that defined the previous actions. */
+void flow_replace_acts(struct sw_flow *flow, const struct ofp_action *actions,
+ int n_actions)
+{
+ struct sw_flow_actions *sfa;
+ struct sw_flow_actions *orig_sfa = flow->sf_acts;
+ int size = sizeof *sfa + (n_actions * sizeof sfa->actions[0]);
+
+ sfa = kmalloc(size, GFP_ATOMIC);
+ if (unlikely(!sfa))
+ return;
+
+ sfa->n_actions = n_actions;
+ memcpy(sfa->actions, actions, n_actions * sizeof sfa->actions[0]);
+
+ rcu_assign_pointer(flow->sf_acts, sfa);
+ flow_deferred_free_acts(orig_sfa);
+
+ return;
+}
+EXPORT_SYMBOL(flow_replace_acts);
+
/* Prints a representation of 'key' to the kernel log. */
void print_flow(const struct sw_flow_key *key)
{
BUILD_BUG_ON(sizeof(struct sw_flow_key) != 44);
}
+/* We keep actions as a separate structure because we need to be able to
+ * swap them out atomically when the modify command comes from a Flow
+ * Modify message. */
+struct sw_flow_actions {
+ unsigned int n_actions;
+ struct rcu_head rcu;
+
+ struct ofp_action actions[0];
+};
+
/* Locking:
*
* - Readers must take rcu_read_lock and hold it the entire time that the flow
uint16_t priority; /* Only used on entries with wildcards. */
uint16_t idle_timeout; /* Idle time before discarding (seconds). */
uint16_t hard_timeout; /* Hard expiration time (seconds) */
- unsigned long used; /* Last used time (in jiffies). */
+ unsigned long used; /* Last used time (in jiffies). */
- /* FIXME? Probably most flows have only a single action. */
- unsigned int n_actions;
- struct ofp_action *actions;
+ struct sw_flow_actions *sf_acts;
/* For use by table implementation. */
struct list_head node;
struct sw_flow *flow_alloc(int n_actions, gfp_t flags);
void flow_free(struct sw_flow *);
void flow_deferred_free(struct sw_flow *);
+void flow_deferred_free_acts(struct sw_flow_actions *);
+void flow_replace_acts(struct sw_flow *, const struct ofp_action *, int);
int flow_extract(struct sk_buff *, uint16_t in_port, struct sw_flow_key *);
void flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from);
void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from);
flow = chain_lookup(chain, &key);
if (likely(flow != NULL)) {
+ struct sw_flow_actions *sf_acts = rcu_dereference(flow->sf_acts);
flow_used(flow, skb);
execute_actions(chain->dp, skb, &key,
- flow->actions, flow->n_actions, 0);
+ sf_acts->actions, sf_acts->n_actions, 0);
return 0;
} else {
return -ESRCH;
flow->idle_timeout = ntohs(ofm->idle_timeout);
flow->hard_timeout = ntohs(ofm->hard_timeout);
flow->used = jiffies;
- flow->n_actions = n_actions;
flow->init_time = jiffies;
flow->byte_count = 0;
flow->packet_count = 0;
spin_lock_init(&flow->lock);
- memcpy(flow->actions, ofm->actions, n_actions * sizeof *flow->actions);
+ memcpy(flow->sf_acts->actions, ofm->actions,
+ n_actions * sizeof *flow->sf_acts->actions);
/* Act. */
error = chain_insert(chain, flow);
return error;
}
+static int
+mod_flow(struct sw_chain *chain, const struct ofp_flow_mod *ofm)
+{
+ int error = -ENOMEM;
+ int i;
+ int n_actions;
+ struct sw_flow_key key;
+
+ /* To prevent loops, make sure there's no action to send to the
+ * OFP_TABLE virtual port.
+ */
+ n_actions = (ntohs(ofm->header.length) - sizeof *ofm)
+ / sizeof *ofm->actions;
+ for (i=0; i<n_actions; i++) {
+ const struct ofp_action *a = &ofm->actions[i];
+
+ if (a->type == htons(OFPAT_OUTPUT)
+ && (a->arg.output.port == htons(OFPP_TABLE)
+ || a->arg.output.port == htons(OFPP_NONE)
+ || a->arg.output.port == ofm->match.in_port)) {
+ /* xxx Send fancy new error message? */
+ goto error;
+ }
+ }
+
+ flow_extract_match(&key, &ofm->match);
+ chain_modify(chain, &key, ofm->actions, n_actions);
+
+ if (ntohl(ofm->buffer_id) != (uint32_t) -1) {
+ struct sk_buff *skb = retrieve_skb(ntohl(ofm->buffer_id));
+ if (skb) {
+ struct sw_flow_key skb_key;
+ flow_extract(skb, ntohs(ofm->match.in_port), &skb_key);
+ execute_actions(chain->dp, skb, &skb_key,
+ ofm->actions, n_actions, 0);
+ }
+ else
+ error = -ESRCH;
+ }
+ return error;
+
+error:
+ if (ntohl(ofm->buffer_id) != (uint32_t) -1)
+ discard_skb(ntohl(ofm->buffer_id));
+ return error;
+}
+
static int
recv_flow(struct sw_chain *chain, const struct sender *sender, const void *msg)
{
if (command == OFPFC_ADD) {
return add_flow(chain, ofm);
+ } else if (command == OFPFC_MODIFY) {
+ return mod_flow(chain, ofm);
} else if (command == OFPFC_DELETE) {
struct sw_flow_key key;
flow_extract_match(&key, &ofm->match);
return 0;
}
+static int table_dummy_modify(struct sw_table *swt,
+ const struct sw_flow_key *key,
+ const struct ofp_action *actions, int n_actions)
+{
+ struct sw_table_dummy *td = (struct sw_table_dummy *) swt;
+ struct sw_flow *flow;
+ unsigned int count = 0;
+
+ list_for_each_entry (flow, &td->flows, node) {
+ if (flow_matches_1wild(&flow->key, key)) {
+ flow_replace_acts(flow, actions, n_actions);
+ /* xxx Do whatever is necessary to modify the entry in hardware */
+ count++;
+ }
+ }
+ return count;
+}
+
static int do_delete(struct sw_table *swt, struct sw_flow *flow)
{
swt = &td->swt;
swt->lookup = table_dummy_lookup;
swt->insert = table_dummy_insert;
+ swt->modify = table_dummy_modify;
swt->delete = table_dummy_delete;
swt->timeout = table_dummy_timeout;
swt->destroy = table_dummy_destroy;
return retval;
}
+static int table_hash_modify(struct sw_table *swt,
+ const struct sw_flow_key *key,
+ const struct ofp_action *actions, int n_actions)
+{
+ 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_matches_1wild(&flow->key, key)) {
+ flow_replace_acts(flow, actions, n_actions);
+ count = 1;
+ }
+ } 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_1wild(&flow->key, key)) {
+ flow_replace_acts(flow, actions, n_actions);
+ count++;
+ }
+ }
+ }
+ return count;
+}
+
/* Caller must update n_flows. */
static int do_delete(struct sw_flow **bucket, struct sw_flow *flow)
{
return table_hash_insert(t2->subtable[1], flow);
}
+static int table_hash2_modify(struct sw_table *swt,
+ const struct sw_flow_key *key,
+ const struct ofp_action *actions, int n_actions)
+{
+ struct sw_table_hash2 *t2 = (struct sw_table_hash2 *) swt;
+ return (table_hash_modify(t2->subtable[0], key, actions, n_actions)
+ + table_hash_modify(t2->subtable[1], key, actions, n_actions));
+}
+
static int table_hash2_delete(struct sw_table *swt,
const struct sw_flow_key *key,
uint16_t priority, int strict)
swt = &t2->swt;
swt->lookup = table_hash2_lookup;
swt->insert = table_hash2_insert;
+ swt->modify = table_hash2_modify;
swt->delete = table_hash2_delete;
swt->timeout = table_hash2_timeout;
swt->destroy = table_hash2_destroy;
return 1;
}
+static int table_linear_modify(struct sw_table *swt,
+ const struct sw_flow_key *key,
+ const struct ofp_action *actions, int n_actions)
+{
+ struct sw_table_linear *tl = (struct sw_table_linear *) swt;
+ struct sw_flow *flow;
+ unsigned int count = 0;
+
+ list_for_each_entry (flow, &tl->flows, node) {
+ if (flow_matches_1wild(&flow->key, key)) {
+ flow_replace_acts(flow, actions, n_actions);
+ count++;
+ }
+ }
+ return count;
+}
+
static int do_delete(struct sw_table *swt, struct sw_flow *flow)
{
list_del_rcu(&flow->node);
swt = &tl->swt;
swt->lookup = table_linear_lookup;
swt->insert = table_linear_insert;
+ swt->modify = table_linear_modify;
swt->delete = table_linear_delete;
swt->timeout = table_linear_timeout;
swt->destroy = table_linear_destroy;
struct sw_flow;
struct sw_flow_key;
+struct ofp_action;
struct datapath;
/* Table statistics. */
* retained by the caller. */
int (*insert)(struct sw_table *table, struct sw_flow *flow);
+ /* Modifies the actions in 'table' that match 'key'. Returns the
+ * number of flows that were modified. */
+ int (*modify)(struct sw_table *table, const struct sw_flow_key *key,
+ const struct ofp_action *actions, int n_actions);
+
/* 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. */
{
struct sw_flow *flow = flow_alloc(n_actions, flags);
if (flow) {
- struct ofp_action *actions = flow->actions;
+ struct sw_flow_actions *sfa = flow->sf_acts;
memset(flow, 0, sizeof *flow);
- flow->actions = actions;
+ flow->sf_acts = sfa;
}
return flow;
}
enum ofp_flow_mod_command {
OFPFC_ADD, /* New flow. */
OFPFC_MODIFY, /* Modify all matching flows. */
- OFPFC_MODIFY_STRICT, /* Strictly match wildcards and priority. */
OFPFC_DELETE, /* Delete all matching flows. */
OFPFC_DELETE_STRICT /* Strictly match wildcards and priority. */
};
uint16_t idle_timeout; /* Idle time before discarding (seconds). */
uint16_t hard_timeout; /* Max time before discarding (seconds). */
uint16_t priority; /* Priority level of flow entry. */
- uint32_t buffer_id; /* Buffered packet to apply to (or -1). */
+ uint32_t buffer_id; /* Buffered packet to apply to (or -1).
+ Not meaningful for OFPFC_DELETE*. */
uint32_t reserved; /* Reserved for future use. */
struct ofp_action actions[0]; /* The number of actions is inferred from
- the length field in the header. */
+ the length field in the header. */
};
OFP_ASSERT(sizeof(struct ofp_flow_mod) == 60);
const struct ofp_flow_mod *ofm = oh;
ofp_print_match(string, &ofm->match, verbosity);
- ds_put_format(string, " cmd:%d idle:%d hard:%d pri:%d buf:%#x",
- ntohs(ofm->command), ntohs(ofm->idle_timeout),
- ntohs(ofm->hard_timeout),
+ switch (ntohs(ofm->command)) {
+ case OFPFC_ADD:
+ ds_put_cstr(string, " ADD: ");
+ break;
+ case OFPFC_MODIFY:
+ ds_put_cstr(string, " MOD: ");
+ break;
+ case OFPFC_DELETE:
+ ds_put_cstr(string, " DEL: ");
+ break;
+ case OFPFC_DELETE_STRICT:
+ ds_put_cstr(string, " DEL_STRICT: ");
+ break;
+ default:
+ ds_put_format(string, " cmd:%d ", ntohs(ofm->command));
+ }
+ ds_put_format(string, "idle:%d hard:%d pri:%d buf:%#x",
+ ntohs(ofm->idle_timeout), ntohs(ofm->hard_timeout),
ofm->match.wildcards ? ntohs(ofm->priority) : (uint16_t)-1,
ntohl(ofm->buffer_id));
ofp_print_actions(string, ofm->actions,
return -ENOBUFS;
}
+/* Modifies actions in 'chain' that match 'key'. Returns the number of
+ * flows that were modified.
+ *
+ * Expensive in the general case as currently implemented, since it requires
+ * iterating through the entire contents of each table for keys that contain
+ * wildcards. Relatively cheap for fully specified keys. */
+int
+chain_modify(struct sw_chain *chain, const struct sw_flow_key *key,
+ const struct ofp_action *actions, int n_actions)
+{
+ int count = 0;
+ int i;
+
+ for (i = 0; i < chain->n_tables; i++) {
+ struct sw_table *t = chain->tables[i];
+ count += t->modify(t, key, actions, n_actions);
+ }
+
+ return count;
+}
+
/* Deletes from 'chain' any and all flows that match 'key'. Returns the number
* of flows that were deleted.
*
struct sw_flow;
struct sw_flow_key;
+struct ofp_action;
struct list;
#define TABLE_LINEAR_MAX_FLOWS 100
struct sw_chain *chain_create(void);
struct sw_flow *chain_lookup(struct sw_chain *, const struct sw_flow_key *);
int chain_insert(struct sw_chain *, struct sw_flow *);
+int chain_modify(struct sw_chain *, const struct sw_flow_key *,
+ const struct ofp_action *, int);
int chain_delete(struct sw_chain *, const struct sw_flow_key *, uint16_t, int);
void chain_timeout(struct sw_chain *, struct list *deleted);
void chain_destroy(struct sw_chain *);
int table_idx, time_t now)
{
struct ofp_flow_stats *ofs;
- int length = sizeof *ofs + sizeof *ofs->actions * flow->n_actions;
+ int length = sizeof *ofs + sizeof *ofs->actions * flow->sf_acts->n_actions;
ofs = buffer_put_uninit(buffer, length);
ofs->length = htons(length);
ofs->table_id = table_idx;
memset(ofs->pad2, 0, sizeof ofs->pad2);
ofs->packet_count = htonll(flow->packet_count);
ofs->byte_count = htonll(flow->byte_count);
- memcpy(ofs->actions, flow->actions,
- sizeof *ofs->actions * flow->n_actions);
+ memcpy(ofs->actions, flow->sf_acts->actions,
+ sizeof *ofs->actions * flow->sf_acts->n_actions);
}
\f
if (flow != NULL) {
flow_used(flow, buffer);
execute_actions(dp, buffer, port_no(dp, p),
- &key, flow->actions, flow->n_actions, false);
+ &key, flow->sf_acts->actions,
+ flow->sf_acts->n_actions, false);
return 0;
} else {
return -ESRCH;
flow->idle_timeout = ntohs(ofm->idle_timeout);
flow->hard_timeout = ntohs(ofm->hard_timeout);
flow->used = flow->created = time_now();
- flow->n_actions = n_actions;
+ flow->sf_acts->n_actions = n_actions;
flow->byte_count = 0;
flow->packet_count = 0;
- memcpy(flow->actions, ofm->actions, n_actions * sizeof *flow->actions);
+ memcpy(flow->sf_acts->actions, ofm->actions,
+ n_actions * sizeof *flow->sf_acts->actions);
/* Act. */
error = chain_insert(dp->chain, flow);
return error;
}
+static int
+mod_flow(struct datapath *dp, const struct ofp_flow_mod *ofm)
+{
+ int error = -ENOMEM;
+ int n_actions;
+ int i;
+ struct sw_flow_key key;
+
+
+ /* To prevent loops, make sure there's no action to send to the
+ * OFP_TABLE virtual port.
+ */
+ n_actions = (ntohs(ofm->header.length) - sizeof *ofm)
+ / sizeof *ofm->actions;
+ for (i=0; i<n_actions; i++) {
+ const struct ofp_action *a = &ofm->actions[i];
+
+ if (a->type == htons(OFPAT_OUTPUT)
+ && (a->arg.output.port == htons(OFPP_TABLE)
+ || a->arg.output.port == htons(OFPP_NONE)
+ || a->arg.output.port == ofm->match.in_port)) {
+ /* xxx Send fancy new error message? */
+ goto error;
+ }
+ }
+
+ flow_extract_match(&key, &ofm->match);
+ chain_modify(dp->chain, &key, ofm->actions, n_actions);
+
+ if (ntohl(ofm->buffer_id) != UINT32_MAX) {
+ struct buffer *buffer = retrieve_buffer(ntohl(ofm->buffer_id));
+ if (buffer) {
+ struct sw_flow_key skb_key;
+ uint16_t in_port = ntohs(ofm->match.in_port);
+ flow_extract(buffer, in_port, &skb_key.flow);
+ execute_actions(dp, buffer, in_port, &skb_key,
+ ofm->actions, n_actions, false);
+ } else {
+ error = -ESRCH;
+ }
+ }
+ return error;
+
+error:
+ if (ntohl(ofm->buffer_id) != (uint32_t) -1)
+ discard_buffer(ntohl(ofm->buffer_id));
+ return error;
+}
+
static int
recv_flow(struct datapath *dp, const struct sender *sender UNUSED,
const void *msg)
if (command == OFPFC_ADD) {
return add_flow(dp, ofm);
+ } else if (command == OFPFC_MODIFY) {
+ return mod_flow(dp, ofm);
} else if (command == OFPFC_DELETE) {
struct sw_flow_key key;
flow_extract_match(&key, &ofm->match);
struct sw_flow *
flow_alloc(int n_actions)
{
+ struct sw_flow_actions *sfa;
struct sw_flow *flow = malloc(sizeof *flow);
if (!flow)
return NULL;
- flow->n_actions = n_actions;
- flow->actions = malloc(n_actions * sizeof *flow->actions);
- if (!flow->actions && n_actions > 0) {
+ sfa = malloc(n_actions * sizeof sfa->actions[0]);
+ if (!sfa) {
free(flow);
return NULL;
}
+ sfa->n_actions = n_actions;
+ flow->sf_acts = sfa;
return flow;
}
if (!flow) {
return;
}
- if (flow->actions) {
- free(flow->actions);
- }
+ free(flow->sf_acts);
free(flow);
}
+/* Copies 'actions' into a newly allocated structure for use by 'flow'
+ * and frees the structure that defined the previous actions. */
+void flow_replace_acts(struct sw_flow *flow, const struct ofp_action *actions,
+ int n_actions)
+{
+ struct sw_flow_actions *sfa;
+ int size = sizeof *sfa + (n_actions * sizeof sfa->actions[0]);
+
+ sfa = malloc(size);
+ if (unlikely(!sfa))
+ return;
+
+ sfa->n_actions = n_actions;
+ memcpy(sfa->actions, actions, n_actions * sizeof sfa->actions[0]);
+
+ free(flow->sf_acts);
+ flow->sf_acts = sfa;
+
+ return;
+}
+
/* Prints a representation of 'key' to the kernel log. */
void
print_flow(const struct sw_flow_key *key)
#define SWITCH_FLOW_H 1
#include <time.h>
+#include "openflow.h"
#include "flow.h"
#include "list.h"
uint32_t nw_dst_mask; /* 1-bit in each significant nw_dst bit. */
};
+struct sw_flow_actions {
+ unsigned int n_actions;
+ struct ofp_action actions[0];
+};
+
struct sw_flow {
struct sw_flow_key key;
uint64_t byte_count; /* Number of bytes seen. */
uint8_t reason; /* Reason flow expired (one of OFPER_*). */
+ struct sw_flow_actions *sf_acts;
+
/* Private to table implementations. */
struct list node;
struct list iter_node;
unsigned long int serial;
-
- /* Actions (XXX probably most flows have only a single action). */
- unsigned int n_actions;
- struct ofp_action *actions;
};
int flow_matches_1wild(const struct sw_flow_key *, const struct sw_flow_key *);
struct sw_flow *flow_alloc(int n_actions);
void flow_free(struct sw_flow *);
void flow_deferred_free(struct sw_flow *);
+void flow_deferred_free_acts(struct sw_flow_actions *);
+void flow_replace_acts(struct sw_flow *, const struct ofp_action *, int);
void flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from);
void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from);
return retval;
}
+static int table_hash_modify(struct sw_table *swt,
+ const struct sw_flow_key *key,
+ const struct ofp_action *actions, int n_actions)
+{
+ 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_matches_1wild(&flow->key, key)) {
+ flow_replace_acts(flow, actions, n_actions);
+ count = 1;
+ }
+ } 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_1wild(&flow->key, key)) {
+ flow_replace_acts(flow, actions, n_actions);
+ count++;
+ }
+ }
+ }
+ return count;
+}
+
/* Caller must update n_flows. */
static void
do_delete(struct sw_flow **bucket)
swt = &th->swt;
swt->lookup = table_hash_lookup;
swt->insert = table_hash_insert;
+ swt->modify = table_hash_modify;
swt->delete = table_hash_delete;
swt->timeout = table_hash_timeout;
swt->destroy = table_hash_destroy;
return table_hash_insert(t2->subtable[1], flow);
}
+static int table_hash2_modify(struct sw_table *swt,
+ const struct sw_flow_key *key,
+ const struct ofp_action *actions, int n_actions)
+{
+ struct sw_table_hash2 *t2 = (struct sw_table_hash2 *) swt;
+ return (table_hash_modify(t2->subtable[0], key, actions, n_actions)
+ + table_hash_modify(t2->subtable[1], key, actions, n_actions));
+}
+
static int table_hash2_delete(struct sw_table *swt,
const struct sw_flow_key *key,
uint16_t priority, int strict)
swt = &t2->swt;
swt->lookup = table_hash2_lookup;
swt->insert = table_hash2_insert;
+ swt->modify = table_hash2_modify;
swt->delete = table_hash2_delete;
swt->timeout = table_hash2_timeout;
swt->destroy = table_hash2_destroy;
return 1;
}
+static int table_linear_modify(struct sw_table *swt,
+ const struct sw_flow_key *key,
+ const struct ofp_action *actions, int n_actions)
+{
+ struct sw_table_linear *tl = (struct sw_table_linear *) swt;
+ struct sw_flow *flow;
+ unsigned int count = 0;
+
+ LIST_FOR_EACH (flow, struct sw_flow, node, &tl->flows) {
+ if (flow_matches_1wild(&flow->key, key)) {
+ flow_replace_acts(flow, actions, n_actions);
+ count++;
+ }
+ }
+ return count;
+}
+
static void
do_delete(struct sw_flow *flow)
{
swt = &tl->swt;
swt->lookup = table_linear_lookup;
swt->insert = table_linear_insert;
+ swt->modify = table_linear_modify;
swt->delete = table_linear_delete;
swt->timeout = table_linear_timeout;
swt->destroy = table_linear_destroy;
struct sw_flow;
struct sw_flow_key;
+struct ofp_action;
struct list;
/* Table statistics. */
* retained by the caller. */
int (*insert)(struct sw_table *table, struct sw_flow *flow);
+ /* Modifies entries in 'table' that match 'key'. Returns the
+ * number of flows that were modified. */
+ int (*modify)(struct sw_table *table, const struct sw_flow_key *key,
+ const struct ofp_action *actions, int n_actions);
+
/* Deletes from 'table' any and all flows that match 'key' from
* 'table'. If 'strict' set, wildcards must match. Returns the
* number of flows that were deleted. */
tables. Each line in \fIfile\fR is a flow entry in the format
described in \fBFLOW SYNTAX\fR, below.
+.TP
+\fBmod-flows \fIswitch flow\fR
+Modify the actions in entries from the datapath \fIswitch\fR's tables
+that match \fIflow\fR. See \fBFLOW SYNTAX\fR, below, for the syntax
+of \fIflows\fR.
+
.TP
\fBdel-flows \fIswitch \fR[\fIflow\fR]
Deletes entries from the datapath \fIswitch\fR's tables that match
" dump-aggregate SWITCH FLOW print aggregate stats for FLOWs\n"
" add-flow SWITCH FLOW add flow described by FLOW\n"
" add-flows SWITCH FILE add flows from FILE\n"
- " del-flows SWITCH FLOW delete matching FLOWs\n"
+ " mod-flows SWITCH FLOW modify actions of matching FLOWs\n"
+ " del-flows SWITCH [FLOW] delete matching FLOWs\n"
" monitor SWITCH print packets received from SWITCH\n"
"\nFor local datapaths, remote switches, and controllers:\n"
" probe VCONN probe whether VCONN is up\n"
size_t size;
int n_actions = MAX_ADD_ACTS;
- open_vconn(argv[1], &vconn);
-
/* Parse and send. */
size = sizeof *ofm + (sizeof ofm->actions[0] * MAX_ADD_ACTS);
ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
/* xxx Should we use the buffer library? */
buffer->size -= (MAX_ADD_ACTS - n_actions) * sizeof ofm->actions[0];
+ open_vconn(argv[1], &vconn);
send_openflow_buffer(vconn, buffer);
vconn_close(vconn);
}
static void do_add_flows(int argc, char *argv[])
{
struct vconn *vconn;
-
FILE *file;
char line[1024];
fclose(file);
}
-static void do_del_flows(int argc, char *argv[])
+static void do_mod_flows(int argc, char *argv[])
{
+ uint16_t idle_timeout, hard_timeout;
struct vconn *vconn;
- uint16_t priority;
+ struct buffer *buffer;
+ struct ofp_flow_mod *ofm;
+ size_t size;
+ int n_actions = MAX_ADD_ACTS;
+
+ /* Parse and send. */
+ size = sizeof *ofm + (sizeof ofm->actions[0] * MAX_ADD_ACTS);
+ ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
+ str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &n_actions,
+ NULL, NULL, &idle_timeout, &hard_timeout);
+ ofm->command = htons(OFPFC_MODIFY);
+ ofm->idle_timeout = htons(idle_timeout);
+ ofm->hard_timeout = htons(hard_timeout);
+ ofm->buffer_id = htonl(UINT32_MAX);
+ ofm->priority = htons(0);
+ ofm->reserved = htonl(0);
+
+ /* xxx Should we use the buffer library? */
+ buffer->size -= (MAX_ADD_ACTS - n_actions) * sizeof ofm->actions[0];
open_vconn(argv[1], &vconn);
+ send_openflow_buffer(vconn, buffer);
+ vconn_close(vconn);
+}
+
+static void do_del_flows(int argc, char *argv[])
+{
+ struct vconn *vconn;
+ uint16_t priority;
struct buffer *buffer;
struct ofp_flow_mod *ofm;
size_t size;
-
/* Parse and send. */
size = sizeof *ofm;
ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
ofm->priority = htons(priority);
ofm->reserved = htonl(0);
+ open_vconn(argv[1], &vconn);
send_openflow_buffer(vconn, buffer);
-
vconn_close(vconn);
}
{ "dump-aggregate", 1, 2, do_dump_aggregate },
{ "add-flow", 2, 2, do_add_flow },
{ "add-flows", 2, 2, do_add_flows },
+ { "mod-flows", 2, 2, do_mod_flows },
{ "del-flows", 1, 2, do_del_flows },
{ "dump-ports", 1, 1, do_dump_ports },
{ "mod-port", 3, 3, do_mod_port },