BPF_PROG action allows an action to be implemented in eBPF language and
downloaded by the userspace at runtime.

Signed-off-by: Andy Zhou <az...@nicira.com>
---
 net/openvswitch/actions.c      | 30 ++++++++++++++
 net/openvswitch/datapath.c     |  6 ++-
 net/openvswitch/flow_netlink.c | 92 +++++++++++++++++++++++++++++++++++++++++-
 net/openvswitch/flow_netlink.h |  8 ++++
 4 files changed, 132 insertions(+), 4 deletions(-)

diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index b4cffe6..29e9171 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -38,8 +38,12 @@
 
 #include "datapath.h"
 #include "flow.h"
+#include "flow_netlink.h"
 #include "vport.h"
 
+typedef int (*ovs_bpf_func_t)(const struct ovs_bpf_action_ctxt *,
+                             const struct bpf_insn *);
+
 static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
                              struct sw_flow_key *key,
                              const struct nlattr *attr, int len);
@@ -747,6 +751,28 @@ static int execute_recirc(struct datapath *dp, struct 
sk_buff *skb,
        return 0;
 }
 
+static int execute_bpf(struct sk_buff *skb, struct sw_flow_key *key,
+                      const struct nlattr *a)
+{
+       struct ovs_action_bpf_runtime *rt;
+       struct bpf_prog *prog;
+       struct ovs_bpf_action_ctxt ctxt;
+       ovs_bpf_func_t ovs_bpf_func;
+       int err;
+
+       rt = nla_data(a);
+       prog = rt->prog;
+
+       /* Build the BPF program runtime context. */
+       ctxt.skb = (void *)skb;
+       ctxt.arg0 = rt->arg0;
+       ctxt.arg1 = rt->arg1;
+
+       ovs_bpf_func = (ovs_bpf_func_t)(prog->bpf_func);
+       err = ovs_bpf_func(&ctxt, prog->insnsi);
+       return err;
+}
+
 /* Execute a list of actions against 'skb'. */
 static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
                              struct sw_flow_key *key,
@@ -814,6 +840,10 @@ static int do_execute_actions(struct datapath *dp, struct 
sk_buff *skb,
                        }
                        break;
 
+               case OVS_ACTION_ATTR_BPF_PROG:
+                       err = execute_bpf(skb, key, a);
+                       break;
+
                case OVS_ACTION_ATTR_SET:
                        err = execute_set_action(skb, key, nla_data(a));
                        break;
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index ae5e77c..810a0bf 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -699,6 +699,8 @@ static size_t ovs_flow_cmd_msg_size(const struct 
sw_flow_actions *acts,
                len += nla_total_size(ovs_key_attr_size());
 
        /* OVS_FLOW_ATTR_ACTIONS */
+       /* XXX this logic needs to be fixed to accommodate BPF_PROG action
+        * will expand the run time action size.   */
        if (should_fill_actions(ufid_flags))
                len += nla_total_size(acts->actions_len);
 
@@ -1017,7 +1019,7 @@ err_unlock_ovs:
        ovs_unlock();
        kfree_skb(reply);
 err_kfree_acts:
-       kfree(acts);
+       free_flow_actions(acts);
 err_kfree_flow:
        ovs_flow_free(new_flow, false);
 error:
@@ -1152,7 +1154,7 @@ err_unlock_ovs:
        ovs_unlock();
        kfree_skb(reply);
 err_kfree_acts:
-       kfree(acts);
+       free_flow_actions(acts);
 error:
        return error;
 }
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index 8b9a612..466e85f 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -42,6 +42,7 @@
 #include <linux/icmp.h>
 #include <linux/icmpv6.h>
 #include <linux/rculist.h>
+#include <linux/bpf.h>
 #include <net/geneve.h>
 #include <net/ip.h>
 #include <net/ipv6.h>
@@ -1546,11 +1547,38 @@ static struct sw_flow_actions 
*nla_alloc_flow_actions(int size, bool log)
        return sfa;
 }
 
+void free_flow_actions(struct sw_flow_actions *sf_acts)
+{
+       const struct nlattr *a;
+       int rem;
+       struct ovs_action_bpf_runtime *rt;
+
+       nla_for_each_attr(a, sf_acts->actions, sf_acts->actions_len, rem) {
+               int type = nla_type(a);
+
+               switch (type) {
+               case OVS_ACTION_ATTR_BPF_PROG:
+                       rt = nla_data(a);
+                       bpf_prog_put(rt->prog);
+                       break;
+               }
+       }
+
+       kfree(sf_acts);
+}
+
+static void free_flow_actions_rcu(struct rcu_head *head)
+{
+       struct sw_flow_actions *acts = container_of(head, struct 
sw_flow_actions, rcu);
+
+       free_flow_actions(acts);
+}
+
 /* Schedules 'sf_acts' to be freed after the next RCU grace period.
  * The caller must hold rcu_read_lock for this to be sensible. */
 void ovs_nla_free_flow_actions(struct sw_flow_actions *sf_acts)
 {
-       kfree_rcu(sf_acts, rcu);
+       call_rcu(&sf_acts->rcu, free_flow_actions_rcu);
 }
 
 static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa,
@@ -1695,6 +1723,37 @@ static int validate_and_copy_sample(const struct nlattr 
*attr,
        return 0;
 }
 
+static int validate_and_copy_bpf(const struct nlattr *attr,
+                                struct sw_flow_actions **sfa,
+                                bool log)
+{
+       const struct ovs_action_bpf_prog *act_bpf = nla_data(attr);
+       struct ovs_action_bpf_runtime rt;
+       u32 fd;
+       int err;
+
+       fd = ntohl(act_bpf->prog_fd);
+       rt.prog = bpf_prog_get(fd);
+       if (!rt.prog)
+               return -EINVAL;
+
+       if (rt.prog->aux->prog_type != BPF_PROG_TYPE_OPENVSWITCH) {
+               bpf_prog_put(rt.prog);
+               return -EINVAL;
+       }
+
+       rt.fd = fd;
+       rt.arg0 = ntohl(act_bpf->arg0);
+       rt.arg1 = ntohl(act_bpf->arg1);
+
+       /* Validation done.  Rewrite the action with runtime datastructure. */
+       err = add_action(sfa, OVS_ACTION_ATTR_BPF_PROG, &rt, sizeof(rt), log);
+       if (err)
+               return err;
+
+       return 0;
+}
+
 static int validate_tp_port(const struct sw_flow_key *flow_key,
                            __be16 eth_type)
 {
@@ -1966,7 +2025,9 @@ static int __ovs_nla_copy_actions(const struct nlattr 
*attr,
                        [OVS_ACTION_ATTR_POP_VLAN] = 0,
                        [OVS_ACTION_ATTR_SET] = (u32)-1,
                        [OVS_ACTION_ATTR_SAMPLE] = (u32)-1,
-                       [OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash)
+                       [OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash),
+                       [OVS_ACTION_ATTR_BPF_PROG] =
+                               sizeof(struct ovs_action_bpf_prog),
                };
                const struct ovs_action_push_vlan *vlan;
                int type = nla_type(a);
@@ -2073,6 +2134,13 @@ static int __ovs_nla_copy_actions(const struct nlattr 
*attr,
                        skip_copy = true;
                        break;
 
+               case OVS_ACTION_ATTR_BPF_PROG:
+                       err = validate_and_copy_bpf(a, sfa, log);
+                       if (err)
+                               return err;
+                       skip_copy = true;
+                       break;
+
                default:
                        OVS_NLERR(log, "Unknown Action type %d", type);
                        return -EINVAL;
@@ -2177,6 +2245,21 @@ static int set_action_to_attr(const struct nlattr *a, 
struct sk_buff *skb)
        return 0;
 }
 
+static int bpf_action_to_attr(const struct nlattr *a, struct sk_buff *skb)
+{
+       const struct ovs_action_bpf_runtime *rt = nla_data(a);
+       struct ovs_action_bpf_prog prog;
+
+       prog.prog_fd = htonl(rt->fd);
+       prog.arg0 = htonl(rt->arg0);
+       prog.arg1 = htonl(rt->arg1);
+
+       if (nla_put(skb, OVS_ACTION_ATTR_BPF_PROG, sizeof(prog), &prog));
+               return -EMSGSIZE;
+
+       return 0;
+}
+
 int ovs_nla_put_actions(const struct nlattr *attr, int len, struct sk_buff 
*skb)
 {
        const struct nlattr *a;
@@ -2197,6 +2280,11 @@ int ovs_nla_put_actions(const struct nlattr *attr, int 
len, struct sk_buff *skb)
                        if (err)
                                return err;
                        break;
+
+               case OVS_ACTION_ATTR_BPF_PROG:
+                       err = bpf_action_to_attr(a, skb);
+                       break;
+
                default:
                        if (nla_put(skb, type, nla_len(a), nla_data(a)))
                                return -EMSGSIZE;
diff --git a/net/openvswitch/flow_netlink.h b/net/openvswitch/flow_netlink.h
index 5c3d75b..e986ddb 100644
--- a/net/openvswitch/flow_netlink.h
+++ b/net/openvswitch/flow_netlink.h
@@ -68,6 +68,14 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
 int ovs_nla_put_actions(const struct nlattr *attr,
                        int len, struct sk_buff *skb);
 
+void free_flow_actions(struct sw_flow_actions *);
 void ovs_nla_free_flow_actions(struct sw_flow_actions *);
 
+struct ovs_action_bpf_runtime {
+       uint32_t fd;
+       uint32_t arg0;
+       uint32_t arg1;
+       struct bpf_prog *prog;
+};
+
 #endif /* flow_netlink.h */
-- 
1.9.1

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to