bpf, xdp: allow to pass flags to dev_change_xdp_fd
authorDaniel Borkmann <daniel@iogearbox.net>
Mon, 28 Nov 2016 22:16:54 +0000 (23:16 +0100)
committerDavid S. Miller <davem@davemloft.net>
Wed, 30 Nov 2016 15:27:20 +0000 (10:27 -0500)
Add an IFLA_XDP_FLAGS attribute that can be passed for setting up
XDP along with IFLA_XDP_FD, which eventually allows user space to
implement typical add/replace/delete logic for programs. Right now,
calling into dev_change_xdp_fd() will always replace previous programs.

When passed XDP_FLAGS_UPDATE_IF_NOEXIST, we can handle this more
graceful when requested by returning -EBUSY in case we try to
attach a new program, but we find that another one is already
attached. This will be used by upcoming front-end for iproute2 as
well.

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netdevice.h
include/uapi/linux/if_link.h
net/core/dev.c
net/core/rtnetlink.c

index 4ffcd874cc20daa822e9c43fa823e49acee35f80..3755317cc6a981c018468ecf7a618f81ec97a763 100644 (file)
@@ -3253,7 +3253,7 @@ int dev_get_phys_port_id(struct net_device *dev,
 int dev_get_phys_port_name(struct net_device *dev,
                           char *name, size_t len);
 int dev_change_proto_down(struct net_device *dev, bool proto_down);
-int dev_change_xdp_fd(struct net_device *dev, int fd);
+int dev_change_xdp_fd(struct net_device *dev, int fd, u32 flags);
 struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev);
 struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
                                    struct netdev_queue *txq, int *ret);
index 92b2d4928bf158636a8566287ff028add5d7f3b0..6b13e591abc9e646ce11b15d7fe5ee8209a509c1 100644 (file)
@@ -876,10 +876,14 @@ enum {
 
 /* XDP section */
 
+#define XDP_FLAGS_UPDATE_IF_NOEXIST    (1U << 0)
+#define XDP_FLAGS_MASK                 (XDP_FLAGS_UPDATE_IF_NOEXIST)
+
 enum {
        IFLA_XDP_UNSPEC,
        IFLA_XDP_FD,
        IFLA_XDP_ATTACHED,
+       IFLA_XDP_FLAGS,
        __IFLA_XDP_MAX,
 };
 
index 048b46b7c92ae10080226ea7050fad3529920baa..bffb5253e77867b1d6a0ada7cc99f4605e03ad28 100644 (file)
@@ -6692,26 +6692,42 @@ EXPORT_SYMBOL(dev_change_proto_down);
  *     dev_change_xdp_fd - set or clear a bpf program for a device rx path
  *     @dev: device
  *     @fd: new program fd or negative value to clear
+ *     @flags: xdp-related flags
  *
  *     Set or clear a bpf program for a device
  */
-int dev_change_xdp_fd(struct net_device *dev, int fd)
+int dev_change_xdp_fd(struct net_device *dev, int fd, u32 flags)
 {
        const struct net_device_ops *ops = dev->netdev_ops;
        struct bpf_prog *prog = NULL;
-       struct netdev_xdp xdp = {};
+       struct netdev_xdp xdp;
        int err;
 
+       ASSERT_RTNL();
+
        if (!ops->ndo_xdp)
                return -EOPNOTSUPP;
        if (fd >= 0) {
+               if (flags & XDP_FLAGS_UPDATE_IF_NOEXIST) {
+                       memset(&xdp, 0, sizeof(xdp));
+                       xdp.command = XDP_QUERY_PROG;
+
+                       err = ops->ndo_xdp(dev, &xdp);
+                       if (err < 0)
+                               return err;
+                       if (xdp.prog_attached)
+                               return -EBUSY;
+               }
+
                prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_XDP);
                if (IS_ERR(prog))
                        return PTR_ERR(prog);
        }
 
+       memset(&xdp, 0, sizeof(xdp));
        xdp.command = XDP_SETUP_PROG;
        xdp.prog = prog;
+
        err = ops->ndo_xdp(dev, &xdp);
        if (err < 0 && prog)
                bpf_prog_put(prog);
index 4e60525ea5861f2ce199890c8135c893b647abbd..bd85570e6e4bb7cd64682ed7631b58a135032768 100644 (file)
@@ -1505,6 +1505,7 @@ static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = {
 static const struct nla_policy ifla_xdp_policy[IFLA_XDP_MAX + 1] = {
        [IFLA_XDP_FD]           = { .type = NLA_S32 },
        [IFLA_XDP_ATTACHED]     = { .type = NLA_U8 },
+       [IFLA_XDP_FLAGS]        = { .type = NLA_U32 },
 };
 
 static const struct rtnl_link_ops *linkinfo_to_kind_ops(const struct nlattr *nla)
@@ -2164,6 +2165,7 @@ static int do_setlink(const struct sk_buff *skb,
 
        if (tb[IFLA_XDP]) {
                struct nlattr *xdp[IFLA_XDP_MAX + 1];
+               u32 xdp_flags = 0;
 
                err = nla_parse_nested(xdp, IFLA_XDP_MAX, tb[IFLA_XDP],
                                       ifla_xdp_policy);
@@ -2174,9 +2176,19 @@ static int do_setlink(const struct sk_buff *skb,
                        err = -EINVAL;
                        goto errout;
                }
+
+               if (xdp[IFLA_XDP_FLAGS]) {
+                       xdp_flags = nla_get_u32(xdp[IFLA_XDP_FLAGS]);
+                       if (xdp_flags & ~XDP_FLAGS_MASK) {
+                               err = -EINVAL;
+                               goto errout;
+                       }
+               }
+
                if (xdp[IFLA_XDP_FD]) {
                        err = dev_change_xdp_fd(dev,
-                                               nla_get_s32(xdp[IFLA_XDP_FD]));
+                                               nla_get_s32(xdp[IFLA_XDP_FD]),
+                                               xdp_flags);
                        if (err)
                                goto errout;
                        status |= DO_SETLINK_NOTIFY;