mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 08:33:17 +02:00
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:
commit
7e1371023a
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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, ð, 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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'")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user