aboutsummaryrefslogtreecommitdiffstats
path: root/samples/bpf
diff options
context:
space:
mode:
authorMartin KaFai Lau <kafai@fb.com>2016-06-30 13:28:45 -0400
committerDavid S. Miller <davem@davemloft.net>2016-07-01 16:32:13 -0400
commita3f74617340b598dbc7eb5b68d4ed53b4a70f5eb (patch)
treece74217c60eeda9e99a8b5e2babcf0847ab3b24f /samples/bpf
parent4a482f34afcc162d8456f449b137ec2a95be60d8 (diff)
cgroup: bpf: Add an example to do cgroup checking in BPF
test_cgrp2_array_pin.c: A userland program that creates a bpf_map (BPF_MAP_TYPE_GROUP_ARRAY), pouplates/updates it with a cgroup2's backed fd and pins it to a bpf-fs's file. The pinned file can be loaded by tc and then used by the bpf prog later. This program can also update an existing pinned array and it could be useful for debugging/testing purpose. test_cgrp2_tc_kern.c: A bpf prog which should be loaded by tc. It is to demonstrate the usage of bpf_skb_in_cgroup. test_cgrp2_tc.sh: A script that glues the test_cgrp2_array_pin.c and test_cgrp2_tc_kern.c together. The idea is like: 1. Load the test_cgrp2_tc_kern.o by tc 2. Use test_cgrp2_array_pin.c to populate a BPF_MAP_TYPE_CGROUP_ARRAY with a cgroup fd 3. Do a 'ping -6 ff02::1%ve' to ensure the packet has been dropped because of a match on the cgroup Most of the lines in test_cgrp2_tc.sh is the boilerplate to setup the cgroup/bpf-fs/net-devices/netns...etc. It is not bulletproof on errors but should work well enough and give enough debug info if things did not go well. Signed-off-by: Martin KaFai Lau <kafai@fb.com> Cc: Alexei Starovoitov <ast@fb.com> Cc: Daniel Borkmann <daniel@iogearbox.net> Cc: Tejun Heo <tj@kernel.org> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'samples/bpf')
-rw-r--r--samples/bpf/Makefile3
-rw-r--r--samples/bpf/bpf_helpers.h2
-rw-r--r--samples/bpf/test_cgrp2_array_pin.c109
-rwxr-xr-xsamples/bpf/test_cgrp2_tc.sh184
-rw-r--r--samples/bpf/test_cgrp2_tc_kern.c69
5 files changed, 367 insertions, 0 deletions
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 0bf2478cb7df..a98b780e974c 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -20,6 +20,7 @@ hostprogs-y += offwaketime
20hostprogs-y += spintest 20hostprogs-y += spintest
21hostprogs-y += map_perf_test 21hostprogs-y += map_perf_test
22hostprogs-y += test_overhead 22hostprogs-y += test_overhead
23hostprogs-y += test_cgrp2_array_pin
23 24
24test_verifier-objs := test_verifier.o libbpf.o 25test_verifier-objs := test_verifier.o libbpf.o
25test_maps-objs := test_maps.o libbpf.o 26test_maps-objs := test_maps.o libbpf.o
@@ -40,6 +41,7 @@ offwaketime-objs := bpf_load.o libbpf.o offwaketime_user.o
40spintest-objs := bpf_load.o libbpf.o spintest_user.o 41spintest-objs := bpf_load.o libbpf.o spintest_user.o
41map_perf_test-objs := bpf_load.o libbpf.o map_perf_test_user.o 42map_perf_test-objs := bpf_load.o libbpf.o map_perf_test_user.o
42test_overhead-objs := bpf_load.o libbpf.o test_overhead_user.o 43test_overhead-objs := bpf_load.o libbpf.o test_overhead_user.o
44test_cgrp2_array_pin-objs := libbpf.o test_cgrp2_array_pin.o
43 45
44# Tell kbuild to always build the programs 46# Tell kbuild to always build the programs
45always := $(hostprogs-y) 47always := $(hostprogs-y)
@@ -61,6 +63,7 @@ always += map_perf_test_kern.o
61always += test_overhead_tp_kern.o 63always += test_overhead_tp_kern.o
62always += test_overhead_kprobe_kern.o 64always += test_overhead_kprobe_kern.o
63always += parse_varlen.o parse_simple.o parse_ldabs.o 65always += parse_varlen.o parse_simple.o parse_ldabs.o
66always += test_cgrp2_tc_kern.o
64 67
65HOSTCFLAGS += -I$(objtree)/usr/include 68HOSTCFLAGS += -I$(objtree)/usr/include
66 69
diff --git a/samples/bpf/bpf_helpers.h b/samples/bpf/bpf_helpers.h
index 7904a2a493de..84e3fd919a06 100644
--- a/samples/bpf/bpf_helpers.h
+++ b/samples/bpf/bpf_helpers.h
@@ -70,6 +70,8 @@ static int (*bpf_l3_csum_replace)(void *ctx, int off, int from, int to, int flag
70 (void *) BPF_FUNC_l3_csum_replace; 70 (void *) BPF_FUNC_l3_csum_replace;
71static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flags) = 71static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flags) =
72 (void *) BPF_FUNC_l4_csum_replace; 72 (void *) BPF_FUNC_l4_csum_replace;
73static int (*bpf_skb_in_cgroup)(void *ctx, void *map, int index) =
74 (void *) BPF_FUNC_skb_in_cgroup;
73 75
74#if defined(__x86_64__) 76#if defined(__x86_64__)
75 77
diff --git a/samples/bpf/test_cgrp2_array_pin.c b/samples/bpf/test_cgrp2_array_pin.c
new file mode 100644
index 000000000000..70e86f7be69d
--- /dev/null
+++ b/samples/bpf/test_cgrp2_array_pin.c
@@ -0,0 +1,109 @@
1/* Copyright (c) 2016 Facebook
2 *
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of version 2 of the GNU General Public
5 * License as published by the Free Software Foundation.
6 */
7#include <linux/unistd.h>
8#include <linux/bpf.h>
9
10#include <stdio.h>
11#include <stdint.h>
12#include <unistd.h>
13#include <string.h>
14#include <errno.h>
15#include <fcntl.h>
16
17#include "libbpf.h"
18
19static void usage(void)
20{
21 printf("Usage: test_cgrp2_array_pin [...]\n");
22 printf(" -F <file> File to pin an BPF cgroup array\n");
23 printf(" -U <file> Update an already pinned BPF cgroup array\n");
24 printf(" -v <value> Full path of the cgroup2\n");
25 printf(" -h Display this help\n");
26}
27
28int main(int argc, char **argv)
29{
30 const char *pinned_file = NULL, *cg2 = NULL;
31 int create_array = 1;
32 int array_key = 0;
33 int array_fd = -1;
34 int cg2_fd = -1;
35 int ret = -1;
36 int opt;
37
38 while ((opt = getopt(argc, argv, "F:U:v:")) != -1) {
39 switch (opt) {
40 /* General args */
41 case 'F':
42 pinned_file = optarg;
43 break;
44 case 'U':
45 pinned_file = optarg;
46 create_array = 0;
47 break;
48 case 'v':
49 cg2 = optarg;
50 break;
51 default:
52 usage();
53 goto out;
54 }
55 }
56
57 if (!cg2 || !pinned_file) {
58 usage();
59 goto out;
60 }
61
62 cg2_fd = open(cg2, O_RDONLY);
63 if (cg2_fd < 0) {
64 fprintf(stderr, "open(%s,...): %s(%d)\n",
65 cg2, strerror(errno), errno);
66 goto out;
67 }
68
69 if (create_array) {
70 array_fd = bpf_create_map(BPF_MAP_TYPE_CGROUP_ARRAY,
71 sizeof(uint32_t), sizeof(uint32_t),
72 1, 0);
73 if (array_fd < 0) {
74 fprintf(stderr,
75 "bpf_create_map(BPF_MAP_TYPE_CGROUP_ARRAY,...): %s(%d)\n",
76 strerror(errno), errno);
77 goto out;
78 }
79 } else {
80 array_fd = bpf_obj_get(pinned_file);
81 if (array_fd < 0) {
82 fprintf(stderr, "bpf_obj_get(%s): %s(%d)\n",
83 pinned_file, strerror(errno), errno);
84 goto out;
85 }
86 }
87
88 ret = bpf_update_elem(array_fd, &array_key, &cg2_fd, 0);
89 if (ret) {
90 perror("bpf_update_elem");
91 goto out;
92 }
93
94 if (create_array) {
95 ret = bpf_obj_pin(array_fd, pinned_file);
96 if (ret) {
97 fprintf(stderr, "bpf_obj_pin(..., %s): %s(%d)\n",
98 pinned_file, strerror(errno), errno);
99 goto out;
100 }
101 }
102
103out:
104 if (array_fd != -1)
105 close(array_fd);
106 if (cg2_fd != -1)
107 close(cg2_fd);
108 return ret;
109}
diff --git a/samples/bpf/test_cgrp2_tc.sh b/samples/bpf/test_cgrp2_tc.sh
new file mode 100755
index 000000000000..0b119eeaf85c
--- /dev/null
+++ b/samples/bpf/test_cgrp2_tc.sh
@@ -0,0 +1,184 @@
1#!/bin/bash
2
3MY_DIR=$(dirname $0)
4# Details on the bpf prog
5BPF_CGRP2_ARRAY_NAME='test_cgrp2_array_pin'
6BPF_PROG="$MY_DIR/test_cgrp2_tc_kern.o"
7BPF_SECTION='filter'
8
9[ -z "$TC" ] && TC='tc'
10[ -z "$IP" ] && IP='ip'
11
12# Names of the veth interface, net namespace...etc.
13HOST_IFC='ve'
14NS_IFC='vens'
15NS='ns'
16
17find_mnt() {
18 cat /proc/mounts | \
19 awk '{ if ($3 == "'$1'" && mnt == "") { mnt = $2 }} END { print mnt }'
20}
21
22# Init cgroup2 vars
23init_cgrp2_vars() {
24 CGRP2_ROOT=$(find_mnt cgroup2)
25 if [ -z "$CGRP2_ROOT" ]
26 then
27 CGRP2_ROOT='/mnt/cgroup2'
28 MOUNT_CGRP2="yes"
29 fi
30 CGRP2_TC="$CGRP2_ROOT/tc"
31 CGRP2_TC_LEAF="$CGRP2_TC/leaf"
32}
33
34# Init bpf fs vars
35init_bpf_fs_vars() {
36 local bpf_fs_root=$(find_mnt bpf)
37 [ -n "$bpf_fs_root" ] || return -1
38 BPF_FS_TC_SHARE="$bpf_fs_root/tc/globals"
39}
40
41setup_cgrp2() {
42 case $1 in
43 start)
44 if [ "$MOUNT_CGRP2" == 'yes' ]
45 then
46 [ -d $CGRP2_ROOT ] || mkdir -p $CGRP2_ROOT
47 mount -t cgroup2 none $CGRP2_ROOT || return $?
48 fi
49 mkdir -p $CGRP2_TC_LEAF
50 ;;
51 *)
52 rmdir $CGRP2_TC_LEAF && rmdir $CGRP2_TC
53 [ "$MOUNT_CGRP2" == 'yes' ] && umount $CGRP2_ROOT
54 ;;
55 esac
56}
57
58setup_bpf_cgrp2_array() {
59 local bpf_cgrp2_array="$BPF_FS_TC_SHARE/$BPF_CGRP2_ARRAY_NAME"
60 case $1 in
61 start)
62 $MY_DIR/test_cgrp2_array_pin -U $bpf_cgrp2_array -v $CGRP2_TC
63 ;;
64 *)
65 [ -d "$BPF_FS_TC_SHARE" ] && rm -f $bpf_cgrp2_array
66 ;;
67 esac
68}
69
70setup_net() {
71 case $1 in
72 start)
73 $IP link add $HOST_IFC type veth peer name $NS_IFC || return $?
74 $IP link set dev $HOST_IFC up || return $?
75 sysctl -q net.ipv6.conf.$HOST_IFC.accept_dad=0
76
77 $IP netns add ns || return $?
78 $IP link set dev $NS_IFC netns ns || return $?
79 $IP -n $NS link set dev $NS_IFC up || return $?
80 $IP netns exec $NS sysctl -q net.ipv6.conf.$NS_IFC.accept_dad=0
81 $TC qdisc add dev $HOST_IFC clsact || return $?
82 $TC filter add dev $HOST_IFC egress bpf da obj $BPF_PROG sec $BPF_SECTION || return $?
83 ;;
84 *)
85 $IP netns del $NS
86 $IP link del $HOST_IFC
87 ;;
88 esac
89}
90
91run_in_cgrp() {
92 # Fork another bash and move it under the specified cgroup.
93 # It makes the cgroup cleanup easier at the end of the test.
94 cmd='echo $$ > '
95 cmd="$cmd $1/cgroup.procs; exec $2"
96 bash -c "$cmd"
97}
98
99do_test() {
100 run_in_cgrp $CGRP2_TC_LEAF "ping -6 -c3 ff02::1%$HOST_IFC >& /dev/null"
101 local dropped=$($TC -s qdisc show dev $HOST_IFC | tail -3 | \
102 awk '/drop/{print substr($7, 0, index($7, ",")-1)}')
103 if [[ $dropped -eq 0 ]]
104 then
105 echo "FAIL"
106 return 1
107 else
108 echo "Successfully filtered $dropped packets"
109 return 0
110 fi
111}
112
113do_exit() {
114 if [ "$DEBUG" == "yes" ] && [ "$MODE" != 'cleanuponly' ]
115 then
116 echo "------ DEBUG ------"
117 echo "mount: "; mount | egrep '(cgroup2|bpf)'; echo
118 echo "$CGRP2_TC_LEAF: "; ls -l $CGRP2_TC_LEAF; echo
119 if [ -d "$BPF_FS_TC_SHARE" ]
120 then
121 echo "$BPF_FS_TC_SHARE: "; ls -l $BPF_FS_TC_SHARE; echo
122 fi
123 echo "Host net:"
124 $IP netns
125 $IP link show dev $HOST_IFC
126 $IP -6 a show dev $HOST_IFC
127 $TC -s qdisc show dev $HOST_IFC
128 echo
129 echo "$NS net:"
130 $IP -n $NS link show dev $NS_IFC
131 $IP -n $NS -6 link show dev $NS_IFC
132 echo "------ DEBUG ------"
133 echo
134 fi
135
136 if [ "$MODE" != 'nocleanup' ]
137 then
138 setup_net stop
139 setup_bpf_cgrp2_array stop
140 setup_cgrp2 stop
141 fi
142}
143
144init_cgrp2_vars
145init_bpf_fs_vars
146
147while [[ $# -ge 1 ]]
148do
149 a="$1"
150 case $a in
151 debug)
152 DEBUG='yes'
153 shift 1
154 ;;
155 cleanup-only)
156 MODE='cleanuponly'
157 shift 1
158 ;;
159 no-cleanup)
160 MODE='nocleanup'
161 shift 1
162 ;;
163 *)
164 echo "test_cgrp2_tc [debug] [cleanup-only | no-cleanup]"
165 echo " debug: Print cgrp and network setup details at the end of the test"
166 echo " cleanup-only: Try to cleanup things from last test. No test will be run"
167 echo " no-cleanup: Run the test but don't do cleanup at the end"
168 echo "[Note: If no arg is given, it will run the test and do cleanup at the end]"
169 echo
170 exit -1
171 ;;
172 esac
173done
174
175trap do_exit 0
176
177[ "$MODE" == 'cleanuponly' ] && exit
178
179setup_cgrp2 start || exit $?
180setup_net start || exit $?
181init_bpf_fs_vars || exit $?
182setup_bpf_cgrp2_array start || exit $?
183do_test
184echo
diff --git a/samples/bpf/test_cgrp2_tc_kern.c b/samples/bpf/test_cgrp2_tc_kern.c
new file mode 100644
index 000000000000..2732c37c8d5b
--- /dev/null
+++ b/samples/bpf/test_cgrp2_tc_kern.c
@@ -0,0 +1,69 @@
1/* Copyright (c) 2016 Facebook
2 *
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of version 2 of the GNU General Public
5 * License as published by the Free Software Foundation.
6 */
7#include <uapi/linux/if_ether.h>
8#include <uapi/linux/in6.h>
9#include <uapi/linux/ipv6.h>
10#include <uapi/linux/pkt_cls.h>
11#include <uapi/linux/bpf.h>
12#include "bpf_helpers.h"
13
14/* copy of 'struct ethhdr' without __packed */
15struct eth_hdr {
16 unsigned char h_dest[ETH_ALEN];
17 unsigned char h_source[ETH_ALEN];
18 unsigned short h_proto;
19};
20
21#define PIN_GLOBAL_NS 2
22struct bpf_elf_map {
23 __u32 type;
24 __u32 size_key;
25 __u32 size_value;
26 __u32 max_elem;
27 __u32 flags;
28 __u32 id;
29 __u32 pinning;
30};
31
32struct bpf_elf_map SEC("maps") test_cgrp2_array_pin = {
33 .type = BPF_MAP_TYPE_CGROUP_ARRAY,
34 .size_key = sizeof(uint32_t),
35 .size_value = sizeof(uint32_t),
36 .pinning = PIN_GLOBAL_NS,
37 .max_elem = 1,
38};
39
40SEC("filter")
41int handle_egress(struct __sk_buff *skb)
42{
43 void *data = (void *)(long)skb->data;
44 struct eth_hdr *eth = data;
45 struct ipv6hdr *ip6h = data + sizeof(*eth);
46 void *data_end = (void *)(long)skb->data_end;
47 char dont_care_msg[] = "dont care %04x %d\n";
48 char pass_msg[] = "pass\n";
49 char reject_msg[] = "reject\n";
50
51 /* single length check */
52 if (data + sizeof(*eth) + sizeof(*ip6h) > data_end)
53 return TC_ACT_OK;
54
55 if (eth->h_proto != htons(ETH_P_IPV6) ||
56 ip6h->nexthdr != IPPROTO_ICMPV6) {
57 bpf_trace_printk(dont_care_msg, sizeof(dont_care_msg),
58 eth->h_proto, ip6h->nexthdr);
59 return TC_ACT_OK;
60 } else if (bpf_skb_in_cgroup(skb, &test_cgrp2_array_pin, 0) != 1) {
61 bpf_trace_printk(pass_msg, sizeof(pass_msg));
62 return TC_ACT_OK;
63 } else {
64 bpf_trace_printk(reject_msg, sizeof(reject_msg));
65 return TC_ACT_SHOT;
66 }
67}
68
69char _license[] SEC("license") = "GPL";