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