Skip to content

Commit 08b623e

Browse files
jsitnickitheihor
authored andcommitted
selftests/bpf: Cover metadata access from a modified skb clone
Demonstrate that skb metadata currently gets cleared when a BPF program which might modify the payload processes a cloned packet. Signed-off-by: Jakub Sitnicki <[email protected]>
1 parent 2f5c6b8 commit 08b623e

File tree

3 files changed

+150
-10
lines changed

3 files changed

+150
-10
lines changed

tools/testing/selftests/bpf/config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ CONFIG_MPLS_IPTUNNEL=y
6161
CONFIG_MPLS_ROUTING=y
6262
CONFIG_MPTCP=y
6363
CONFIG_NET_ACT_GACT=y
64+
CONFIG_NET_ACT_MIRRED=y
6465
CONFIG_NET_ACT_SKBMOD=y
6566
CONFIG_NET_CLS=y
6667
CONFIG_NET_CLS_ACT=y

tools/testing/selftests/bpf/prog_tests/xdp_context_test_run.c

Lines changed: 97 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#define TX_NETNS "xdp_context_tx"
1010
#define RX_NETNS "xdp_context_rx"
1111
#define TAP_NAME "tap0"
12+
#define DUMMY_NAME "dum0"
1213
#define TAP_NETNS "xdp_context_tuntap"
1314

1415
#define TEST_PAYLOAD_LEN 32
@@ -156,6 +157,22 @@ static int send_test_packet(int ifindex)
156157
return -1;
157158
}
158159

160+
static int write_test_packet(int tap_fd)
161+
{
162+
__u8 packet[sizeof(struct ethhdr) + TEST_PAYLOAD_LEN];
163+
int n;
164+
165+
/* The ethernet header doesn't need to be valid for this test */
166+
memset(packet, 0, sizeof(struct ethhdr));
167+
memcpy(packet + sizeof(struct ethhdr), test_payload, TEST_PAYLOAD_LEN);
168+
169+
n = write(tap_fd, packet, sizeof(packet));
170+
if (!ASSERT_EQ(n, sizeof(packet), "write packet"))
171+
return -1;
172+
173+
return 0;
174+
}
175+
159176
static void assert_test_result(const struct bpf_map *result_map)
160177
{
161178
int err;
@@ -276,7 +293,6 @@ static void test_tuntap(struct bpf_program *xdp_prog,
276293
LIBBPF_OPTS(bpf_tc_hook, tc_hook, .attach_point = BPF_TC_INGRESS);
277294
LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1);
278295
struct netns_obj *ns = NULL;
279-
__u8 packet[sizeof(struct ethhdr) + TEST_PAYLOAD_LEN];
280296
int tap_fd = -1;
281297
int tap_ifindex;
282298
int ret;
@@ -322,19 +338,82 @@ static void test_tuntap(struct bpf_program *xdp_prog,
322338
if (!ASSERT_GE(ret, 0, "bpf_xdp_attach"))
323339
goto close;
324340

325-
/* The ethernet header is not relevant for this test and doesn't need to
326-
* be meaningful.
327-
*/
328-
struct ethhdr eth = { 0 };
341+
ret = write_test_packet(tap_fd);
342+
if (!ASSERT_OK(ret, "write_test_packet"))
343+
goto close;
329344

330-
memcpy(packet, &eth, sizeof(eth));
331-
memcpy(packet + sizeof(eth), test_payload, TEST_PAYLOAD_LEN);
345+
assert_test_result(result_map);
346+
347+
close:
348+
if (tap_fd >= 0)
349+
close(tap_fd);
350+
netns_free(ns);
351+
}
352+
353+
/* Write a packet to a tap dev and copy it to ingress of a dummy dev */
354+
static void test_tuntap_mirred(struct bpf_program *xdp_prog,
355+
struct bpf_program *tc_prog,
356+
bool *test_pass)
357+
{
358+
LIBBPF_OPTS(bpf_tc_hook, tc_hook, .attach_point = BPF_TC_INGRESS);
359+
LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1);
360+
struct netns_obj *ns = NULL;
361+
int dummy_ifindex;
362+
int tap_fd = -1;
363+
int tap_ifindex;
364+
int ret;
332365

333-
ret = write(tap_fd, packet, sizeof(packet));
334-
if (!ASSERT_EQ(ret, sizeof(packet), "write packet"))
366+
*test_pass = false;
367+
368+
ns = netns_new(TAP_NETNS, true);
369+
if (!ASSERT_OK_PTR(ns, "netns_new"))
370+
return;
371+
372+
/* Setup dummy interface */
373+
SYS(close, "ip link add name " DUMMY_NAME " type dummy");
374+
SYS(close, "ip link set dev " DUMMY_NAME " up");
375+
376+
dummy_ifindex = if_nametoindex(DUMMY_NAME);
377+
if (!ASSERT_GE(dummy_ifindex, 0, "if_nametoindex"))
335378
goto close;
336379

337-
assert_test_result(result_map);
380+
tc_hook.ifindex = dummy_ifindex;
381+
ret = bpf_tc_hook_create(&tc_hook);
382+
if (!ASSERT_OK(ret, "bpf_tc_hook_create"))
383+
goto close;
384+
385+
tc_opts.prog_fd = bpf_program__fd(tc_prog);
386+
ret = bpf_tc_attach(&tc_hook, &tc_opts);
387+
if (!ASSERT_OK(ret, "bpf_tc_attach"))
388+
goto close;
389+
390+
/* Setup TAP interface */
391+
tap_fd = open_tuntap(TAP_NAME, true);
392+
if (!ASSERT_GE(tap_fd, 0, "open_tuntap"))
393+
goto close;
394+
395+
SYS(close, "ip link set dev " TAP_NAME " up");
396+
397+
tap_ifindex = if_nametoindex(TAP_NAME);
398+
if (!ASSERT_GE(tap_ifindex, 0, "if_nametoindex"))
399+
goto close;
400+
401+
ret = bpf_xdp_attach(tap_ifindex, bpf_program__fd(xdp_prog), 0, NULL);
402+
if (!ASSERT_GE(ret, 0, "bpf_xdp_attach"))
403+
goto close;
404+
405+
/* Copy all packets received from TAP to dummy ingress */
406+
SYS(close, "tc qdisc add dev " TAP_NAME " clsact");
407+
SYS(close, "tc filter add dev " TAP_NAME " ingress "
408+
"protocol all matchall "
409+
"action mirred ingress mirror dev " DUMMY_NAME);
410+
411+
/* Receive a packet on TAP */
412+
ret = write_test_packet(tap_fd);
413+
if (!ASSERT_OK(ret, "write_test_packet"))
414+
goto close;
415+
416+
ASSERT_TRUE(*test_pass, "test_pass");
338417

339418
close:
340419
if (tap_fd >= 0)
@@ -385,6 +464,14 @@ void test_xdp_context_tuntap(void)
385464
skel->progs.ing_cls_dynptr_offset_oob,
386465
skel->progs.ing_cls,
387466
skel->maps.test_result);
467+
if (test__start_subtest("skb_clone_data_meta_empty"))
468+
test_tuntap_mirred(skel->progs.ing_xdp,
469+
skel->progs.ing_cls_data_meta_empty,
470+
&skel->bss->test_pass);
471+
if (test__start_subtest("skb_clone_dynptr_empty"))
472+
test_tuntap_mirred(skel->progs.ing_xdp,
473+
skel->progs.ing_cls_dynptr_empty,
474+
&skel->bss->test_pass);
388475

389476
test_xdp_meta__destroy(skel);
390477
}

tools/testing/selftests/bpf/progs/test_xdp_meta.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ struct {
2626
__uint(value_size, META_SIZE);
2727
} test_result SEC(".maps");
2828

29+
bool test_pass;
30+
2931
SEC("tc")
3032
int ing_cls(struct __sk_buff *ctx)
3133
{
@@ -43,6 +45,56 @@ int ing_cls(struct __sk_buff *ctx)
4345
return TC_ACT_SHOT;
4446
}
4547

48+
/* Check that skb->data_meta..skb->data is empty */
49+
SEC("tc")
50+
int ing_cls_data_meta_empty(struct __sk_buff *ctx)
51+
{
52+
struct ethhdr *eth = ctx_ptr(ctx, data);
53+
54+
if (eth + 1 > ctx_ptr(ctx, data_end))
55+
goto out;
56+
/* Ignore non-test packets */
57+
if (eth->h_proto != 0)
58+
goto out;
59+
/* Packet write to trigger unclone in prologue */
60+
eth->h_proto = 42;
61+
62+
/* Expect no metadata */
63+
if (ctx->data_meta < ctx->data)
64+
goto out;
65+
66+
test_pass = true;
67+
out:
68+
return TC_ACT_SHOT;
69+
}
70+
71+
/* Check that skb_meta dynptr is empty */
72+
SEC("tc")
73+
int ing_cls_dynptr_empty(struct __sk_buff *ctx)
74+
{
75+
struct bpf_dynptr data, meta;
76+
struct ethhdr *eth;
77+
78+
bpf_dynptr_from_skb(ctx, 0, &data);
79+
eth = bpf_dynptr_slice_rdwr(&data, 0, NULL, sizeof(*eth));
80+
if (!eth)
81+
goto out;
82+
/* Ignore non-test packets */
83+
if (eth->h_proto != 0)
84+
goto out;
85+
/* Packet write to trigger unclone in prologue */
86+
eth->h_proto = 42;
87+
88+
/* Expect no metadata */
89+
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
90+
if (bpf_dynptr_size(&meta) > 0)
91+
goto out;
92+
93+
test_pass = true;
94+
out:
95+
return TC_ACT_SHOT;
96+
}
97+
4698
/* Read from metadata using bpf_dynptr_read helper */
4799
SEC("tc")
48100
int ing_cls_dynptr_read(struct __sk_buff *ctx)

0 commit comments

Comments
 (0)