aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Borkmann <daniel@iogearbox.net>2018-08-12 19:02:40 -0400
committerDaniel Borkmann <daniel@iogearbox.net>2018-08-12 19:02:41 -0400
commit2ce3206b9eb3943de09f3bf4ec9134568420d8b9 (patch)
tree7903c536230dc9665d7ebbae2166f1e39cfebd72
parente8d2bec0457962e8f348a9a3627b398f7fe5c5fc (diff)
parent5ecd8c22739b9a5f6d6431234decd912aa3f48ad (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.h30
-rw-r--r--include/uapi/linux/bpf.h21
-rw-r--r--net/core/filter.c28
-rw-r--r--tools/include/uapi/linux/bpf.h21
-rw-r--r--tools/testing/selftests/bpf/Makefile9
-rw-r--r--tools/testing/selftests/bpf/bpf_helpers.h4
-rwxr-xr-xtools/testing/selftests/bpf/test_skb_cgroup_id.sh62
-rw-r--r--tools/testing/selftests/bpf/test_skb_cgroup_id_kern.c47
-rw-r--r--tools/testing/selftests/bpf/test_skb_cgroup_id_user.c187
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 */
567static 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
3782BPF_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
3800static 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
3783static unsigned long bpf_xdp_copy(void *dst_buff, const void *src_buff, 3809static 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
40TEST_PROGS := test_kmod.sh \ 41TEST_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'
51TEST_GEN_PROGS_EXTENDED = test_libbpf_open test_sock_addr 53TEST_GEN_PROGS_EXTENDED = test_libbpf_open test_sock_addr test_skb_cgroup_id_user
52 54
53include ../lib.mk 55include ../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;
140static void *(*bpf_get_local_storage)(void *map, unsigned long long flags) = 140static 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;
142static unsigned long long (*bpf_skb_cgroup_id)(void *ctx) =
143 (void *) BPF_FUNC_skb_cgroup_id;
144static 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
5set -eu
6
7wait_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
23setup()
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
40cleanup()
41{
42 ip link del ${TEST_IF} 2>/dev/null || :
43 ip link del ${TEST_IF_PEER} 2>/dev/null || :
44}
45
46main()
47{
48 trap cleanup EXIT 2 3 6 15
49 setup
50 ${PROG} ${TEST_IF} ${BPF_PROG_ID}
51}
52
53DIR=$(dirname $0)
54TEST_IF="test_cgid_1"
55TEST_IF_PEER="test_cgid_2"
56MAX_PING_TRIES=5
57BPF_PROG_OBJ="${DIR}/test_skb_cgroup_id_kern.o"
58BPF_PROG_SECTION="cgroup_id_logger"
59BPF_PROG_ID=0
60PROG="${DIR}/test_skb_cgroup_id_user"
61
62main
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
13struct 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
20static __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
31SEC("cgroup_id_logger")
32int 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
45int _version SEC("version") = 1;
46
47char _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
27static 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
49static 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;
72err:
73 err = -1;
74out:
75 if (fd >= 0)
76 close(fd);
77 return err;
78}
79
80int 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]);
110err:
111 if (prog_fd >= 0)
112 close(prog_fd);
113 return map_fd;
114}
115
116int 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;
145err:
146 err = -1;
147out:
148 if (map_fd >= 0)
149 close(map_fd);
150 return err;
151}
152
153int 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;
180err:
181 err = -1;
182out:
183 close(cgfd);
184 cleanup_cgroup_environment();
185 printf("[%s]\n", err ? "FAIL" : "PASS");
186 return err;
187}