Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xdp: Support specifying expected existing program when attaching XDP

While it is currently possible for userspace to specify that an existing
XDP program should not be replaced when attaching to an interface, there is
no mechanism to safely replace a specific XDP program with another.

This patch adds a new netlink attribute, IFLA_XDP_EXPECTED_FD, which can be
set along with IFLA_XDP_FD. If set, the kernel will check that the program
currently loaded on the interface matches the expected one, and fail the
operation if it does not. This corresponds to a 'cmpxchg' memory operation.
Setting the new attribute with a negative value means that no program is
expected to be attached, which corresponds to setting the UPDATE_IF_NOEXIST
flag.

A new companion flag, XDP_FLAGS_REPLACE, is also added to explicitly
request checking of the EXPECTED_FD attribute. This is needed for userspace
to discover whether the kernel supports the new attribute.

Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Reviewed-by: Jakub Kicinski <kuba@kernel.org>
Link: https://lore.kernel.org/bpf/158515700640.92963.3551295145441017022.stgit@toke.dk

authored by

Toke Høiland-Jørgensen and committed by
Alexei Starovoitov
92234c8f e9ff9d52

+39 -7
+1 -1
include/linux/netdevice.h
··· 3768 3768 3769 3769 typedef int (*bpf_op_t)(struct net_device *dev, struct netdev_bpf *bpf); 3770 3770 int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, 3771 - int fd, u32 flags); 3771 + int fd, int expected_fd, u32 flags); 3772 3772 u32 __dev_xdp_query(struct net_device *dev, bpf_op_t xdp_op, 3773 3773 enum bpf_netdev_command cmd); 3774 3774 int xdp_umem_query(struct net_device *dev, u16 queue_id);
+3 -1
include/uapi/linux/if_link.h
··· 972 972 #define XDP_FLAGS_SKB_MODE (1U << 1) 973 973 #define XDP_FLAGS_DRV_MODE (1U << 2) 974 974 #define XDP_FLAGS_HW_MODE (1U << 3) 975 + #define XDP_FLAGS_REPLACE (1U << 4) 975 976 #define XDP_FLAGS_MODES (XDP_FLAGS_SKB_MODE | \ 976 977 XDP_FLAGS_DRV_MODE | \ 977 978 XDP_FLAGS_HW_MODE) 978 979 #define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST | \ 979 - XDP_FLAGS_MODES) 980 + XDP_FLAGS_MODES | XDP_FLAGS_REPLACE) 980 981 981 982 /* These are stored into IFLA_XDP_ATTACHED on dump. */ 982 983 enum { ··· 997 996 IFLA_XDP_DRV_PROG_ID, 998 997 IFLA_XDP_SKB_PROG_ID, 999 998 IFLA_XDP_HW_PROG_ID, 999 + IFLA_XDP_EXPECTED_FD, 1000 1000 __IFLA_XDP_MAX, 1001 1001 }; 1002 1002
+21 -5
net/core/dev.c
··· 8655 8655 * @dev: device 8656 8656 * @extack: netlink extended ack 8657 8657 * @fd: new program fd or negative value to clear 8658 + * @expected_fd: old program fd that userspace expects to replace or clear 8658 8659 * @flags: xdp-related flags 8659 8660 * 8660 8661 * Set or clear a bpf program for a device 8661 8662 */ 8662 8663 int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, 8663 - int fd, u32 flags) 8664 + int fd, int expected_fd, u32 flags) 8664 8665 { 8665 8666 const struct net_device_ops *ops = dev->netdev_ops; 8666 8667 enum bpf_netdev_command query; 8668 + u32 prog_id, expected_id = 0; 8667 8669 struct bpf_prog *prog = NULL; 8668 8670 bpf_op_t bpf_op, bpf_chk; 8669 8671 bool offload; ··· 8686 8684 if (bpf_op == bpf_chk) 8687 8685 bpf_chk = generic_xdp_install; 8688 8686 8689 - if (fd >= 0) { 8690 - u32 prog_id; 8687 + prog_id = __dev_xdp_query(dev, bpf_op, query); 8688 + if (flags & XDP_FLAGS_REPLACE) { 8689 + if (expected_fd >= 0) { 8690 + prog = bpf_prog_get_type_dev(expected_fd, 8691 + BPF_PROG_TYPE_XDP, 8692 + bpf_op == ops->ndo_bpf); 8693 + if (IS_ERR(prog)) 8694 + return PTR_ERR(prog); 8695 + expected_id = prog->aux->id; 8696 + bpf_prog_put(prog); 8697 + } 8691 8698 8699 + if (prog_id != expected_id) { 8700 + NL_SET_ERR_MSG(extack, "Active program does not match expected"); 8701 + return -EEXIST; 8702 + } 8703 + } 8704 + if (fd >= 0) { 8692 8705 if (!offload && __dev_xdp_query(dev, bpf_chk, XDP_QUERY_PROG)) { 8693 8706 NL_SET_ERR_MSG(extack, "native and generic XDP can't be active at the same time"); 8694 8707 return -EEXIST; 8695 8708 } 8696 8709 8697 - prog_id = __dev_xdp_query(dev, bpf_op, query); 8698 8710 if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) && prog_id) { 8699 8711 NL_SET_ERR_MSG(extack, "XDP program already attached"); 8700 8712 return -EBUSY; ··· 8731 8715 return 0; 8732 8716 } 8733 8717 } else { 8734 - if (!__dev_xdp_query(dev, bpf_op, query)) 8718 + if (!prog_id) 8735 8719 return 0; 8736 8720 } 8737 8721
+14
net/core/rtnetlink.c
··· 1872 1872 }; 1873 1873 1874 1874 static const struct nla_policy ifla_xdp_policy[IFLA_XDP_MAX + 1] = { 1875 + [IFLA_XDP_UNSPEC] = { .strict_start_type = IFLA_XDP_EXPECTED_FD }, 1875 1876 [IFLA_XDP_FD] = { .type = NLA_S32 }, 1877 + [IFLA_XDP_EXPECTED_FD] = { .type = NLA_S32 }, 1876 1878 [IFLA_XDP_ATTACHED] = { .type = NLA_U8 }, 1877 1879 [IFLA_XDP_FLAGS] = { .type = NLA_U32 }, 1878 1880 [IFLA_XDP_PROG_ID] = { .type = NLA_U32 }, ··· 2801 2799 } 2802 2800 2803 2801 if (xdp[IFLA_XDP_FD]) { 2802 + int expected_fd = -1; 2803 + 2804 + if (xdp_flags & XDP_FLAGS_REPLACE) { 2805 + if (!xdp[IFLA_XDP_EXPECTED_FD]) { 2806 + err = -EINVAL; 2807 + goto errout; 2808 + } 2809 + expected_fd = 2810 + nla_get_s32(xdp[IFLA_XDP_EXPECTED_FD]); 2811 + } 2812 + 2804 2813 err = dev_change_xdp_fd(dev, extack, 2805 2814 nla_get_s32(xdp[IFLA_XDP_FD]), 2815 + expected_fd, 2806 2816 xdp_flags); 2807 2817 if (err) 2808 2818 goto errout;