Merge branch 'bpf-next/skb-meta-dynptr' into 'bpf-next/net'

Merge 'skb-meta-dynptr' branch into 'net' branch. No conflict.

Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
This commit is contained in:
Martin KaFai Lau 2025-08-18 17:58:21 -07:00
commit 7e1371023a
13 changed files with 1029 additions and 29 deletions

View File

@ -767,12 +767,15 @@ enum bpf_type_flag {
*/
MEM_WRITE = BIT(18 + BPF_BASE_TYPE_BITS),
/* DYNPTR points to skb_metadata_end()-skb_metadata_len() */
DYNPTR_TYPE_SKB_META = BIT(19 + BPF_BASE_TYPE_BITS),
__BPF_TYPE_FLAG_MAX,
__BPF_TYPE_LAST_FLAG = __BPF_TYPE_FLAG_MAX - 1,
};
#define DYNPTR_TYPE_FLAG_MASK (DYNPTR_TYPE_LOCAL | DYNPTR_TYPE_RINGBUF | DYNPTR_TYPE_SKB \
| DYNPTR_TYPE_XDP)
| DYNPTR_TYPE_XDP | DYNPTR_TYPE_SKB_META)
/* Max number of base types. */
#define BPF_BASE_TYPE_LIMIT (1UL << BPF_BASE_TYPE_BITS)
@ -1358,6 +1361,8 @@ enum bpf_dynptr_type {
BPF_DYNPTR_TYPE_SKB,
/* Underlying data is a xdp_buff */
BPF_DYNPTR_TYPE_XDP,
/* Points to skb_metadata_end()-skb_metadata_len() */
BPF_DYNPTR_TYPE_SKB_META,
};
int bpf_dynptr_check_size(u32 size);

View File

@ -1784,6 +1784,7 @@ int __bpf_xdp_store_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len);
void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len);
void bpf_xdp_copy_buf(struct xdp_buff *xdp, unsigned long off,
void *buf, unsigned long len, bool flush);
void *bpf_skb_meta_pointer(struct sk_buff *skb, u32 offset);
#else /* CONFIG_NET */
static inline int __bpf_skb_load_bytes(const struct sk_buff *skb, u32 offset,
void *to, u32 len)
@ -1818,6 +1819,11 @@ static inline void bpf_xdp_copy_buf(struct xdp_buff *xdp, unsigned long off, voi
unsigned long len, bool flush)
{
}
static inline void *bpf_skb_meta_pointer(struct sk_buff *skb, u32 offset)
{
return NULL;
}
#endif /* CONFIG_NET */
#endif /* __LINUX_FILTER_H__ */

View File

@ -1780,6 +1780,9 @@ static int __bpf_dynptr_read(void *dst, u32 len, const struct bpf_dynptr_kern *s
return __bpf_skb_load_bytes(src->data, src->offset + offset, dst, len);
case BPF_DYNPTR_TYPE_XDP:
return __bpf_xdp_load_bytes(src->data, src->offset + offset, dst, len);
case BPF_DYNPTR_TYPE_SKB_META:
memmove(dst, bpf_skb_meta_pointer(src->data, src->offset + offset), len);
return 0;
default:
WARN_ONCE(true, "bpf_dynptr_read: unknown dynptr type %d\n", type);
return -EFAULT;
@ -1836,6 +1839,11 @@ int __bpf_dynptr_write(const struct bpf_dynptr_kern *dst, u32 offset, void *src,
if (flags)
return -EINVAL;
return __bpf_xdp_store_bytes(dst->data, dst->offset + offset, src, len);
case BPF_DYNPTR_TYPE_SKB_META:
if (flags)
return -EINVAL;
memmove(bpf_skb_meta_pointer(dst->data, dst->offset + offset), src, len);
return 0;
default:
WARN_ONCE(true, "bpf_dynptr_write: unknown dynptr type %d\n", type);
return -EFAULT;
@ -1882,6 +1890,7 @@ BPF_CALL_3(bpf_dynptr_data, const struct bpf_dynptr_kern *, ptr, u32, offset, u3
return (unsigned long)(ptr->data + ptr->offset + offset);
case BPF_DYNPTR_TYPE_SKB:
case BPF_DYNPTR_TYPE_XDP:
case BPF_DYNPTR_TYPE_SKB_META:
/* skb and xdp dynptrs should use bpf_dynptr_slice / bpf_dynptr_slice_rdwr */
return 0;
default:
@ -2710,6 +2719,8 @@ __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr *p, u32 offset,
bpf_xdp_copy_buf(ptr->data, ptr->offset + offset, buffer__opt, len, false);
return buffer__opt;
}
case BPF_DYNPTR_TYPE_SKB_META:
return bpf_skb_meta_pointer(ptr->data, ptr->offset + offset);
default:
WARN_ONCE(true, "unknown dynptr type %d\n", type);
return NULL;

View File

@ -498,6 +498,8 @@ const char *dynptr_type_str(enum bpf_dynptr_type type)
return "skb";
case BPF_DYNPTR_TYPE_XDP:
return "xdp";
case BPF_DYNPTR_TYPE_SKB_META:
return "skb_meta";
case BPF_DYNPTR_TYPE_INVALID:
return "<invalid>";
default:

View File

@ -674,6 +674,8 @@ static enum bpf_dynptr_type arg_to_dynptr_type(enum bpf_arg_type arg_type)
return BPF_DYNPTR_TYPE_SKB;
case DYNPTR_TYPE_XDP:
return BPF_DYNPTR_TYPE_XDP;
case DYNPTR_TYPE_SKB_META:
return BPF_DYNPTR_TYPE_SKB_META;
default:
return BPF_DYNPTR_TYPE_INVALID;
}
@ -690,6 +692,8 @@ static enum bpf_type_flag get_dynptr_type_flag(enum bpf_dynptr_type type)
return DYNPTR_TYPE_SKB;
case BPF_DYNPTR_TYPE_XDP:
return DYNPTR_TYPE_XDP;
case BPF_DYNPTR_TYPE_SKB_META:
return DYNPTR_TYPE_SKB_META;
default:
return 0;
}
@ -2274,7 +2278,8 @@ static bool reg_is_pkt_pointer_any(const struct bpf_reg_state *reg)
static bool reg_is_dynptr_slice_pkt(const struct bpf_reg_state *reg)
{
return base_type(reg->type) == PTR_TO_MEM &&
(reg->type & DYNPTR_TYPE_SKB || reg->type & DYNPTR_TYPE_XDP);
(reg->type &
(DYNPTR_TYPE_SKB | DYNPTR_TYPE_XDP | DYNPTR_TYPE_SKB_META));
}
/* Unmodified PTR_TO_PACKET[_META,_END] register from ctx access. */
@ -11641,7 +11646,8 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
if (dynptr_type == BPF_DYNPTR_TYPE_INVALID)
return -EFAULT;
if (dynptr_type == BPF_DYNPTR_TYPE_SKB)
if (dynptr_type == BPF_DYNPTR_TYPE_SKB ||
dynptr_type == BPF_DYNPTR_TYPE_SKB_META)
/* this will trigger clear_all_pkt_pointers(), which will
* invalidate all dynptr slices associated with the skb
*/
@ -12228,6 +12234,7 @@ enum special_kfunc_type {
KF_bpf_rbtree_right,
KF_bpf_dynptr_from_skb,
KF_bpf_dynptr_from_xdp,
KF_bpf_dynptr_from_skb_meta,
KF_bpf_dynptr_slice,
KF_bpf_dynptr_slice_rdwr,
KF_bpf_dynptr_clone,
@ -12277,9 +12284,11 @@ BTF_ID(func, bpf_rbtree_right)
#ifdef CONFIG_NET
BTF_ID(func, bpf_dynptr_from_skb)
BTF_ID(func, bpf_dynptr_from_xdp)
BTF_ID(func, bpf_dynptr_from_skb_meta)
#else
BTF_ID_UNUSED
BTF_ID_UNUSED
BTF_ID_UNUSED
#endif
BTF_ID(func, bpf_dynptr_slice)
BTF_ID(func, bpf_dynptr_slice_rdwr)
@ -13253,6 +13262,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
dynptr_arg_type |= DYNPTR_TYPE_SKB;
} else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_xdp]) {
dynptr_arg_type |= DYNPTR_TYPE_XDP;
} else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_skb_meta]) {
dynptr_arg_type |= DYNPTR_TYPE_SKB_META;
} else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_clone] &&
(dynptr_arg_type & MEM_UNINIT)) {
enum bpf_dynptr_type parent_type = meta->initialized_dynptr.type;

View File

@ -11990,6 +11990,16 @@ bpf_sk_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
return func;
}
/**
* bpf_skb_meta_pointer() - Gets a mutable pointer within the skb metadata area.
* @skb: socket buffer carrying the metadata
* @offset: offset into the metadata area, must be <= skb_metadata_len()
*/
void *bpf_skb_meta_pointer(struct sk_buff *skb, u32 offset)
{
return skb_metadata_end(skb) - skb_metadata_len(skb) + offset;
}
__bpf_kfunc_start_defs();
__bpf_kfunc int bpf_dynptr_from_skb(struct __sk_buff *s, u64 flags,
struct bpf_dynptr *ptr__uninit)
@ -12007,6 +12017,42 @@ __bpf_kfunc int bpf_dynptr_from_skb(struct __sk_buff *s, u64 flags,
return 0;
}
/**
* bpf_dynptr_from_skb_meta() - Initialize a dynptr to the skb metadata area.
* @skb_: socket buffer carrying the metadata
* @flags: future use, must be zero
* @ptr__uninit: dynptr to initialize
*
* Set up a dynptr for access to the metadata area earlier allocated from the
* XDP context with bpf_xdp_adjust_meta(). Serves as an alternative to
* &__sk_buff->data_meta.
*
* If passed @skb_ is a clone which shares the data with the original, the
* dynptr will be read-only. This limitation may be lifted in the future.
*
* Return:
* * %0 - dynptr ready to use
* * %-EINVAL - invalid flags, dynptr set to null
*/
__bpf_kfunc int bpf_dynptr_from_skb_meta(struct __sk_buff *skb_, u64 flags,
struct bpf_dynptr *ptr__uninit)
{
struct bpf_dynptr_kern *ptr = (struct bpf_dynptr_kern *)ptr__uninit;
struct sk_buff *skb = (struct sk_buff *)skb_;
if (flags) {
bpf_dynptr_set_null(ptr);
return -EINVAL;
}
bpf_dynptr_init(ptr, skb, BPF_DYNPTR_TYPE_SKB_META, 0, skb_metadata_len(skb));
if (skb_cloned(skb))
bpf_dynptr_set_rdonly(ptr);
return 0;
}
__bpf_kfunc int bpf_dynptr_from_xdp(struct xdp_md *x, u64 flags,
struct bpf_dynptr *ptr__uninit)
{
@ -12181,6 +12227,10 @@ BTF_KFUNCS_START(bpf_kfunc_check_set_skb)
BTF_ID_FLAGS(func, bpf_dynptr_from_skb, KF_TRUSTED_ARGS)
BTF_KFUNCS_END(bpf_kfunc_check_set_skb)
BTF_KFUNCS_START(bpf_kfunc_check_set_skb_meta)
BTF_ID_FLAGS(func, bpf_dynptr_from_skb_meta, KF_TRUSTED_ARGS)
BTF_KFUNCS_END(bpf_kfunc_check_set_skb_meta)
BTF_KFUNCS_START(bpf_kfunc_check_set_xdp)
BTF_ID_FLAGS(func, bpf_dynptr_from_xdp)
BTF_KFUNCS_END(bpf_kfunc_check_set_xdp)
@ -12202,6 +12252,11 @@ static const struct btf_kfunc_id_set bpf_kfunc_set_skb = {
.set = &bpf_kfunc_check_set_skb,
};
static const struct btf_kfunc_id_set bpf_kfunc_set_skb_meta = {
.owner = THIS_MODULE,
.set = &bpf_kfunc_check_set_skb_meta,
};
static const struct btf_kfunc_id_set bpf_kfunc_set_xdp = {
.owner = THIS_MODULE,
.set = &bpf_kfunc_check_set_xdp,
@ -12237,6 +12292,8 @@ static int __init bpf_kfunc_init(void)
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_LWT_SEG6LOCAL, &bpf_kfunc_set_skb);
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_NETFILTER, &bpf_kfunc_set_skb);
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &bpf_kfunc_set_skb);
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_kfunc_set_skb_meta);
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_ACT, &bpf_kfunc_set_skb_meta);
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, &bpf_kfunc_set_xdp);
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
&bpf_kfunc_set_sock_addr);

View File

@ -19,6 +19,9 @@ extern int bpf_dynptr_from_skb(struct __sk_buff *skb, __u64 flags,
extern int bpf_dynptr_from_xdp(struct xdp_md *xdp, __u64 flags,
struct bpf_dynptr *ptr__uninit) __ksym __weak;
extern int bpf_dynptr_from_skb_meta(struct __sk_buff *skb, __u64 flags,
struct bpf_dynptr *ptr__uninit) __ksym __weak;
/* Description
* Obtain a read-only pointer to the dynptr's data
* Returns

View File

@ -61,6 +61,7 @@ CONFIG_MPLS_IPTUNNEL=y
CONFIG_MPLS_ROUTING=y
CONFIG_MPTCP=y
CONFIG_NET_ACT_GACT=y
CONFIG_NET_ACT_MIRRED=y
CONFIG_NET_ACT_SKBMOD=y
CONFIG_NET_CLS=y
CONFIG_NET_CLS_ACT=y

View File

@ -32,6 +32,8 @@ static struct {
{"test_ringbuf", SETUP_SYSCALL_SLEEP},
{"test_skb_readonly", SETUP_SKB_PROG},
{"test_dynptr_skb_data", SETUP_SKB_PROG},
{"test_dynptr_skb_meta_data", SETUP_SKB_PROG},
{"test_dynptr_skb_meta_flags", SETUP_SKB_PROG},
{"test_adjust", SETUP_SYSCALL_SLEEP},
{"test_adjust_err", SETUP_SYSCALL_SLEEP},
{"test_zero_size_dynptr", SETUP_SYSCALL_SLEEP},

View File

@ -9,6 +9,7 @@
#define TX_NETNS "xdp_context_tx"
#define RX_NETNS "xdp_context_rx"
#define TAP_NAME "tap0"
#define DUMMY_NAME "dum0"
#define TAP_NETNS "xdp_context_tuntap"
#define TEST_PAYLOAD_LEN 32
@ -156,15 +157,30 @@ static int send_test_packet(int ifindex)
return -1;
}
static void assert_test_result(struct test_xdp_meta *skel)
static int write_test_packet(int tap_fd)
{
__u8 packet[sizeof(struct ethhdr) + TEST_PAYLOAD_LEN];
int n;
/* The ethernet header doesn't need to be valid for this test */
memset(packet, 0, sizeof(struct ethhdr));
memcpy(packet + sizeof(struct ethhdr), test_payload, TEST_PAYLOAD_LEN);
n = write(tap_fd, packet, sizeof(packet));
if (!ASSERT_EQ(n, sizeof(packet), "write packet"))
return -1;
return 0;
}
static void assert_test_result(const struct bpf_map *result_map)
{
int err;
__u32 map_key = 0;
__u8 map_value[TEST_PAYLOAD_LEN];
err = bpf_map__lookup_elem(skel->maps.test_result, &map_key,
sizeof(map_key), &map_value,
TEST_PAYLOAD_LEN, BPF_ANY);
err = bpf_map__lookup_elem(result_map, &map_key, sizeof(map_key),
&map_value, TEST_PAYLOAD_LEN, BPF_ANY);
if (!ASSERT_OK(err, "lookup test_result"))
return;
@ -172,6 +188,18 @@ static void assert_test_result(struct test_xdp_meta *skel)
"test_result map contains test payload");
}
static bool clear_test_result(struct bpf_map *result_map)
{
const __u8 v[sizeof(test_payload)] = {};
const __u32 k = 0;
int err;
err = bpf_map__update_elem(result_map, &k, sizeof(k), v, sizeof(v), BPF_ANY);
ASSERT_OK(err, "update test_result");
return err == 0;
}
void test_xdp_context_veth(void)
{
LIBBPF_OPTS(bpf_tc_hook, tc_hook, .attach_point = BPF_TC_INGRESS);
@ -248,7 +276,7 @@ void test_xdp_context_veth(void)
if (!ASSERT_OK(ret, "send_test_packet"))
goto close;
assert_test_result(skel);
assert_test_result(skel->maps.test_result);
close:
close_netns(nstoken);
@ -257,17 +285,21 @@ void test_xdp_context_veth(void)
netns_free(tx_ns);
}
void test_xdp_context_tuntap(void)
static void test_tuntap(struct bpf_program *xdp_prog,
struct bpf_program *tc_prio_1_prog,
struct bpf_program *tc_prio_2_prog,
struct bpf_map *result_map)
{
LIBBPF_OPTS(bpf_tc_hook, tc_hook, .attach_point = BPF_TC_INGRESS);
LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1);
struct netns_obj *ns = NULL;
struct test_xdp_meta *skel = NULL;
__u8 packet[sizeof(struct ethhdr) + TEST_PAYLOAD_LEN];
int tap_fd = -1;
int tap_ifindex;
int ret;
if (!clear_test_result(result_map))
return;
ns = netns_new(TAP_NETNS, true);
if (!ASSERT_OK_PTR(ns, "create and open ns"))
return;
@ -278,10 +310,6 @@ void test_xdp_context_tuntap(void)
SYS(close, "ip link set dev " TAP_NAME " up");
skel = test_xdp_meta__open_and_load();
if (!ASSERT_OK_PTR(skel, "open and load skeleton"))
goto close;
tap_ifindex = if_nametoindex(TAP_NAME);
if (!ASSERT_GE(tap_ifindex, 0, "if_nametoindex"))
goto close;
@ -291,33 +319,175 @@ void test_xdp_context_tuntap(void)
if (!ASSERT_OK(ret, "bpf_tc_hook_create"))
goto close;
tc_opts.prog_fd = bpf_program__fd(skel->progs.ing_cls);
tc_opts.prog_fd = bpf_program__fd(tc_prio_1_prog);
ret = bpf_tc_attach(&tc_hook, &tc_opts);
if (!ASSERT_OK(ret, "bpf_tc_attach"))
goto close;
ret = bpf_xdp_attach(tap_ifindex, bpf_program__fd(skel->progs.ing_xdp),
if (tc_prio_2_prog) {
LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 2,
.prog_fd = bpf_program__fd(tc_prio_2_prog));
ret = bpf_tc_attach(&tc_hook, &tc_opts);
if (!ASSERT_OK(ret, "bpf_tc_attach"))
goto close;
}
ret = bpf_xdp_attach(tap_ifindex, bpf_program__fd(xdp_prog),
0, NULL);
if (!ASSERT_GE(ret, 0, "bpf_xdp_attach"))
goto close;
/* The ethernet header is not relevant for this test and doesn't need to
* be meaningful.
*/
struct ethhdr eth = { 0 };
memcpy(packet, &eth, sizeof(eth));
memcpy(packet + sizeof(eth), test_payload, TEST_PAYLOAD_LEN);
ret = write(tap_fd, packet, sizeof(packet));
if (!ASSERT_EQ(ret, sizeof(packet), "write packet"))
ret = write_test_packet(tap_fd);
if (!ASSERT_OK(ret, "write_test_packet"))
goto close;
assert_test_result(skel);
assert_test_result(result_map);
close:
if (tap_fd >= 0)
close(tap_fd);
test_xdp_meta__destroy(skel);
netns_free(ns);
}
/* Write a packet to a tap dev and copy it to ingress of a dummy dev */
static void test_tuntap_mirred(struct bpf_program *xdp_prog,
struct bpf_program *tc_prog,
bool *test_pass)
{
LIBBPF_OPTS(bpf_tc_hook, tc_hook, .attach_point = BPF_TC_INGRESS);
LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1);
struct netns_obj *ns = NULL;
int dummy_ifindex;
int tap_fd = -1;
int tap_ifindex;
int ret;
*test_pass = false;
ns = netns_new(TAP_NETNS, true);
if (!ASSERT_OK_PTR(ns, "netns_new"))
return;
/* Setup dummy interface */
SYS(close, "ip link add name " DUMMY_NAME " type dummy");
SYS(close, "ip link set dev " DUMMY_NAME " up");
dummy_ifindex = if_nametoindex(DUMMY_NAME);
if (!ASSERT_GE(dummy_ifindex, 0, "if_nametoindex"))
goto close;
tc_hook.ifindex = dummy_ifindex;
ret = bpf_tc_hook_create(&tc_hook);
if (!ASSERT_OK(ret, "bpf_tc_hook_create"))
goto close;
tc_opts.prog_fd = bpf_program__fd(tc_prog);
ret = bpf_tc_attach(&tc_hook, &tc_opts);
if (!ASSERT_OK(ret, "bpf_tc_attach"))
goto close;
/* Setup TAP interface */
tap_fd = open_tuntap(TAP_NAME, true);
if (!ASSERT_GE(tap_fd, 0, "open_tuntap"))
goto close;
SYS(close, "ip link set dev " TAP_NAME " up");
tap_ifindex = if_nametoindex(TAP_NAME);
if (!ASSERT_GE(tap_ifindex, 0, "if_nametoindex"))
goto close;
ret = bpf_xdp_attach(tap_ifindex, bpf_program__fd(xdp_prog), 0, NULL);
if (!ASSERT_GE(ret, 0, "bpf_xdp_attach"))
goto close;
/* Copy all packets received from TAP to dummy ingress */
SYS(close, "tc qdisc add dev " TAP_NAME " clsact");
SYS(close, "tc filter add dev " TAP_NAME " ingress "
"protocol all matchall "
"action mirred ingress mirror dev " DUMMY_NAME);
/* Receive a packet on TAP */
ret = write_test_packet(tap_fd);
if (!ASSERT_OK(ret, "write_test_packet"))
goto close;
ASSERT_TRUE(*test_pass, "test_pass");
close:
if (tap_fd >= 0)
close(tap_fd);
netns_free(ns);
}
void test_xdp_context_tuntap(void)
{
struct test_xdp_meta *skel = NULL;
skel = test_xdp_meta__open_and_load();
if (!ASSERT_OK_PTR(skel, "open and load skeleton"))
return;
if (test__start_subtest("data_meta"))
test_tuntap(skel->progs.ing_xdp,
skel->progs.ing_cls,
NULL, /* tc prio 2 */
skel->maps.test_result);
if (test__start_subtest("dynptr_read"))
test_tuntap(skel->progs.ing_xdp,
skel->progs.ing_cls_dynptr_read,
NULL, /* tc prio 2 */
skel->maps.test_result);
if (test__start_subtest("dynptr_slice"))
test_tuntap(skel->progs.ing_xdp,
skel->progs.ing_cls_dynptr_slice,
NULL, /* tc prio 2 */
skel->maps.test_result);
if (test__start_subtest("dynptr_write"))
test_tuntap(skel->progs.ing_xdp_zalloc_meta,
skel->progs.ing_cls_dynptr_write,
skel->progs.ing_cls_dynptr_read,
skel->maps.test_result);
if (test__start_subtest("dynptr_slice_rdwr"))
test_tuntap(skel->progs.ing_xdp_zalloc_meta,
skel->progs.ing_cls_dynptr_slice_rdwr,
skel->progs.ing_cls_dynptr_slice,
skel->maps.test_result);
if (test__start_subtest("dynptr_offset"))
test_tuntap(skel->progs.ing_xdp_zalloc_meta,
skel->progs.ing_cls_dynptr_offset_wr,
skel->progs.ing_cls_dynptr_offset_rd,
skel->maps.test_result);
if (test__start_subtest("dynptr_offset_oob"))
test_tuntap(skel->progs.ing_xdp,
skel->progs.ing_cls_dynptr_offset_oob,
skel->progs.ing_cls,
skel->maps.test_result);
if (test__start_subtest("clone_data_meta_empty_on_data_write"))
test_tuntap_mirred(skel->progs.ing_xdp,
skel->progs.clone_data_meta_empty_on_data_write,
&skel->bss->test_pass);
if (test__start_subtest("clone_data_meta_empty_on_meta_write"))
test_tuntap_mirred(skel->progs.ing_xdp,
skel->progs.clone_data_meta_empty_on_meta_write,
&skel->bss->test_pass);
if (test__start_subtest("clone_dynptr_empty_on_data_slice_write"))
test_tuntap_mirred(skel->progs.ing_xdp,
skel->progs.clone_dynptr_empty_on_data_slice_write,
&skel->bss->test_pass);
if (test__start_subtest("clone_dynptr_empty_on_meta_slice_write"))
test_tuntap_mirred(skel->progs.ing_xdp,
skel->progs.clone_dynptr_empty_on_meta_slice_write,
&skel->bss->test_pass);
if (test__start_subtest("clone_dynptr_rdonly_before_data_dynptr_write"))
test_tuntap_mirred(skel->progs.ing_xdp,
skel->progs.clone_dynptr_rdonly_before_data_dynptr_write,
&skel->bss->test_pass);
if (test__start_subtest("clone_dynptr_rdonly_before_meta_dynptr_write"))
test_tuntap_mirred(skel->progs.ing_xdp,
skel->progs.clone_dynptr_rdonly_before_meta_dynptr_write,
&skel->bss->test_pass);
test_xdp_meta__destroy(skel);
}

View File

@ -269,6 +269,26 @@ int data_slice_out_of_bounds_skb(struct __sk_buff *skb)
return SK_PASS;
}
/* A metadata slice can't be accessed out of bounds */
SEC("?tc")
__failure __msg("value is outside of the allowed memory range")
int data_slice_out_of_bounds_skb_meta(struct __sk_buff *skb)
{
struct bpf_dynptr meta;
__u8 *md;
bpf_dynptr_from_skb_meta(skb, 0, &meta);
md = bpf_dynptr_slice_rdwr(&meta, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
/* this should fail */
*(md + 1) = 42;
return SK_PASS;
}
SEC("?raw_tp")
__failure __msg("value is outside of the allowed memory range")
int data_slice_out_of_bounds_map_value(void *ctx)
@ -1089,6 +1109,26 @@ int skb_invalid_slice_write(struct __sk_buff *skb)
return SK_PASS;
}
/* bpf_dynptr_slice()s are read-only and cannot be written to */
SEC("?tc")
__failure __msg("R{{[0-9]+}} cannot write into rdonly_mem")
int skb_meta_invalid_slice_write(struct __sk_buff *skb)
{
struct bpf_dynptr meta;
__u8 *md;
bpf_dynptr_from_skb_meta(skb, 0, &meta);
md = bpf_dynptr_slice(&meta, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
/* this should fail */
*md = 42;
return SK_PASS;
}
/* The read-only data slice is invalidated whenever a helper changes packet data */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
@ -1192,6 +1232,188 @@ int skb_invalid_data_slice4(struct __sk_buff *skb)
return SK_PASS;
}
/* Read-only skb data slice is invalidated on write to skb metadata */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int ro_skb_slice_invalid_after_metadata_write(struct __sk_buff *skb)
{
struct bpf_dynptr data, meta;
__u8 *d;
bpf_dynptr_from_skb(skb, 0, &data);
bpf_dynptr_from_skb_meta(skb, 0, &meta);
d = bpf_dynptr_slice(&data, 0, NULL, sizeof(*d));
if (!d)
return SK_DROP;
bpf_dynptr_write(&meta, 0, "x", 1, 0);
/* this should fail */
val = *d;
return SK_PASS;
}
/* Read-write skb data slice is invalidated on write to skb metadata */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int rw_skb_slice_invalid_after_metadata_write(struct __sk_buff *skb)
{
struct bpf_dynptr data, meta;
__u8 *d;
bpf_dynptr_from_skb(skb, 0, &data);
bpf_dynptr_from_skb_meta(skb, 0, &meta);
d = bpf_dynptr_slice_rdwr(&data, 0, NULL, sizeof(*d));
if (!d)
return SK_DROP;
bpf_dynptr_write(&meta, 0, "x", 1, 0);
/* this should fail */
*d = 42;
return SK_PASS;
}
/* Read-only skb metadata slice is invalidated on write to skb data */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int ro_skb_meta_slice_invalid_after_payload_write(struct __sk_buff *skb)
{
struct bpf_dynptr data, meta;
__u8 *md;
bpf_dynptr_from_skb(skb, 0, &data);
bpf_dynptr_from_skb_meta(skb, 0, &meta);
md = bpf_dynptr_slice(&meta, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
bpf_dynptr_write(&data, 0, "x", 1, 0);
/* this should fail */
val = *md;
return SK_PASS;
}
/* Read-write skb metadata slice is invalidated on write to skb data slice */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int rw_skb_meta_slice_invalid_after_payload_write(struct __sk_buff *skb)
{
struct bpf_dynptr data, meta;
__u8 *md;
bpf_dynptr_from_skb(skb, 0, &data);
bpf_dynptr_from_skb_meta(skb, 0, &meta);
md = bpf_dynptr_slice_rdwr(&meta, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
bpf_dynptr_write(&data, 0, "x", 1, 0);
/* this should fail */
*md = 42;
return SK_PASS;
}
/* Read-only skb metadata slice is invalidated whenever a helper changes packet data */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int ro_skb_meta_slice_invalid_after_payload_helper(struct __sk_buff *skb)
{
struct bpf_dynptr meta;
__u8 *md;
bpf_dynptr_from_skb_meta(skb, 0, &meta);
md = bpf_dynptr_slice(&meta, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
if (bpf_skb_pull_data(skb, skb->len))
return SK_DROP;
/* this should fail */
val = *md;
return SK_PASS;
}
/* Read-write skb metadata slice is invalidated whenever a helper changes packet data */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int rw_skb_meta_slice_invalid_after_payload_helper(struct __sk_buff *skb)
{
struct bpf_dynptr meta;
__u8 *md;
bpf_dynptr_from_skb_meta(skb, 0, &meta);
md = bpf_dynptr_slice_rdwr(&meta, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
if (bpf_skb_pull_data(skb, skb->len))
return SK_DROP;
/* this should fail */
*md = 42;
return SK_PASS;
}
/* Read-only skb metadata slice is invalidated on write to skb metadata */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int ro_skb_meta_slice_invalid_after_metadata_write(struct __sk_buff *skb)
{
struct bpf_dynptr meta;
__u8 *md;
bpf_dynptr_from_skb_meta(skb, 0, &meta);
md = bpf_dynptr_slice(&meta, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
bpf_dynptr_write(&meta, 0, "x", 1, 0);
/* this should fail */
val = *md;
return SK_PASS;
}
/* Read-write skb metadata slice is invalidated on write to skb metadata */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int rw_skb_meta_slice_invalid_after_metadata_write(struct __sk_buff *skb)
{
struct bpf_dynptr meta;
__u8 *md;
bpf_dynptr_from_skb_meta(skb, 0, &meta);
md = bpf_dynptr_slice_rdwr(&meta, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
bpf_dynptr_write(&meta, 0, "x", 1, 0);
/* this should fail */
*md = 42;
return SK_PASS;
}
/* The read-only data slice is invalidated whenever a helper changes packet data */
SEC("?xdp")
__failure __msg("invalid mem access 'scalar'")
@ -1255,6 +1477,19 @@ int skb_invalid_ctx(void *ctx)
return 0;
}
/* Only supported prog type can create skb_meta-type dynptrs */
SEC("?raw_tp")
__failure __msg("calling kernel function bpf_dynptr_from_skb_meta is not allowed")
int skb_meta_invalid_ctx(void *ctx)
{
struct bpf_dynptr meta;
/* this should fail */
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
return 0;
}
SEC("fentry/skb_tx_error")
__failure __msg("must be referenced or trusted")
int BPF_PROG(skb_invalid_ctx_fentry, void *skb)
@ -1665,6 +1900,29 @@ int clone_skb_packet_data(struct __sk_buff *skb)
return 0;
}
/* A skb clone's metadata slice becomes invalid anytime packet data changes */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int clone_skb_packet_meta(struct __sk_buff *skb)
{
struct bpf_dynptr clone, meta;
__u8 *md;
bpf_dynptr_from_skb_meta(skb, 0, &meta);
bpf_dynptr_clone(&meta, &clone);
md = bpf_dynptr_slice_rdwr(&clone, 0, NULL, sizeof(*md));
if (!md)
return SK_DROP;
if (bpf_skb_pull_data(skb, skb->len))
return SK_DROP;
/* this should fail */
*md = 42;
return 0;
}
/* A xdp clone's data slices should be invalid anytime packet data changes */
SEC("?xdp")
__failure __msg("invalid mem access 'scalar'")

View File

@ -211,6 +211,61 @@ int test_dynptr_skb_data(struct __sk_buff *skb)
return 1;
}
SEC("?tc")
int test_dynptr_skb_meta_data(struct __sk_buff *skb)
{
struct bpf_dynptr meta;
__u8 *md;
int ret;
err = 1;
ret = bpf_dynptr_from_skb_meta(skb, 0, &meta);
if (ret)
return 1;
/* This should return NULL. Must use bpf_dynptr_slice API */
err = 2;
md = bpf_dynptr_data(&meta, 0, sizeof(*md));
if (md)
return 1;
err = 0;
return 1;
}
/* Check that skb metadata dynptr ops don't accept any flags. */
SEC("?tc")
int test_dynptr_skb_meta_flags(struct __sk_buff *skb)
{
const __u64 INVALID_FLAGS = ~0ULL;
struct bpf_dynptr meta;
__u8 buf;
int ret;
err = 1;
ret = bpf_dynptr_from_skb_meta(skb, INVALID_FLAGS, &meta);
if (ret != -EINVAL)
return 1;
err = 2;
ret = bpf_dynptr_from_skb_meta(skb, 0, &meta);
if (ret)
return 1;
err = 3;
ret = bpf_dynptr_read(&buf, 0, &meta, 0, INVALID_FLAGS);
if (ret != -EINVAL)
return 1;
err = 4;
ret = bpf_dynptr_write(&meta, 0, &buf, 0, INVALID_FLAGS);
if (ret != -EINVAL)
return 1;
err = 0;
return 1;
}
SEC("tp/syscalls/sys_enter_nanosleep")
int test_adjust(void *ctx)
{

View File

@ -1,8 +1,11 @@
#include <stdbool.h>
#include <linux/bpf.h>
#include <linux/errno.h>
#include <linux/if_ether.h>
#include <linux/pkt_cls.h>
#include <bpf/bpf_helpers.h>
#include "bpf_kfuncs.h"
#define META_SIZE 32
@ -23,6 +26,8 @@ struct {
__uint(value_size, META_SIZE);
} test_result SEC(".maps");
bool test_pass;
SEC("tc")
int ing_cls(struct __sk_buff *ctx)
{
@ -40,6 +45,231 @@ int ing_cls(struct __sk_buff *ctx)
return TC_ACT_SHOT;
}
/* Read from metadata using bpf_dynptr_read helper */
SEC("tc")
int ing_cls_dynptr_read(struct __sk_buff *ctx)
{
struct bpf_dynptr meta;
const __u32 zero = 0;
__u8 *dst;
dst = bpf_map_lookup_elem(&test_result, &zero);
if (!dst)
return TC_ACT_SHOT;
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
bpf_dynptr_read(dst, META_SIZE, &meta, 0, 0);
return TC_ACT_SHOT;
}
/* Write to metadata using bpf_dynptr_write helper */
SEC("tc")
int ing_cls_dynptr_write(struct __sk_buff *ctx)
{
struct bpf_dynptr data, meta;
__u8 *src;
bpf_dynptr_from_skb(ctx, 0, &data);
src = bpf_dynptr_slice(&data, sizeof(struct ethhdr), NULL, META_SIZE);
if (!src)
return TC_ACT_SHOT;
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
bpf_dynptr_write(&meta, 0, src, META_SIZE, 0);
return TC_ACT_UNSPEC; /* pass */
}
/* Read from metadata using read-only dynptr slice */
SEC("tc")
int ing_cls_dynptr_slice(struct __sk_buff *ctx)
{
struct bpf_dynptr meta;
const __u32 zero = 0;
__u8 *dst, *src;
dst = bpf_map_lookup_elem(&test_result, &zero);
if (!dst)
return TC_ACT_SHOT;
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
src = bpf_dynptr_slice(&meta, 0, NULL, META_SIZE);
if (!src)
return TC_ACT_SHOT;
__builtin_memcpy(dst, src, META_SIZE);
return TC_ACT_SHOT;
}
/* Write to metadata using writeable dynptr slice */
SEC("tc")
int ing_cls_dynptr_slice_rdwr(struct __sk_buff *ctx)
{
struct bpf_dynptr data, meta;
__u8 *src, *dst;
bpf_dynptr_from_skb(ctx, 0, &data);
src = bpf_dynptr_slice(&data, sizeof(struct ethhdr), NULL, META_SIZE);
if (!src)
return TC_ACT_SHOT;
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
dst = bpf_dynptr_slice_rdwr(&meta, 0, NULL, META_SIZE);
if (!dst)
return TC_ACT_SHOT;
__builtin_memcpy(dst, src, META_SIZE);
return TC_ACT_UNSPEC; /* pass */
}
/* Read skb metadata in chunks from various offsets in different ways. */
SEC("tc")
int ing_cls_dynptr_offset_rd(struct __sk_buff *ctx)
{
struct bpf_dynptr meta;
const __u32 chunk_len = META_SIZE / 4;
const __u32 zero = 0;
__u8 *dst, *src;
dst = bpf_map_lookup_elem(&test_result, &zero);
if (!dst)
return TC_ACT_SHOT;
/* 1. Regular read */
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
bpf_dynptr_read(dst, chunk_len, &meta, 0, 0);
dst += chunk_len;
/* 2. Read from an offset-adjusted dynptr */
bpf_dynptr_adjust(&meta, chunk_len, bpf_dynptr_size(&meta));
bpf_dynptr_read(dst, chunk_len, &meta, 0, 0);
dst += chunk_len;
/* 3. Read at an offset */
bpf_dynptr_read(dst, chunk_len, &meta, chunk_len, 0);
dst += chunk_len;
/* 4. Read from a slice starting at an offset */
src = bpf_dynptr_slice(&meta, 2 * chunk_len, NULL, chunk_len);
if (!src)
return TC_ACT_SHOT;
__builtin_memcpy(dst, src, chunk_len);
return TC_ACT_SHOT;
}
/* Write skb metadata in chunks at various offsets in different ways. */
SEC("tc")
int ing_cls_dynptr_offset_wr(struct __sk_buff *ctx)
{
const __u32 chunk_len = META_SIZE / 4;
__u8 payload[META_SIZE];
struct bpf_dynptr meta;
__u8 *dst, *src;
bpf_skb_load_bytes(ctx, sizeof(struct ethhdr), payload, sizeof(payload));
src = payload;
/* 1. Regular write */
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
bpf_dynptr_write(&meta, 0, src, chunk_len, 0);
src += chunk_len;
/* 2. Write to an offset-adjusted dynptr */
bpf_dynptr_adjust(&meta, chunk_len, bpf_dynptr_size(&meta));
bpf_dynptr_write(&meta, 0, src, chunk_len, 0);
src += chunk_len;
/* 3. Write at an offset */
bpf_dynptr_write(&meta, chunk_len, src, chunk_len, 0);
src += chunk_len;
/* 4. Write to a slice starting at an offset */
dst = bpf_dynptr_slice_rdwr(&meta, 2 * chunk_len, NULL, chunk_len);
if (!dst)
return TC_ACT_SHOT;
__builtin_memcpy(dst, src, chunk_len);
return TC_ACT_UNSPEC; /* pass */
}
/* Pass an OOB offset to dynptr read, write, adjust, slice. */
SEC("tc")
int ing_cls_dynptr_offset_oob(struct __sk_buff *ctx)
{
struct bpf_dynptr meta;
__u8 md, *p;
int err;
err = bpf_dynptr_from_skb_meta(ctx, 0, &meta);
if (err)
goto fail;
/* read offset OOB */
err = bpf_dynptr_read(&md, sizeof(md), &meta, META_SIZE, 0);
if (err != -E2BIG)
goto fail;
/* write offset OOB */
err = bpf_dynptr_write(&meta, META_SIZE, &md, sizeof(md), 0);
if (err != -E2BIG)
goto fail;
/* adjust end offset OOB */
err = bpf_dynptr_adjust(&meta, 0, META_SIZE + 1);
if (err != -ERANGE)
goto fail;
/* adjust start offset OOB */
err = bpf_dynptr_adjust(&meta, META_SIZE + 1, META_SIZE + 1);
if (err != -ERANGE)
goto fail;
/* slice offset OOB */
p = bpf_dynptr_slice(&meta, META_SIZE, NULL, sizeof(*p));
if (p)
goto fail;
/* slice rdwr offset OOB */
p = bpf_dynptr_slice_rdwr(&meta, META_SIZE, NULL, sizeof(*p));
if (p)
goto fail;
return TC_ACT_UNSPEC;
fail:
return TC_ACT_SHOT;
}
/* Reserve and clear space for metadata but don't populate it */
SEC("xdp")
int ing_xdp_zalloc_meta(struct xdp_md *ctx)
{
struct ethhdr *eth = ctx_ptr(ctx, data);
__u8 *meta;
int ret;
/* Drop any non-test packets */
if (eth + 1 > ctx_ptr(ctx, data_end))
return XDP_DROP;
if (eth->h_proto != 0)
return XDP_DROP;
ret = bpf_xdp_adjust_meta(ctx, -META_SIZE);
if (ret < 0)
return XDP_DROP;
meta = ctx_ptr(ctx, data_meta);
if (meta + META_SIZE > ctx_ptr(ctx, data))
return XDP_DROP;
__builtin_memset(meta, 0, META_SIZE);
return XDP_PASS;
}
SEC("xdp")
int ing_xdp(struct xdp_md *ctx)
{
@ -73,4 +303,193 @@ int ing_xdp(struct xdp_md *ctx)
return XDP_PASS;
}
/*
* Check that skb->data_meta..skb->data is empty if prog writes to packet
* _payload_ using packet pointers. Applies only to cloned skbs.
*/
SEC("tc")
int clone_data_meta_empty_on_data_write(struct __sk_buff *ctx)
{
struct ethhdr *eth = ctx_ptr(ctx, data);
if (eth + 1 > ctx_ptr(ctx, data_end))
goto out;
/* Ignore non-test packets */
if (eth->h_proto != 0)
goto out;
/* Expect no metadata */
if (ctx->data_meta != ctx->data)
goto out;
/* Packet write to trigger unclone in prologue */
eth->h_proto = 42;
test_pass = true;
out:
return TC_ACT_SHOT;
}
/*
* Check that skb->data_meta..skb->data is empty if prog writes to packet
* _metadata_ using packet pointers. Applies only to cloned skbs.
*/
SEC("tc")
int clone_data_meta_empty_on_meta_write(struct __sk_buff *ctx)
{
struct ethhdr *eth = ctx_ptr(ctx, data);
__u8 *md = ctx_ptr(ctx, data_meta);
if (eth + 1 > ctx_ptr(ctx, data_end))
goto out;
/* Ignore non-test packets */
if (eth->h_proto != 0)
goto out;
if (md + 1 > ctx_ptr(ctx, data)) {
/* Expect no metadata */
test_pass = true;
} else {
/* Metadata write to trigger unclone in prologue */
*md = 42;
}
out:
return TC_ACT_SHOT;
}
/*
* Check that skb_meta dynptr is writable but empty if prog writes to packet
* _payload_ using a dynptr slice. Applies only to cloned skbs.
*/
SEC("tc")
int clone_dynptr_empty_on_data_slice_write(struct __sk_buff *ctx)
{
struct bpf_dynptr data, meta;
struct ethhdr *eth;
bpf_dynptr_from_skb(ctx, 0, &data);
eth = bpf_dynptr_slice_rdwr(&data, 0, NULL, sizeof(*eth));
if (!eth)
goto out;
/* Ignore non-test packets */
if (eth->h_proto != 0)
goto out;
/* Expect no metadata */
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
if (bpf_dynptr_is_rdonly(&meta) || bpf_dynptr_size(&meta) > 0)
goto out;
/* Packet write to trigger unclone in prologue */
eth->h_proto = 42;
test_pass = true;
out:
return TC_ACT_SHOT;
}
/*
* Check that skb_meta dynptr is writable but empty if prog writes to packet
* _metadata_ using a dynptr slice. Applies only to cloned skbs.
*/
SEC("tc")
int clone_dynptr_empty_on_meta_slice_write(struct __sk_buff *ctx)
{
struct bpf_dynptr data, meta;
const struct ethhdr *eth;
__u8 *md;
bpf_dynptr_from_skb(ctx, 0, &data);
eth = bpf_dynptr_slice(&data, 0, NULL, sizeof(*eth));
if (!eth)
goto out;
/* Ignore non-test packets */
if (eth->h_proto != 0)
goto out;
/* Expect no metadata */
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
if (bpf_dynptr_is_rdonly(&meta) || bpf_dynptr_size(&meta) > 0)
goto out;
/* Metadata write to trigger unclone in prologue */
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
md = bpf_dynptr_slice_rdwr(&meta, 0, NULL, sizeof(*md));
if (md)
*md = 42;
test_pass = true;
out:
return TC_ACT_SHOT;
}
/*
* Check that skb_meta dynptr is read-only before prog writes to packet payload
* using dynptr_write helper. Applies only to cloned skbs.
*/
SEC("tc")
int clone_dynptr_rdonly_before_data_dynptr_write(struct __sk_buff *ctx)
{
struct bpf_dynptr data, meta;
const struct ethhdr *eth;
bpf_dynptr_from_skb(ctx, 0, &data);
eth = bpf_dynptr_slice(&data, 0, NULL, sizeof(*eth));
if (!eth)
goto out;
/* Ignore non-test packets */
if (eth->h_proto != 0)
goto out;
/* Expect read-only metadata before unclone */
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
if (!bpf_dynptr_is_rdonly(&meta) || bpf_dynptr_size(&meta) != META_SIZE)
goto out;
/* Helper write to payload will unclone the packet */
bpf_dynptr_write(&data, offsetof(struct ethhdr, h_proto), "x", 1, 0);
/* Expect no metadata after unclone */
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
if (bpf_dynptr_is_rdonly(&meta) || bpf_dynptr_size(&meta) != 0)
goto out;
test_pass = true;
out:
return TC_ACT_SHOT;
}
/*
* Check that skb_meta dynptr is read-only if prog writes to packet
* metadata using dynptr_write helper. Applies only to cloned skbs.
*/
SEC("tc")
int clone_dynptr_rdonly_before_meta_dynptr_write(struct __sk_buff *ctx)
{
struct bpf_dynptr data, meta;
const struct ethhdr *eth;
bpf_dynptr_from_skb(ctx, 0, &data);
eth = bpf_dynptr_slice(&data, 0, NULL, sizeof(*eth));
if (!eth)
goto out;
/* Ignore non-test packets */
if (eth->h_proto != 0)
goto out;
/* Expect read-only metadata */
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
if (!bpf_dynptr_is_rdonly(&meta) || bpf_dynptr_size(&meta) != META_SIZE)
goto out;
/* Metadata write. Expect failure. */
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
if (bpf_dynptr_write(&meta, 0, "x", 1, 0) != -EINVAL)
goto out;
test_pass = true;
out:
return TC_ACT_SHOT;
}
char _license[] SEC("license") = "GPL";