Add support for vendor-defined and variable-length actions.
authorJustin Pettit <jpettit@nicira.com>
Fri, 3 Oct 2008 23:44:13 +0000 (16:44 -0700)
committerJustin Pettit <jpettit@nicira.com>
Fri, 3 Oct 2008 23:44:13 +0000 (16:44 -0700)
Allow vendors to define their own actions.  Actions were originally fixed-
length, which was a bit constraining.  Actions now contain a length field,
which gives them more flexibility.

37 files changed:
datapath/Modules.mk
datapath/chain.c
datapath/chain.h
datapath/datapath.c
datapath/dp_act.c [new file with mode: 0644]
datapath/dp_act.h [new file with mode: 0644]
datapath/flow.c
datapath/flow.h
datapath/forward.c
datapath/forward.h
datapath/hwtable_dummy/hwtable_dummy.c
datapath/linux-2.4/.gitignore
datapath/linux-2.6/.gitignore
datapath/nx_act.c [new file with mode: 0644]
datapath/nx_act.h [new file with mode: 0644]
datapath/table-hash.c
datapath/table-linear.c
datapath/table.h
include/openflow.h
include/vconn.h
lib/ofp-print.c
lib/vconn.c
switch/Makefile.am
switch/chain.c
switch/chain.h
switch/datapath.c
switch/datapath.h
switch/dp_act.c [new file with mode: 0644]
switch/dp_act.h [new file with mode: 0644]
switch/nx_act.c [new file with mode: 0644]
switch/nx_act.h [new file with mode: 0644]
switch/switch-flow.c
switch/switch-flow.h
switch/table-hash.c
switch/table-linear.c
switch/table.h
utilities/dpctl.c

index 516ebd9df09227a5164b3c2ba16d473760167853..ab5cf87f8b1f84cd1fbe88b2bb59c7bed64c4799 100644 (file)
@@ -5,10 +5,12 @@ openflow_sources = \
        chain.c \
        crc32.c \
        datapath.c \
+       dp_act.c \
        dp_dev.c \
        dp_notify.c \
        flow.c \
        forward.c \
+       nx_act.c \
        table-hash.c \
        table-linear.c 
 
index 7d980566063be4a97055e17b430ca3add0d59411..cd3b6e53eb28b10a7bbcd8c4e5844ae43881a7c6 100644 (file)
@@ -111,14 +111,14 @@ int chain_insert(struct sw_chain *chain, struct sw_flow *flow)
 int
 chain_modify(struct sw_chain *chain, const struct sw_flow_key *key, 
                uint16_t priority, int strict,
-               const struct ofp_action *actions, int n_actions)
+               const struct ofp_action_header *actions, size_t actions_len)
 {
        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, priority, strict, actions, n_actions);
+               count += t->modify(t, key, priority, strict, actions, actions_len);
        }
 
        return count;
index a7d044eee612273fc338f146b20a7b05bd36d75f..26edd47c0c553e264e499db01e9374f16b264229 100644 (file)
@@ -5,7 +5,7 @@
 
 struct sw_flow;
 struct sw_flow_key;
-struct ofp_action;
+struct ofp_action_header;
 struct datapath;
 
 
@@ -26,7 +26,7 @@ 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 *, 
-               uint16_t, int, const struct ofp_action *, int);
+               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_timeout(struct sw_chain *);
 void chain_destroy(struct sw_chain *);
index 056f186ee08ee3f1cdd5a9732e9c067c2b632eb6..d84240bc24795110109dbc14f9de3197cbecc5cb 100644 (file)
@@ -1308,11 +1308,9 @@ 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 * sf_acts->n_actions;
-       length = sizeof *ofs + actions_length;
+       length = sizeof *ofs + sf_acts->actions_len;
        if (length + s->bytes_used > s->bytes_allocated)
                return 1;
 
@@ -1339,7 +1337,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, sf_acts->actions, actions_length);
+       memcpy(ofs->actions, sf_acts->actions, sf_acts->actions_len);
 
        s->bytes_used += length;
        return 0;
diff --git a/datapath/dp_act.c b/datapath/dp_act.c
new file mode 100644 (file)
index 0000000..3401c4a
--- /dev/null
@@ -0,0 +1,529 @@
+/*
+ * Distributed under the terms of the GNU GPL version 2.
+ * Copyright (c) 2007, 2008 The Board of Trustees of The Leland 
+ * Stanford Junior University
+ */
+
+/* Functions for executing OpenFlow actions. */
+
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/in6.h>
+#include <linux/if_vlan.h>
+#include <net/checksum.h>
+#include "forward.h"
+#include "dp_act.h"
+#include "nicira-ext.h"
+#include "nx_act.h"
+
+static int make_writable(struct sk_buff **);
+
+
+static uint16_t
+validate_output(struct datapath *dp, const struct sw_flow_key *key, 
+               const struct ofp_action_header *ah) 
+{
+       struct ofp_action_output *oa = (struct ofp_action_output *)ah;
+
+       if (oa->port == htons(OFPP_NONE) || oa->port == key->in_port)
+               return OFPBAC_BAD_OUT_PORT;
+
+       return ACT_VALIDATION_OK;
+}
+
+static int 
+do_output(struct datapath *dp, struct sk_buff *skb, size_t max_len,
+               int out_port, int ignore_no_fwd)
+{
+       if (!skb)
+               return -ENOMEM;
+       return (likely(out_port != OFPP_CONTROLLER)
+               ? dp_output_port(dp, skb, out_port, ignore_no_fwd)
+               : dp_output_control(dp, skb, fwd_save_skb(skb),
+                                        max_len, OFPR_ACTION));
+}
+
+
+static struct sk_buff *
+vlan_pull_tag(struct sk_buff *skb)
+{
+       struct vlan_ethhdr *vh = vlan_eth_hdr(skb);
+       struct ethhdr *eh;
+
+
+       /* Verify we were given a vlan packet */
+       if (vh->h_vlan_proto != htons(ETH_P_8021Q))
+               return skb;
+
+       memmove(skb->data + VLAN_HLEN, skb->data, 2 * VLAN_ETH_ALEN);
+
+       eh = (struct ethhdr *)skb_pull(skb, VLAN_HLEN);
+
+       skb->protocol = eh->h_proto;
+       skb->mac_header += VLAN_HLEN;
+
+       return skb;
+}
+
+
+static struct sk_buff *
+modify_vlan_tci(struct sk_buff *skb, struct sw_flow_key *key, 
+               uint16_t tci, uint16_t mask)
+{
+       struct vlan_ethhdr *vh = vlan_eth_hdr(skb);
+
+       if (key->dl_vlan != htons(OFP_VLAN_NONE)) {
+               /* Modify vlan id, but maintain other TCI values */
+               vh->h_vlan_TCI = (vh->h_vlan_TCI & ~(htons(mask))) | htons(tci);
+       } else  {
+               /* Add vlan header */
+
+               /* xxx The vlan_put_tag function, doesn't seem to work
+                * xxx reliably when it attempts to use the hardware-accelerated
+                * xxx version.  We'll directly use the software version
+                * xxx until the problem can be diagnosed.
+                */
+               skb = __vlan_put_tag(skb, tci);
+               vh = vlan_eth_hdr(skb);
+       }
+       key->dl_vlan = vh->h_vlan_TCI & htons(VLAN_VID_MASK);
+
+       return skb;
+}
+
+static struct sk_buff *
+set_vlan_vid(struct sk_buff *skb, struct sw_flow_key *key, 
+               const struct ofp_action_header *ah)
+{
+       struct ofp_action_vlan_vid *va = (struct ofp_action_vlan_vid *)ah;
+       uint16_t tci = ntohs(va->vlan_vid);
+
+       return modify_vlan_tci(skb, key, tci, VLAN_VID_MASK);
+}
+
+/* Mask for the priority bits in a vlan header.  The kernel doesn't
+ * define this like it does for VID. */
+#define VLAN_PCP_MASK 0xe000
+
+static struct sk_buff *
+set_vlan_pcp(struct sk_buff *skb, struct sw_flow_key *key, 
+               const struct ofp_action_header *ah)
+{
+       struct ofp_action_vlan_pcp *va = (struct ofp_action_vlan_pcp *)ah;
+       uint16_t tci = (uint16_t)va->vlan_pcp << 13;
+
+       return modify_vlan_tci(skb, key, tci, VLAN_PCP_MASK);
+}
+
+static struct sk_buff *
+strip_vlan(struct sk_buff *skb, struct sw_flow_key *key, 
+               const struct ofp_action_header *ah)
+{
+       vlan_pull_tag(skb);
+       key->dl_vlan = htons(OFP_VLAN_NONE);
+
+       return skb;
+}
+
+static struct sk_buff *
+set_dl_addr(struct sk_buff *skb, struct sw_flow_key *key, 
+               const struct ofp_action_header *ah)
+{
+       struct ofp_action_dl_addr *da = (struct ofp_action_dl_addr *)ah;
+       struct ethhdr *eh = eth_hdr(skb);
+
+       if (da->type == htons(OFPAT_SET_DL_SRC))
+               memcpy(eh->h_source, da->dl_addr, sizeof eh->h_source);
+       else 
+               memcpy(eh->h_dest, da->dl_addr, sizeof eh->h_dest);
+
+       return skb;
+}
+
+/* Updates 'sum', which is a field in 'skb''s data, given that a 4-byte field
+ * covered by the sum has been changed from 'from' to 'to'.  If set,
+ * 'pseudohdr' indicates that the field is in the TCP or UDP pseudo-header.
+ * Based on nf_proto_csum_replace4. */
+static void update_csum(__sum16 *sum, struct sk_buff *skb,
+                       __be32 from, __be32 to, int pseudohdr)
+{
+       __be32 diff[] = { ~from, to };
+       if (skb->ip_summed != CHECKSUM_PARTIAL) {
+               *sum = csum_fold(csum_partial((char *)diff, sizeof(diff),
+                               ~csum_unfold(*sum)));
+               if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr)
+                       skb->csum = ~csum_partial((char *)diff, sizeof(diff),
+                                               ~skb->csum);
+       } else if (pseudohdr)
+               *sum = ~csum_fold(csum_partial((char *)diff, sizeof(diff),
+                               csum_unfold(*sum)));
+}
+
+static struct sk_buff * 
+set_nw_addr(struct sk_buff *skb, struct sw_flow_key *key, 
+               const struct ofp_action_header *ah)
+{
+       struct ofp_action_nw_addr *na = (struct ofp_action_nw_addr *)ah;
+       uint16_t eth_proto = ntohs(key->dl_type);
+
+       if (eth_proto == ETH_P_IP) {
+               struct iphdr *nh = ip_hdr(skb);
+               uint32_t new, *field;
+
+               new = na->nw_addr;
+
+               if (ah->type == htons(OFPAT_SET_NW_SRC))
+                       field = &nh->saddr;
+               else
+                       field = &nh->daddr;
+
+               if (key->nw_proto == IPPROTO_TCP) {
+                       struct tcphdr *th = tcp_hdr(skb);
+                       update_csum(&th->check, skb, *field, new, 1);
+               } else if (key->nw_proto == IPPROTO_UDP) {
+                       struct udphdr *th = udp_hdr(skb);
+                       update_csum(&th->check, skb, *field, new, 1);
+               }
+               update_csum(&nh->check, skb, *field, new, 0);
+               *field = new;
+       }
+
+       return skb;
+}
+
+static struct sk_buff *
+set_tp_port(struct sk_buff *skb, struct sw_flow_key *key, 
+               const struct ofp_action_header *ah)
+{
+       struct ofp_action_tp_port *ta = (struct ofp_action_tp_port *)ah;
+       uint16_t eth_proto = ntohs(key->dl_type);
+
+       if (eth_proto == ETH_P_IP) {
+               uint16_t new, *field;
+
+               new = ta->tp_port;
+
+               if (key->nw_proto == IPPROTO_TCP) {
+                       struct tcphdr *th = tcp_hdr(skb);
+
+                       if (ah->type == htons(OFPAT_SET_TP_SRC))
+                               field = &th->source;
+                       else
+                               field = &th->dest;
+
+                       update_csum(&th->check, skb, *field, new, 1);
+                       *field = new;
+               } else if (key->nw_proto == IPPROTO_UDP) {
+                       struct udphdr *th = udp_hdr(skb);
+
+                       if (ah->type == htons(OFPAT_SET_TP_SRC))
+                               field = &th->source;
+                       else
+                               field = &th->dest;
+
+                       update_csum(&th->check, skb, *field, new, 1);
+                       *field = new;
+               }
+       }
+
+       return skb;
+}
+
+struct openflow_action {
+       size_t min_size;
+       size_t max_size;
+       uint16_t (*validate)(struct datapath *dp, 
+                       const struct sw_flow_key *key,
+                       const struct ofp_action_header *ah);
+       struct sk_buff *(*execute)(struct sk_buff *skb, 
+                       struct sw_flow_key *key, 
+                       const struct ofp_action_header *ah);
+};
+
+static const struct openflow_action of_actions[] = {
+       [OFPAT_OUTPUT] = {
+               sizeof(struct ofp_action_output),
+               sizeof(struct ofp_action_output),
+               validate_output,
+               NULL                   /* This is optimized into execute_actions */
+       },
+       [OFPAT_SET_VLAN_VID] = {
+               sizeof(struct ofp_action_vlan_vid),
+               sizeof(struct ofp_action_vlan_vid),
+               NULL,
+               set_vlan_vid
+       },
+       [OFPAT_SET_VLAN_PCP] = {
+               sizeof(struct ofp_action_vlan_pcp),
+               sizeof(struct ofp_action_vlan_pcp),
+               NULL,
+               set_vlan_pcp
+       },
+       [OFPAT_STRIP_VLAN] = {
+               sizeof(struct ofp_action_header),
+               sizeof(struct ofp_action_header),
+               NULL,
+               strip_vlan
+       },
+       [OFPAT_SET_DL_SRC] = {
+               sizeof(struct ofp_action_dl_addr),
+               sizeof(struct ofp_action_dl_addr),
+               NULL,
+               set_dl_addr
+       },
+       [OFPAT_SET_DL_DST] = {
+               sizeof(struct ofp_action_dl_addr),
+               sizeof(struct ofp_action_dl_addr),
+               NULL,
+               set_dl_addr
+       },
+       [OFPAT_SET_NW_SRC] = {
+               sizeof(struct ofp_action_nw_addr),
+               sizeof(struct ofp_action_nw_addr),
+               NULL,
+               set_nw_addr
+       },
+       [OFPAT_SET_NW_DST] = {
+               sizeof(struct ofp_action_nw_addr),
+               sizeof(struct ofp_action_nw_addr),
+               NULL,
+               set_nw_addr
+       },
+       [OFPAT_SET_TP_SRC] = {
+               sizeof(struct ofp_action_tp_port),
+               sizeof(struct ofp_action_tp_port),
+               NULL,
+               set_tp_port
+       },
+       [OFPAT_SET_TP_DST] = {
+               sizeof(struct ofp_action_tp_port),
+               sizeof(struct ofp_action_tp_port),
+               NULL,
+               set_tp_port
+       }
+       /* OFPAT_VENDOR is not here, since it would blow up the array size. */
+};
+
+/* Validate built-in OpenFlow actions.  Either returns ACT_VALIDATION_OK
+ * or an OFPET_BAD_ACTION error code. */
+static uint16_t 
+validate_ofpat(struct datapath *dp, const struct sw_flow_key *key, 
+               const struct ofp_action_header *ah, uint16_t type, uint16_t len)
+{
+       int ret = ACT_VALIDATION_OK;
+       const struct openflow_action *act = &of_actions[type];
+
+       if ((len < act->min_size) || (len > act->max_size)) 
+               return OFPBAC_BAD_LEN;
+
+       if (act->validate) 
+               ret = act->validate(dp, key, ah);
+
+       return ret;
+}
+
+/* Validate vendor-defined actions.  Either returns ACT_VALIDATION_OK
+ * or an OFPET_BAD_ACTION error code. */
+static uint16_t 
+validate_vendor(struct datapath *dp, const struct sw_flow_key *key, 
+               const struct ofp_action_header *ah, uint16_t len)
+{
+       struct ofp_action_vendor_header *avh;
+       int ret = ACT_VALIDATION_OK;
+
+       if (len < sizeof(struct ofp_action_vendor_header))
+               return OFPBAC_BAD_LEN;
+
+       avh = (struct ofp_action_vendor_header *)ah;
+
+       switch(ntohl(avh->vendor)) {
+       case NX_VENDOR_ID: 
+               ret = nx_validate_act(dp, key, avh, len);
+               break;
+
+       default:
+               return OFPBAC_BAD_VENDOR;
+       }
+
+       return ret;
+}
+
+/* Validates a list of actions.  If a problem is found, a code for the
+ * OFPET_BAD_ACTION error type is returned.  If the action list validates, 
+ * ACT_VALIDATION_OK is returned. */
+uint16_t 
+validate_actions(struct datapath *dp, const struct sw_flow_key *key,
+               const struct ofp_action_header *actions, size_t actions_len)
+{
+       uint8_t *p = (uint8_t *)actions;
+       int err;
+
+       while (actions_len >= sizeof(struct ofp_action_header)) {
+               struct ofp_action_header *ah = (struct ofp_action_header *)p;
+               size_t len = ntohs(ah->len);
+               uint16_t type;
+
+               /* Make there's enough remaining data for the specified length
+                * and that the action length is a multiple of 64 bits. */
+               if ((actions_len < len) || (len % 8) != 0)
+                       return OFPBAC_BAD_LEN;
+
+               type = ntohs(ah->type);
+               if (type < ARRAY_SIZE(of_actions)) {
+                       err = validate_ofpat(dp, key, ah, type, len);
+                       if (err != ACT_VALIDATION_OK)
+                               return err;
+               } else if (type == OFPAT_VENDOR) {
+                       err = validate_vendor(dp, key, ah, len);
+                       if (err != ACT_VALIDATION_OK)
+                               return err;
+               } else 
+                       return OFPBAC_BAD_TYPE;
+
+               p += len;
+               actions_len -= len;
+       }
+
+       /* Check if there's any trailing garbage. */
+       if (actions_len != 0) 
+               return OFPBAC_BAD_LEN;
+
+       return ACT_VALIDATION_OK;
+}
+
+/* Execute a built-in OpenFlow action against 'skb'. */
+static struct sk_buff *
+execute_ofpat(struct sk_buff *skb, struct sw_flow_key *key, 
+               const struct ofp_action_header *ah, uint16_t type)
+{
+       const struct openflow_action *act = &of_actions[type];
+
+       if (act->execute)  {
+               if (!make_writable(&skb)) {
+                       if (net_ratelimit())
+                               printk("make_writable failed\n");
+                       return skb;
+               }
+               skb = act->execute(skb, key, ah);
+       }
+
+       return skb;
+}
+
+/* Execute a vendor-defined action against 'skb'. */
+static struct sk_buff *
+execute_vendor(struct sk_buff *skb, const struct sw_flow_key *key, 
+               const struct ofp_action_header *ah)
+{
+       struct ofp_action_vendor_header *avh 
+                       = (struct ofp_action_vendor_header *)ah;
+
+       /* NB: If changes need to be made to the packet, a call should be
+        * made to make_writable or its equivalent first. */
+
+       switch(ntohl(avh->vendor)) {
+       case NX_VENDOR_ID: 
+               skb = nx_execute_act(skb, key, avh);
+               break;
+
+       default:
+               /* This should not be possible due to prior validation. */
+               if (net_ratelimit())
+                       printk("attempt to execute action with unknown vendor: %#x\n", 
+                                       ntohl(avh->vendor));
+               break;
+       }
+
+       return skb;
+}
+
+/* Execute a list of actions against 'skb'. */
+void execute_actions(struct datapath *dp, struct sk_buff *skb,
+                    struct sw_flow_key *key,
+                    const struct ofp_action_header *actions, size_t actions_len,
+                    int ignore_no_fwd)
+{
+       /* Every output action needs a separate clone of 'skb', but the common
+        * case is just a single output action, so that doing a clone and
+        * then freeing the original skbuff is wasteful.  So the following code
+        * is slightly obscure just to avoid that. */
+       int prev_port;
+       size_t max_len=0;        /* Initialze to make compiler happy */
+       uint8_t *p = (uint8_t *)actions;
+
+       prev_port = -1;
+
+       /* The action list was already validated, so we can be a bit looser
+        * in our sanity-checking. */
+       while (actions_len > 0) {
+               struct ofp_action_header *ah = (struct ofp_action_header *)p;
+               size_t len = htons(ah->len);
+
+               if (prev_port != -1) {
+                       do_output(dp, skb_clone(skb, GFP_ATOMIC),
+                                 max_len, prev_port, ignore_no_fwd);
+                       prev_port = -1;
+               }
+
+               if (likely(ah->type == htons(OFPAT_OUTPUT))) {
+                       struct ofp_action_output *oa = (struct ofp_action_output *)p;
+                       prev_port = ntohs(oa->port);
+                       max_len = ntohs(oa->max_len);
+               } else {
+                       uint16_t type = ntohs(ah->type);
+
+                       if (type < ARRAY_SIZE(of_actions)) 
+                               skb = execute_ofpat(skb, key, ah, type);
+                       else if (type == OFPAT_VENDOR) 
+                               skb = execute_vendor(skb, key, ah);
+
+                       if (!skb) {
+                               if (net_ratelimit())
+                                       printk("execute_actions lost skb\n");
+                               return;
+                       }
+               }
+
+               p += len;
+               actions_len -= len;
+       }
+       if (prev_port != -1)
+               do_output(dp, skb, max_len, prev_port, ignore_no_fwd);
+       else
+               kfree_skb(skb);
+}
+
+/* Utility functions. */
+
+/* Makes '*pskb' writable, possibly copying it and setting '*pskb' to point to
+ * the copy.
+ * Returns 1 if successful, 0 on failure. */
+static int
+make_writable(struct sk_buff **pskb)
+{
+       /* Based on skb_make_writable() in net/netfilter/core.c. */
+       struct sk_buff *nskb;
+
+       /* Not exclusive use of packet?  Must copy. */
+       if (skb_shared(*pskb) || skb_cloned(*pskb))
+               goto copy_skb;
+
+       return pskb_may_pull(*pskb, 40); /* FIXME? */
+
+copy_skb:
+       nskb = skb_copy(*pskb, GFP_ATOMIC);
+       if (!nskb)
+               return 0;
+       BUG_ON(skb_is_nonlinear(nskb));
+
+       /* Rest of kernel will get very unhappy if we pass it a
+          suddenly-orphaned skbuff */
+       if ((*pskb)->sk)
+               skb_set_owner_w(nskb, (*pskb)->sk);
+       kfree_skb(*pskb);
+       *pskb = nskb;
+       return 1;
+}
diff --git a/datapath/dp_act.h b/datapath/dp_act.h
new file mode 100644 (file)
index 0000000..a93d20f
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef DP_ACT_H
+#define DP_ACT_H 1
+
+#include "datapath.h"
+
+#define ACT_VALIDATION_OK ((uint16_t)-1)
+
+uint16_t validate_actions(struct datapath *, const struct sw_flow_key *,
+               const struct ofp_action_header *, size_t);
+void execute_actions(struct datapath *, struct sk_buff *,
+               struct sw_flow_key *, const struct ofp_action_header *, 
+               size_t action_len, int ignore_no_fwd);
+
+#endif /* dp_act.h */
index ae2f78792d8bc6a0b3c4c90b481d9a0873ea344b..9dcec90afe01bf72f4a4b4b50b889b9e74c33866 100644 (file)
@@ -165,12 +165,13 @@ int flow_timeout(struct sw_flow *flow)
 }
 EXPORT_SYMBOL(flow_timeout);
 
-/* Allocates and returns a new flow with 'n_actions' action, using allocation
- * flags 'flags'.  Returns the new flow or a null pointer on failure. */
-struct sw_flow *flow_alloc(int n_actions, gfp_t flags)
+/* 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. */
+struct sw_flow *flow_alloc(size_t actions_len, gfp_t flags)
 {
        struct sw_flow_actions *sfa;
-       int size = sizeof *sfa + (n_actions * sizeof sfa->actions[0]);
+       size_t size = sizeof *sfa + actions_len;
        struct sw_flow *flow = kmem_cache_alloc(flow_cache, flags);
        if (unlikely(!flow))
                return NULL;
@@ -180,7 +181,7 @@ struct sw_flow *flow_alloc(int n_actions, gfp_t flags)
                kmem_cache_free(flow_cache, flow);
                return NULL;
        }
-       sfa->n_actions = n_actions;
+       sfa->actions_len = actions_len;
        flow->sf_acts = sfa;
 
        return flow;
@@ -229,19 +230,19 @@ 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)
+void flow_replace_acts(struct sw_flow *flow, 
+               const struct ofp_action_header *actions, size_t actions_len)
 {
        struct sw_flow_actions *sfa;
        struct sw_flow_actions *orig_sfa = flow->sf_acts;
-       int size = sizeof *sfa + (n_actions * sizeof sfa->actions[0]);
+       size_t size = sizeof *sfa + actions_len;
 
        sfa = kmalloc(size, GFP_ATOMIC);
        if (unlikely(!sfa))
                return;
 
-       sfa->n_actions = n_actions;
-       memcpy(sfa->actions, actions, n_actions * sizeof sfa->actions[0]);
+       sfa->actions_len = actions_len;
+       memcpy(sfa->actions, actions, actions_len);
 
        rcu_assign_pointer(flow->sf_acts, sfa);
        flow_deferred_free_acts(orig_sfa);
index 8c0c27feed228374b5fb18bf551749b5ea3b53cf..b425f35d35435fdd6027de76f3fd555e7add3ba1 100644 (file)
@@ -60,10 +60,10 @@ static inline void check_key_align(void)
  * swap them out atomically when the modify command comes from a Flow
  * Modify message. */
 struct sw_flow_actions {
-       unsigned int n_actions;
+       size_t actions_len;
        struct rcu_head rcu;
 
-       struct ofp_action actions[0];
+       struct ofp_action_header actions[0];
 };
 
 /* Locking:
@@ -101,11 +101,12 @@ 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);
-struct sw_flow *flow_alloc(int n_actions, gfp_t flags);
+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 *);
 void flow_deferred_free_acts(struct sw_flow_actions *);
-void flow_replace_acts(struct sw_flow *, const struct ofp_action *, int);
+void flow_replace_acts(struct sw_flow *, const struct ofp_action_header *, 
+               size_t);
 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 db5e751ecbc594bd11d88791db4cd74a2c46feef..9bbb030b6d414ddd2941687a37aecb3cf924b2cb 100644 (file)
@@ -8,22 +8,16 @@
 #include <linux/etherdevice.h>
 #include <linux/if_ether.h>
 #include <linux/if_vlan.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/udp.h>
-#include <linux/in6.h>
 #include <asm/uaccess.h>
 #include <linux/types.h>
-#include <net/checksum.h>
 #include "forward.h"
 #include "datapath.h"
+#include "dp_act.h"
 #include "chain.h"
 #include "flow.h"
 
 /* FIXME: do we need to use GFP_ATOMIC everywhere here? */
 
-static int make_writable(struct sk_buff **);
 
 static struct sk_buff *retrieve_skb(uint32_t id);
 static void discard_skb(uint32_t id);
@@ -59,7 +53,7 @@ int run_flow_through_tables(struct sw_chain *chain, struct sk_buff *skb,
                struct sw_flow_actions *sf_acts = rcu_dereference(flow->sf_acts);
                flow_used(flow, skb);
                execute_actions(chain->dp, skb, &key,
-                               sf_acts->actions, sf_acts->n_actions, 0);
+                               sf_acts->actions, sf_acts->actions_len, 0);
                return 0;
        } else {
                return -ESRCH;
@@ -78,241 +72,6 @@ void fwd_port_input(struct sw_chain *chain, struct sk_buff *skb,
                                  OFPR_NO_MATCH);
 }
 
-static int do_output(struct datapath *dp, struct sk_buff *skb, size_t max_len,
-                    int out_port, int ignore_no_fwd)
-{
-       if (!skb)
-               return -ENOMEM;
-       return (likely(out_port != OFPP_CONTROLLER)
-               ? dp_output_port(dp, skb, out_port, ignore_no_fwd)
-               : dp_output_control(dp, skb, fwd_save_skb(skb),
-                                        max_len, OFPR_ACTION));
-}
-
-void execute_actions(struct datapath *dp, struct sk_buff *skb,
-                    struct sw_flow_key *key,
-                    const struct ofp_action *actions, int n_actions,
-                    int ignore_no_fwd)
-{
-       /* Every output action needs a separate clone of 'skb', but the common
-        * case is just a single output action, so that doing a clone and
-        * then freeing the original skbuff is wasteful.  So the following code
-        * is slightly obscure just to avoid that. */
-       int prev_port;
-       size_t max_len=0;        /* Initialze to make compiler happy */
-       uint16_t eth_proto;
-       int i;
-
-       prev_port = -1;
-       eth_proto = ntohs(key->dl_type);
-
-       for (i = 0; i < n_actions; i++) {
-               const struct ofp_action *a = &actions[i];
-
-               if (prev_port != -1) {
-                       do_output(dp, skb_clone(skb, GFP_ATOMIC),
-                                 max_len, prev_port, ignore_no_fwd);
-                       prev_port = -1;
-               }
-
-               if (likely(a->type == htons(OFPAT_OUTPUT))) {
-                       prev_port = ntohs(a->arg.output.port);
-                       max_len = ntohs(a->arg.output.max_len);
-               } else {
-                       if (!make_writable(&skb)) {
-                               if (net_ratelimit())
-                                   printk("make_writable failed\n");
-                               break;
-                       }
-                       skb = execute_setter(skb, eth_proto, key, a);
-                       if (!skb) {
-                               if (net_ratelimit())
-                                       printk("execute_setter lost skb\n");
-                               return;
-                       }
-               }
-       }
-       if (prev_port != -1)
-               do_output(dp, skb, max_len, prev_port, ignore_no_fwd);
-       else
-               kfree_skb(skb);
-}
-
-/* Updates 'sum', which is a field in 'skb''s data, given that a 4-byte field
- * covered by the sum has been changed from 'from' to 'to'.  If set,
- * 'pseudohdr' indicates that the field is in the TCP or UDP pseudo-header.
- * Based on nf_proto_csum_replace4. */
-static void update_csum(__sum16 *sum, struct sk_buff *skb,
-                       __be32 from, __be32 to, int pseudohdr)
-{
-       __be32 diff[] = { ~from, to };
-       if (skb->ip_summed != CHECKSUM_PARTIAL) {
-               *sum = csum_fold(csum_partial((char *)diff, sizeof(diff),
-                               ~csum_unfold(*sum)));
-               if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr)
-                       skb->csum = ~csum_partial((char *)diff, sizeof(diff),
-                                               ~skb->csum);
-       } else if (pseudohdr)
-               *sum = ~csum_fold(csum_partial((char *)diff, sizeof(diff),
-                               csum_unfold(*sum)));
-}
-
-static void modify_nh(struct sk_buff *skb, uint16_t eth_proto,
-                       uint8_t nw_proto, const struct ofp_action *a)
-{
-       if (eth_proto == ETH_P_IP) {
-               struct iphdr *nh = ip_hdr(skb);
-               uint32_t new, *field;
-
-               new = a->arg.nw_addr;
-
-               if (a->type == htons(OFPAT_SET_NW_SRC))
-                       field = &nh->saddr;
-               else
-                       field = &nh->daddr;
-
-               if (nw_proto == IPPROTO_TCP) {
-                       struct tcphdr *th = tcp_hdr(skb);
-                       update_csum(&th->check, skb, *field, new, 1);
-               } else if (nw_proto == IPPROTO_UDP) {
-                       struct udphdr *th = udp_hdr(skb);
-                       update_csum(&th->check, skb, *field, new, 1);
-               }
-               update_csum(&nh->check, skb, *field, new, 0);
-               *field = new;
-       }
-}
-
-static void modify_th(struct sk_buff *skb, uint16_t eth_proto,
-                       uint8_t nw_proto, const struct ofp_action *a)
-{
-       if (eth_proto == ETH_P_IP) {
-               uint16_t new, *field;
-
-               new = a->arg.tp;
-
-               if (nw_proto == IPPROTO_TCP) {
-                       struct tcphdr *th = tcp_hdr(skb);
-
-                       if (a->type == htons(OFPAT_SET_TP_SRC))
-                               field = &th->source;
-                       else
-                               field = &th->dest;
-
-                       update_csum(&th->check, skb, *field, new, 1);
-                       *field = new;
-               } else if (nw_proto == IPPROTO_UDP) {
-                       struct udphdr *th = udp_hdr(skb);
-
-                       if (a->type == htons(OFPAT_SET_TP_SRC))
-                               field = &th->source;
-                       else
-                               field = &th->dest;
-
-                       update_csum(&th->check, skb, *field, new, 1);
-                       *field = new;
-               }
-       }
-}
-
-static struct sk_buff *vlan_pull_tag(struct sk_buff *skb)
-{
-       struct vlan_ethhdr *vh = vlan_eth_hdr(skb);
-       struct ethhdr *eh;
-
-
-       /* Verify we were given a vlan packet */
-       if (vh->h_vlan_proto != htons(ETH_P_8021Q))
-               return skb;
-
-       memmove(skb->data + VLAN_HLEN, skb->data, 2 * VLAN_ETH_ALEN);
-
-       eh = (struct ethhdr *)skb_pull(skb, VLAN_HLEN);
-
-       skb->protocol = eh->h_proto;
-       skb->mac_header += VLAN_HLEN;
-
-       return skb;
-}
-
-static struct sk_buff *modify_vlan_tci(struct sk_buff *skb, 
-               struct sw_flow_key *key, uint16_t tci, uint16_t mask)
-{
-       struct vlan_ethhdr *vh = vlan_eth_hdr(skb);
-
-       if (key->dl_vlan != htons(OFP_VLAN_NONE)) {
-               /* Modify vlan id, but maintain other TCI values */
-               vh->h_vlan_TCI = (vh->h_vlan_TCI & ~(htons(mask))) | htons(tci);
-       } else  {
-               /* Add vlan header */
-
-               /* xxx The vlan_put_tag function, doesn't seem to work
-                * xxx reliably when it attempts to use the hardware-accelerated
-                * xxx version.  We'll directly use the software version
-                * xxx until the problem can be diagnosed.
-                */
-               skb = __vlan_put_tag(skb, tci);
-               vh = vlan_eth_hdr(skb);
-       }
-       key->dl_vlan = vh->h_vlan_TCI & htons(VLAN_VID_MASK);
-
-       return skb;
-}
-
-/* Mask for the priority bits in a vlan header.  The kernel doesn't
- * define this like it does for VID. */
-#define VLAN_PCP_MASK 0xe000
-
-struct sk_buff *execute_setter(struct sk_buff *skb, uint16_t eth_proto,
-                       struct sw_flow_key *key, const struct ofp_action *a)
-{
-       switch (ntohs(a->type)) {
-       case OFPAT_SET_VLAN_VID: {
-               uint16_t tci = ntohs(a->arg.vlan_vid);
-               skb = modify_vlan_tci(skb, key, tci, VLAN_VID_MASK);
-               break;
-       }
-
-       case OFPAT_SET_VLAN_PCP: {
-               uint16_t tci = (uint16_t)a->arg.vlan_pcp << 13;
-               skb = modify_vlan_tci(skb, key, tci, VLAN_PCP_MASK);
-               break;
-       }
-
-       case OFPAT_STRIP_VLAN:
-               vlan_pull_tag(skb);
-               key->dl_vlan = htons(OFP_VLAN_NONE);
-               break;
-
-       case OFPAT_SET_DL_SRC: {
-               struct ethhdr *eh = eth_hdr(skb);
-               memcpy(eh->h_source, a->arg.dl_addr, sizeof eh->h_source);
-               break;
-       }
-       case OFPAT_SET_DL_DST: {
-               struct ethhdr *eh = eth_hdr(skb);
-               memcpy(eh->h_dest, a->arg.dl_addr, sizeof eh->h_dest);
-               break;
-       }
-
-       case OFPAT_SET_NW_SRC:
-       case OFPAT_SET_NW_DST:
-               modify_nh(skb, eth_proto, key->nw_proto, a);
-               break;
-
-       case OFPAT_SET_TP_SRC:
-       case OFPAT_SET_TP_DST:
-               modify_th(skb, eth_proto, key->nw_proto, a);
-               break;
-       
-       default:
-               if (net_ratelimit())
-                       printk("execute_setter: unknown action: %d\n", ntohs(a->type));
-       }
-
-       return skb;
-}
-
 static int
 recv_hello(struct sw_chain *chain, const struct sender *sender,
           const void *msg)
@@ -361,18 +120,18 @@ recv_packet_out(struct sw_chain *chain, const struct sender *sender,
        struct sk_buff *skb;
        struct vlan_ethhdr *mac;
        int nh_ofs;
+       uint16_t v_code;
        struct sw_flow_key key;
-       int n_actions = ntohs(opo->n_actions);
-       int act_len = n_actions * sizeof opo->actions[0];
+       size_t actions_len = ntohs(opo->actions_len);
 
-       if (act_len > (ntohs(opo->header.length) - sizeof *opo)) {
+       if (actions_len > (ntohs(opo->header.length) - sizeof *opo)) {
                if (net_ratelimit()) 
                        printk("message too short for number of actions\n");
                return -EINVAL;
        }
 
        if (ntohl(opo->buffer_id) == (uint32_t) -1) {
-               int data_len = ntohs(opo->header.length) - sizeof *opo - act_len;
+               int data_len = ntohs(opo->header.length) - sizeof *opo - actions_len;
 
                /* FIXME: there is likely a way to reuse the data in msg. */
                skb = alloc_skb(data_len, GFP_ATOMIC);
@@ -382,7 +141,8 @@ recv_packet_out(struct sw_chain *chain, const struct sender *sender,
                /* FIXME?  We don't reserve NET_IP_ALIGN or NET_SKB_PAD since
                 * we're just transmitting this raw without examining anything
                 * at those layers. */
-               memcpy(skb_put(skb, data_len), &opo->actions[n_actions], data_len);
+               memcpy(skb_put(skb, data_len), (uint8_t *)opo->actions + actions_len, 
+                               data_len);
 
                skb_set_mac_header(skb, 0);
                mac = vlan_eth_hdr(skb);
@@ -400,9 +160,21 @@ recv_packet_out(struct sw_chain *chain, const struct sender *sender,
        dp_set_origin(chain->dp, ntohs(opo->in_port), skb);
 
        flow_extract(skb, ntohs(opo->in_port), &key);
-       execute_actions(chain->dp, skb, &key, opo->actions, n_actions, 1);
+
+       v_code = validate_actions(chain->dp, &key, opo->actions, actions_len);
+       if (v_code != ACT_VALIDATION_OK) {
+               dp_send_error_msg(chain->dp, sender, OFPET_BAD_ACTION, v_code,
+                                 msg, ntohs(opo->header.length));
+               goto error;
+       }
+
+       execute_actions(chain->dp, skb, &key, opo->actions, actions_len, 1);
 
        return 0;
+
+error:
+       kfree_skb(skb);
+       return -EINVAL;
 }
 
 static int
@@ -431,38 +203,29 @@ recv_echo_reply(struct sw_chain *chain, const struct sender *sender,
 }
 
 static int
-add_flow(struct sw_chain *chain, const struct ofp_flow_mod *ofm)
+add_flow(struct sw_chain *chain, const struct sender *sender, 
+               const struct ofp_flow_mod *ofm)
 {
        int error = -ENOMEM;
-       int i;
-       int n_actions;
+       uint16_t v_code;
        struct sw_flow *flow;
-
-
-       /* 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;
-               }
-       }
+       size_t actions_len = ntohs(ofm->header.length) - sizeof *ofm;
 
        /* Allocate memory. */
-       flow = flow_alloc(n_actions, GFP_ATOMIC);
+       flow = flow_alloc(actions_len, GFP_ATOMIC);
        if (flow == NULL)
                goto error;
 
-       /* Fill out flow. */
        flow_extract_match(&flow->key, &ofm->match);
+
+       v_code = validate_actions(chain->dp, &flow->key, ofm->actions, actions_len);
+       if (v_code != ACT_VALIDATION_OK) {
+               dp_send_error_msg(chain->dp, sender, OFPET_BAD_ACTION, v_code,
+                                 ofm, ntohs(ofm->header.length));
+               goto error;
+       }
+
+       /* Fill out flow. */
        flow->priority = flow->key.wildcards ? ntohs(ofm->priority) : -1;
        flow->idle_timeout = ntohs(ofm->idle_timeout);
        flow->hard_timeout = ntohs(ofm->hard_timeout);
@@ -471,8 +234,7 @@ add_flow(struct sw_chain *chain, const struct ofp_flow_mod *ofm)
        flow->byte_count = 0;
        flow->packet_count = 0;
        spin_lock_init(&flow->lock);
-       memcpy(flow->sf_acts->actions, ofm->actions, 
-                       n_actions * sizeof *flow->sf_acts->actions);
+       memcpy(flow->sf_acts->actions, ofm->actions, actions_len);
 
        /* Act. */
        error = chain_insert(chain, flow);
@@ -485,7 +247,7 @@ add_flow(struct sw_chain *chain, const struct ofp_flow_mod *ofm)
                        struct sw_flow_key key;
                        flow_used(flow, skb);
                        flow_extract(skb, ntohs(ofm->match.in_port), &key);
-                       execute_actions(chain->dp, skb, &key, ofm->actions, n_actions, 0);
+                       execute_actions(chain->dp, skb, &key, ofm->actions, actions_len, 0);
                }
                else
                        error = -ESRCH;
@@ -501,36 +263,30 @@ error:
 }
 
 static int
-mod_flow(struct sw_chain *chain, const struct ofp_flow_mod *ofm)
+mod_flow(struct sw_chain *chain, const struct sender *sender,
+               const struct ofp_flow_mod *ofm)
 {
        int error = -ENOMEM;
-       int i;
-       int n_actions;
+       uint16_t v_code;
+       size_t actions_len;
        struct sw_flow_key key;
        uint16_t priority;
        int strict;
 
-       /* 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);
+
+       actions_len = ntohs(ofm->header.length) - sizeof *ofm;
+
+       v_code = validate_actions(chain->dp, &key, ofm->actions, actions_len);
+       if (v_code != ACT_VALIDATION_OK) {
+               dp_send_error_msg(chain->dp, sender, OFPET_BAD_ACTION, v_code,
+                                 ofm, ntohs(ofm->header.length));
+               goto error;
        }
 
-       flow_extract_match(&key, &ofm->match);
        priority = key.wildcards ? ntohs(ofm->priority) : -1;
        strict = (ofm->command == htons(OFPFC_MODIFY_STRICT)) ? 1 : 0;
-       chain_modify(chain, &key, priority, strict, ofm->actions, n_actions);
+       chain_modify(chain, &key, priority, strict, ofm->actions, actions_len);
 
        if (ntohl(ofm->buffer_id) != (uint32_t) -1) {
                struct sk_buff *skb = retrieve_skb(ntohl(ofm->buffer_id));
@@ -538,7 +294,7 @@ mod_flow(struct sw_chain *chain, const struct ofp_flow_mod *ofm)
                        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);
+                                       ofm->actions, actions_len, 0);
                }
                else
                        error = -ESRCH;
@@ -558,9 +314,9 @@ recv_flow(struct sw_chain *chain, const struct sender *sender, const void *msg)
        uint16_t command = ntohs(ofm->command);
 
        if (command == OFPFC_ADD) {
-               return add_flow(chain, ofm);
+               return add_flow(chain, sender, ofm);
        } else if ((command == OFPFC_MODIFY) || (command == OFPFC_MODIFY_STRICT)) {
-               return mod_flow(chain, ofm);
+               return mod_flow(chain, sender, ofm);
        }  else if (command == OFPFC_DELETE) {
                struct sw_flow_key key;
                flow_extract_match(&key, &ofm->match);
@@ -772,34 +528,3 @@ void fwd_exit(void)
        fwd_discard_all();
 }
 
-/* Utility functions. */
-
-/* Makes '*pskb' writable, possibly copying it and setting '*pskb' to point to
- * the copy.
- * Returns 1 if successful, 0 on failure. */
-static int
-make_writable(struct sk_buff **pskb)
-{
-       /* Based on skb_make_writable() in net/netfilter/core.c. */
-       struct sk_buff *nskb;
-
-       /* Not exclusive use of packet?  Must copy. */
-       if (skb_shared(*pskb) || skb_cloned(*pskb))
-               goto copy_skb;
-
-       return pskb_may_pull(*pskb, 40); /* FIXME? */
-
-copy_skb:
-       nskb = skb_copy(*pskb, GFP_ATOMIC);
-       if (!nskb)
-               return 0;
-       BUG_ON(skb_is_nonlinear(nskb));
-
-       /* Rest of kernel will get very unhappy if we pass it a
-          suddenly-orphaned skbuff */
-       if ((*pskb)->sk)
-               skb_set_owner_w(nskb, (*pskb)->sk);
-       kfree_skb(*pskb);
-       *pskb = nskb;
-       return 1;
-}
index fc2bc607fa0eaf8ba1a4fa62e39cfa0be17cc31c..35457f8a852723c662034129735def27a3cb9ab8 100644 (file)
@@ -7,7 +7,6 @@
 
 struct sk_buff;
 struct sw_chain;
-struct ofp_action;
 struct sender;
 
 /* Buffers are identified to userspace by a 31-bit opaque ID.  We divide the ID
@@ -34,11 +33,4 @@ void fwd_discard_all(void);
 
 void fwd_exit(void);
 
-void execute_actions(struct datapath *, struct sk_buff *,
-                   struct sw_flow_key *, 
-                   const struct ofp_action *, int n_actions,
-                   int ignore_no_fwd);
-struct sk_buff *execute_setter(struct sk_buff *, uint16_t,
-                       struct sw_flow_key *, const struct ofp_action *);
-
 #endif /* forward.h */
index 1edb83af4898a8cca9f5092ea01c8f79382c3048..f55e9fdcfe3154167fb835a71b02d4c4393bbca2 100644 (file)
@@ -99,7 +99,7 @@ static int table_dummy_insert(struct sw_table *swt, struct sw_flow *flow)
 
 static int table_dummy_modify(struct sw_table *swt, 
                const struct sw_flow_key *key, uint16_t priority, int strict,
-               const struct ofp_action *actions, int n_actions)
+               const struct ofp_action_header *actions, size_t actions_len)
 {
        struct sw_table_dummy *td = (struct sw_table_dummy *) swt;
        struct sw_flow *flow;
@@ -108,7 +108,7 @@ static int table_dummy_modify(struct sw_table *swt,
        list_for_each_entry (flow, &td->flows, node) {
                if (flow_matches_desc(&flow->key, key, strict)
                                && (!strict || (flow->priority == priority))) {
-                       flow_replace_acts(flow, actions, n_actions);
+                       flow_replace_acts(flow, actions, actions_len);
                        /* xxx Do whatever is necessary to modify the entry in hardware */
                        count++;
                }
index 739e15716060e49fe68d6d0f3d1ac9b1a2f8c1ef..0328906106d16a0ab47981e0af99a0c17f9291a5 100644 (file)
@@ -8,6 +8,7 @@
 /compat24.c
 /crc32.c
 /crc_t.c
+/dp_act.c
 /dp_dev.c
 /dp_notify.c
 /flow.c
@@ -18,6 +19,7 @@
 /kernel.c
 /kthread.c
 /netlink.c
+/nx_act.c
 /random32.c
 /rcupdate.c
 /sched.c
index 56f38adabb80952aec1055d496e2636cde3d03a4..4dd9ed736a926fbda799804ec7b2647cecdca68d 100644 (file)
@@ -5,6 +5,7 @@
 /crc32.c
 /crc_t.c
 /datapath.c
+/dp_act.c
 /dp_dev.c
 /dp_notify.c
 /flow.c
@@ -12,6 +13,7 @@
 /forward_t.c
 /genetlink.c
 /hwtable_dummy.c
+/nx_act.c
 /random32.c
 /table-hash.c
 /table-linear.c
diff --git a/datapath/nx_act.c b/datapath/nx_act.c
new file mode 100644 (file)
index 0000000..01aca73
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Distributed under the terms of the GNU GPL version 2.
+ * Copyright (c) 2007, 2008 The Board of Trustees of The Leland 
+ * Stanford Junior University
+ */
+
+/* Functions for Nicira-extended actions. */
+#include "nicira-ext.h"
+#include "nx_act.h"
+
+uint16_t
+nx_validate_act(struct datapath *dp, const struct sw_flow_key *key,
+               const struct ofp_action_vendor_header *avh, uint16_t len)
+{
+       /* Nothing to validate yet */
+       return OFPBAC_BAD_VENDOR_TYPE;
+}
+
+struct sk_buff *
+nx_execute_act(struct sk_buff *skb, const struct sw_flow_key *key,
+               const struct ofp_action_vendor_header *avh)
+{
+       /* Nothing to execute yet */
+       return skb;
+}
+
diff --git a/datapath/nx_act.h b/datapath/nx_act.h
new file mode 100644 (file)
index 0000000..fe39882
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef NX_ACT_H
+#define NX_ACT_H 1
+
+#include "datapath.h"
+
+
+uint16_t nx_validate_act(struct datapath *dp, const struct sw_flow_key *key,
+               const struct ofp_action_vendor_header *avh, uint16_t len);
+
+struct sk_buff *nx_execute_act(struct sk_buff *skb, 
+               const struct sw_flow_key *key,
+               const struct ofp_action_vendor_header *avh);
+
+#endif /* nx_act.h */
index 2663bf4b289cef11d3b41098a98bd771f0eccc44..86e19201e555c2d808c876df6a43b180580a0c3b 100644 (file)
@@ -72,7 +72,7 @@ static int table_hash_insert(struct sw_table *swt, struct sw_flow *flow)
 
 static int table_hash_modify(struct sw_table *swt, 
                const struct sw_flow_key *key, uint16_t priority, int strict,
-               const struct ofp_action *actions, int n_actions
+               const struct ofp_action_header *actions, size_t actions_len
 {
        struct sw_table_hash *th = (struct sw_table_hash *) swt;
        unsigned int count = 0;
@@ -82,7 +82,7 @@ static int table_hash_modify(struct sw_table *swt,
                struct sw_flow *flow = *bucket;
                if (flow && flow_matches_desc(&flow->key, key, strict)
                                && (!strict || (flow->priority == priority))) {
-                       flow_replace_acts(flow, actions, n_actions);
+                       flow_replace_acts(flow, actions, actions_len);
                        count = 1;
                }
        } else {
@@ -93,7 +93,7 @@ static int table_hash_modify(struct sw_table *swt,
                        struct sw_flow *flow = *bucket;
                        if (flow && flow_matches_desc(&flow->key, key, strict)
                                        && (!strict || (flow->priority == priority))) {
-                               flow_replace_acts(flow, actions, n_actions);
+                               flow_replace_acts(flow, actions, actions_len);
                                count++;
                        }
                }
@@ -290,13 +290,13 @@ static int table_hash2_insert(struct sw_table *swt, struct sw_flow *flow)
 
 static int table_hash2_modify(struct sw_table *swt, 
                const struct sw_flow_key *key, uint16_t priority, int strict,
-               const struct ofp_action *actions, int n_actions)
+               const struct ofp_action_header *actions, size_t actions_len)
 {
        struct sw_table_hash2 *t2 = (struct sw_table_hash2 *) swt;
        return (table_hash_modify(t2->subtable[0], key, priority, strict, 
-                                       actions, n_actions)
+                                       actions, actions_len)
                        + table_hash_modify(t2->subtable[1], key, priority, strict, 
-                                       actions, n_actions));
+                                       actions, actions_len));
 }
 
 static int table_hash2_delete(struct sw_table *swt,
index c71f0bdbdc98cbb699811cfbdc9f126006f8e5f8..2a0752a2ae1684d2b7715e0b490f86b903fe75ed 100644 (file)
@@ -74,7 +74,7 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow)
 
 static int table_linear_modify(struct sw_table *swt,
                                const struct sw_flow_key *key, uint16_t priority, int strict,
-                               const struct ofp_action *actions, int n_actions)
+                               const struct ofp_action_header *actions, size_t actions_len)
 {
        struct sw_table_linear *tl = (struct sw_table_linear *) swt;
        struct sw_flow *flow;
@@ -83,7 +83,7 @@ static int table_linear_modify(struct sw_table *swt,
        list_for_each_entry (flow, &tl->flows, node) {
                if (flow_matches_desc(&flow->key, key, strict)
                                && (!strict || (flow->priority == priority))) {
-                       flow_replace_acts(flow, actions, n_actions);
+                       flow_replace_acts(flow, actions, actions_len);
                        count++;
                }
        }
index c40b4ca67802ddc8046922e7e3c86dbe44678507..80865e6d3a5e92e5067fc2375193f1bfe4151915 100644 (file)
@@ -8,7 +8,7 @@
 
 struct sw_flow;
 struct sw_flow_key;
-struct ofp_action;
+struct ofp_action_header;
 struct datapath;
 
 /* Table statistics. */
@@ -62,7 +62,7 @@ struct sw_table {
         * that were modified. */
        int (*modify)(struct sw_table *table, const struct sw_flow_key *key,
                        uint16_t priority, int strict,
-                       const struct ofp_action *actions, int n_actions);
+                       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.  
index f33c6dbe1a75c4dee32a99752b2210470e007bf3..a1ca791c2b8fb8354fc3f743186d564e8fee4c9a 100644 (file)
@@ -63,7 +63,7 @@
 /* The most significant bit being set in the version field indicates an
  * experimental OpenFlow version.  
  */
-#define OFP_VERSION   0x94
+#define OFP_VERSION   0x95
 
 #define OFP_MAX_TABLE_NAME_LEN 32
 #define OFP_MAX_PORT_NAME_LEN  16
@@ -323,47 +323,109 @@ struct ofp_packet_in {
 OFP_ASSERT(sizeof(struct ofp_packet_in) == 20);
 
 enum ofp_action_type {
-    OFPAT_OUTPUT,            /* Output to switch port. */
-    OFPAT_SET_VLAN_VID,      /* Set the 802.1q VLAN id. */
-    OFPAT_SET_VLAN_PCP,      /* Set the 802.1q priority. */
-    OFPAT_STRIP_VLAN,        /* Strip the 802.1q header. */
-    OFPAT_SET_DL_SRC,        /* Ethernet source address. */
-    OFPAT_SET_DL_DST,        /* Ethernet destination address. */
-    OFPAT_SET_NW_SRC,        /* IP source address. */
-    OFPAT_SET_NW_DST,        /* IP destination address. */
-    OFPAT_SET_TP_SRC,        /* TCP/UDP source port. */
-    OFPAT_SET_TP_DST         /* TCP/UDP destination port. */
-};
-
-/* An output action sends packets out 'port'.  When the 'port' is the
- * OFPP_CONTROLLER, 'max_len' indicates the max number of bytes to
- * send.  A 'max_len' of zero means the entire packet should be sent. */
+    OFPAT_OUTPUT,           /* Output to switch port. */
+    OFPAT_SET_VLAN_VID,     /* Set the 802.1q VLAN id. */
+    OFPAT_SET_VLAN_PCP,     /* Set the 802.1q priority. */
+    OFPAT_STRIP_VLAN,       /* Strip the 802.1q header. */
+    OFPAT_SET_DL_SRC,       /* Ethernet source address. */
+    OFPAT_SET_DL_DST,       /* Ethernet destination address. */
+    OFPAT_SET_NW_SRC,       /* IP source address. */
+    OFPAT_SET_NW_DST,       /* IP destination address. */
+    OFPAT_SET_TP_SRC,       /* TCP/UDP source port. */
+    OFPAT_SET_TP_DST,       /* TCP/UDP destination port. */
+    OFPAT_VENDOR = 0xffff
+};
+
+/* Action structure for OFPAT_OUTPUT, which sends packets out 'port'.  
+ * When the 'port' is the OFPP_CONTROLLER, 'max_len' indicates the max 
+ * number of bytes to send.  A 'max_len' of zero means the entire packet 
+ * should be sent. */
 struct ofp_action_output {
-    uint16_t max_len;
-    uint16_t port;
+    uint16_t type;                  /* OFPAT_OUTPUT. */
+    uint16_t len;                   /* Length is 8. */
+    uint16_t port;                  /* Ouptut port. */
+    uint16_t max_len;               /* Max length to send to controller. */
 };
-OFP_ASSERT(sizeof(struct ofp_action_output) == 4);
+OFP_ASSERT(sizeof(struct ofp_action_output) == 8);
 
-struct ofp_action {
-    uint16_t type;                       /* One of OFPAT_* */
-    union {
-        struct ofp_action_output output; /* OFPAT_OUTPUT: output struct. */
-        uint8_t vlan_pcp;                /* OFPAT_SET_VLAN_PCP: priority. */
-        uint16_t vlan_vid;               /* OFPAT_SET_VLAN_VID: VLAN id. */
-        uint8_t  dl_addr[OFP_ETH_ALEN];  /* OFPAT_SET_DL_SRC/DST */
-        uint32_t nw_addr OFP_PACKED;     /* OFPAT_SET_NW_SRC/DST */
-        uint16_t tp;                     /* OFPAT_SET_TP_SRC/DST */
-    } arg;
-};
-OFP_ASSERT(sizeof(struct ofp_action) == 8);
+/* The VLAN id is 12 bits, so we can use the entire 16 bits to indicate
+ * special conditions.  All ones is used to match that no VLAN id was
+ * set. */
+#define OFP_VLAN_NONE      0xffff
+
+/* Action structure for OFPAT_SET_VLAN_VID. */
+struct ofp_action_vlan_vid {
+    uint16_t type;                  /* OFPAT_SET_VLAN_VID. */
+    uint16_t len;                   /* Length is 8. */
+    uint16_t vlan_vid;              /* VLAN id. */
+    uint8_t pad[2];
+};
+OFP_ASSERT(sizeof(struct ofp_action_vlan_vid) == 8);
+
+/* Action structure for OFPAT_SET_VLAN_PCP. */
+struct ofp_action_vlan_pcp {
+    uint16_t type;                  /* OFPAT_SET_VLAN_PCP. */
+    uint16_t len;                   /* Length is 8. */
+    uint8_t vlan_pcp;               /* VLAN priority. */
+    uint8_t pad[3];
+};
+OFP_ASSERT(sizeof(struct ofp_action_vlan_vid) == 8);
+
+/* Action structure for OFPAT_SET_DL_SRC/DST. */
+struct ofp_action_dl_addr {
+    uint16_t type;                  /* OFPAT_SET_DL_SRC/DST. */
+    uint16_t len;                   /* Length is 16. */
+    uint8_t dl_addr[OFP_ETH_ALEN];  /* Ethernet address. */
+    uint8_t pad[6];
+};
+OFP_ASSERT(sizeof(struct ofp_action_dl_addr) == 16);
+
+/* Action structure for OFPAT_SET_NW_SRC/DST. */
+struct ofp_action_nw_addr {
+    uint16_t type;                  /* OFPAT_SET_TW_SRC/DST. */
+    uint16_t len;                   /* Length is 8. */
+    uint32_t nw_addr;               /* IP address. */
+};
+OFP_ASSERT(sizeof(struct ofp_action_nw_addr) == 8);
+
+/* Action structure for OFPAT_SET_TP_SRC/DST. */
+struct ofp_action_tp_port {
+    uint16_t type;                  /* OFPAT_SET_TP_SRC/DST. */
+    uint16_t len;                   /* Length is 8. */
+    uint16_t tp_port;               /* TCP/UDP port. */
+    uint8_t pad[2];
+};
+OFP_ASSERT(sizeof(struct ofp_action_tp_port) == 8);
+
+/* Action header for OFPAT_VENDOR. The rest of the body is vendor-defined. */
+struct ofp_action_vendor_header {
+    uint16_t type;                  /* OFPAT_VENDOR. */
+    uint16_t len;                   /* Length is 8. */
+    uint32_t vendor;                /* Vendor ID, which takes the same form 
+                                       as in "struct ofp_vendor". */ 
+};
+OFP_ASSERT(sizeof(struct ofp_action_vendor_header) == 8);
+
+/* Action header that is common to all actions.  The length includes the 
+ * header and any padding used to make the action 64-bit aligned.  
+ * NB: The length of an action *must* always be a multiple of eight. */
+struct ofp_action_header {
+    uint16_t type;                  /* One of OFPAT_*. */
+    uint16_t len;                   /* Length of action, including this 
+                                       header.  This is the length of action, 
+                                       including any padding to make it 
+                                       64-bit aligned. */
+    uint8_t pad[4];
+};
+OFP_ASSERT(sizeof(struct ofp_action_header) == 8);
 
 /* Send packet (controller -> datapath). */
 struct ofp_packet_out {
     struct ofp_header header;
     uint32_t buffer_id;           /* ID assigned by datapath (-1 if none). */
     uint16_t in_port;             /* Packet's input port (OFPP_NONE if none). */
-    uint16_t n_actions;           /* Number of actions. */
-    struct ofp_action actions[0]; /* Actions. */
+    uint16_t actions_len;          /* Size of action array in bytes. */
+    struct ofp_action_header actions[0]; /* Actions. */
     /* uint8_t data[0]; */        /* Packet data.  The length is inferred 
                                      from the length field in the header.  
                                      (Only meaningful if buffer_id == -1.) */
@@ -461,8 +523,9 @@ struct ofp_flow_mod {
     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. */
+    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);
 
@@ -493,7 +556,8 @@ OFP_ASSERT(sizeof(struct ofp_flow_expired) == 72);
  * be added). */
 enum ofp_error_type {
     OFPET_HELLO_FAILED,         /* Hello protocol failed. */
-    OFPET_BAD_REQUEST           /* Request was not understood. */
+    OFPET_BAD_REQUEST,          /* Request was not understood. */
+    OFPET_BAD_ACTION            /* Error in action description. */
 };
 
 /* ofp_error_msg 'code' values for OFPET_HELLO_FAILED.  'data' contains an
@@ -512,6 +576,16 @@ enum ofp_bad_request_code {
                                  * ofp_stats_request or ofp_stats_reply). */
 };
 
+/* ofp_error_msg 'code' values for OFPET_BAD_ACTION.  'data' contains at least 
+ * the first 64 bytes of the failed request. */
+enum ofp_bad_action_code {
+    OFPBAC_BAD_TYPE,           /* Unknown action type. */
+    OFPBAC_BAD_LEN,            /* Length problem in actions. */
+    OFPBAC_BAD_VENDOR,         /* Unknown vendor id specified. */
+    OFPBAC_BAD_VENDOR_TYPE,    /* Unknown action type for vendor id. */
+    OFPBAC_BAD_OUT_PORT        /* Problem validating output action. */
+};
+
 /* OFPT_ERROR: Error message (datapath -> controller). */
 struct ofp_error_msg {
     struct ofp_header header;
@@ -611,7 +685,7 @@ struct ofp_flow_stats {
     uint16_t pad2[3];         /* Pad to 64 bits. */
     uint64_t packet_count;    /* Number of packets in flow. */
     uint64_t byte_count;      /* Number of bytes in flow. */
-    struct ofp_action actions[0]; /* Actions. */
+    struct ofp_action_header actions[0]; /* Actions. */
 };
 OFP_ASSERT(sizeof(struct ofp_flow_stats) == 72);
 
index f085dcbab3e13a88404531fa59cb1b59258e491c..106e47acb7aaf247838e978466b05e70424f1c6d 100644 (file)
@@ -82,7 +82,7 @@ void *make_openflow_xid(size_t openflow_len, uint8_t type,
                         uint32_t xid, struct ofpbuf **);
 void update_openflow_length(struct ofpbuf *);
 struct ofpbuf *make_add_flow(const struct flow *, uint32_t buffer_id,
-                             uint16_t max_idle, size_t n_actions);
+                             uint16_t max_idle, size_t actions_len);
 struct ofpbuf *make_add_simple_flow(const struct flow *,
                                     uint32_t buffer_id, uint16_t out_port,
                                     uint16_t max_idle);
index c8a6a322f0a3c266e363dd855875ec0eb093db0d..5a1e607894a4154e62b97cfe439da0fbad400432 100644 (file)
@@ -243,93 +243,202 @@ static void ofp_print_port_name(struct ds *string, uint16_t port)
     ds_put_cstr(string, name);
 }
 
-static void
-ofp_print_action(struct ds *string, const struct ofp_action *a) 
+static int
+ofp_print_action(struct ds *string, const struct ofp_action_header *ah, 
+        size_t actions_len) 
 {
-    switch (ntohs(a->type)) {
-    case OFPAT_OUTPUT:
-        {
-            uint16_t port = ntohs(a->arg.output.port); 
-            if (port < OFPP_MAX) {
-                ds_put_format(string, "output:%"PRIu16, port);
-            } else {
-                ofp_print_port_name(string, port);
-                if (port == OFPP_CONTROLLER) {
-                    if (a->arg.output.max_len) {
-                        ds_put_format(string, ":%"PRIu16, 
-                                ntohs(a->arg.output.max_len));
-                    } else {
-                        ds_put_cstr(string, ":all");
-                    }
+    uint16_t type;
+    size_t len;
+
+    struct openflow_action {
+        size_t min_size;
+        size_t max_size;
+    };
+
+    const struct openflow_action of_actions[] = {
+        [OFPAT_OUTPUT] = {
+            sizeof(struct ofp_action_output),
+            sizeof(struct ofp_action_output),
+        },
+        [OFPAT_SET_VLAN_VID] = {
+            sizeof(struct ofp_action_vlan_vid),
+            sizeof(struct ofp_action_vlan_vid),
+        },
+        [OFPAT_SET_VLAN_PCP] = {
+            sizeof(struct ofp_action_vlan_pcp),
+            sizeof(struct ofp_action_vlan_pcp),
+        },
+        [OFPAT_STRIP_VLAN] = {
+            sizeof(struct ofp_action_header),
+            sizeof(struct ofp_action_header),
+        },
+        [OFPAT_SET_DL_SRC] = {
+            sizeof(struct ofp_action_dl_addr),
+            sizeof(struct ofp_action_dl_addr),
+        },
+        [OFPAT_SET_DL_DST] = {
+            sizeof(struct ofp_action_dl_addr),
+            sizeof(struct ofp_action_dl_addr),
+        },
+        [OFPAT_SET_NW_SRC] = {
+            sizeof(struct ofp_action_nw_addr),
+            sizeof(struct ofp_action_nw_addr),
+        },
+        [OFPAT_SET_NW_DST] = {
+            sizeof(struct ofp_action_nw_addr),
+            sizeof(struct ofp_action_nw_addr),
+        },
+        [OFPAT_SET_TP_SRC] = {
+            sizeof(struct ofp_action_tp_port),
+            sizeof(struct ofp_action_tp_port),
+        },
+        [OFPAT_SET_TP_DST] = {
+            sizeof(struct ofp_action_tp_port),
+            sizeof(struct ofp_action_tp_port),
+        }
+        /* OFPAT_VENDOR is not here, since it would blow up the array size. */
+    };
+
+    if (actions_len < sizeof *ah) {
+        ds_put_format(string, "***action array too short for next action***\n");
+        return -1;
+    }
+
+    type = ntohs(ah->type);
+    len = ntohs(ah->len);
+    if (actions_len < len) {
+        ds_put_format(string, "***truncated action %"PRIu16"***\n", type);
+        return -1;
+    }
+
+    if ((len % 8) != 0) {
+        ds_put_format(string, 
+                "***action %"PRIu16" length not a multiple of 8***\n",
+                type);
+        return -1;
+    }
+
+    if (type < ARRAY_SIZE(of_actions)) {
+        const struct openflow_action *act = &of_actions[type];
+        if ((len < act->min_size) || (len > act->max_size)) {
+            ds_put_format(string, 
+                    "***action %"PRIu16" wrong length: %"PRIu16"***\n", 
+                    type, len);
+            return -1;
+        }
+    }
+    
+    switch (type) {
+    case OFPAT_OUTPUT: {
+        struct ofp_action_output *oa = (struct ofp_action_output *)ah;
+        uint16_t port = ntohs(oa->port); 
+        if (port < OFPP_MAX) {
+            ds_put_format(string, "output:%"PRIu16, port);
+        } else {
+            ofp_print_port_name(string, port);
+            if (port == OFPP_CONTROLLER) {
+                if (oa->max_len) {
+                    ds_put_format(string, ":%"PRIu16, ntohs(oa->max_len));
+                } else {
+                    ds_put_cstr(string, ":all");
                 }
             }
         }
         break;
+    }
 
-    case OFPAT_SET_VLAN_VID:
-        ds_put_format(string, "mod_vlan_vid:%"PRIu16, ntohs(a->arg.vlan_vid));
+    case OFPAT_SET_VLAN_VID: {
+        struct ofp_action_vlan_vid *va = (struct ofp_action_vlan_vid *)ah;
+        ds_put_format(string, "mod_vlan_vid:%"PRIu16, ntohs(va->vlan_vid));
         break;
+    }
 
-    case OFPAT_SET_VLAN_PCP:
-        ds_put_format(string, "mod_vlan_pcp:%"PRIu8, a->arg.vlan_pcp);
+    case OFPAT_SET_VLAN_PCP: {
+        struct ofp_action_vlan_pcp *va = (struct ofp_action_vlan_pcp *)ah;
+        ds_put_format(string, "mod_vlan_pcp:%"PRIu8, va->vlan_pcp);
         break;
+    }
 
     case OFPAT_STRIP_VLAN:
         ds_put_cstr(string, "strip_vlan");
         break;
 
-    case OFPAT_SET_DL_SRC:
+    case OFPAT_SET_DL_SRC: {
+        struct ofp_action_dl_addr *da = (struct ofp_action_dl_addr *)ah;
         ds_put_format(string, "mod_dl_src:"ETH_ADDR_FMT, 
-                ETH_ADDR_ARGS(a->arg.dl_addr));
+                ETH_ADDR_ARGS(da->dl_addr));
         break;
+    }
 
-    case OFPAT_SET_DL_DST:
+    case OFPAT_SET_DL_DST: {
+        struct ofp_action_dl_addr *da = (struct ofp_action_dl_addr *)ah;
         ds_put_format(string, "mod_dl_dst:"ETH_ADDR_FMT, 
-                ETH_ADDR_ARGS(a->arg.dl_addr));
+                ETH_ADDR_ARGS(da->dl_addr));
         break;
+    }
 
-    case OFPAT_SET_NW_SRC:
-        ds_put_format(string, "mod_nw_src:"IP_FMT, IP_ARGS(&a->arg.nw_addr));
+    case OFPAT_SET_NW_SRC: {
+        struct ofp_action_nw_addr *na = (struct ofp_action_nw_addr *)ah;
+        ds_put_format(string, "mod_nw_src:"IP_FMT, IP_ARGS(na->nw_addr));
         break;
+    }
 
-    case OFPAT_SET_NW_DST:
-        ds_put_format(string, "mod_nw_dst:"IP_FMT, IP_ARGS(&a->arg.nw_addr));
+    case OFPAT_SET_NW_DST: {
+        struct ofp_action_nw_addr *na = (struct ofp_action_nw_addr *)ah;
+        ds_put_format(string, "mod_nw_dst:"IP_FMT, IP_ARGS(na->nw_addr));
         break;
+    }
 
-    case OFPAT_SET_TP_SRC:
-        ds_put_format(string, "mod_tp_src:%d", ntohs(a->arg.tp));
+    case OFPAT_SET_TP_SRC: {
+        struct ofp_action_tp_port *ta = (struct ofp_action_tp_port *)ah;
+        ds_put_format(string, "mod_tp_src:%d", ntohs(ta->tp_port));
         break;
+    }
 
-    case OFPAT_SET_TP_DST:
-        ds_put_format(string, "mod_tp_dst:%d", ntohs(a->arg.tp));
+    case OFPAT_SET_TP_DST: {
+        struct ofp_action_tp_port *ta = (struct ofp_action_tp_port *)ah;
+        ds_put_format(string, "mod_tp_dst:%d", ntohs(ta->tp_port));
         break;
+    }
+
+    case OFPAT_VENDOR: {
+        struct ofp_action_vendor_header *avh 
+                = (struct ofp_action_vendor_header *)ah;
+        if (len < sizeof *avh) {
+            ds_put_format(string, "***ofpat_vendor truncated***\n");
+            return -1;
+        }
+        ds_put_format(string, "vendor action:0x%x", ntohl(avh->vendor));
+        break;
+    }
 
     default:
-        ds_put_format(string, "(decoder %"PRIu16" not implemented)", 
-                ntohs(a->type));
+        ds_put_format(string, "(decoder %"PRIu16" not implemented)", type);
         break;
     }
+
+    return len;
 }
 
-static void ofp_print_actions(struct ds *string,
-                              const struct ofp_action actions[],
-                              size_t n_bytes
+static void 
+ofp_print_actions(struct ds *string, const struct ofp_action_header *action,
+                  size_t actions_len
 {
-    size_t i;
-    int n_actions = n_bytes / sizeof *actions;
+    uint8_t *p = (uint8_t *)action;
+    size_t len = 0;
 
-    ds_put_format(string, "action%s=", n_actions == 1 ? "" : "s");
-    for (i = 0; i < n_actions; i++) {
-        if (i) {
+    ds_put_cstr(string, "actions=");
+    while (actions_len > 0) {
+        if (len) {
             ds_put_cstr(string, ",");
         }
-        ofp_print_action(string, &actions[i]);
-    }
-    if (n_bytes % sizeof *actions) {
-        if (i) {
-            ds_put_cstr(string, ",");
+        len = ofp_print_action(string, (struct ofp_action_header *)p, 
+                actions_len);
+        if (len < 0) {
+            return;
         }
-        ds_put_cstr(string, ", ***trailing garbage***");
+        p += len;
+        actions_len -= len;
     }
 }
 
@@ -339,25 +448,24 @@ static void ofp_packet_out(struct ds *string, const void *oh, size_t len,
                            int verbosity) 
 {
     const struct ofp_packet_out *opo = oh;
-    int n_actions = ntohs(opo->n_actions);
-    int act_len = n_actions * sizeof opo->actions[0];
+    size_t actions_len = ntohs(opo->actions_len);
 
     ds_put_cstr(string, " in_port=");
     ofp_print_port_name(string, ntohs(opo->in_port));
 
-    ds_put_format(string, " n_actions=%d ", n_actions);
-    if (act_len > (ntohs(opo->header.length) - sizeof *opo)) {
-        ds_put_format(string, "***packet too short for number of actions***\n");
+    ds_put_format(string, " actions_len=%zu ", actions_len);
+    if (actions_len > (ntohs(opo->header.length) - sizeof *opo)) {
+        ds_put_format(string, "***packet too short for action length***\n");
         return;
     }
-    ofp_print_actions(string, opo->actions, act_len);
+    ofp_print_actions(string, opo->actions, actions_len);
 
     if (ntohl(opo->buffer_id) == UINT32_MAX) {
-        int data_len = len - sizeof *opo - act_len;
+        int data_len = len - sizeof *opo - actions_len;
         ds_put_format(string, " data_len=%d", data_len);
         if (verbosity > 0 && len > sizeof *opo) {
-            char *packet = ofp_packet_to_string(&opo->actions[n_actions], 
-                                                data_len, data_len);
+            char *packet = ofp_packet_to_string(
+                    (uint8_t *)opo->actions + actions_len, data_len, data_len);
             ds_put_char(string, '\n');
             ds_put_cstr(string, packet);
             free(packet);
@@ -712,6 +820,13 @@ static const struct error_type error_types[] = {
     ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE),
     ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT),
     ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION),
+
+    ERROR_TYPE(OFPET_BAD_ACTION),
+    ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_TYPE),
+    ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_LEN),
+    ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR),
+    ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE),
+    ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT),
 };
 #define N_ERROR_TYPES ARRAY_SIZE(error_types)
 
index f287bfd4b9ee21f13d76e2f9ab49aed0156bbf71..434fa46e9ae659095f7c4ee93e9e7d8c06c943b9 100644 (file)
@@ -759,10 +759,10 @@ update_openflow_length(struct ofpbuf *buffer)
 
 struct ofpbuf *
 make_add_flow(const struct flow *flow, uint32_t buffer_id,
-              uint16_t idle_timeout, size_t n_actions)
+              uint16_t idle_timeout, size_t actions_len)
 {
     struct ofp_flow_mod *ofm;
-    size_t size = sizeof *ofm + n_actions * sizeof ofm->actions[0];
+    size_t size = sizeof *ofm + actions_len;
     struct ofpbuf *out = ofpbuf_new(size);
     ofm = ofpbuf_put_uninit(out, size);
     memset(ofm, 0, size);
@@ -792,11 +792,14 @@ make_add_simple_flow(const struct flow *flow,
                      uint32_t buffer_id, uint16_t out_port,
                      uint16_t idle_timeout)
 {
-    struct ofpbuf *buffer = make_add_flow(flow, buffer_id, idle_timeout, 1);
+    struct ofp_action_output *oao;
+    struct ofpbuf *buffer = make_add_flow(flow, buffer_id, idle_timeout, 
+            sizeof *oao);
     struct ofp_flow_mod *ofm = buffer->data;
-    ofm->actions[0].type = htons(OFPAT_OUTPUT);
-    ofm->actions[0].arg.output.max_len = htons(0);
-    ofm->actions[0].arg.output.port = htons(out_port);
+    oao = (struct ofp_action_output *)&ofm->actions[0];
+    oao->type = htons(OFPAT_OUTPUT);
+    oao->len = htons(sizeof *oao);
+    oao->port = htons(out_port);
     return buffer;
 }
 
@@ -805,18 +808,24 @@ make_unbuffered_packet_out(const struct ofpbuf *packet,
                            uint16_t in_port, uint16_t out_port)
 {
     struct ofp_packet_out *opo;
-    size_t size = sizeof *opo + sizeof opo->actions[0];
+    struct ofp_action_output *oao;
+    size_t size = sizeof *opo + sizeof *oao;
     struct ofpbuf *out = ofpbuf_new(size + packet->size);
+
     opo = ofpbuf_put_uninit(out, size);
     memset(opo, 0, size);
     opo->header.version = OFP_VERSION;
     opo->header.type = OFPT_PACKET_OUT;
     opo->buffer_id = htonl(UINT32_MAX);
     opo->in_port = htons(in_port);
-    opo->n_actions = htons(1);
-    opo->actions[0].type = htons(OFPAT_OUTPUT);
-    opo->actions[0].arg.output.max_len = htons(0);
-    opo->actions[0].arg.output.port = htons(out_port);
+
+    oao = (struct ofp_action_output *)&opo->actions[0];
+    oao->type = htons(OFPAT_OUTPUT);
+    oao->len = htons(sizeof *oao);
+    oao->port = htons(out_port);
+
+    opo->actions_len = htons(sizeof *oao);
+
     ofpbuf_put(out, packet->data, packet->size);
     update_openflow_length(out);
     return out;
@@ -827,7 +836,8 @@ make_buffered_packet_out(uint32_t buffer_id,
                          uint16_t in_port, uint16_t out_port)
 {
     struct ofp_packet_out *opo;
-    size_t size = sizeof *opo + sizeof opo->actions[0];
+    struct ofp_action_output *oao;
+    size_t size = sizeof *opo + sizeof *oao;
     struct ofpbuf *out = ofpbuf_new(size);
     opo = ofpbuf_put_uninit(out, size);
     memset(opo, 0, size);
@@ -836,10 +846,13 @@ make_buffered_packet_out(uint32_t buffer_id,
     opo->header.length = htons(size);
     opo->buffer_id = htonl(buffer_id);
     opo->in_port = htons(in_port);
-    opo->n_actions = htons(1);
-    opo->actions[0].type = htons(OFPAT_OUTPUT);
-    opo->actions[0].arg.output.max_len = htons(0);
-    opo->actions[0].arg.output.port = htons(out_port);
+
+    oao = (struct ofp_action_output *)&opo->actions[0];
+    oao->type = htons(OFPAT_OUTPUT);
+    oao->len = htons(sizeof *oao);
+    oao->port = htons(out_port);
+
+    opo->actions_len = htons(sizeof *oao);
     return out;
 }
 
index 164c21f3ab8cccc831716335cdf9bdc2f7ec2356..620db5d3bea0e81caaf60ce61031e029608737ee 100644 (file)
@@ -10,6 +10,10 @@ switch_SOURCES = \
        crc32.h \
        datapath.c \
        datapath.h \
+       dp_act.c \
+       dp_act.h \
+       nx_act.c \
+       nx_act.h \
        switch.c \
        switch-flow.c \
        switch-flow.h \
index a04aac905b86c41e63a3f0b10a851f5807b7d3ca..d64cd12e59e21672a7af97ad64b373d91761855d 100644 (file)
@@ -124,14 +124,14 @@ chain_insert(struct sw_chain *chain, struct sw_flow *flow)
 int
 chain_modify(struct sw_chain *chain, const struct sw_flow_key *key,
         uint16_t priority, int strict,
-        const struct ofp_action *actions, int n_actions)
+        const struct ofp_action_header *actions, size_t actions_len)
 {
     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, priority, strict, actions, n_actions);
+        count += t->modify(t, key, priority, strict, actions, actions_len);
     }
 
     return count;
index 9ad54d0eee779f685fcf0cc14f100d7e4e146036..39a9f30e3afe326bb4fcf74ad715c33e5ba26607 100644 (file)
 #ifndef CHAIN_H
 #define CHAIN_H 1
 
+#include <stddef.h>
 #include <stdint.h>
 
 struct sw_flow;
 struct sw_flow_key;
-struct ofp_action;
+struct ofp_action_header;
 struct list;
 
 #define TABLE_LINEAR_MAX_FLOWS  100
@@ -57,7 +58,7 @@ 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 *, 
-        uint16_t, int, const struct ofp_action *, int);
+        uint16_t, int, const struct ofp_action_header *, size_t);
 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 8358bf21eaf22c269f8719b8379a0f58fac14a3f..ce9c31e27915596d684f7edf26fff58093e4f782 100644 (file)
@@ -54,6 +54,7 @@
 #include "timeval.h"
 #include "vconn.h"
 #include "xtoxll.h"
+#include "dp_act.h"
 
 #define THIS_MODULE VLM_datapath
 #include "vlog.h"
@@ -144,27 +145,12 @@ static void remote_run(struct datapath *, struct remote *);
 static void remote_wait(struct remote *);
 static void remote_destroy(struct remote *);
 
-void dp_output_port(struct datapath *, struct ofpbuf *,
-                    int in_port, int out_port, bool ignore_no_fwd);
 void dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm);
-void dp_output_control(struct datapath *, struct ofpbuf *, int in_port,
-                       size_t max_len, int reason);
 static void send_flow_expired(struct datapath *, struct sw_flow *,
                               enum ofp_flow_expired_reason);
 static int update_port_status(struct sw_port *p);
 static void send_port_status(struct sw_port *p, uint8_t status);
 static void del_switch_port(struct sw_port *p);
-static void execute_actions(struct datapath *, struct ofpbuf *,
-                            int in_port, struct sw_flow_key *,
-                            const struct ofp_action *, int n_actions,
-                            bool ignore_no_fwd);
-static void modify_vlan_tci(struct ofpbuf *buffer, struct sw_flow_key *key,
-                            uint16_t tci, uint16_t mask);
-static void strip_vlan(struct ofpbuf *buffer);
-static void modify_nh(struct ofpbuf *buffer, uint16_t eth_proto,
-                      uint8_t nw_proto, const struct ofp_action *a);
-static void modify_th(struct ofpbuf *buffer, uint16_t eth_proto,
-                          uint8_t nw_proto, const struct ofp_action *a);
 
 /* Buffers are identified to userspace by a 31-bit opaque ID.  We divide the ID
  * into a buffer number (low bits) and a cookie (high bits).  The buffer number
@@ -815,7 +801,7 @@ fill_flow_stats(struct ofpbuf *buffer, struct sw_flow *flow,
                 int table_idx, time_t now)
 {
     struct ofp_flow_stats *ofs;
-    int length = sizeof *ofs + sizeof *ofs->actions * flow->sf_acts->n_actions;
+    int length = sizeof *ofs + flow->sf_acts->actions_len;
     ofs = ofpbuf_put_uninit(buffer, length);
     ofs->length          = htons(length);
     ofs->table_id        = table_idx;
@@ -839,8 +825,7 @@ fill_flow_stats(struct ofpbuf *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->sf_acts->actions,
-           sizeof *ofs->actions * flow->sf_acts->n_actions);
+    memcpy(ofs->actions, flow->sf_acts->actions, flow->sf_acts->actions_len);
 }
 
 \f
@@ -871,9 +856,8 @@ int run_flow_through_tables(struct datapath *dp, struct ofpbuf *buffer,
     flow = chain_lookup(dp->chain, &key);
     if (flow != NULL) {
         flow_used(flow, buffer);
-        execute_actions(dp, buffer, port_no(dp, p),
-                        &key, flow->sf_acts->actions, 
-                        flow->sf_acts->n_actions, false);
+        execute_actions(dp, buffer, &key, flow->sf_acts->actions, 
+                        flow->sf_acts->actions_len, false);
         return 0;
     } else {
         return -ESRCH;
@@ -892,196 +876,6 @@ void fwd_port_input(struct datapath *dp, struct ofpbuf *buffer,
     }
 }
 
-static void
-do_output(struct datapath *dp, struct ofpbuf *buffer, int in_port,
-          size_t max_len, int out_port, bool ignore_no_fwd)
-{
-    if (out_port != OFPP_CONTROLLER) {
-        dp_output_port(dp, buffer, in_port, out_port, ignore_no_fwd);
-    } else {
-        dp_output_control(dp, buffer, in_port, max_len, OFPR_ACTION);
-    }
-}
-
-static void
-execute_actions(struct datapath *dp, struct ofpbuf *buffer,
-                int in_port, struct sw_flow_key *key,
-                const struct ofp_action *actions, int n_actions,
-                bool ignore_no_fwd)
-{
-    /* Every output action needs a separate clone of 'buffer', but the common
-     * case is just a single output action, so that doing a clone and then
-     * freeing the original buffer is wasteful.  So the following code is
-     * slightly obscure just to avoid that. */
-    int prev_port;
-    size_t max_len=0;        /* Initialze to make compiler happy */
-    uint16_t eth_proto;
-    int i;
-
-    prev_port = -1;
-    eth_proto = ntohs(key->flow.dl_type);
-
-    for (i = 0; i < n_actions; i++) {
-        const struct ofp_action *a = &actions[i];
-        struct eth_header *eh = buffer->l2;
-
-        if (prev_port != -1) {
-            do_output(dp, ofpbuf_clone(buffer), in_port, max_len, prev_port,
-                      ignore_no_fwd);
-            prev_port = -1;
-        }
-
-        switch (ntohs(a->type)) {
-        case OFPAT_OUTPUT:
-            prev_port = ntohs(a->arg.output.port);
-            max_len = ntohs(a->arg.output.max_len);
-            break;
-
-        case OFPAT_SET_VLAN_VID: {
-            uint16_t tci = ntohs(a->arg.vlan_vid);
-            modify_vlan_tci(buffer, key, tci, VLAN_VID_MASK);
-            break;
-        }
-
-        case OFPAT_SET_VLAN_PCP: {
-            uint16_t tci = (uint16_t)a->arg.vlan_pcp << 13;
-            modify_vlan_tci(buffer, key, tci, VLAN_PCP_MASK);
-            break;
-        }
-
-        case OFPAT_STRIP_VLAN:
-            strip_vlan(buffer);
-            key->flow.dl_vlan = htons(OFP_VLAN_NONE);
-            break;
-
-        case OFPAT_SET_DL_SRC:
-            memcpy(eh->eth_src, a->arg.dl_addr, sizeof eh->eth_src);
-            break;
-
-        case OFPAT_SET_DL_DST:
-            memcpy(eh->eth_dst, a->arg.dl_addr, sizeof eh->eth_dst);
-            break;
-
-        case OFPAT_SET_NW_SRC:
-        case OFPAT_SET_NW_DST:
-            modify_nh(buffer, eth_proto, key->flow.nw_proto, a);
-            break;
-
-        case OFPAT_SET_TP_SRC:
-        case OFPAT_SET_TP_DST:
-            modify_th(buffer, eth_proto, key->flow.nw_proto, a);
-            break;
-
-        default:
-            NOT_REACHED();
-        }
-    }
-    if (prev_port != -1)
-        do_output(dp, buffer, in_port, max_len, prev_port, ignore_no_fwd);
-    else
-        ofpbuf_delete(buffer);
-}
-
-static void modify_nh(struct ofpbuf *buffer, uint16_t eth_proto,
-                      uint8_t nw_proto, const struct ofp_action *a)
-{
-    if (eth_proto == ETH_TYPE_IP) {
-        struct ip_header *nh = buffer->l3;
-        uint32_t new, *field;
-
-        new = a->arg.nw_addr;
-        field = a->type == OFPAT_SET_NW_SRC ? &nh->ip_src : &nh->ip_dst;
-        if (nw_proto == IP_TYPE_TCP) {
-            struct tcp_header *th = buffer->l4;
-            th->tcp_csum = recalc_csum32(th->tcp_csum, *field, new);
-        } else if (nw_proto == IP_TYPE_UDP) {
-            struct udp_header *th = buffer->l4;
-            if (th->udp_csum) {
-                th->udp_csum = recalc_csum32(th->udp_csum, *field, new);
-                if (!th->udp_csum) {
-                    th->udp_csum = 0xffff;
-                }
-            }
-        }
-        nh->ip_csum = recalc_csum32(nh->ip_csum, *field, new);
-        *field = new;
-    }
-}
-
-static void modify_th(struct ofpbuf *buffer, uint16_t eth_proto,
-                      uint8_t nw_proto, const struct ofp_action *a)
-{
-    if (eth_proto == ETH_TYPE_IP) {
-        uint16_t new, *field;
-
-        new = a->arg.tp;
-
-        if (nw_proto == IP_TYPE_TCP) {
-            struct tcp_header *th = buffer->l4;
-            field = a->type == OFPAT_SET_TP_SRC ? &th->tcp_src : &th->tcp_dst;
-            th->tcp_csum = recalc_csum16(th->tcp_csum, *field, new);
-            *field = new;
-        } else if (nw_proto == IP_TYPE_UDP) {
-            struct udp_header *th = buffer->l4;
-            field = a->type == OFPAT_SET_TP_SRC ? &th->udp_src : &th->udp_dst;
-            th->udp_csum = recalc_csum16(th->udp_csum, *field, new);
-            *field = new;
-        }
-    }
-}
-
-/* Modify vlan tag control information (TCI).  Only sets the TCI bits
- * indicated by 'mask'.  If no vlan tag is present, one is added.
- */
-static void
-modify_vlan_tci(struct ofpbuf *buffer, struct sw_flow_key *key, 
-        uint16_t tci, uint16_t mask)
-{
-    struct vlan_eth_header *veh;
-
-    if (key->flow.dl_vlan != htons(OFP_VLAN_NONE)) {
-        /* Modify vlan id, but maintain other TCI values */
-        veh = buffer->l2;
-        veh->veth_tci &= ~htons(mask);
-        veh->veth_tci |= htons(tci);
-    } else {
-        /* Insert new vlan id. */
-        struct eth_header *eh = buffer->l2;
-        struct vlan_eth_header tmp;
-        memcpy(tmp.veth_dst, eh->eth_dst, ETH_ADDR_LEN);
-        memcpy(tmp.veth_src, eh->eth_src, ETH_ADDR_LEN);
-        tmp.veth_type = htons(ETH_TYPE_VLAN);
-        tmp.veth_tci = htons(tci);
-        tmp.veth_next_type = eh->eth_type;
-        
-        veh = ofpbuf_push_uninit(buffer, VLAN_HEADER_LEN);
-        memcpy(veh, &tmp, sizeof tmp);
-        buffer->l2 = (char*)buffer->l2 - VLAN_HEADER_LEN;
-    }
-
-    key->flow.dl_vlan = veh->veth_tci & htons(VLAN_VID_MASK);
-}
-
-/* Remove an existing vlan header if it exists. */
-static void
-strip_vlan(struct ofpbuf *buffer)
-{
-    struct vlan_eth_header *veh = buffer->l2;
-
-    if (veh->veth_type == htons(ETH_TYPE_VLAN)) {
-        struct eth_header tmp;
-        
-        memcpy(tmp.eth_dst, veh->veth_dst, ETH_ADDR_LEN);
-        memcpy(tmp.eth_src, veh->veth_src, ETH_ADDR_LEN);
-        tmp.eth_type = veh->veth_next_type;
-        
-        buffer->size -= VLAN_HEADER_LEN;
-        buffer->data = (char*)buffer->data + VLAN_HEADER_LEN;
-        buffer->l2 = (char*)buffer->l2 + VLAN_HEADER_LEN;
-        memcpy(buffer->data, &tmp, sizeof tmp);
-    }
-}
-
 static int
 recv_features_request(struct datapath *dp, const struct sender *sender,
                       const void *msg) 
@@ -1124,25 +918,25 @@ recv_set_config(struct datapath *dp, const struct sender *sender UNUSED,
 }
 
 static int
-recv_packet_out(struct datapath *dp, const struct sender *sender UNUSED,
+recv_packet_out(struct datapath *dp, const struct sender *sender,
                 const void *msg)
 {
     const struct ofp_packet_out *opo = msg;
     struct sw_flow_key key;
+    uint16_t v_code;
     struct ofpbuf *buffer;
-    int n_actions = ntohs(opo->n_actions);
-    int act_len = n_actions * sizeof opo->actions[0];
+    size_t actions_len = ntohs(opo->actions_len);
 
-    if (act_len > (ntohs(opo->header.length) - sizeof *opo)) {
+    if (actions_len > (ntohs(opo->header.length) - sizeof *opo)) {
         VLOG_DBG_RL(&rl, "message too short for number of actions");
         return -EINVAL;
     }
 
     if (ntohl(opo->buffer_id) == (uint32_t) -1) {
         /* FIXME: can we avoid copying data here? */
-        int data_len = ntohs(opo->header.length) - sizeof *opo - act_len;
+        int data_len = ntohs(opo->header.length) - sizeof *opo - actions_len;
         buffer = ofpbuf_new(data_len);
-        ofpbuf_put(buffer, &opo->actions[n_actions], data_len);
+        ofpbuf_put(buffer, (uint8_t *)opo->actions + actions_len, data_len);
     } else {
         buffer = retrieve_buffer(ntohl(opo->buffer_id));
         if (!buffer) {
@@ -1151,10 +945,21 @@ recv_packet_out(struct datapath *dp, const struct sender *sender UNUSED,
     }
  
     flow_extract(buffer, ntohs(opo->in_port), &key.flow);
-    execute_actions(dp, buffer, ntohs(opo->in_port),
-                    &key, opo->actions, n_actions, true);
 
-   return 0;
+    v_code = validate_actions(dp, &key, opo->actions, actions_len);
+    if (v_code != ACT_VALIDATION_OK) {
+        dp_send_error_msg(dp, sender, OFPET_BAD_ACTION, v_code,
+                  msg, ntohs(opo->header.length));
+        goto error;
+    }
+
+    execute_actions(dp, buffer, &key, opo->actions, actions_len, true);
+
+    return 0;
+
+error:
+    ofpbuf_delete(buffer);
+    return -EINVAL;
 }
 
 static int
@@ -1169,47 +974,37 @@ recv_port_mod(struct datapath *dp, const struct sender *sender UNUSED,
 }
 
 static int
-add_flow(struct datapath *dp, const struct ofp_flow_mod *ofm)
+add_flow(struct datapath *dp, const struct sender *sender,
+        const struct ofp_flow_mod *ofm)
 {
     int error = -ENOMEM;
-    int n_actions;
-    int i;
-    struct sw_flow *flow;
-
-
-    /* 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;
-        }
-    }
+    uint16_t v_code;
+    struct sw_flow *flow; 
+    size_t actions_len = ntohs(ofm->header.length) - sizeof *ofm;
 
     /* Allocate memory. */
-    flow = flow_alloc(n_actions);
+    flow = flow_alloc(actions_len);
     if (flow == NULL)
         goto error;
 
-    /* Fill out flow. */
     flow_extract_match(&flow->key, &ofm->match);
+
+    v_code = validate_actions(dp, &flow->key, ofm->actions, actions_len);
+    if (v_code != ACT_VALIDATION_OK) {
+        dp_send_error_msg(dp, sender, OFPET_BAD_ACTION, v_code,
+                  ofm, ntohs(ofm->header.length));
+        goto error;
+    }
+
+    /* Fill out flow. */
     flow->priority = flow->key.wildcards ? ntohs(ofm->priority) : -1;
     flow->idle_timeout = ntohs(ofm->idle_timeout);
     flow->hard_timeout = ntohs(ofm->hard_timeout);
     flow->used = flow->created = time_now();
-    flow->sf_acts->n_actions = n_actions;
+    flow->sf_acts->actions_len = actions_len;
     flow->byte_count = 0;
     flow->packet_count = 0;
-    memcpy(flow->sf_acts->actions, ofm->actions, 
-                n_actions * sizeof *flow->sf_acts->actions);
+    memcpy(flow->sf_acts->actions, ofm->actions, actions_len);
 
     /* Act. */
     error = chain_insert(dp->chain, flow);
@@ -1224,8 +1019,8 @@ add_flow(struct datapath *dp, const struct ofp_flow_mod *ofm)
             uint16_t in_port = ntohs(ofm->match.in_port);
             flow_used(flow, buffer);
             flow_extract(buffer, in_port, &key.flow);
-            execute_actions(dp, buffer, in_port, &key,
-                            ofm->actions, n_actions, false);
+            execute_actions(dp, buffer, &key, 
+                    ofm->actions, actions_len, false);
         } else {
             error = -ESRCH; 
         }
@@ -1241,37 +1036,30 @@ error:
 }
 
 static int
-mod_flow(struct datapath *dp, const struct ofp_flow_mod *ofm)
+mod_flow(struct datapath *dp, const struct sender *sender,
+        const struct ofp_flow_mod *ofm)
 {
     int error = -ENOMEM;
-    int n_actions;
-    int i;
+    uint16_t v_code;
+    size_t actions_len;
     struct sw_flow_key key;
     uint16_t priority;
     int strict;
 
-
-    /* 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);
+    actions_len = ntohs(ofm->header.length) - sizeof *ofm;
+    v_code = validate_actions(dp, &key, ofm->actions, actions_len);
+    if (v_code != ACT_VALIDATION_OK) {
+        dp_send_error_msg(dp, sender, OFPET_BAD_ACTION, v_code,
+                  ofm, ntohs(ofm->header.length));
+        goto error;
     }
 
-    flow_extract_match(&key, &ofm->match);
     priority = key.wildcards ? ntohs(ofm->priority) : -1;
     strict = (ofm->command == htons(OFPFC_MODIFY_STRICT)) ? 1 : 0;
-    chain_modify(dp->chain, &key, priority, strict, ofm->actions, n_actions);
+    chain_modify(dp->chain, &key, priority, strict, ofm->actions, actions_len);
 
     if (ntohl(ofm->buffer_id) != UINT32_MAX) {
         struct ofpbuf *buffer = retrieve_buffer(ntohl(ofm->buffer_id));
@@ -1279,8 +1067,8 @@ mod_flow(struct datapath *dp, const struct ofp_flow_mod *ofm)
             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);
+            execute_actions(dp, buffer, &skb_key,
+                            ofm->actions, actions_len, false);
         } else {
             error = -ESRCH; 
         }
@@ -1294,16 +1082,16 @@ error:
 }
 
 static int
-recv_flow(struct datapath *dp, const struct sender *sender UNUSED,
+recv_flow(struct datapath *dp, const struct sender *sender,
           const void *msg)
 {
     const struct ofp_flow_mod *ofm = msg;
     uint16_t command = ntohs(ofm->command);
 
     if (command == OFPFC_ADD) {
-        return add_flow(dp, ofm);
+        return add_flow(dp, sender, ofm);
     } else if ((command == OFPFC_MODIFY) || (command == OFPFC_MODIFY_STRICT)) {
-        return mod_flow(dp, ofm);
+        return mod_flow(dp, sender, ofm);
     }  else if (command == OFPFC_DELETE) {
         struct sw_flow_key key;
         flow_extract_match(&key, &ofm->match);
index f0423deb918660914b471aa80cd5d72fbb8651b0..56d5324f2be703c0a54a8cf6412dcd04637ba1f7 100644 (file)
@@ -36,7 +36,9 @@
 #ifndef DATAPATH_H
 #define DATAPATH_H 1
 
+#include <stdbool.h>
 #include <stdint.h>
+#include "ofpbuf.h"
 
 struct datapath;
 struct rconn;
@@ -47,5 +49,9 @@ int dp_add_port(struct datapath *, const char *netdev);
 void dp_add_listen_pvconn(struct datapath *, struct pvconn *);
 void dp_run(struct datapath *);
 void dp_wait(struct datapath *);
+void dp_output_port(struct datapath *, struct ofpbuf *, int in_port, 
+        int out_port, bool ignore_no_fwd);
+void dp_output_control(struct datapath *, struct ofpbuf *, int in_port,
+        size_t max_len, int reason);
 
 #endif /* datapath.h */
diff --git a/switch/dp_act.c b/switch/dp_act.c
new file mode 100644 (file)
index 0000000..f1774f3
--- /dev/null
@@ -0,0 +1,476 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ * 
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * 
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+/* Functions for executing OpenFlow actions. */
+
+#include <arpa/inet.h>
+#include "csum.h"
+#include "packets.h"
+#include "dp_act.h"
+#include "nicira-ext.h"
+#include "nx_act.h"
+
+
+static uint16_t
+validate_output(struct datapath *dp, const struct sw_flow_key *key, 
+        const struct ofp_action_header *ah) 
+{
+    struct ofp_action_output *oa = (struct ofp_action_output *)ah;
+
+    /* To prevent loops, make sure there's no action to send to the
+     * OFP_TABLE virtual port.
+     */
+    if (oa->port == htons(OFPP_NONE) || oa->port == key->flow.in_port) {
+        return OFPBAC_BAD_OUT_PORT;
+    }
+    return ACT_VALIDATION_OK;
+}
+
+static void
+do_output(struct datapath *dp, struct ofpbuf *buffer, int in_port,
+          size_t max_len, int out_port, bool ignore_no_fwd)
+{
+    if (out_port != OFPP_CONTROLLER) {
+        dp_output_port(dp, buffer, in_port, out_port, ignore_no_fwd);
+    } else {
+        dp_output_control(dp, buffer, in_port, max_len, OFPR_ACTION);
+    }
+}
+
+/* Modify vlan tag control information (TCI).  Only sets the TCI bits
+ * indicated by 'mask'.  If no vlan tag is present, one is added.
+ */
+static void
+modify_vlan_tci(struct ofpbuf *buffer, struct sw_flow_key *key,
+        uint16_t tci, uint16_t mask)
+{
+    struct vlan_eth_header *veh;
+
+    if (key->flow.dl_vlan != htons(OFP_VLAN_NONE)) {
+        /* Modify vlan id, but maintain other TCI values */
+        veh = buffer->l2;
+        veh->veth_tci &= ~htons(mask);
+        veh->veth_tci |= htons(tci);
+    } else {
+        /* Insert new vlan id. */
+        struct eth_header *eh = buffer->l2;
+        struct vlan_eth_header tmp;
+        memcpy(tmp.veth_dst, eh->eth_dst, ETH_ADDR_LEN);
+        memcpy(tmp.veth_src, eh->eth_src, ETH_ADDR_LEN);
+        tmp.veth_type = htons(ETH_TYPE_VLAN);
+        tmp.veth_tci = htons(tci);
+        tmp.veth_next_type = eh->eth_type;
+
+        veh = ofpbuf_push_uninit(buffer, VLAN_HEADER_LEN);
+        memcpy(veh, &tmp, sizeof tmp);
+        buffer->l2 = (char*)buffer->l2 - VLAN_HEADER_LEN;
+    }
+
+    key->flow.dl_vlan = veh->veth_tci & htons(VLAN_VID_MASK);
+}
+
+
+/* Remove an existing vlan header if it exists. */
+static void
+vlan_pull_tag(struct ofpbuf *buffer)
+{
+    struct vlan_eth_header *veh = buffer->l2;
+
+    if (veh->veth_type == htons(ETH_TYPE_VLAN)) {
+        struct eth_header tmp;
+
+        memcpy(tmp.eth_dst, veh->veth_dst, ETH_ADDR_LEN);
+        memcpy(tmp.eth_src, veh->veth_src, ETH_ADDR_LEN);
+        tmp.eth_type = veh->veth_next_type;
+
+        buffer->size -= VLAN_HEADER_LEN;
+        buffer->data = (char*)buffer->data + VLAN_HEADER_LEN;
+        buffer->l2 = (char*)buffer->l2 + VLAN_HEADER_LEN;
+        memcpy(buffer->data, &tmp, sizeof tmp);
+    }
+}
+
+static void
+set_vlan_vid(struct ofpbuf *buffer, struct sw_flow_key *key, 
+        const struct ofp_action_header *ah)
+{
+    struct ofp_action_vlan_vid *va = (struct ofp_action_vlan_vid *)ah;
+    uint16_t tci = ntohs(va->vlan_vid);
+
+    modify_vlan_tci(buffer, key, tci, VLAN_VID_MASK);
+}
+
+static void
+set_vlan_pcp(struct ofpbuf *buffer, struct sw_flow_key *key, 
+        const struct ofp_action_header *ah)
+{
+    struct ofp_action_vlan_pcp *va = (struct ofp_action_vlan_pcp *)ah;
+    uint16_t tci = (uint16_t)va->vlan_pcp << 13;
+
+    modify_vlan_tci(buffer, key, tci, VLAN_PCP_MASK);
+}
+
+static void
+strip_vlan(struct ofpbuf *buffer, struct sw_flow_key *key, 
+        const struct ofp_action_header *ah)
+{
+    vlan_pull_tag(buffer);
+    key->flow.dl_vlan = htons(OFP_VLAN_NONE);
+}
+
+static void
+set_dl_addr(struct ofpbuf *buffer, struct sw_flow_key *key, 
+        const struct ofp_action_header *ah)
+{
+    struct ofp_action_dl_addr *da = (struct ofp_action_dl_addr *)ah;
+    struct eth_header *eh = buffer->l2;
+
+    if (da->type == htons(OFPAT_SET_DL_SRC)) {
+        memcpy(eh->eth_src, da->dl_addr, sizeof eh->eth_src);
+    } else {
+        memcpy(eh->eth_dst, da->dl_addr, sizeof eh->eth_dst);
+    }
+}
+
+static void
+set_nw_addr(struct ofpbuf *buffer, struct sw_flow_key *key, 
+        const struct ofp_action_header *ah)
+{
+    struct ofp_action_nw_addr *na = (struct ofp_action_nw_addr *)ah;
+    uint16_t eth_proto = ntohs(key->flow.dl_type);
+
+    if (eth_proto == ETH_TYPE_IP) {
+        struct ip_header *nh = buffer->l3;
+        uint8_t nw_proto = key->flow.nw_proto;
+        uint32_t new, *field;
+
+        new = na->nw_addr;
+        field = na->type == OFPAT_SET_NW_SRC ? &nh->ip_src : &nh->ip_dst;
+        if (nw_proto == IP_TYPE_TCP) {
+            struct tcp_header *th = buffer->l4;
+            th->tcp_csum = recalc_csum32(th->tcp_csum, *field, new);
+        } else if (nw_proto == IP_TYPE_UDP) {
+            struct udp_header *th = buffer->l4;
+            if (th->udp_csum) {
+                th->udp_csum = recalc_csum32(th->udp_csum, *field, new);
+                if (!th->udp_csum) {
+                    th->udp_csum = 0xffff;
+                }
+            }
+        }
+        nh->ip_csum = recalc_csum32(nh->ip_csum, *field, new);
+        *field = new;
+    }
+}
+
+static void
+set_tp_port(struct ofpbuf *buffer, struct sw_flow_key *key, 
+        const struct ofp_action_header *ah)
+{
+    struct ofp_action_tp_port *ta = (struct ofp_action_tp_port *)ah;
+    uint16_t eth_proto = ntohs(key->flow.dl_type);
+
+    if (eth_proto == ETH_TYPE_IP) {
+        uint8_t nw_proto = key->flow.nw_proto;
+        uint16_t new, *field;
+
+        new = ta->tp_port;
+        if (nw_proto == IP_TYPE_TCP) {
+            struct tcp_header *th = buffer->l4;
+            field = ta->type == OFPAT_SET_TP_SRC ? &th->tcp_src : &th->tcp_dst;
+            th->tcp_csum = recalc_csum16(th->tcp_csum, *field, new);
+            *field = new;
+        } else if (nw_proto == IP_TYPE_UDP) {
+            struct udp_header *th = buffer->l4;
+            field = ta->type == OFPAT_SET_TP_SRC ? &th->udp_src : &th->udp_dst;
+            th->udp_csum = recalc_csum16(th->udp_csum, *field, new);
+            *field = new;
+        }
+    }
+}
+
+struct openflow_action {
+    size_t min_size;
+    size_t max_size;
+    uint16_t (*validate)(struct datapath *dp, 
+            const struct sw_flow_key *key,
+            const struct ofp_action_header *ah);
+    void (*execute)(struct ofpbuf *buffer,
+            struct sw_flow_key *key, 
+            const struct ofp_action_header *ah);
+};
+
+static const struct openflow_action of_actions[] = {
+    [OFPAT_OUTPUT] = {
+        sizeof(struct ofp_action_output),
+        sizeof(struct ofp_action_output),
+        validate_output,
+        NULL                   /* This is optimized into execute_actions */
+    },
+    [OFPAT_SET_VLAN_VID] = {
+        sizeof(struct ofp_action_vlan_vid),
+        sizeof(struct ofp_action_vlan_vid),
+        NULL,
+        set_vlan_vid
+    },
+    [OFPAT_SET_VLAN_PCP] = {
+        sizeof(struct ofp_action_vlan_pcp),
+        sizeof(struct ofp_action_vlan_pcp),
+        NULL,
+        set_vlan_pcp
+    },
+    [OFPAT_STRIP_VLAN] = {
+        sizeof(struct ofp_action_header),
+        sizeof(struct ofp_action_header),
+        NULL,
+        strip_vlan
+    },
+    [OFPAT_SET_DL_SRC] = {
+        sizeof(struct ofp_action_dl_addr),
+        sizeof(struct ofp_action_dl_addr),
+        NULL,
+        set_dl_addr
+    },
+    [OFPAT_SET_DL_DST] = {
+        sizeof(struct ofp_action_dl_addr),
+        sizeof(struct ofp_action_dl_addr),
+        NULL,
+        set_dl_addr
+    },
+    [OFPAT_SET_NW_SRC] = {
+        sizeof(struct ofp_action_nw_addr),
+        sizeof(struct ofp_action_nw_addr),
+        NULL,
+        set_nw_addr
+    },
+    [OFPAT_SET_NW_DST] = {
+        sizeof(struct ofp_action_nw_addr),
+        sizeof(struct ofp_action_nw_addr),
+        NULL,
+        set_nw_addr
+    },
+    [OFPAT_SET_TP_SRC] = {
+        sizeof(struct ofp_action_tp_port),
+        sizeof(struct ofp_action_tp_port),
+        NULL,
+        set_tp_port
+    },
+    [OFPAT_SET_TP_DST] = {
+        sizeof(struct ofp_action_tp_port),
+        sizeof(struct ofp_action_tp_port),
+        NULL,
+        set_tp_port
+    }
+    /* OFPAT_VENDOR is not here, since it would blow up the array size. */
+};
+
+/* Validate built-in OpenFlow actions.  Either returns ACT_VALIDATION_OK
+ * or an OFPET_BAD_ACTION error code. */
+static uint16_t 
+validate_ofpat(struct datapath *dp, const struct sw_flow_key *key, 
+        const struct ofp_action_header *ah, uint16_t type, uint16_t len)
+{
+    int ret = ACT_VALIDATION_OK;
+    const struct openflow_action *act = &of_actions[type];
+
+    if ((len < act->min_size) || (len > act->max_size)) {
+        return OFPBAC_BAD_LEN;
+    }
+
+    if (act->validate) {
+        ret = act->validate(dp, key, ah);
+    }
+
+    return ret;
+}
+
+/* Validate vendor-defined actions.  Either returns ACT_VALIDATION_OK
+ * or an OFPET_BAD_ACTION error code. */
+static uint16_t 
+validate_vendor(struct datapath *dp, const struct sw_flow_key *key, 
+        const struct ofp_action_header *ah, uint16_t len)
+{
+    struct ofp_action_vendor_header *avh;
+    int ret = ACT_VALIDATION_OK;
+
+    if (len < sizeof(struct ofp_action_vendor_header)) {
+        return OFPBAC_BAD_LEN;
+    }
+
+    avh = (struct ofp_action_vendor_header *)ah;
+
+    switch(ntohl(avh->vendor)) {
+    case NX_VENDOR_ID: 
+        ret = nx_validate_act(dp, key, avh, len);
+        break;
+
+    default:
+        return OFPBAC_BAD_VENDOR;
+    }
+
+    return ret;
+}
+
+/* Validates a list of actions.  If a problem is found, a code for the
+ * OFPET_BAD_ACTION error type is returned.  If the action list validates, 
+ * ACT_VALIDATION_OK is returned. */
+uint16_t 
+validate_actions(struct datapath *dp, const struct sw_flow_key *key,
+        const struct ofp_action_header *actions, size_t actions_len)
+{
+    uint8_t *p = (uint8_t *)actions;
+    int err;
+
+    while (actions_len >= sizeof(struct ofp_action_header)) {
+        struct ofp_action_header *ah = (struct ofp_action_header *)p;
+        size_t len = ntohs(ah->len);
+        uint16_t type;
+
+        /* Make there's enough remaining data for the specified length
+         * and that the action length is a multiple of 64 bits. */
+        if ((actions_len < len) || (len % 8) != 0) {
+            return OFPBAC_BAD_LEN;
+        }
+
+        type = ntohs(ah->type);
+        if (type < ARRAY_SIZE(of_actions)) {
+            err = validate_ofpat(dp, key, ah, type, len);
+            if (err != ACT_VALIDATION_OK) {
+                return err;
+            }
+        } else if (type == OFPAT_VENDOR) {
+            err = validate_vendor(dp, key, ah, len);
+            if (err != ACT_VALIDATION_OK) {
+                return err;
+            }
+        } else {
+            return OFPBAC_BAD_TYPE;
+        }
+
+        p += len;
+        actions_len -= len;
+    }
+
+    /* Check if there's any trailing garbage. */
+    if (actions_len != 0) {
+        return OFPBAC_BAD_LEN;
+    }
+
+    return ACT_VALIDATION_OK;
+}
+
+/* Execute a built-in OpenFlow action against 'buffer'. */
+static void
+execute_ofpat(struct ofpbuf *buffer, struct sw_flow_key *key, 
+        const struct ofp_action_header *ah, uint16_t type)
+{
+    const struct openflow_action *act = &of_actions[type];
+
+    if (act->execute) {
+        act->execute(buffer, key, ah);
+    }
+}
+
+/* Execute a vendor-defined action against 'buffer'. */
+static void
+execute_vendor(struct ofpbuf *buffer, const struct sw_flow_key *key, 
+        const struct ofp_action_header *ah)
+{
+    struct ofp_action_vendor_header *avh 
+            = (struct ofp_action_vendor_header *)ah;
+
+    switch(ntohl(avh->vendor)) {
+    case NX_VENDOR_ID: 
+        nx_execute_act(buffer, key, avh);
+        break;
+
+    default:
+        /* This should not be possible due to prior validation. */
+        printf("attempt to execute action with unknown vendor: %#x\n", 
+                ntohl(avh->vendor));
+        break;
+    }
+}
+
+/* Execute a list of actions against 'buffer'. */
+void execute_actions(struct datapath *dp, struct ofpbuf *buffer,
+             struct sw_flow_key *key,
+             const struct ofp_action_header *actions, size_t actions_len,
+             int ignore_no_fwd)
+{
+    /* Every output action needs a separate clone of 'buffer', but the common
+     * case is just a single output action, so that doing a clone and then
+     * freeing the original buffer is wasteful.  So the following code is
+     * slightly obscure just to avoid that. */
+    int prev_port;
+    size_t max_len=0;     /* Initialze to make compiler happy */
+    uint16_t in_port = ntohs(key->flow.in_port);
+    uint8_t *p = (uint8_t *)actions;
+
+    prev_port = -1;
+
+    /* The action list was already validated, so we can be a bit looser
+     * in our sanity-checking. */
+    while (actions_len > 0) {
+        struct ofp_action_header *ah = (struct ofp_action_header *)p;
+        size_t len = htons(ah->len);
+
+        if (prev_port != -1) {
+            do_output(dp, ofpbuf_clone(buffer), in_port, max_len, 
+                    prev_port, ignore_no_fwd);
+            prev_port = -1;
+        }
+
+        if (ah->type == htons(OFPAT_OUTPUT)) {
+            struct ofp_action_output *oa = (struct ofp_action_output *)p;
+            prev_port = ntohs(oa->port);
+            max_len = ntohs(oa->max_len);
+        } else {
+            uint16_t type = ntohs(ah->type);
+
+            if (type < ARRAY_SIZE(of_actions)) {
+                execute_ofpat(buffer, key, ah, type);
+            } else if (type == OFPAT_VENDOR) {
+                execute_vendor(buffer, key, ah);
+            }
+        }
+
+        p += len;
+        actions_len -= len;
+    }
+    if (prev_port != -1) {
+        do_output(dp, buffer, in_port, max_len, prev_port, ignore_no_fwd);
+    } else {
+        ofpbuf_delete(buffer);
+    }
+}
diff --git a/switch/dp_act.h b/switch/dp_act.h
new file mode 100644 (file)
index 0000000..f556973
--- /dev/null
@@ -0,0 +1,49 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ * 
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * 
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#ifndef DP_ACT_H
+#define DP_ACT_H 1
+
+#include "openflow.h"
+#include "switch-flow.h"
+#include "datapath.h"
+
+#define ACT_VALIDATION_OK ((uint16_t)-1)
+
+uint16_t validate_actions(struct datapath *, const struct sw_flow_key *,
+               const struct ofp_action_header *, size_t);
+void execute_actions(struct datapath *, struct ofpbuf *,
+               struct sw_flow_key *, const struct ofp_action_header *, 
+               size_t action_len, int ignore_no_fwd);
+
+#endif /* dp_act.h */
diff --git a/switch/nx_act.c b/switch/nx_act.c
new file mode 100644 (file)
index 0000000..37d5464
--- /dev/null
@@ -0,0 +1,52 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ * 
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * 
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+/* Functions for Nicira-extended actions. */
+#include "nicira-ext.h"
+#include "nx_act.h"
+
+uint16_t
+nx_validate_act(struct datapath *dp, const struct sw_flow_key *key,
+               const struct ofp_action_vendor_header *avh, uint16_t len)
+{
+       /* Nothing to validate yet */
+       return OFPBAC_BAD_VENDOR_TYPE;
+}
+
+void
+nx_execute_act(struct ofpbuf *buffer, const struct sw_flow_key *key,
+               const struct ofp_action_vendor_header *avh)
+{
+       /* Nothing to execute yet */
+}
+
diff --git a/switch/nx_act.h b/switch/nx_act.h
new file mode 100644 (file)
index 0000000..92d1065
--- /dev/null
@@ -0,0 +1,48 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ * 
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * 
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#ifndef NX_ACT_H
+#define NX_ACT_H 1
+
+#include "switch-flow.h"
+#include "datapath.h"
+
+
+uint16_t nx_validate_act(struct datapath *dp, const struct sw_flow_key *key,
+               const struct ofp_action_vendor_header *avh, uint16_t len);
+
+void nx_execute_act(struct ofpbuf *buffer, 
+               const struct sw_flow_key *key,
+               const struct ofp_action_vendor_header *avh);
+
+#endif /* nx_act.h */
index 133e4f89a3397df066b202f50213693da33c7d71..50a0dee79729e221fb89b498e306fb2a32c3c720 100644 (file)
@@ -167,13 +167,13 @@ flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from)
     to->pad           = 0;
 }
 
-/* Allocates and returns a new flow with 'n_actions' action, using allocation
- * flags 'flags'.  Returns the new flow or a null pointer on failure. */
+/* Allocates and returns a new flow with room for 'actions_len' actions. 
+ * Returns the new flow or a null pointer on failure. */
 struct sw_flow *
-flow_alloc(int n_actions)
+flow_alloc(size_t actions_len)
 {
     struct sw_flow_actions *sfa;
-    size_t size = sizeof *sfa + (n_actions * sizeof sfa->actions[0]);
+    size_t size = sizeof *sfa + actions_len;
     struct sw_flow *flow = malloc(sizeof *flow);
     if (!flow)
         return NULL;
@@ -183,7 +183,7 @@ flow_alloc(int n_actions)
         free(flow);
         return NULL;
     }
-    sfa->n_actions = n_actions;
+    sfa->actions_len = actions_len;
     flow->sf_acts = sfa;
     return flow;
 }
@@ -201,18 +201,18 @@ flow_free(struct sw_flow *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)
+void flow_replace_acts(struct sw_flow *flow, 
+        const struct ofp_action_header *actions, size_t actions_len)
 {
     struct sw_flow_actions *sfa;
-    int size = sizeof *sfa + (n_actions * sizeof sfa->actions[0]);
+    int size = sizeof *sfa + actions_len;
 
     sfa = malloc(size);
     if (unlikely(!sfa))
         return;
 
-    sfa->n_actions = n_actions;
-    memcpy(sfa->actions, actions, n_actions * sizeof sfa->actions[0]);
+    sfa->actions_len = actions_len;
+    memcpy(sfa->actions, actions, actions_len);
 
     free(flow->sf_acts);
     flow->sf_acts = sfa;
index 817998c7f7017cce0391214ce4df6a1944f71dd1..11ef27a99eb6f0668acc90d966a54ecde98bbead 100644 (file)
@@ -50,8 +50,8 @@ struct sw_flow_key {
 };
 
 struct sw_flow_actions {
-    unsigned int n_actions;
-    struct ofp_action actions[0];
+    size_t actions_len;
+    struct ofp_action_header actions[0];
 };
 
 struct sw_flow {
@@ -78,11 +78,12 @@ 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);
-struct sw_flow *flow_alloc(int n_actions);
+struct sw_flow *flow_alloc(size_t);
 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_replace_acts(struct sw_flow *, const struct ofp_action_header *, 
+        size_t);
 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 d342eeb7e3543357bef6b000ed3d2493c62e4f6f..bef7d9e10a5cddc86e39682c1d8478537fbda913 100644 (file)
@@ -94,7 +94,7 @@ static int table_hash_insert(struct sw_table *swt, struct sw_flow *flow)
 
 static int table_hash_modify(struct sw_table *swt, 
         const struct sw_flow_key *key, uint16_t priority, int strict,
-        const struct ofp_action *actions, int n_actions
+        const struct ofp_action_header *actions, size_t actions_len
 {
     struct sw_table_hash *th = (struct sw_table_hash *) swt;
     unsigned int count = 0;
@@ -104,7 +104,7 @@ static int table_hash_modify(struct sw_table *swt,
         struct sw_flow *flow = *bucket;
         if (flow && flow_matches_desc(&flow->key, key, strict)
                 && (!strict || (flow->priority == priority))) {
-            flow_replace_acts(flow, actions, n_actions);
+            flow_replace_acts(flow, actions, actions_len);
             count = 1;
         }
     } else {
@@ -115,7 +115,7 @@ static int table_hash_modify(struct sw_table *swt,
             struct sw_flow *flow = *bucket;
             if (flow && flow_matches_desc(&flow->key, key, strict)
                     && (!strict || (flow->priority == priority))) {
-                flow_replace_acts(flow, actions, n_actions);
+                flow_replace_acts(flow, actions, actions_len);
                 count++;
             }
         }
@@ -305,13 +305,13 @@ static int table_hash2_insert(struct sw_table *swt, struct sw_flow *flow)
 
 static int table_hash2_modify(struct sw_table *swt, 
         const struct sw_flow_key *key, uint16_t priority, int strict,
-        const struct ofp_action *actions, int n_actions
+        const struct ofp_action_header *actions, size_t actions_len
 {
     struct sw_table_hash2 *t2 = (struct sw_table_hash2 *) swt;
     return (table_hash_modify(t2->subtable[0], key, priority, strict,
-                    actions, n_actions)
+                    actions, actions_len)
             + table_hash_modify(t2->subtable[1], key, priority, strict,
-                    actions, n_actions));
+                    actions, actions_len));
 }
 
 static int table_hash2_delete(struct sw_table *swt,
index 97f458f181ae91b9d2ec5aae03ccb4ccae85e19a..d3d3c5226281618ed0250e7f7ee68afcfae6dc97 100644 (file)
@@ -102,7 +102,7 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow)
 
 static int table_linear_modify(struct sw_table *swt,
                 const struct sw_flow_key *key, uint16_t priority, int strict,
-                const struct ofp_action *actions, int n_actions)
+                const struct ofp_action_header *actions, size_t actions_len)
 {
     struct sw_table_linear *tl = (struct sw_table_linear *) swt;
     struct sw_flow *flow;
@@ -111,7 +111,7 @@ static int table_linear_modify(struct sw_table *swt,
     LIST_FOR_EACH (flow, struct sw_flow, node, &tl->flows) {
         if (flow_matches_desc(&flow->key, key, strict)
                 && (!strict || (flow->priority == priority))) {
-            flow_replace_acts(flow, actions, n_actions);
+            flow_replace_acts(flow, actions, actions_len);
             count++;
         }
     }
index 37b91413fb48ae99ad3a8aa11f730e562d9e8aaa..b73a8fe580dcefd5f20597ab57606a046c261b4a 100644 (file)
 #ifndef TABLE_H
 #define TABLE_H 1
 
+#include <stddef.h>
 #include <stdint.h>
 
 struct sw_flow;
 struct sw_flow_key;
-struct ofp_action;
+struct ofp_action_header;
 struct list;
 
 /* Table statistics. */
@@ -91,7 +92,7 @@ struct sw_table {
      * that were modified. */
     int (*modify)(struct sw_table *table, const struct sw_flow_key *key,
             uint16_t priority, int strict,
-            const struct ofp_action *actions, int n_actions);
+            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 
index dc0d9cee2c08fa26915f079d15e8e2b37077f2f8..b0d2353e2235b5d371d658388331a062a6a43984 100644 (file)
@@ -69,7 +69,9 @@
 #define THIS_MODULE VLM_dpctl
 
 #define DEFAULT_IDLE_TIMEOUT 60
-#define MAX_ADD_ACTS 5
+
+/* Maximum size of action buffer for adding and modify flows */
+#define MAX_ACT_LEN 60
 
 #define MOD_PORT_CMD_UP      "up"
 #define MOD_PORT_CMD_DOWN    "down"
@@ -564,19 +566,22 @@ str_to_ip(const char *str_, uint32_t *ip)
 }
 
 static void
-str_to_action(char *str, struct ofp_action *action, int *n_actions) 
+str_to_action(char *str, struct ofp_action_header *actions, 
+        size_t *actions_len) 
 {
-    uint16_t port;
-    int i;
-    int max_actions = *n_actions;
+    size_t len = *actions_len;
     char *act, *arg;
     char *saveptr = NULL;
+    uint8_t *p = (uint8_t *)actions;
     
-    memset(action, 0, sizeof(*action) * max_actions);
-    for (i=0, act = strtok_r(str, ", \t\r\n", &saveptr); 
-         i<max_actions && act;
-         i++, act = strtok_r(NULL, ", \t\r\n", &saveptr)) 
+    memset(actions, 0, len);
+    for (act = strtok_r(str, ", \t\r\n", &saveptr); 
+         (len >= sizeof(struct ofp_action_header)) && act;
+         act = strtok_r(NULL, ", \t\r\n", &saveptr)) 
     {
+        uint16_t port;
+        struct ofp_action_header *ah = (struct ofp_action_header *)p;
+        int act_len = sizeof *ah;
         port = OFPP_MAX;
 
         /* Arguments are separated by colons */
@@ -587,13 +592,27 @@ str_to_action(char *str, struct ofp_action *action, int *n_actions)
         } 
 
         if (!strcasecmp(act, "mod_vlan_vid")) {
-            action[i].type = htons(OFPAT_SET_VLAN_VID);
-            action[i].arg.vlan_vid = htons(str_to_int(arg));
+            struct ofp_action_vlan_vid *va = (struct ofp_action_vlan_vid *)ah;
+
+            if (len < sizeof *va) {
+                ofp_fatal(0, "Insufficient room for vlan vid action\n");
+            }
+
+            act_len = sizeof *va;
+            va->type = htons(OFPAT_SET_VLAN_VID);
+            va->vlan_vid = htons(str_to_int(arg));
         } else if (!strcasecmp(act, "mod_vlan_pcp")) {
-            action[i].type = htons(OFPAT_SET_VLAN_PCP);
-            action[i].arg.vlan_pcp = str_to_int(arg);
+            struct ofp_action_vlan_pcp *va = (struct ofp_action_vlan_pcp *)ah;
+
+            if (len < sizeof *va) {
+                ofp_fatal(0, "Insufficient room for vlan pcp action\n");
+            }
+
+            act_len = sizeof *va;
+            va->type = htons(OFPAT_SET_VLAN_PCP);
+            va->vlan_pcp = str_to_int(arg);
         } else if (!strcasecmp(act, "strip_vlan")) {
-            action[i].type = htons(OFPAT_STRIP_VLAN);
+            ah->type = htons(OFPAT_STRIP_VLAN);
         } else if (!strcasecmp(act, "output")) {
             port = str_to_int(arg);
         } else if (!strcasecmp(act, "TABLE")) {
@@ -605,13 +624,20 @@ str_to_action(char *str, struct ofp_action *action, int *n_actions)
         } else if (!strcasecmp(act, "ALL")) {
             port = OFPP_ALL;
         } else if (!strcasecmp(act, "CONTROLLER")) {
-            port = OFPP_CONTROLLER;
-            if (arg) {
-                if (!strcasecmp(arg, "all")) {
-                    action[i].arg.output.max_len= htons(0);
-                } else {
-                    action[i].arg.output.max_len= htons(str_to_int(arg));
-                }
+            struct ofp_action_output *ca = (struct ofp_action_output *)ah;
+
+            if (act_len < sizeof *ca) {
+                ofp_fatal(0, "Insufficient room for controller action\n");
+            }
+
+            act_len = sizeof *ca;
+            ca->type = htons(OFPAT_OUTPUT);
+            ca->port = htons(OFPP_CONTROLLER);
+
+            /* Unless a numeric argument is specified, we send the whole
+             * packet to the controller. */
+            if (arg && (strspn(act, "0123456789") == strlen(act))) {
+               ca->max_len= htons(str_to_int(arg));
             }
         } else if (!strcasecmp(act, "LOCAL")) {
             port = OFPP_LOCAL;
@@ -622,12 +648,23 @@ str_to_action(char *str, struct ofp_action *action, int *n_actions)
         }
 
         if (port != OFPP_MAX) {
-            action[i].type = htons(OFPAT_OUTPUT);
-            action[i].arg.output.port = htons(port);
+            struct ofp_action_output *oa = (struct ofp_action_output *)p;
+
+            if (act_len < sizeof *oa) {
+                ofp_fatal(0, "Insufficient room for output action\n");
+            }
+
+            act_len = sizeof *oa;
+            oa->type = htons(OFPAT_OUTPUT);
+            oa->port = htons(port);
         }
+
+        ah->len = htons(act_len);
+        p += act_len;
+        len -= act_len;
     }
 
-    *n_actions = i;
+    *actions_len -= len;
 }
 
 struct protocol {
@@ -697,8 +734,9 @@ parse_field(const char *name, const struct field **f_out)
 
 static void
 str_to_flow(char *string, struct ofp_match *match, 
-            struct ofp_action *action, int *n_actions, uint8_t *table_idx, 
-            uint16_t *priority, uint16_t *idle_timeout, uint16_t *hard_timeout)
+            struct ofp_action_header *actions, size_t *actions_len, 
+            uint8_t *table_idx, uint16_t *priority, 
+            uint16_t *idle_timeout, uint16_t *hard_timeout)
 {
 
     char *name;
@@ -716,7 +754,7 @@ str_to_flow(char *string, struct ofp_match *match,
     if (hard_timeout) {
         *hard_timeout = OFP_FLOW_PERMANENT;
     }
-    if (action) {
+    if (actions) {
         char *act_str = strstr(string, "action");
         if (!act_str) {
             ofp_fatal(0, "must specify an action");
@@ -730,7 +768,7 @@ str_to_flow(char *string, struct ofp_match *match,
 
         act_str++;
 
-        str_to_action(act_str, action, n_actions);
+        str_to_action(act_str, actions, actions_len);
     }
     memset(match, 0, sizeof *match);
     wildcards = OFPFW_ALL;
@@ -822,12 +860,12 @@ static void do_add_flow(const struct settings *s, int argc, char *argv[])
     struct ofp_flow_mod *ofm;
     uint16_t priority, idle_timeout, hard_timeout;
     size_t size;
-    int n_actions = MAX_ADD_ACTS;
+    size_t actions_len = MAX_ACT_LEN;
 
     /* Parse and send. */
-    size = sizeof *ofm + (sizeof ofm->actions[0] * MAX_ADD_ACTS);
+    size = sizeof *ofm + actions_len;
     ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
-    str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &n_actions
+    str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &actions_len
                 NULL, &priority, &idle_timeout, &hard_timeout);
     ofm->command = htons(OFPFC_ADD);
     ofm->idle_timeout = htons(idle_timeout);
@@ -837,7 +875,7 @@ static void do_add_flow(const struct settings *s, int argc, char *argv[])
     ofm->reserved = htonl(0);
 
     /* xxx Should we use the ofpbuf library? */
-    buffer->size -= (MAX_ADD_ACTS - n_actions) * sizeof ofm->actions[0];
+    buffer->size -= MAX_ACT_LEN - actions_len;
 
     open_vconn(argv[1], &vconn);
     send_openflow_buffer(vconn, buffer);
@@ -861,7 +899,7 @@ static void do_add_flows(const struct settings *s, int argc, char *argv[])
         struct ofp_flow_mod *ofm;
         uint16_t priority, idle_timeout, hard_timeout;
         size_t size;
-        int n_actions = MAX_ADD_ACTS;
+        size_t actions_len = MAX_ACT_LEN;
 
         char *comment;
 
@@ -877,9 +915,9 @@ static void do_add_flows(const struct settings *s, int argc, char *argv[])
         }
 
         /* Parse and send. */
-        size = sizeof *ofm + (sizeof ofm->actions[0] * MAX_ADD_ACTS);
+        size = sizeof *ofm + actions_len;
         ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
-        str_to_flow(line, &ofm->match, &ofm->actions[0], &n_actions
+        str_to_flow(line, &ofm->match, &ofm->actions[0], &actions_len
                     NULL, &priority, &idle_timeout, &hard_timeout);
         ofm->command = htons(OFPFC_ADD);
         ofm->idle_timeout = htons(idle_timeout);
@@ -889,7 +927,7 @@ static void do_add_flows(const struct settings *s, int argc, char *argv[])
         ofm->reserved = htonl(0);
 
         /* xxx Should we use the ofpbuf library? */
-        buffer->size -= (MAX_ADD_ACTS - n_actions) * sizeof ofm->actions[0];
+        buffer->size -= MAX_ACT_LEN - actions_len;
 
         send_openflow_buffer(vconn, buffer);
     }
@@ -904,12 +942,12 @@ static void do_mod_flows(const struct settings *s, int argc, char *argv[])
     struct ofpbuf *buffer;
     struct ofp_flow_mod *ofm;
     size_t size;
-    int n_actions = MAX_ADD_ACTS;
+    size_t actions_len = MAX_ACT_LEN;
 
     /* Parse and send. */
-    size = sizeof *ofm + (sizeof ofm->actions[0] * MAX_ADD_ACTS);
+    size = sizeof *ofm + actions_len;
     ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
-    str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &n_actions
+    str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &actions_len
                 NULL, &priority, &idle_timeout, &hard_timeout);
     if (s->strict) {
         ofm->command = htons(OFPFC_MODIFY_STRICT);
@@ -923,7 +961,7 @@ static void do_mod_flows(const struct settings *s, int argc, char *argv[])
     ofm->reserved = htonl(0);
 
     /* xxx Should we use the buffer library? */
-    buffer->size -= (MAX_ADD_ACTS - n_actions) * sizeof ofm->actions[0];
+    buffer->size -= MAX_ACT_LEN - actions_len;
 
     open_vconn(argv[1], &vconn);
     send_openflow_buffer(vconn, buffer);