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
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;
struct sw_flow;
struct sw_flow_key;
-struct ofp_action;
+struct ofp_action_header;
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 *);
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;
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;
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#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 */
}
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;
kmem_cache_free(flow_cache, flow);
return NULL;
}
- sfa->n_actions = n_actions;
+ sfa->actions_len = actions_len;
flow->sf_acts = sfa;
return flow;
/* 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);
* 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:
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);
#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);
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;
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)
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);
/* 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);
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
}
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);
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);
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;
}
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));
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;
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);
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;
-}
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
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 */
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;
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++;
}
/compat24.c
/crc32.c
/crc_t.c
+/dp_act.c
/dp_dev.c
/dp_notify.c
/flow.c
/kernel.c
/kthread.c
/netlink.c
+/nx_act.c
/random32.c
/rcupdate.c
/sched.c
/crc32.c
/crc_t.c
/datapath.c
+/dp_act.c
/dp_dev.c
/dp_notify.c
/flow.c
/forward_t.c
/genetlink.c
/hwtable_dummy.c
+/nx_act.c
/random32.c
/table-hash.c
/table-linear.c
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+#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 */
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;
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 {
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++;
}
}
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,
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;
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++;
}
}
struct sw_flow;
struct sw_flow_key;
-struct ofp_action;
+struct ofp_action_header;
struct datapath;
/* Table statistics. */
* 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.
/* 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
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.) */
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);
* 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
* 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;
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);
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);
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;
}
}
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);
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)
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);
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;
}
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;
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);
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;
}
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 \
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;
#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
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 *);
#include "timeval.h"
#include "vconn.h"
#include "xtoxll.h"
+#include "dp_act.h"
#define THIS_MODULE VLM_datapath
#include "vlog.h"
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
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;
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
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;
}
}
-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)
}
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) {
}
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
}
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);
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;
}
}
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));
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;
}
}
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);
#ifndef DATAPATH_H
#define DATAPATH_H 1
+#include <stdbool.h>
#include <stdint.h>
+#include "ofpbuf.h"
struct datapath;
struct rconn;
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 */
--- /dev/null
+/* 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);
+ }
+}
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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 */
+}
+
--- /dev/null
+/* 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 */
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;
free(flow);
return NULL;
}
- sfa->n_actions = n_actions;
+ sfa->actions_len = actions_len;
flow->sf_acts = sfa;
return 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;
};
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 {
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);
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;
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 {
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++;
}
}
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,
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;
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++;
}
}
#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. */
* 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
#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"
}
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 */
}
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")) {
} 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;
}
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 {
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;
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");
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;
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);
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);
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;
}
/* 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);
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);
}
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);
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);