Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 85de8576 authored by Daniel Borkmann's avatar Daniel Borkmann Committed by David S. Miller
Browse files

bpf, xdp: allow to pass flags to dev_change_xdp_fd



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: default avatarDaniel Borkmann <daniel@iogearbox.net>
Acked-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0f29f05b
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -3253,7 +3253,7 @@ int dev_get_phys_port_id(struct net_device *dev,
int dev_get_phys_port_name(struct net_device *dev,
int dev_get_phys_port_name(struct net_device *dev,
			   char *name, size_t len);
			   char *name, size_t len);
int dev_change_proto_down(struct net_device *dev, bool proto_down);
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 *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 sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
				    struct netdev_queue *txq, int *ret);
				    struct netdev_queue *txq, int *ret);
+4 −0
Original line number Original line Diff line number Diff line
@@ -876,10 +876,14 @@ enum {


/* XDP section */
/* XDP section */


#define XDP_FLAGS_UPDATE_IF_NOEXIST	(1U << 0)
#define XDP_FLAGS_MASK			(XDP_FLAGS_UPDATE_IF_NOEXIST)

enum {
enum {
	IFLA_XDP_UNSPEC,
	IFLA_XDP_UNSPEC,
	IFLA_XDP_FD,
	IFLA_XDP_FD,
	IFLA_XDP_ATTACHED,
	IFLA_XDP_ATTACHED,
	IFLA_XDP_FLAGS,
	__IFLA_XDP_MAX,
	__IFLA_XDP_MAX,
};
};


+18 −2
Original line number Original line Diff line number Diff line
@@ -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_change_xdp_fd - set or clear a bpf program for a device rx path
 *	@dev: device
 *	@dev: device
 *	@fd: new program fd or negative value to clear
 *	@fd: new program fd or negative value to clear
 *	@flags: xdp-related flags
 *
 *
 *	Set or clear a bpf program for a device
 *	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;
	const struct net_device_ops *ops = dev->netdev_ops;
	struct bpf_prog *prog = NULL;
	struct bpf_prog *prog = NULL;
	struct netdev_xdp xdp = {};
	struct netdev_xdp xdp;
	int err;
	int err;


	ASSERT_RTNL();

	if (!ops->ndo_xdp)
	if (!ops->ndo_xdp)
		return -EOPNOTSUPP;
		return -EOPNOTSUPP;
	if (fd >= 0) {
	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);
		prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_XDP);
		if (IS_ERR(prog))
		if (IS_ERR(prog))
			return PTR_ERR(prog);
			return PTR_ERR(prog);
	}
	}


	memset(&xdp, 0, sizeof(xdp));
	xdp.command = XDP_SETUP_PROG;
	xdp.command = XDP_SETUP_PROG;
	xdp.prog = prog;
	xdp.prog = prog;

	err = ops->ndo_xdp(dev, &xdp);
	err = ops->ndo_xdp(dev, &xdp);
	if (err < 0 && prog)
	if (err < 0 && prog)
		bpf_prog_put(prog);
		bpf_prog_put(prog);
+13 −1
Original line number Original line Diff line number Diff line
@@ -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] = {
static const struct nla_policy ifla_xdp_policy[IFLA_XDP_MAX + 1] = {
	[IFLA_XDP_FD]		= { .type = NLA_S32 },
	[IFLA_XDP_FD]		= { .type = NLA_S32 },
	[IFLA_XDP_ATTACHED]	= { .type = NLA_U8 },
	[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)
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]) {
	if (tb[IFLA_XDP]) {
		struct nlattr *xdp[IFLA_XDP_MAX + 1];
		struct nlattr *xdp[IFLA_XDP_MAX + 1];
		u32 xdp_flags = 0;


		err = nla_parse_nested(xdp, IFLA_XDP_MAX, tb[IFLA_XDP],
		err = nla_parse_nested(xdp, IFLA_XDP_MAX, tb[IFLA_XDP],
				       ifla_xdp_policy);
				       ifla_xdp_policy);
@@ -2174,9 +2176,19 @@ static int do_setlink(const struct sk_buff *skb,
			err = -EINVAL;
			err = -EINVAL;
			goto errout;
			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]) {
		if (xdp[IFLA_XDP_FD]) {
			err = dev_change_xdp_fd(dev,
			err = dev_change_xdp_fd(dev,
						nla_get_s32(xdp[IFLA_XDP_FD]));
						nla_get_s32(xdp[IFLA_XDP_FD]),
						xdp_flags);
			if (err)
			if (err)
				goto errout;
				goto errout;
			status |= DO_SETLINK_NOTIFY;
			status |= DO_SETLINK_NOTIFY;