diff options
author | Daniel Borkmann <daniel@iogearbox.net> | 2018-08-12 19:02:40 -0400 |
---|---|---|
committer | Daniel Borkmann <daniel@iogearbox.net> | 2018-08-12 19:02:41 -0400 |
commit | 2ce3206b9eb3943de09f3bf4ec9134568420d8b9 (patch) | |
tree | 7903c536230dc9665d7ebbae2166f1e39cfebd72 | |
parent | e8d2bec0457962e8f348a9a3627b398f7fe5c5fc (diff) | |
parent | 5ecd8c22739b9a5f6d6431234decd912aa3f48ad (diff) |
Merge branch 'bpf-ancestor-cgroup-id'
Andrey Ignatov says:
====================
This patch set adds new BPF helper bpf_skb_ancestor_cgroup_id that returns
id of cgroup v2 that is ancestor of cgroup associated with the skb at the
ancestor_level.
The helper is useful to implement policies in TC based on cgroups that are
upper in hierarchy than immediate cgroup associated with skb.
v1->v2:
- more reliable check for testing IPv6 to become ready in selftest.
====================
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
-rw-r--r-- | include/linux/cgroup.h | 30 | ||||
-rw-r--r-- | include/uapi/linux/bpf.h | 21 | ||||
-rw-r--r-- | net/core/filter.c | 28 | ||||
-rw-r--r-- | tools/include/uapi/linux/bpf.h | 21 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/Makefile | 9 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/bpf_helpers.h | 4 | ||||
-rwxr-xr-x | tools/testing/selftests/bpf/test_skb_cgroup_id.sh | 62 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_skb_cgroup_id_kern.c | 47 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_skb_cgroup_id_user.c | 187 |
9 files changed, 404 insertions, 5 deletions
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index c9fdf6f57913..32c553556bbd 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h | |||
@@ -554,6 +554,36 @@ static inline bool cgroup_is_descendant(struct cgroup *cgrp, | |||
554 | } | 554 | } |
555 | 555 | ||
556 | /** | 556 | /** |
557 | * cgroup_ancestor - find ancestor of cgroup | ||
558 | * @cgrp: cgroup to find ancestor of | ||
559 | * @ancestor_level: level of ancestor to find starting from root | ||
560 | * | ||
561 | * Find ancestor of cgroup at specified level starting from root if it exists | ||
562 | * and return pointer to it. Return NULL if @cgrp doesn't have ancestor at | ||
563 | * @ancestor_level. | ||
564 | * | ||
565 | * This function is safe to call as long as @cgrp is accessible. | ||
566 | */ | ||
567 | static inline struct cgroup *cgroup_ancestor(struct cgroup *cgrp, | ||
568 | int ancestor_level) | ||
569 | { | ||
570 | struct cgroup *ptr; | ||
571 | |||
572 | if (cgrp->level < ancestor_level) | ||
573 | return NULL; | ||
574 | |||
575 | for (ptr = cgrp; | ||
576 | ptr && ptr->level > ancestor_level; | ||
577 | ptr = cgroup_parent(ptr)) | ||
578 | ; | ||
579 | |||
580 | if (ptr && ptr->level == ancestor_level) | ||
581 | return ptr; | ||
582 | |||
583 | return NULL; | ||
584 | } | ||
585 | |||
586 | /** | ||
557 | * task_under_cgroup_hierarchy - test task's membership of cgroup ancestry | 587 | * task_under_cgroup_hierarchy - test task's membership of cgroup ancestry |
558 | * @task: the task to be tested | 588 | * @task: the task to be tested |
559 | * @ancestor: possible ancestor of @task's cgroup | 589 | * @ancestor: possible ancestor of @task's cgroup |
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 3102a2a23c31..66917a4eba27 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h | |||
@@ -2093,6 +2093,24 @@ union bpf_attr { | |||
2093 | * Return | 2093 | * Return |
2094 | * The id is returned or 0 in case the id could not be retrieved. | 2094 | * The id is returned or 0 in case the id could not be retrieved. |
2095 | * | 2095 | * |
2096 | * u64 bpf_skb_ancestor_cgroup_id(struct sk_buff *skb, int ancestor_level) | ||
2097 | * Description | ||
2098 | * Return id of cgroup v2 that is ancestor of cgroup associated | ||
2099 | * with the *skb* at the *ancestor_level*. The root cgroup is at | ||
2100 | * *ancestor_level* zero and each step down the hierarchy | ||
2101 | * increments the level. If *ancestor_level* == level of cgroup | ||
2102 | * associated with *skb*, then return value will be same as that | ||
2103 | * of **bpf_skb_cgroup_id**\ (). | ||
2104 | * | ||
2105 | * The helper is useful to implement policies based on cgroups | ||
2106 | * that are upper in hierarchy than immediate cgroup associated | ||
2107 | * with *skb*. | ||
2108 | * | ||
2109 | * The format of returned id and helper limitations are same as in | ||
2110 | * **bpf_skb_cgroup_id**\ (). | ||
2111 | * Return | ||
2112 | * The id is returned or 0 in case the id could not be retrieved. | ||
2113 | * | ||
2096 | * u64 bpf_get_current_cgroup_id(void) | 2114 | * u64 bpf_get_current_cgroup_id(void) |
2097 | * Return | 2115 | * Return |
2098 | * A 64-bit integer containing the current cgroup id based | 2116 | * A 64-bit integer containing the current cgroup id based |
@@ -2207,7 +2225,8 @@ union bpf_attr { | |||
2207 | FN(skb_cgroup_id), \ | 2225 | FN(skb_cgroup_id), \ |
2208 | FN(get_current_cgroup_id), \ | 2226 | FN(get_current_cgroup_id), \ |
2209 | FN(get_local_storage), \ | 2227 | FN(get_local_storage), \ |
2210 | FN(sk_select_reuseport), | 2228 | FN(sk_select_reuseport), \ |
2229 | FN(skb_ancestor_cgroup_id), | ||
2211 | 2230 | ||
2212 | /* integer value in 'imm' field of BPF_CALL instruction selects which helper | 2231 | /* integer value in 'imm' field of BPF_CALL instruction selects which helper |
2213 | * function eBPF program intends to call | 2232 | * function eBPF program intends to call |
diff --git a/net/core/filter.c b/net/core/filter.c index 22906b31d43f..15b9d2df92ca 100644 --- a/net/core/filter.c +++ b/net/core/filter.c | |||
@@ -3778,6 +3778,32 @@ static const struct bpf_func_proto bpf_skb_cgroup_id_proto = { | |||
3778 | .ret_type = RET_INTEGER, | 3778 | .ret_type = RET_INTEGER, |
3779 | .arg1_type = ARG_PTR_TO_CTX, | 3779 | .arg1_type = ARG_PTR_TO_CTX, |
3780 | }; | 3780 | }; |
3781 | |||
3782 | BPF_CALL_2(bpf_skb_ancestor_cgroup_id, const struct sk_buff *, skb, int, | ||
3783 | ancestor_level) | ||
3784 | { | ||
3785 | struct sock *sk = skb_to_full_sk(skb); | ||
3786 | struct cgroup *ancestor; | ||
3787 | struct cgroup *cgrp; | ||
3788 | |||
3789 | if (!sk || !sk_fullsock(sk)) | ||
3790 | return 0; | ||
3791 | |||
3792 | cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); | ||
3793 | ancestor = cgroup_ancestor(cgrp, ancestor_level); | ||
3794 | if (!ancestor) | ||
3795 | return 0; | ||
3796 | |||
3797 | return ancestor->kn->id.id; | ||
3798 | } | ||
3799 | |||
3800 | static const struct bpf_func_proto bpf_skb_ancestor_cgroup_id_proto = { | ||
3801 | .func = bpf_skb_ancestor_cgroup_id, | ||
3802 | .gpl_only = false, | ||
3803 | .ret_type = RET_INTEGER, | ||
3804 | .arg1_type = ARG_PTR_TO_CTX, | ||
3805 | .arg2_type = ARG_ANYTHING, | ||
3806 | }; | ||
3781 | #endif | 3807 | #endif |
3782 | 3808 | ||
3783 | static unsigned long bpf_xdp_copy(void *dst_buff, const void *src_buff, | 3809 | static unsigned long bpf_xdp_copy(void *dst_buff, const void *src_buff, |
@@ -4966,6 +4992,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) | |||
4966 | #ifdef CONFIG_SOCK_CGROUP_DATA | 4992 | #ifdef CONFIG_SOCK_CGROUP_DATA |
4967 | case BPF_FUNC_skb_cgroup_id: | 4993 | case BPF_FUNC_skb_cgroup_id: |
4968 | return &bpf_skb_cgroup_id_proto; | 4994 | return &bpf_skb_cgroup_id_proto; |
4995 | case BPF_FUNC_skb_ancestor_cgroup_id: | ||
4996 | return &bpf_skb_ancestor_cgroup_id_proto; | ||
4969 | #endif | 4997 | #endif |
4970 | default: | 4998 | default: |
4971 | return bpf_base_func_proto(func_id); | 4999 | return bpf_base_func_proto(func_id); |
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 3102a2a23c31..66917a4eba27 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h | |||
@@ -2093,6 +2093,24 @@ union bpf_attr { | |||
2093 | * Return | 2093 | * Return |
2094 | * The id is returned or 0 in case the id could not be retrieved. | 2094 | * The id is returned or 0 in case the id could not be retrieved. |
2095 | * | 2095 | * |
2096 | * u64 bpf_skb_ancestor_cgroup_id(struct sk_buff *skb, int ancestor_level) | ||
2097 | * Description | ||
2098 | * Return id of cgroup v2 that is ancestor of cgroup associated | ||
2099 | * with the *skb* at the *ancestor_level*. The root cgroup is at | ||
2100 | * *ancestor_level* zero and each step down the hierarchy | ||
2101 | * increments the level. If *ancestor_level* == level of cgroup | ||
2102 | * associated with *skb*, then return value will be same as that | ||
2103 | * of **bpf_skb_cgroup_id**\ (). | ||
2104 | * | ||
2105 | * The helper is useful to implement policies based on cgroups | ||
2106 | * that are upper in hierarchy than immediate cgroup associated | ||
2107 | * with *skb*. | ||
2108 | * | ||
2109 | * The format of returned id and helper limitations are same as in | ||
2110 | * **bpf_skb_cgroup_id**\ (). | ||
2111 | * Return | ||
2112 | * The id is returned or 0 in case the id could not be retrieved. | ||
2113 | * | ||
2096 | * u64 bpf_get_current_cgroup_id(void) | 2114 | * u64 bpf_get_current_cgroup_id(void) |
2097 | * Return | 2115 | * Return |
2098 | * A 64-bit integer containing the current cgroup id based | 2116 | * A 64-bit integer containing the current cgroup id based |
@@ -2207,7 +2225,8 @@ union bpf_attr { | |||
2207 | FN(skb_cgroup_id), \ | 2225 | FN(skb_cgroup_id), \ |
2208 | FN(get_current_cgroup_id), \ | 2226 | FN(get_current_cgroup_id), \ |
2209 | FN(get_local_storage), \ | 2227 | FN(get_local_storage), \ |
2210 | FN(sk_select_reuseport), | 2228 | FN(sk_select_reuseport), \ |
2229 | FN(skb_ancestor_cgroup_id), | ||
2211 | 2230 | ||
2212 | /* integer value in 'imm' field of BPF_CALL instruction selects which helper | 2231 | /* integer value in 'imm' field of BPF_CALL instruction selects which helper |
2213 | * function eBPF program intends to call | 2232 | * function eBPF program intends to call |
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index daed162043c2..fff7fb1285fc 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile | |||
@@ -34,7 +34,8 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test | |||
34 | test_btf_haskv.o test_btf_nokv.o test_sockmap_kern.o test_tunnel_kern.o \ | 34 | test_btf_haskv.o test_btf_nokv.o test_sockmap_kern.o test_tunnel_kern.o \ |
35 | test_get_stack_rawtp.o test_sockmap_kern.o test_sockhash_kern.o \ | 35 | test_get_stack_rawtp.o test_sockmap_kern.o test_sockhash_kern.o \ |
36 | test_lwt_seg6local.o sendmsg4_prog.o sendmsg6_prog.o test_lirc_mode2_kern.o \ | 36 | test_lwt_seg6local.o sendmsg4_prog.o sendmsg6_prog.o test_lirc_mode2_kern.o \ |
37 | get_cgroup_id_kern.o socket_cookie_prog.o test_select_reuseport_kern.o | 37 | get_cgroup_id_kern.o socket_cookie_prog.o test_select_reuseport_kern.o \ |
38 | test_skb_cgroup_id_kern.o | ||
38 | 39 | ||
39 | # Order correspond to 'make run_tests' order | 40 | # Order correspond to 'make run_tests' order |
40 | TEST_PROGS := test_kmod.sh \ | 41 | TEST_PROGS := test_kmod.sh \ |
@@ -45,10 +46,11 @@ TEST_PROGS := test_kmod.sh \ | |||
45 | test_sock_addr.sh \ | 46 | test_sock_addr.sh \ |
46 | test_tunnel.sh \ | 47 | test_tunnel.sh \ |
47 | test_lwt_seg6local.sh \ | 48 | test_lwt_seg6local.sh \ |
48 | test_lirc_mode2.sh | 49 | test_lirc_mode2.sh \ |
50 | test_skb_cgroup_id.sh | ||
49 | 51 | ||
50 | # Compile but not part of 'make run_tests' | 52 | # Compile but not part of 'make run_tests' |
51 | TEST_GEN_PROGS_EXTENDED = test_libbpf_open test_sock_addr | 53 | TEST_GEN_PROGS_EXTENDED = test_libbpf_open test_sock_addr test_skb_cgroup_id_user |
52 | 54 | ||
53 | include ../lib.mk | 55 | include ../lib.mk |
54 | 56 | ||
@@ -59,6 +61,7 @@ $(TEST_GEN_PROGS): $(BPFOBJ) | |||
59 | $(TEST_GEN_PROGS_EXTENDED): $(OUTPUT)/libbpf.a | 61 | $(TEST_GEN_PROGS_EXTENDED): $(OUTPUT)/libbpf.a |
60 | 62 | ||
61 | $(OUTPUT)/test_dev_cgroup: cgroup_helpers.c | 63 | $(OUTPUT)/test_dev_cgroup: cgroup_helpers.c |
64 | $(OUTPUT)/test_skb_cgroup_id_user: cgroup_helpers.c | ||
62 | $(OUTPUT)/test_sock: cgroup_helpers.c | 65 | $(OUTPUT)/test_sock: cgroup_helpers.c |
63 | $(OUTPUT)/test_sock_addr: cgroup_helpers.c | 66 | $(OUTPUT)/test_sock_addr: cgroup_helpers.c |
64 | $(OUTPUT)/test_socket_cookie: cgroup_helpers.c | 67 | $(OUTPUT)/test_socket_cookie: cgroup_helpers.c |
diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h index 5c32266c2c38..e4be7730222d 100644 --- a/tools/testing/selftests/bpf/bpf_helpers.h +++ b/tools/testing/selftests/bpf/bpf_helpers.h | |||
@@ -139,6 +139,10 @@ static unsigned long long (*bpf_get_current_cgroup_id)(void) = | |||
139 | (void *) BPF_FUNC_get_current_cgroup_id; | 139 | (void *) BPF_FUNC_get_current_cgroup_id; |
140 | static void *(*bpf_get_local_storage)(void *map, unsigned long long flags) = | 140 | static void *(*bpf_get_local_storage)(void *map, unsigned long long flags) = |
141 | (void *) BPF_FUNC_get_local_storage; | 141 | (void *) BPF_FUNC_get_local_storage; |
142 | static unsigned long long (*bpf_skb_cgroup_id)(void *ctx) = | ||
143 | (void *) BPF_FUNC_skb_cgroup_id; | ||
144 | static unsigned long long (*bpf_skb_ancestor_cgroup_id)(void *ctx, int level) = | ||
145 | (void *) BPF_FUNC_skb_ancestor_cgroup_id; | ||
142 | 146 | ||
143 | /* llvm builtin functions that eBPF C program may use to | 147 | /* llvm builtin functions that eBPF C program may use to |
144 | * emit BPF_LD_ABS and BPF_LD_IND instructions | 148 | * emit BPF_LD_ABS and BPF_LD_IND instructions |
diff --git a/tools/testing/selftests/bpf/test_skb_cgroup_id.sh b/tools/testing/selftests/bpf/test_skb_cgroup_id.sh new file mode 100755 index 000000000000..42544a969abc --- /dev/null +++ b/tools/testing/selftests/bpf/test_skb_cgroup_id.sh | |||
@@ -0,0 +1,62 @@ | |||
1 | #!/bin/sh | ||
2 | # SPDX-License-Identifier: GPL-2.0 | ||
3 | # Copyright (c) 2018 Facebook | ||
4 | |||
5 | set -eu | ||
6 | |||
7 | wait_for_ip() | ||
8 | { | ||
9 | local _i | ||
10 | echo -n "Wait for testing link-local IP to become available " | ||
11 | for _i in $(seq ${MAX_PING_TRIES}); do | ||
12 | echo -n "." | ||
13 | if ping -6 -q -c 1 -W 1 ff02::1%${TEST_IF} >/dev/null 2>&1; then | ||
14 | echo " OK" | ||
15 | return | ||
16 | fi | ||
17 | sleep 1 | ||
18 | done | ||
19 | echo 1>&2 "ERROR: Timeout waiting for test IP to become available." | ||
20 | exit 1 | ||
21 | } | ||
22 | |||
23 | setup() | ||
24 | { | ||
25 | # Create testing interfaces not to interfere with current environment. | ||
26 | ip link add dev ${TEST_IF} type veth peer name ${TEST_IF_PEER} | ||
27 | ip link set ${TEST_IF} up | ||
28 | ip link set ${TEST_IF_PEER} up | ||
29 | |||
30 | wait_for_ip | ||
31 | |||
32 | tc qdisc add dev ${TEST_IF} clsact | ||
33 | tc filter add dev ${TEST_IF} egress bpf obj ${BPF_PROG_OBJ} \ | ||
34 | sec ${BPF_PROG_SECTION} da | ||
35 | |||
36 | BPF_PROG_ID=$(tc filter show dev ${TEST_IF} egress | \ | ||
37 | awk '/ id / {sub(/.* id /, "", $0); print($1)}') | ||
38 | } | ||
39 | |||
40 | cleanup() | ||
41 | { | ||
42 | ip link del ${TEST_IF} 2>/dev/null || : | ||
43 | ip link del ${TEST_IF_PEER} 2>/dev/null || : | ||
44 | } | ||
45 | |||
46 | main() | ||
47 | { | ||
48 | trap cleanup EXIT 2 3 6 15 | ||
49 | setup | ||
50 | ${PROG} ${TEST_IF} ${BPF_PROG_ID} | ||
51 | } | ||
52 | |||
53 | DIR=$(dirname $0) | ||
54 | TEST_IF="test_cgid_1" | ||
55 | TEST_IF_PEER="test_cgid_2" | ||
56 | MAX_PING_TRIES=5 | ||
57 | BPF_PROG_OBJ="${DIR}/test_skb_cgroup_id_kern.o" | ||
58 | BPF_PROG_SECTION="cgroup_id_logger" | ||
59 | BPF_PROG_ID=0 | ||
60 | PROG="${DIR}/test_skb_cgroup_id_user" | ||
61 | |||
62 | main | ||
diff --git a/tools/testing/selftests/bpf/test_skb_cgroup_id_kern.c b/tools/testing/selftests/bpf/test_skb_cgroup_id_kern.c new file mode 100644 index 000000000000..68cf9829f5a7 --- /dev/null +++ b/tools/testing/selftests/bpf/test_skb_cgroup_id_kern.c | |||
@@ -0,0 +1,47 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (c) 2018 Facebook | ||
3 | |||
4 | #include <linux/bpf.h> | ||
5 | #include <linux/pkt_cls.h> | ||
6 | |||
7 | #include <string.h> | ||
8 | |||
9 | #include "bpf_helpers.h" | ||
10 | |||
11 | #define NUM_CGROUP_LEVELS 4 | ||
12 | |||
13 | struct bpf_map_def SEC("maps") cgroup_ids = { | ||
14 | .type = BPF_MAP_TYPE_ARRAY, | ||
15 | .key_size = sizeof(__u32), | ||
16 | .value_size = sizeof(__u64), | ||
17 | .max_entries = NUM_CGROUP_LEVELS, | ||
18 | }; | ||
19 | |||
20 | static __always_inline void log_nth_level(struct __sk_buff *skb, __u32 level) | ||
21 | { | ||
22 | __u64 id; | ||
23 | |||
24 | /* [1] &level passed to external function that may change it, it's | ||
25 | * incompatible with loop unroll. | ||
26 | */ | ||
27 | id = bpf_skb_ancestor_cgroup_id(skb, level); | ||
28 | bpf_map_update_elem(&cgroup_ids, &level, &id, 0); | ||
29 | } | ||
30 | |||
31 | SEC("cgroup_id_logger") | ||
32 | int log_cgroup_id(struct __sk_buff *skb) | ||
33 | { | ||
34 | /* Loop unroll can't be used here due to [1]. Unrolling manually. | ||
35 | * Number of calls should be in sync with NUM_CGROUP_LEVELS. | ||
36 | */ | ||
37 | log_nth_level(skb, 0); | ||
38 | log_nth_level(skb, 1); | ||
39 | log_nth_level(skb, 2); | ||
40 | log_nth_level(skb, 3); | ||
41 | |||
42 | return TC_ACT_OK; | ||
43 | } | ||
44 | |||
45 | int _version SEC("version") = 1; | ||
46 | |||
47 | char _license[] SEC("license") = "GPL"; | ||
diff --git a/tools/testing/selftests/bpf/test_skb_cgroup_id_user.c b/tools/testing/selftests/bpf/test_skb_cgroup_id_user.c new file mode 100644 index 000000000000..c121cc59f314 --- /dev/null +++ b/tools/testing/selftests/bpf/test_skb_cgroup_id_user.c | |||
@@ -0,0 +1,187 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (c) 2018 Facebook | ||
3 | |||
4 | #include <stdlib.h> | ||
5 | #include <string.h> | ||
6 | #include <unistd.h> | ||
7 | |||
8 | #include <arpa/inet.h> | ||
9 | #include <net/if.h> | ||
10 | #include <netinet/in.h> | ||
11 | #include <sys/socket.h> | ||
12 | #include <sys/types.h> | ||
13 | |||
14 | |||
15 | #include <bpf/bpf.h> | ||
16 | #include <bpf/libbpf.h> | ||
17 | |||
18 | #include "bpf_rlimit.h" | ||
19 | #include "cgroup_helpers.h" | ||
20 | |||
21 | #define CGROUP_PATH "/skb_cgroup_test" | ||
22 | #define NUM_CGROUP_LEVELS 4 | ||
23 | |||
24 | /* RFC 4291, Section 2.7.1 */ | ||
25 | #define LINKLOCAL_MULTICAST "ff02::1" | ||
26 | |||
27 | static int mk_dst_addr(const char *ip, const char *iface, | ||
28 | struct sockaddr_in6 *dst) | ||
29 | { | ||
30 | memset(dst, 0, sizeof(*dst)); | ||
31 | |||
32 | dst->sin6_family = AF_INET6; | ||
33 | dst->sin6_port = htons(1025); | ||
34 | |||
35 | if (inet_pton(AF_INET6, ip, &dst->sin6_addr) != 1) { | ||
36 | log_err("Invalid IPv6: %s", ip); | ||
37 | return -1; | ||
38 | } | ||
39 | |||
40 | dst->sin6_scope_id = if_nametoindex(iface); | ||
41 | if (!dst->sin6_scope_id) { | ||
42 | log_err("Failed to get index of iface: %s", iface); | ||
43 | return -1; | ||
44 | } | ||
45 | |||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | static int send_packet(const char *iface) | ||
50 | { | ||
51 | struct sockaddr_in6 dst; | ||
52 | char msg[] = "msg"; | ||
53 | int err = 0; | ||
54 | int fd = -1; | ||
55 | |||
56 | if (mk_dst_addr(LINKLOCAL_MULTICAST, iface, &dst)) | ||
57 | goto err; | ||
58 | |||
59 | fd = socket(AF_INET6, SOCK_DGRAM, 0); | ||
60 | if (fd == -1) { | ||
61 | log_err("Failed to create UDP socket"); | ||
62 | goto err; | ||
63 | } | ||
64 | |||
65 | if (sendto(fd, &msg, sizeof(msg), 0, (const struct sockaddr *)&dst, | ||
66 | sizeof(dst)) == -1) { | ||
67 | log_err("Failed to send datagram"); | ||
68 | goto err; | ||
69 | } | ||
70 | |||
71 | goto out; | ||
72 | err: | ||
73 | err = -1; | ||
74 | out: | ||
75 | if (fd >= 0) | ||
76 | close(fd); | ||
77 | return err; | ||
78 | } | ||
79 | |||
80 | int get_map_fd_by_prog_id(int prog_id) | ||
81 | { | ||
82 | struct bpf_prog_info info = {}; | ||
83 | __u32 info_len = sizeof(info); | ||
84 | __u32 map_ids[1]; | ||
85 | int prog_fd = -1; | ||
86 | int map_fd = -1; | ||
87 | |||
88 | prog_fd = bpf_prog_get_fd_by_id(prog_id); | ||
89 | if (prog_fd < 0) { | ||
90 | log_err("Failed to get fd by prog id %d", prog_id); | ||
91 | goto err; | ||
92 | } | ||
93 | |||
94 | info.nr_map_ids = 1; | ||
95 | info.map_ids = (__u64) (unsigned long) map_ids; | ||
96 | |||
97 | if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) { | ||
98 | log_err("Failed to get info by prog fd %d", prog_fd); | ||
99 | goto err; | ||
100 | } | ||
101 | |||
102 | if (!info.nr_map_ids) { | ||
103 | log_err("No maps found for prog fd %d", prog_fd); | ||
104 | goto err; | ||
105 | } | ||
106 | |||
107 | map_fd = bpf_map_get_fd_by_id(map_ids[0]); | ||
108 | if (map_fd < 0) | ||
109 | log_err("Failed to get fd by map id %d", map_ids[0]); | ||
110 | err: | ||
111 | if (prog_fd >= 0) | ||
112 | close(prog_fd); | ||
113 | return map_fd; | ||
114 | } | ||
115 | |||
116 | int check_ancestor_cgroup_ids(int prog_id) | ||
117 | { | ||
118 | __u64 actual_ids[NUM_CGROUP_LEVELS], expected_ids[NUM_CGROUP_LEVELS]; | ||
119 | __u32 level; | ||
120 | int err = 0; | ||
121 | int map_fd; | ||
122 | |||
123 | expected_ids[0] = 0x100000001; /* root cgroup */ | ||
124 | expected_ids[1] = get_cgroup_id(""); | ||
125 | expected_ids[2] = get_cgroup_id(CGROUP_PATH); | ||
126 | expected_ids[3] = 0; /* non-existent cgroup */ | ||
127 | |||
128 | map_fd = get_map_fd_by_prog_id(prog_id); | ||
129 | if (map_fd < 0) | ||
130 | goto err; | ||
131 | |||
132 | for (level = 0; level < NUM_CGROUP_LEVELS; ++level) { | ||
133 | if (bpf_map_lookup_elem(map_fd, &level, &actual_ids[level])) { | ||
134 | log_err("Failed to lookup key %d", level); | ||
135 | goto err; | ||
136 | } | ||
137 | if (actual_ids[level] != expected_ids[level]) { | ||
138 | log_err("%llx (actual) != %llx (expected), level: %u\n", | ||
139 | actual_ids[level], expected_ids[level], level); | ||
140 | goto err; | ||
141 | } | ||
142 | } | ||
143 | |||
144 | goto out; | ||
145 | err: | ||
146 | err = -1; | ||
147 | out: | ||
148 | if (map_fd >= 0) | ||
149 | close(map_fd); | ||
150 | return err; | ||
151 | } | ||
152 | |||
153 | int main(int argc, char **argv) | ||
154 | { | ||
155 | int cgfd = -1; | ||
156 | int err = 0; | ||
157 | |||
158 | if (argc < 3) { | ||
159 | fprintf(stderr, "Usage: %s iface prog_id\n", argv[0]); | ||
160 | exit(EXIT_FAILURE); | ||
161 | } | ||
162 | |||
163 | if (setup_cgroup_environment()) | ||
164 | goto err; | ||
165 | |||
166 | cgfd = create_and_get_cgroup(CGROUP_PATH); | ||
167 | if (!cgfd) | ||
168 | goto err; | ||
169 | |||
170 | if (join_cgroup(CGROUP_PATH)) | ||
171 | goto err; | ||
172 | |||
173 | if (send_packet(argv[1])) | ||
174 | goto err; | ||
175 | |||
176 | if (check_ancestor_cgroup_ids(atoi(argv[2]))) | ||
177 | goto err; | ||
178 | |||
179 | goto out; | ||
180 | err: | ||
181 | err = -1; | ||
182 | out: | ||
183 | close(cgfd); | ||
184 | cleanup_cgroup_environment(); | ||
185 | printf("[%s]\n", err ? "FAIL" : "PASS"); | ||
186 | return err; | ||
187 | } | ||