/* 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 <linux/openvswitch.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/in6.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"
return 0;
}
-static int output_userspace(struct datapath *dp, struct sk_buff *skb, u64 arg)
+static int output_userspace(struct datapath *dp, struct sk_buff *skb,
+ const struct nlattr *attr)
{
struct dp_upcall_info upcall;
+ const struct nlattr *a;
+ int rem;
upcall.cmd = OVS_PACKET_CMD_ACTION;
upcall.key = &OVS_CB(skb)->flow->key;
- upcall.userdata = arg;
+ upcall.userdata = NULL;
+ upcall.pid = 0;
+
+ for (a = nla_data(attr), rem = nla_len(attr); rem > 0;
+ a = nla_next(a, &rem)) {
+ switch (nla_type(a)) {
+ case OVS_USERSPACE_ATTR_USERDATA:
+ upcall.userdata = a;
+ break;
+
+ case OVS_USERSPACE_ATTR_PID:
+ upcall.pid = nla_get_u32(a);
+ break;
+ }
+ }
+
return dp_upcall(dp, skb, &upcall);
}
break;
case OVS_ACTION_ATTR_USERSPACE:
- output_userspace(dp, skb, nla_get_u64(a));
+ output_userspace(dp, skb, a);
break;
case OVS_ACTION_ATTR_SET_TUNNEL:
return 0;
}
+/* 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
+
+struct loop_counter {
+ u8 count; /* Count. */
+ bool looping; /* Loop detected? */
+};
+
+static DEFINE_PER_CPU(struct loop_counter, loop_counters);
+
+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 execute_actions(struct datapath *dp, struct sk_buff *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)) {
/* Decrement loop counter. */
if (!--loop->count)
loop->looping = false;
- loop_put_counter();
return error;
}