#include "actions.h"
#include "checksum.h"
#include "datapath.h"
+#include "loop_counter.h"
#include "openvswitch/datapath-protocol.h"
#include "vlan.h"
#include "vport.h"
static int do_execute_actions(struct datapath *, struct sk_buff *,
- const struct sw_flow_key *,
- const struct nlattr *actions, u32 actions_len);
+ struct sw_flow_actions *acts);
static struct sk_buff *make_writable(struct sk_buff *skb, unsigned min_headroom)
{
return skb;
}
-static bool is_ip(struct sk_buff *skb, const struct sw_flow_key *key)
+static bool is_ip(struct sk_buff *skb)
{
- return (key->dl_type == htons(ETH_P_IP) &&
+ return (OVS_CB(skb)->flow->key.dl_type == htons(ETH_P_IP) &&
skb->transport_header > skb->network_header);
}
-static __sum16 *get_l4_checksum(struct sk_buff *skb, const struct sw_flow_key *key)
+static __sum16 *get_l4_checksum(struct sk_buff *skb)
{
+ u8 nw_proto = OVS_CB(skb)->flow->key.nw_proto;
int transport_len = skb->len - skb_transport_offset(skb);
- if (key->nw_proto == IPPROTO_TCP) {
+ if (nw_proto == IPPROTO_TCP) {
if (likely(transport_len >= sizeof(struct tcphdr)))
return &tcp_hdr(skb)->check;
- } else if (key->nw_proto == IPPROTO_UDP) {
+ } else if (nw_proto == IPPROTO_UDP) {
if (likely(transport_len >= sizeof(struct udphdr)))
return &udp_hdr(skb)->check;
}
return NULL;
}
-static struct sk_buff *set_nw_addr(struct sk_buff *skb,
- const struct sw_flow_key *key,
- const struct nlattr *a)
+static struct sk_buff *set_nw_addr(struct sk_buff *skb, const struct nlattr *a)
{
__be32 new_nwaddr = nla_get_be32(a);
struct iphdr *nh;
__sum16 *check;
__be32 *nwaddr;
- if (unlikely(!is_ip(skb, key)))
+ if (unlikely(!is_ip(skb)))
return skb;
skb = make_writable(skb, 0);
nh = ip_hdr(skb);
nwaddr = nla_type(a) == ODP_ACTION_ATTR_SET_NW_SRC ? &nh->saddr : &nh->daddr;
- check = get_l4_checksum(skb, key);
+ check = get_l4_checksum(skb);
if (likely(check))
inet_proto_csum_replace4(check, skb, *nwaddr, new_nwaddr, 1);
csum_replace4(&nh->check, *nwaddr, new_nwaddr);
return skb;
}
-static struct sk_buff *set_nw_tos(struct sk_buff *skb,
- const struct sw_flow_key *key,
- u8 nw_tos)
+static struct sk_buff *set_nw_tos(struct sk_buff *skb, u8 nw_tos)
{
- if (unlikely(!is_ip(skb, key)))
+ if (unlikely(!is_ip(skb)))
return skb;
skb = make_writable(skb, 0);
return skb;
}
-static struct sk_buff *set_tp_port(struct sk_buff *skb,
- const struct sw_flow_key *key,
- const struct nlattr *a)
+static struct sk_buff *set_tp_port(struct sk_buff *skb, const struct nlattr *a)
{
struct udphdr *th;
__sum16 *check;
__be16 *port;
- if (unlikely(!is_ip(skb, key)))
+ if (unlikely(!is_ip(skb)))
return skb;
skb = make_writable(skb, 0);
return NULL;
/* Must follow make_writable() since that can move the skb data. */
- check = get_l4_checksum(skb, key);
+ check = get_l4_checksum(skb);
if (unlikely(!check))
return skb;
*
* @skb: skbuff containing an Ethernet packet, with network header pointing
* just past the Ethernet and optional 802.1Q header.
- * @key: flow key extracted from @skb by flow_extract()
*
* Returns true if @skb is an invalid Ethernet+IPv4 ARP packet: one with screwy
* or truncated header fields or one whose inner and outer Ethernet address
* differ.
*/
-static bool is_spoofed_arp(struct sk_buff *skb, const struct sw_flow_key *key)
+static bool is_spoofed_arp(struct sk_buff *skb)
{
struct arp_eth_header *arp;
- if (key->dl_type != htons(ETH_P_ARP))
+ if (OVS_CB(skb)->flow->key.dl_type != htons(ETH_P_ARP))
return false;
if (skb_network_offset(skb) + sizeof(struct arp_eth_header) > skb->len)
kfree_skb(skb);
}
-static int output_control(struct datapath *dp, struct sk_buff *skb, u64 arg,
- const struct sw_flow_key *key)
+static int output_control(struct datapath *dp, struct sk_buff *skb, u64 arg)
{
struct dp_upcall_info upcall;
return -ENOMEM;
upcall.cmd = ODP_PACKET_CMD_ACTION;
- upcall.key = key;
+ upcall.key = &OVS_CB(skb)->flow->key;
upcall.userdata = arg;
upcall.sample_pool = 0;
upcall.actions = NULL;
/* Execute a list of actions against 'skb'. */
static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
- const struct sw_flow_key *key,
- const struct nlattr *actions, u32 actions_len)
+ struct sw_flow_actions *acts)
{
/* 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
const struct nlattr *a;
int rem, err;
- for (a = actions, rem = actions_len; rem > 0; a = nla_next(a, &rem)) {
+ for (a = acts->actions, rem = acts->actions_len; rem > 0;
+ a = nla_next(a, &rem)) {
if (prev_port != -1) {
do_output(dp, skb_clone(skb, GFP_ATOMIC), prev_port);
prev_port = -1;
break;
case ODP_ACTION_ATTR_CONTROLLER:
- err = output_control(dp, skb, nla_get_u64(a), key);
+ err = output_control(dp, skb, nla_get_u64(a));
if (err) {
kfree_skb(skb);
return err;
case ODP_ACTION_ATTR_SET_NW_SRC:
case ODP_ACTION_ATTR_SET_NW_DST:
- skb = set_nw_addr(skb, key, a);
+ skb = set_nw_addr(skb, a);
break;
case ODP_ACTION_ATTR_SET_NW_TOS:
- skb = set_nw_tos(skb, key, nla_get_u8(a));
+ skb = set_nw_tos(skb, nla_get_u8(a));
break;
case ODP_ACTION_ATTR_SET_TP_SRC:
case ODP_ACTION_ATTR_SET_TP_DST:
- skb = set_tp_port(skb, key, a);
+ skb = set_tp_port(skb, a);
break;
case ODP_ACTION_ATTR_SET_PRIORITY:
break;
case ODP_ACTION_ATTR_DROP_SPOOFED_ARP:
- if (unlikely(is_spoofed_arp(skb, key)))
+ if (unlikely(is_spoofed_arp(skb)))
goto exit;
break;
}
}
static void sflow_sample(struct datapath *dp, struct sk_buff *skb,
- const struct sw_flow_key *key,
- const struct nlattr *a, u32 actions_len)
+ struct sw_flow_actions *acts)
{
struct sk_buff *nskb;
struct vport *p = OVS_CB(skb)->vport;
return;
upcall.cmd = ODP_PACKET_CMD_SAMPLE;
- upcall.key = key;
+ upcall.key = &OVS_CB(skb)->flow->key;
upcall.userdata = 0;
upcall.sample_pool = atomic_read(&p->sflow_pool);
- upcall.actions = a;
- upcall.actions_len = actions_len;
+ upcall.actions = acts->actions;
+ upcall.actions_len = acts->actions_len;
dp_upcall(dp, nskb, &upcall);
}
/* Execute a list of actions against 'skb'. */
-int execute_actions(struct datapath *dp, struct sk_buff *skb,
- const struct sw_flow_key *key,
- const struct nlattr *actions, u32 actions_len)
+int execute_actions(struct datapath *dp, struct sk_buff *skb)
{
- if (dp->sflow_probability)
- sflow_sample(dp, skb, key, actions, actions_len);
+ struct sw_flow_actions *acts = rcu_dereference(OVS_CB(skb)->flow->sf_acts);
+ struct loop_counter *loop;
+ int error;
+
+ /* Check whether we've looped too much. */
+ loop = loop_get_counter();
+ if (unlikely(++loop->count > MAX_LOOPS))
+ loop->looping = true;
+ if (unlikely(loop->looping)) {
+ error = loop_suppress(dp, acts);
+ kfree_skb(skb);
+ goto out_loop;
+ }
+ /* Really execute actions. */
+ if (dp->sflow_probability)
+ sflow_sample(dp, skb, acts);
OVS_CB(skb)->tun_id = 0;
+ error = do_execute_actions(dp, skb, acts);
+
+ /* Check whether sub-actions looped too much. */
+ if (unlikely(loop->looping))
+ error = loop_suppress(dp, acts);
+
+out_loop:
+ /* Decrement loop counter. */
+ if (!--loop->count)
+ loop->looping = false;
+ loop_put_counter();
- return do_execute_actions(dp, skb, key, actions, actions_len);
+ return error;
}