Add support for OFPFC_MODIFY Flow Mod command.
authorJustin Pettit <jpettit@nicira.com>
Mon, 15 Sep 2008 22:27:04 +0000 (15:27 -0700)
committerJustin Pettit <jpettit@nicira.com>
Mon, 15 Sep 2008 22:27:04 +0000 (15:27 -0700)
The OFPFC_MODIFY command allows the controller to modify the actions of
existing flows.  When it does this, it does not reset counters or timers.

23 files changed:
datapath/chain.c
datapath/chain.h
datapath/datapath.c
datapath/flow.c
datapath/flow.h
datapath/forward.c
datapath/hwtable_dummy/hwtable_dummy.c
datapath/table-hash.c
datapath/table-linear.c
datapath/table.h
datapath/table_t.c
include/openflow.h
lib/ofp-print.c
switch/chain.c
switch/chain.h
switch/datapath.c
switch/switch-flow.c
switch/switch-flow.h
switch/table-hash.c
switch/table-linear.c
switch/table.h
utilities/dpctl.8
utilities/dpctl.c

index 87b8b79..36749f3 100644 (file)
@@ -101,6 +101,27 @@ int chain_insert(struct sw_chain *chain, struct sw_flow *flow)
        return -ENOBUFS;
 }
 
+/* Modifies actions in 'chain' that match 'key'.  Returns the number of 
+ * flows that were modified.
+ *
+ * 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.
  *
index 42208d6..dbbae41 100644 (file)
@@ -5,6 +5,7 @@
 
 struct sw_flow;
 struct sw_flow_key;
+struct ofp_action;
 struct datapath;
 
 
@@ -24,6 +25,8 @@ struct sw_chain {
 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 *);
index 8eb0660..598b347 100644 (file)
@@ -1272,13 +1272,14 @@ static int flow_stats_init(struct datapath *dp, const void *body, int body_len,
 
 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;
 
@@ -1305,7 +1306,7 @@ static int flow_stats_dump_callback(struct sw_flow *flow, void *private)
        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;
index 71dd823..2093dbb 100644 (file)
@@ -142,12 +142,12 @@ void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from)
        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)
@@ -168,17 +168,20 @@ EXPORT_SYMBOL(flow_timeout);
  * 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;
 }
 
@@ -187,14 +190,13 @@ void flow_free(struct sw_flow *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);
@@ -204,10 +206,49 @@ static void rcu_callback(struct rcu_head *rcu)
  * 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)
 {
index cd58327..3b25ad5 100644 (file)
@@ -56,6 +56,16 @@ static inline void check_key_align(void)
        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
@@ -69,11 +79,9 @@ struct sw_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;
@@ -96,6 +104,8 @@ int flow_del_matches(const struct sw_flow_key *, const struct sw_flow_key *,
 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);
index 4334655..7d77571 100644 (file)
@@ -56,9 +56,10 @@ int run_flow_through_tables(struct sw_chain *chain, struct sk_buff *skb,
 
        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;
@@ -454,12 +455,12 @@ add_flow(struct sw_chain *chain, const struct ofp_flow_mod *ofm)
        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);
@@ -487,6 +488,53 @@ error:
        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)
 {
@@ -495,6 +543,8 @@ 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);
index e3e0eb4..b78d364 100644 (file)
@@ -97,6 +97,24 @@ static int table_dummy_insert(struct sw_table *swt, struct sw_flow *flow)
        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)
 {
@@ -233,6 +251,7 @@ static struct sw_table *table_dummy_create(void)
        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;
index b31e8f0..f6a41de 100644 (file)
@@ -75,6 +75,35 @@ static int table_hash_insert(struct sw_table *swt, struct sw_flow *flow)
        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)
 {
@@ -261,6 +290,15 @@ static int table_hash2_insert(struct sw_table *swt, 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)
@@ -344,6 +382,7 @@ struct sw_table *table_hash2_create(unsigned int poly0, unsigned int buckets0,
        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;
index a9c7dcc..4a10df5 100644 (file)
@@ -77,6 +77,23 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow)
        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);
@@ -181,6 +198,7 @@ struct sw_table *table_linear_create(unsigned int max_flows)
        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;
index c47e1e6..8cbcfb7 100644 (file)
@@ -8,6 +8,7 @@
 
 struct sw_flow;
 struct sw_flow_key;
+struct ofp_action;
 struct datapath;
 
 /* Table statistics. */
@@ -54,6 +55,11 @@ struct sw_table {
         * retained by the caller. */
        int (*insert)(struct sw_table *table, struct sw_flow *flow);
 
+       /* Modifies the actions in 'table' that match 'key'.  Returns the
+        * number of flows that were modified. */
+       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. */
index 5f7cd1c..a824804 100644 (file)
@@ -35,9 +35,9 @@ static struct sw_flow *flow_zalloc(int n_actions, gfp_t flags)
 {
        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;
 }
index 77dbc52..e04cfbe 100644 (file)
@@ -353,7 +353,6 @@ OFP_ASSERT(sizeof(struct ofp_packet_out) == 16);
 enum ofp_flow_mod_command {
     OFPFC_ADD,              /* New flow. */
     OFPFC_MODIFY,           /* Modify all matching flows. */
-    OFPFC_MODIFY_STRICT,    /* Strictly match wildcards and priority. */
     OFPFC_DELETE,           /* Delete all matching flows. */
     OFPFC_DELETE_STRICT     /* Strictly match wildcards and priority. */
 };
@@ -433,10 +432,11 @@ struct ofp_flow_mod {
     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);
 
index 1f16396..95d180b 100644 (file)
@@ -558,9 +558,24 @@ ofp_print_flow_mod(struct ds *string, const void *oh, size_t len,
     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,
index 8419070..0388951 100644 (file)
@@ -114,6 +114,27 @@ chain_insert(struct sw_chain *chain, struct sw_flow *flow)
     return -ENOBUFS;
 }
 
+/* Modifies actions in 'chain' that match 'key'.  Returns the number of 
+ * flows that were modified.
+ *
+ * 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.
  *
index 7e5b0ad..eaccad4 100644 (file)
@@ -38,6 +38,7 @@
 
 struct sw_flow;
 struct sw_flow_key;
+struct ofp_action;
 struct list;
 
 #define TABLE_LINEAR_MAX_FLOWS  100
@@ -55,6 +56,8 @@ struct sw_chain {
 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 *);
index 7ed4f2d..24617ef 100644 (file)
@@ -809,7 +809,7 @@ fill_flow_stats(struct buffer *buffer, struct sw_flow *flow,
                 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;
@@ -833,8 +833,8 @@ fill_flow_stats(struct buffer *buffer, struct sw_flow *flow,
     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
@@ -866,7 +866,8 @@ int run_flow_through_tables(struct datapath *dp, struct buffer *buffer,
     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;
@@ -1178,10 +1179,11 @@ add_flow(struct datapath *dp, const struct ofp_flow_mod *ofm)
     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);
@@ -1212,6 +1214,55 @@ error:
     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)
@@ -1221,6 +1272,8 @@ recv_flow(struct datapath *dp, const struct sender *sender UNUSED,
 
     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);
index 16a7177..cc462c0 100644 (file)
@@ -171,16 +171,18 @@ flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from)
 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;
 }
 
@@ -191,12 +193,31 @@ flow_free(struct sw_flow *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)
index 0f6c721..0eb8c89 100644 (file)
@@ -35,6 +35,7 @@
 #define SWITCH_FLOW_H 1
 
 #include <time.h>
+#include "openflow.h"
 #include "flow.h"
 #include "list.h"
 
@@ -48,6 +49,11 @@ struct sw_flow_key {
     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;
 
@@ -60,14 +66,12 @@ struct sw_flow {
     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 *);
@@ -77,6 +81,8 @@ int flow_del_matches(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);
 
index 7e675a1..d8d17db 100644 (file)
@@ -97,6 +97,35 @@ static int table_hash_insert(struct sw_table *swt, struct sw_flow *flow)
     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)
@@ -234,6 +263,7 @@ struct sw_table *table_hash_create(unsigned int polynomial,
     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;
@@ -275,6 +305,15 @@ static int table_hash2_insert(struct sw_table *swt, 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)
@@ -359,6 +398,7 @@ struct sw_table *table_hash2_create(unsigned int poly0, unsigned int buckets0,
     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;
index 355bda2..474d5ee 100644 (file)
@@ -105,6 +105,23 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow)
     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) 
 {
@@ -207,6 +224,7 @@ struct sw_table *table_linear_create(unsigned int max_flows)
     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;
index 1068a48..9a48c4b 100644 (file)
@@ -41,6 +41,7 @@
 
 struct sw_flow;
 struct sw_flow_key;
+struct ofp_action;
 struct list;
 
 /* Table statistics. */
@@ -83,6 +84,11 @@ struct sw_table {
      * retained by the caller. */
     int (*insert)(struct sw_table *table, struct sw_flow *flow);
 
+    /* Modifies entries in 'table' that match 'key'.  Returns the
+     * number of flows that were modified. */
+    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. */
index 4c863e5..870ffb9 100644 (file)
@@ -169,6 +169,12 @@ Add flow entries as described in \fIfile\fR to the datapath \fIswitch\fR's
 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
index af0593f..bd89065 100644 (file)
@@ -204,7 +204,8 @@ usage(void)
            "  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"
@@ -799,8 +800,6 @@ static void do_add_flow(int argc, char *argv[])
     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);
@@ -816,6 +815,7 @@ static void do_add_flow(int argc, char *argv[])
     /* 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);
 }
@@ -823,7 +823,6 @@ static void do_add_flow(int argc, char *argv[])
 static void do_add_flows(int argc, char *argv[])
 {
     struct vconn *vconn;
-
     FILE *file;
     char line[1024];
 
@@ -874,17 +873,43 @@ static void do_add_flows(int argc, char *argv[])
     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);
@@ -897,8 +922,8 @@ static void do_del_flows(int argc, char *argv[])
     ofm->priority = htons(priority);
     ofm->reserved = htonl(0);
 
+    open_vconn(argv[1], &vconn);
     send_openflow_buffer(vconn, buffer);
-
     vconn_close(vconn);
 }
 
@@ -1140,6 +1165,7 @@ static struct command all_commands[] = {
     { "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 },