Skip to content

Commit 92234c8

Browse files
tohojoAlexei Starovoitov
authored andcommitted
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 <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Reviewed-by: Jakub Kicinski <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent e9ff9d5 commit 92234c8

File tree

4 files changed

+39
-7
lines changed

4 files changed

+39
-7
lines changed

include/linux/netdevice.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3768,7 +3768,7 @@ struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
37683768

37693769
typedef int (*bpf_op_t)(struct net_device *dev, struct netdev_bpf *bpf);
37703770
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);
37723772
u32 __dev_xdp_query(struct net_device *dev, bpf_op_t xdp_op,
37733773
enum bpf_netdev_command cmd);
37743774
int xdp_umem_query(struct net_device *dev, u16 queue_id);

include/uapi/linux/if_link.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -972,11 +972,12 @@ enum {
972972
#define XDP_FLAGS_SKB_MODE (1U << 1)
973973
#define XDP_FLAGS_DRV_MODE (1U << 2)
974974
#define XDP_FLAGS_HW_MODE (1U << 3)
975+
#define XDP_FLAGS_REPLACE (1U << 4)
975976
#define XDP_FLAGS_MODES (XDP_FLAGS_SKB_MODE | \
976977
XDP_FLAGS_DRV_MODE | \
977978
XDP_FLAGS_HW_MODE)
978979
#define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST | \
979-
XDP_FLAGS_MODES)
980+
XDP_FLAGS_MODES | XDP_FLAGS_REPLACE)
980981

981982
/* These are stored into IFLA_XDP_ATTACHED on dump. */
982983
enum {
@@ -996,6 +997,7 @@ enum {
996997
IFLA_XDP_DRV_PROG_ID,
997998
IFLA_XDP_SKB_PROG_ID,
998999
IFLA_XDP_HW_PROG_ID,
1000+
IFLA_XDP_EXPECTED_FD,
9991001
__IFLA_XDP_MAX,
10001002
};
10011003

net/core/dev.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8655,15 +8655,17 @@ static void dev_xdp_uninstall(struct net_device *dev)
86558655
* @dev: device
86568656
* @extack: netlink extended ack
86578657
* @fd: new program fd or negative value to clear
8658+
* @expected_fd: old program fd that userspace expects to replace or clear
86588659
* @flags: xdp-related flags
86598660
*
86608661
* Set or clear a bpf program for a device
86618662
*/
86628663
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)
86648665
{
86658666
const struct net_device_ops *ops = dev->netdev_ops;
86668667
enum bpf_netdev_command query;
8668+
u32 prog_id, expected_id = 0;
86678669
struct bpf_prog *prog = NULL;
86688670
bpf_op_t bpf_op, bpf_chk;
86698671
bool offload;
@@ -8684,15 +8686,29 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
86848686
if (bpf_op == bpf_chk)
86858687
bpf_chk = generic_xdp_install;
86868688

8687-
if (fd >= 0) {
8688-
u32 prog_id;
8689+
prog_id = __dev_xdp_query(dev, bpf_op, query);
8690+
if (flags & XDP_FLAGS_REPLACE) {
8691+
if (expected_fd >= 0) {
8692+
prog = bpf_prog_get_type_dev(expected_fd,
8693+
BPF_PROG_TYPE_XDP,
8694+
bpf_op == ops->ndo_bpf);
8695+
if (IS_ERR(prog))
8696+
return PTR_ERR(prog);
8697+
expected_id = prog->aux->id;
8698+
bpf_prog_put(prog);
8699+
}
86898700

8701+
if (prog_id != expected_id) {
8702+
NL_SET_ERR_MSG(extack, "Active program does not match expected");
8703+
return -EEXIST;
8704+
}
8705+
}
8706+
if (fd >= 0) {
86908707
if (!offload && __dev_xdp_query(dev, bpf_chk, XDP_QUERY_PROG)) {
86918708
NL_SET_ERR_MSG(extack, "native and generic XDP can't be active at the same time");
86928709
return -EEXIST;
86938710
}
86948711

8695-
prog_id = __dev_xdp_query(dev, bpf_op, query);
86968712
if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) && prog_id) {
86978713
NL_SET_ERR_MSG(extack, "XDP program already attached");
86988714
return -EBUSY;
@@ -8715,7 +8731,7 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
87158731
return 0;
87168732
}
87178733
} else {
8718-
if (!__dev_xdp_query(dev, bpf_op, query))
8734+
if (!prog_id)
87198735
return 0;
87208736
}
87218737

net/core/rtnetlink.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,7 +1872,9 @@ static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = {
18721872
};
18731873

18741874
static const struct nla_policy ifla_xdp_policy[IFLA_XDP_MAX + 1] = {
1875+
[IFLA_XDP_UNSPEC] = { .strict_start_type = IFLA_XDP_EXPECTED_FD },
18751876
[IFLA_XDP_FD] = { .type = NLA_S32 },
1877+
[IFLA_XDP_EXPECTED_FD] = { .type = NLA_S32 },
18761878
[IFLA_XDP_ATTACHED] = { .type = NLA_U8 },
18771879
[IFLA_XDP_FLAGS] = { .type = NLA_U32 },
18781880
[IFLA_XDP_PROG_ID] = { .type = NLA_U32 },
@@ -2799,8 +2801,20 @@ static int do_setlink(const struct sk_buff *skb,
27992801
}
28002802

28012803
if (xdp[IFLA_XDP_FD]) {
2804+
int expected_fd = -1;
2805+
2806+
if (xdp_flags & XDP_FLAGS_REPLACE) {
2807+
if (!xdp[IFLA_XDP_EXPECTED_FD]) {
2808+
err = -EINVAL;
2809+
goto errout;
2810+
}
2811+
expected_fd =
2812+
nla_get_s32(xdp[IFLA_XDP_EXPECTED_FD]);
2813+
}
2814+
28022815
err = dev_change_xdp_fd(dev, extack,
28032816
nla_get_s32(xdp[IFLA_XDP_FD]),
2817+
expected_fd,
28042818
xdp_flags);
28052819
if (err)
28062820
goto errout;

0 commit comments

Comments
 (0)