Skip to content

Commit 1f9127a

Browse files
jsitnickiKernel Patches Daemon
authored andcommitted
selftests/bpf: Expect unclone to preserve metadata
Since pskb_expand_head() no longer clears metadata on unclone, update tests for cloned packets to expect metadata to remain intact. Verify metadata contents directly in the BPF program. This allows for multiple checks as packet passes through a chain of BPF programs, rather than a one-time check in user-space. Signed-off-by: Jakub Sitnicki <[email protected]>
1 parent 4e0d58b commit 1f9127a

File tree

2 files changed

+66
-48
lines changed

2 files changed

+66
-48
lines changed

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -462,25 +462,25 @@ void test_xdp_context_tuntap(void)
462462
skel->progs.ing_cls_dynptr_offset_oob,
463463
skel->progs.ing_cls,
464464
skel->maps.test_result);
465-
if (test__start_subtest("clone_data_meta_empty_on_data_write"))
465+
if (test__start_subtest("clone_data_meta_kept_on_data_write"))
466466
test_tuntap_mirred(skel->progs.ing_xdp,
467-
skel->progs.clone_data_meta_empty_on_data_write,
467+
skel->progs.clone_data_meta_kept_on_data_write,
468468
&skel->bss->test_pass);
469-
if (test__start_subtest("clone_data_meta_empty_on_meta_write"))
469+
if (test__start_subtest("clone_data_meta_kept_on_meta_write"))
470470
test_tuntap_mirred(skel->progs.ing_xdp,
471-
skel->progs.clone_data_meta_empty_on_meta_write,
471+
skel->progs.clone_data_meta_kept_on_meta_write,
472472
&skel->bss->test_pass);
473-
if (test__start_subtest("clone_dynptr_empty_on_data_slice_write"))
473+
if (test__start_subtest("clone_dynptr_kept_on_data_slice_write"))
474474
test_tuntap_mirred(skel->progs.ing_xdp,
475-
skel->progs.clone_dynptr_empty_on_data_slice_write,
475+
skel->progs.clone_dynptr_kept_on_data_slice_write,
476476
&skel->bss->test_pass);
477-
if (test__start_subtest("clone_dynptr_empty_on_meta_slice_write"))
477+
if (test__start_subtest("clone_dynptr_kept_on_meta_slice_write"))
478478
test_tuntap_mirred(skel->progs.ing_xdp,
479-
skel->progs.clone_dynptr_empty_on_meta_slice_write,
479+
skel->progs.clone_dynptr_kept_on_meta_slice_write,
480480
&skel->bss->test_pass);
481-
if (test__start_subtest("clone_dynptr_rdonly_before_data_dynptr_write"))
481+
if (test__start_subtest("clone_dynptr_rdonly_before_data_dynptr_write_then_rw"))
482482
test_tuntap_mirred(skel->progs.ing_xdp,
483-
skel->progs.clone_dynptr_rdonly_before_data_dynptr_write,
483+
skel->progs.clone_dynptr_rdonly_before_data_dynptr_write_then_rw,
484484
&skel->bss->test_pass);
485485
if (test__start_subtest("clone_dynptr_rdonly_before_meta_dynptr_write"))
486486
test_tuntap_mirred(skel->progs.ing_xdp,

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

Lines changed: 56 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ struct {
2828

2929
bool test_pass;
3030

31+
static const __u8 meta_want[META_SIZE] = {
32+
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
33+
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
34+
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
35+
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
36+
};
37+
3138
SEC("tc")
3239
int ing_cls(struct __sk_buff *ctx)
3340
{
@@ -304,12 +311,13 @@ int ing_xdp(struct xdp_md *ctx)
304311
}
305312

306313
/*
307-
* Check that skb->data_meta..skb->data is empty if prog writes to packet
314+
* Check that skb->data_meta..skb->data is kept in tact if prog writes to packet
308315
* _payload_ using packet pointers. Applies only to cloned skbs.
309316
*/
310317
SEC("tc")
311-
int clone_data_meta_empty_on_data_write(struct __sk_buff *ctx)
318+
int clone_data_meta_kept_on_data_write(struct __sk_buff *ctx)
312319
{
320+
__u8 *meta_have = ctx_ptr(ctx, data_meta);
313321
struct ethhdr *eth = ctx_ptr(ctx, data);
314322

315323
if (eth + 1 > ctx_ptr(ctx, data_end))
@@ -318,8 +326,10 @@ int clone_data_meta_empty_on_data_write(struct __sk_buff *ctx)
318326
if (eth->h_proto != 0)
319327
goto out;
320328

321-
/* Expect no metadata */
322-
if (ctx->data_meta != ctx->data)
329+
if (meta_have + META_SIZE > eth)
330+
goto out;
331+
332+
if (__builtin_memcmp(meta_want, meta_have, META_SIZE))
323333
goto out;
324334

325335
/* Packet write to trigger unclone in prologue */
@@ -331,40 +341,44 @@ int clone_data_meta_empty_on_data_write(struct __sk_buff *ctx)
331341
}
332342

333343
/*
334-
* Check that skb->data_meta..skb->data is empty if prog writes to packet
344+
* Check that skb->data_meta..skb->data is kept in tact if prog writes to packet
335345
* _metadata_ using packet pointers. Applies only to cloned skbs.
336346
*/
337347
SEC("tc")
338-
int clone_data_meta_empty_on_meta_write(struct __sk_buff *ctx)
348+
int clone_data_meta_kept_on_meta_write(struct __sk_buff *ctx)
339349
{
350+
__u8 *meta_have = ctx_ptr(ctx, data_meta);
340351
struct ethhdr *eth = ctx_ptr(ctx, data);
341-
__u8 *md = ctx_ptr(ctx, data_meta);
342352

343353
if (eth + 1 > ctx_ptr(ctx, data_end))
344354
goto out;
345355
/* Ignore non-test packets */
346356
if (eth->h_proto != 0)
347357
goto out;
348358

349-
if (md + 1 > ctx_ptr(ctx, data)) {
350-
/* Expect no metadata */
351-
test_pass = true;
352-
} else {
353-
/* Metadata write to trigger unclone in prologue */
354-
*md = 42;
355-
}
359+
if (meta_have + META_SIZE > eth)
360+
goto out;
361+
362+
if (__builtin_memcmp(meta_want, meta_have, META_SIZE))
363+
goto out;
364+
365+
/* Metadata write to trigger unclone in prologue */
366+
*meta_have = 42;
367+
368+
test_pass = true;
356369
out:
357370
return TC_ACT_SHOT;
358371
}
359372

360373
/*
361-
* Check that skb_meta dynptr is writable but empty if prog writes to packet
362-
* _payload_ using a dynptr slice. Applies only to cloned skbs.
374+
* Check that skb_meta dynptr is writable and was kept in tact if prog creates a
375+
* r/w slice to packet _payload_. Applies only to cloned skbs.
363376
*/
364377
SEC("tc")
365-
int clone_dynptr_empty_on_data_slice_write(struct __sk_buff *ctx)
378+
int clone_dynptr_kept_on_data_slice_write(struct __sk_buff *ctx)
366379
{
367380
struct bpf_dynptr data, meta;
381+
__u8 meta_have[META_SIZE];
368382
struct ethhdr *eth;
369383

370384
bpf_dynptr_from_skb(ctx, 0, &data);
@@ -375,29 +389,26 @@ int clone_dynptr_empty_on_data_slice_write(struct __sk_buff *ctx)
375389
if (eth->h_proto != 0)
376390
goto out;
377391

378-
/* Expect no metadata */
379392
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
380-
if (bpf_dynptr_is_rdonly(&meta) || bpf_dynptr_size(&meta) > 0)
393+
bpf_dynptr_read(meta_have, META_SIZE, &meta, 0, 0);
394+
if (__builtin_memcmp(meta_want, meta_have, META_SIZE))
381395
goto out;
382396

383-
/* Packet write to trigger unclone in prologue */
384-
eth->h_proto = 42;
385-
386397
test_pass = true;
387398
out:
388399
return TC_ACT_SHOT;
389400
}
390401

391402
/*
392-
* Check that skb_meta dynptr is writable but empty if prog writes to packet
393-
* _metadata_ using a dynptr slice. Applies only to cloned skbs.
403+
* Check that skb_meta dynptr is writable and was kept in tact if prog creates
404+
* an r/w slice to packet _metadata_. Applies only to cloned skbs.
394405
*/
395406
SEC("tc")
396-
int clone_dynptr_empty_on_meta_slice_write(struct __sk_buff *ctx)
407+
int clone_dynptr_kept_on_meta_slice_write(struct __sk_buff *ctx)
397408
{
398409
struct bpf_dynptr data, meta;
399410
const struct ethhdr *eth;
400-
__u8 *md;
411+
__u8 *meta_have;
401412

402413
bpf_dynptr_from_skb(ctx, 0, &data);
403414
eth = bpf_dynptr_slice(&data, 0, NULL, sizeof(*eth));
@@ -407,16 +418,13 @@ int clone_dynptr_empty_on_meta_slice_write(struct __sk_buff *ctx)
407418
if (eth->h_proto != 0)
408419
goto out;
409420

410-
/* Expect no metadata */
411421
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
412-
if (bpf_dynptr_is_rdonly(&meta) || bpf_dynptr_size(&meta) > 0)
422+
meta_have = bpf_dynptr_slice_rdwr(&meta, 0, NULL, META_SIZE);
423+
if (!meta_have)
413424
goto out;
414425

415-
/* Metadata write to trigger unclone in prologue */
416-
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
417-
md = bpf_dynptr_slice_rdwr(&meta, 0, NULL, sizeof(*md));
418-
if (md)
419-
*md = 42;
426+
if (__builtin_memcmp(meta_want, meta_have, META_SIZE))
427+
goto out;
420428

421429
test_pass = true;
422430
out:
@@ -425,12 +433,14 @@ int clone_dynptr_empty_on_meta_slice_write(struct __sk_buff *ctx)
425433

426434
/*
427435
* Check that skb_meta dynptr is read-only before prog writes to packet payload
428-
* using dynptr_write helper. Applies only to cloned skbs.
436+
* using dynptr_write helper, and becomes read-write afterwards. Applies only to
437+
* cloned skbs.
429438
*/
430439
SEC("tc")
431-
int clone_dynptr_rdonly_before_data_dynptr_write(struct __sk_buff *ctx)
440+
int clone_dynptr_rdonly_before_data_dynptr_write_then_rw(struct __sk_buff *ctx)
432441
{
433442
struct bpf_dynptr data, meta;
443+
__u8 meta_have[META_SIZE];
434444
const struct ethhdr *eth;
435445

436446
bpf_dynptr_from_skb(ctx, 0, &data);
@@ -443,15 +453,23 @@ int clone_dynptr_rdonly_before_data_dynptr_write(struct __sk_buff *ctx)
443453

444454
/* Expect read-only metadata before unclone */
445455
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
446-
if (!bpf_dynptr_is_rdonly(&meta) || bpf_dynptr_size(&meta) != META_SIZE)
456+
if (!bpf_dynptr_is_rdonly(&meta))
457+
goto out;
458+
459+
bpf_dynptr_read(meta_have, META_SIZE, &meta, 0, 0);
460+
if (__builtin_memcmp(meta_want, meta_have, META_SIZE))
447461
goto out;
448462

449463
/* Helper write to payload will unclone the packet */
450464
bpf_dynptr_write(&data, offsetof(struct ethhdr, h_proto), "x", 1, 0);
451465

452-
/* Expect no metadata after unclone */
466+
/* Expect r/w metadata after unclone */
453467
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
454-
if (bpf_dynptr_is_rdonly(&meta) || bpf_dynptr_size(&meta) != 0)
468+
if (bpf_dynptr_is_rdonly(&meta))
469+
goto out;
470+
471+
bpf_dynptr_read(meta_have, META_SIZE, &meta, 0, 0);
472+
if (__builtin_memcmp(meta_want, meta_have, META_SIZE))
455473
goto out;
456474

457475
test_pass = true;

0 commit comments

Comments
 (0)