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