/* Functions for executing flow actions. */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/skbuff.h>
#include <linux/in.h>
#include <linux/ip.h>
#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 *,
- struct sw_flow_actions *acts);
+static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
+ const struct nlattr *attr, int len, bool keep_skb);
static int make_writable(struct sk_buff *skb, int write_len)
{
return 0;
}
-static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port)
+static int do_output(struct datapath *dp, struct sk_buff *skb, int out_port)
{
- struct vport *p;
+ struct vport *vport;
- if (!skb)
- goto error;
-
- p = rcu_dereference(dp->ports[out_port]);
- if (!p)
- goto error;
+ if (unlikely(!skb))
+ return -ENOMEM;
- vport_send(p, skb);
- return;
+ vport = rcu_dereference(dp->ports[out_port]);
+ if (unlikely(!vport)) {
+ kfree_skb(skb);
+ return -ENODEV;
+ }
-error:
- kfree_skb(skb);
+ vport_send(vport, skb);
+ return 0;
}
static int output_userspace(struct datapath *dp, struct sk_buff *skb, u64 arg)
{
struct dp_upcall_info upcall;
- skb = skb_clone(skb, GFP_ATOMIC);
- if (!skb)
- return -ENOMEM;
-
upcall.cmd = OVS_PACKET_CMD_ACTION;
upcall.key = &OVS_CB(skb)->flow->key;
upcall.userdata = arg;
- upcall.sample_pool = 0;
- upcall.actions = NULL;
- upcall.actions_len = 0;
return dp_upcall(dp, skb, &upcall);
}
+static int sample(struct datapath *dp, struct sk_buff *skb,
+ const struct nlattr *attr)
+{
+ const struct nlattr *acts_list = NULL;
+ const struct nlattr *a;
+ int rem;
+
+ for (a = nla_data(attr), rem = nla_len(attr); rem > 0;
+ a = nla_next(a, &rem)) {
+ switch (nla_type(a)) {
+ case OVS_SAMPLE_ATTR_PROBABILITY:
+ if (net_random() >= nla_get_u32(a))
+ return 0;
+ break;
+
+ case OVS_SAMPLE_ATTR_ACTIONS:
+ acts_list = a;
+ break;
+ }
+ }
+
+ return do_execute_actions(dp, skb, nla_data(acts_list),
+ nla_len(acts_list), true);
+}
+
/* Execute a list of actions against 'skb'. */
static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
- struct sw_flow_actions *acts)
+ const struct nlattr *attr, int len, bool keep_skb)
{
/* 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;
- for (a = acts->actions, rem = acts->actions_len; rem > 0;
+ for (a = attr, rem = len; rem > 0;
a = nla_next(a, &rem)) {
int err = 0;
break;
case OVS_ACTION_ATTR_USERSPACE:
- err = output_userspace(dp, skb, nla_get_u64(a));
+ output_userspace(dp, skb, nla_get_u64(a));
break;
case OVS_ACTION_ATTR_SET_TUNNEL:
case OVS_ACTION_ATTR_POP_PRIORITY:
skb->priority = priority;
break;
- }
+ case OVS_ACTION_ATTR_SAMPLE:
+ err = sample(dp, skb, a);
+ break;
+
+ }
if (unlikely(err)) {
kfree_skb(skb);
return err;
}
}
- if (prev_port != -1)
+ if (prev_port != -1) {
+ if (keep_skb)
+ skb = skb_clone(skb, GFP_ATOMIC);
+
do_output(dp, skb, prev_port);
- else
+ } else if (!keep_skb)
consume_skb(skb);
return 0;
}
-static void sflow_sample(struct datapath *dp, struct sk_buff *skb,
- struct sw_flow_actions *acts)
-{
- struct sk_buff *nskb;
- struct vport *p = OVS_CB(skb)->vport;
- struct dp_upcall_info upcall;
-
- if (unlikely(!p))
- return;
+/* We limit the number of times that we pass into execute_actions()
+ * to avoid blowing out the stack in the event that we have a loop. */
+#define MAX_LOOPS 5
- atomic_inc(&p->sflow_pool);
- if (net_random() >= dp->sflow_probability)
- return;
+struct loop_counter {
+ u8 count; /* Count. */
+ bool looping; /* Loop detected? */
+};
- nskb = skb_clone(skb, GFP_ATOMIC);
- if (unlikely(!nskb))
- return;
+static DEFINE_PER_CPU(struct loop_counter, loop_counters);
- upcall.cmd = OVS_PACKET_CMD_SAMPLE;
- upcall.key = &OVS_CB(skb)->flow->key;
- upcall.userdata = 0;
- upcall.sample_pool = atomic_read(&p->sflow_pool);
- upcall.actions = acts->actions;
- upcall.actions_len = acts->actions_len;
- dp_upcall(dp, nskb, &upcall);
+static int loop_suppress(struct datapath *dp, struct sw_flow_actions *actions)
+{
+ if (net_ratelimit())
+ pr_warn("%s: flow looped %d times, dropping\n",
+ dp_name(dp), MAX_LOOPS);
+ actions->actions_len = 0;
+ return -ELOOP;
}
/* Execute a list of actions against 'skb'. */
int error;
/* Check whether we've looped too much. */
- loop = loop_get_counter();
+ loop = &__get_cpu_var(loop_counters);
if (unlikely(++loop->count > MAX_LOOPS))
loop->looping = true;
if (unlikely(loop->looping)) {
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);
+ error = do_execute_actions(dp, skb, acts->actions,
+ acts->actions_len, false);
/* Check whether sub-actions looped too much. */
if (unlikely(loop->looping))
/* Decrement loop counter. */
if (!--loop->count)
loop->looping = false;
- loop_put_counter();
return error;
}