aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexei Starovoitov <ast@plumgrid.com>2014-05-08 17:10:52 -0400
committerDavid S. Miller <davem@davemloft.net>2014-05-12 00:23:55 -0400
commit64a8946b447e418b4283c3573ef397980cca0cd8 (patch)
treeaf67f72649e1e3ac18a74bdb425ad3f48f611875
parent9739eef13c926645fbf88bcb77e66442fa75d688 (diff)
net: filter: BPF testsuite
The testsuite covers classic and internal BPF instructions. It is particularly useful for JIT compiler developers. Adds to "net" selftest target. The testsuite can be used as a set of micro-benchmarks. It measures execution time of each BPF program in nsec. This patch adds core framework. Signed-off-by: Alexei Starovoitov <ast@plumgrid.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--lib/Kconfig.debug13
-rw-r--r--lib/Makefile1
-rw-r--r--lib/test_bpf.c322
-rw-r--r--tools/testing/selftests/net/Makefile8
4 files changed, 343 insertions, 1 deletions
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 819ac51202c0..423ca319a5f8 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1620,6 +1620,19 @@ config TEST_USER_COPY
1620 1620
1621 If unsure, say N. 1621 If unsure, say N.
1622 1622
1623config TEST_BPF
1624 tristate "Test BPF filter functionality"
1625 default n
1626 depends on m
1627 help
1628 This builds the "test_bpf" module that runs various test vectors
1629 against the BPF interpreter or BPF JIT compiler depending on the
1630 current setting. This is in particular useful for BPF JIT compiler
1631 development, but also to run regression tests against changes in
1632 the interpreter code.
1633
1634 If unsure, say N.
1635
1623source "samples/Kconfig" 1636source "samples/Kconfig"
1624 1637
1625source "lib/Kconfig.kgdb" 1638source "lib/Kconfig.kgdb"
diff --git a/lib/Makefile b/lib/Makefile
index 0cd7b68e1382..b2be1ef1e8ec 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -33,6 +33,7 @@ obj-y += kstrtox.o
33obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o 33obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o
34obj-$(CONFIG_TEST_MODULE) += test_module.o 34obj-$(CONFIG_TEST_MODULE) += test_module.o
35obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o 35obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o
36obj-$(CONFIG_TEST_BPF) += test_bpf.o
36 37
37ifeq ($(CONFIG_DEBUG_KOBJECT),y) 38ifeq ($(CONFIG_DEBUG_KOBJECT),y)
38CFLAGS_kobject.o += -DDEBUG 39CFLAGS_kobject.o += -DDEBUG
diff --git a/lib/test_bpf.c b/lib/test_bpf.c
new file mode 100644
index 000000000000..9f25dc127330
--- /dev/null
+++ b/lib/test_bpf.c
@@ -0,0 +1,322 @@
1/*
2 * Testsuite for BPF interpreter and BPF JIT compiler
3 *
4 * Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of version 2 of the GNU General Public
8 * License as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 */
15
16#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
17
18#include <linux/init.h>
19#include <linux/module.h>
20#include <linux/filter.h>
21#include <linux/skbuff.h>
22#include <linux/netdevice.h>
23#include <linux/if_vlan.h>
24
25#define MAX_SUBTESTS 3
26#define MAX_DATA 128
27#define MAX_INSNS 512
28#define MAX_K 0xffffFFFF
29
30/* define few constants used to init test 'skb' */
31#define SKB_TYPE 3
32#define SKB_MARK 0x1234aaaa
33#define SKB_HASH 0x1234aaab
34#define SKB_QUEUE_MAP 123
35#define SKB_VLAN_TCI 0xffff
36#define SKB_DEV_IFINDEX 577
37#define SKB_DEV_TYPE 588
38
39/* redefine REGs to make tests less verbose */
40#define R0 BPF_REG_0
41#define R1 BPF_REG_1
42#define R2 BPF_REG_2
43#define R3 BPF_REG_3
44#define R4 BPF_REG_4
45#define R5 BPF_REG_5
46#define R6 BPF_REG_6
47#define R7 BPF_REG_7
48#define R8 BPF_REG_8
49#define R9 BPF_REG_9
50#define R10 BPF_REG_10
51
52struct bpf_test {
53 const char *descr;
54 union {
55 struct sock_filter insns[MAX_INSNS];
56 struct sock_filter_int insns_int[MAX_INSNS];
57 };
58 enum {
59 NO_DATA,
60 EXPECTED_FAIL,
61 SKB,
62 SKB_INT
63 } data_type;
64 __u8 data[MAX_DATA];
65 struct {
66 int data_size;
67 __u32 result;
68 } test[MAX_SUBTESTS];
69};
70
71static struct bpf_test tests[] = {
72 {
73 "TAX",
74 .insns = {
75 BPF_STMT(BPF_LD | BPF_IMM, 1),
76 BPF_STMT(BPF_MISC | BPF_TAX, 0),
77 BPF_STMT(BPF_LD | BPF_IMM, 2),
78 BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
79 BPF_STMT(BPF_ALU | BPF_NEG, 0), /* A == -3 */
80 BPF_STMT(BPF_MISC | BPF_TAX, 0),
81 BPF_STMT(BPF_LD | BPF_LEN, 0),
82 BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
83 BPF_STMT(BPF_MISC | BPF_TAX, 0), /* X == len - 3 */
84 BPF_STMT(BPF_LD | BPF_B | BPF_IND, 1),
85 BPF_STMT(BPF_RET | BPF_A, 0)
86 },
87 SKB,
88 { 10, 20, 30, 40, 50 },
89 { { 2, 10 }, { 3, 20 }, { 4, 30 } },
90 },
91 {
92 "tcpdump port 22",
93 .insns = {
94 { 0x28, 0, 0, 0x0000000c },
95 { 0x15, 0, 8, 0x000086dd },
96 { 0x30, 0, 0, 0x00000014 },
97 { 0x15, 2, 0, 0x00000084 },
98 { 0x15, 1, 0, 0x00000006 },
99 { 0x15, 0, 17, 0x00000011 },
100 { 0x28, 0, 0, 0x00000036 },
101 { 0x15, 14, 0, 0x00000016 },
102 { 0x28, 0, 0, 0x00000038 },
103 { 0x15, 12, 13, 0x00000016 },
104 { 0x15, 0, 12, 0x00000800 },
105 { 0x30, 0, 0, 0x00000017 },
106 { 0x15, 2, 0, 0x00000084 },
107 { 0x15, 1, 0, 0x00000006 },
108 { 0x15, 0, 8, 0x00000011 },
109 { 0x28, 0, 0, 0x00000014 },
110 { 0x45, 6, 0, 0x00001fff },
111 { 0xb1, 0, 0, 0x0000000e },
112 { 0x48, 0, 0, 0x0000000e },
113 { 0x15, 2, 0, 0x00000016 },
114 { 0x48, 0, 0, 0x00000010 },
115 { 0x15, 0, 1, 0x00000016 },
116 { 0x06, 0, 0, 0x0000ffff },
117 { 0x06, 0, 0, 0x00000000 },
118 },
119 SKB,
120 /* 3c:07:54:43:e5:76 > 10:bf:48:d6:43:d6, ethertype IPv4(0x0800)
121 * length 114: 10.1.1.149.49700 > 10.1.2.10.22: Flags [P.],
122 * seq 1305692979:1305693027, ack 3650467037, win 65535,
123 * options [nop,nop,TS val 2502645400 ecr 3971138], length 48
124 */
125 { 0x10, 0xbf, 0x48, 0xd6, 0x43, 0xd6,
126 0x3c, 0x07, 0x54, 0x43, 0xe5, 0x76,
127 0x08, 0x00,
128 0x45, 0x10, 0x00, 0x64, 0x75, 0xb5,
129 0x40, 0x00, 0x40, 0x06, 0xad, 0x2e, /* IP header */
130 0x0a, 0x01, 0x01, 0x95, /* ip src */
131 0x0a, 0x01, 0x02, 0x0a, /* ip dst */
132 0xc2, 0x24,
133 0x00, 0x16 /* dst port */ },
134 { { 10, 0 }, { 30, 0 }, { 100, 65535 } },
135 },
136 {
137 "INT: DIV + ABS",
138 .insns_int = {
139 BPF_ALU64_REG(BPF_MOV, R6, R1),
140 BPF_LD_ABS(BPF_B, 3),
141 BPF_ALU64_IMM(BPF_MOV, R2, 2),
142 BPF_ALU32_REG(BPF_DIV, R0, R2),
143 BPF_ALU64_REG(BPF_MOV, R8, R0),
144 BPF_LD_ABS(BPF_B, 4),
145 BPF_ALU64_REG(BPF_ADD, R8, R0),
146 BPF_LD_IND(BPF_B, R8, -70),
147 BPF_EXIT_INSN(),
148 },
149 SKB_INT,
150 { 10, 20, 30, 40, 50 },
151 { { 4, 0 }, { 5, 10 } }
152 },
153 {
154 "check: missing ret",
155 .insns = {
156 BPF_STMT(BPF_LD | BPF_IMM, 1),
157 },
158 EXPECTED_FAIL,
159 { },
160 { }
161 },
162};
163
164static int get_length(struct sock_filter *fp)
165{
166 int len = 0;
167
168 while (fp->code != 0 || fp->k != 0) {
169 fp++;
170 len++;
171 }
172
173 return len;
174}
175
176struct net_device dev;
177struct sk_buff *populate_skb(char *buf, int size)
178{
179 struct sk_buff *skb;
180
181 if (size >= MAX_DATA)
182 return NULL;
183
184 skb = alloc_skb(MAX_DATA, GFP_KERNEL);
185 if (!skb)
186 return NULL;
187
188 memcpy(__skb_put(skb, size), buf, size);
189 skb_reset_mac_header(skb);
190 skb->protocol = htons(ETH_P_IP);
191 skb->pkt_type = SKB_TYPE;
192 skb->mark = SKB_MARK;
193 skb->hash = SKB_HASH;
194 skb->queue_mapping = SKB_QUEUE_MAP;
195 skb->vlan_tci = SKB_VLAN_TCI;
196 skb->dev = &dev;
197 skb->dev->ifindex = SKB_DEV_IFINDEX;
198 skb->dev->type = SKB_DEV_TYPE;
199 skb_set_network_header(skb, min(size, ETH_HLEN));
200
201 return skb;
202}
203
204static int run_one(struct sk_filter *fp, struct bpf_test *t)
205{
206 u64 start, finish, res, cnt = 100000;
207 int err_cnt = 0, err, i, j;
208 u32 ret = 0;
209 void *data;
210
211 for (i = 0; i < MAX_SUBTESTS; i++) {
212 if (t->test[i].data_size == 0 &&
213 t->test[i].result == 0)
214 break;
215 if (t->data_type == SKB ||
216 t->data_type == SKB_INT) {
217 data = populate_skb(t->data, t->test[i].data_size);
218 if (!data)
219 return -ENOMEM;
220 } else {
221 data = NULL;
222 }
223
224 start = ktime_to_us(ktime_get());
225 for (j = 0; j < cnt; j++)
226 ret = SK_RUN_FILTER(fp, data);
227 finish = ktime_to_us(ktime_get());
228
229 res = (finish - start) * 1000;
230 do_div(res, cnt);
231
232 err = ret != t->test[i].result;
233 if (!err)
234 pr_cont("%lld ", res);
235
236 if (t->data_type == SKB || t->data_type == SKB_INT)
237 kfree_skb(data);
238
239 if (err) {
240 pr_cont("ret %d != %d ", ret, t->test[i].result);
241 err_cnt++;
242 }
243 }
244
245 return err_cnt;
246}
247
248static __init int test_bpf(void)
249{
250 struct sk_filter *fp, *fp_ext = NULL;
251 struct sock_fprog fprog;
252 int err, i, err_cnt = 0;
253
254 for (i = 0; i < ARRAY_SIZE(tests); i++) {
255 pr_info("#%d %s ", i, tests[i].descr);
256
257 fprog.filter = tests[i].insns;
258 fprog.len = get_length(fprog.filter);
259
260 if (tests[i].data_type == SKB_INT) {
261 fp_ext = kzalloc(4096, GFP_KERNEL);
262 if (!fp_ext)
263 return -ENOMEM;
264 fp = fp_ext;
265 memcpy(fp_ext->insns, tests[i].insns_int,
266 fprog.len * 8);
267 fp->len = fprog.len;
268 fp->bpf_func = sk_run_filter_int_skb;
269 } else {
270 err = sk_unattached_filter_create(&fp, &fprog);
271 if (tests[i].data_type == EXPECTED_FAIL) {
272 if (err == -EINVAL) {
273 pr_cont("PASS\n");
274 continue;
275 } else {
276 pr_cont("UNEXPECTED_PASS\n");
277 /* verifier didn't reject the test
278 * that's bad enough, just return
279 */
280 return -EINVAL;
281 }
282 }
283 if (err) {
284 pr_cont("FAIL to attach err=%d len=%d\n",
285 err, fprog.len);
286 return err;
287 }
288 }
289
290 err = run_one(fp, &tests[i]);
291
292 if (tests[i].data_type != SKB_INT)
293 sk_unattached_filter_destroy(fp);
294 else
295 kfree(fp);
296
297 if (err) {
298 pr_cont("FAIL %d\n", err);
299 err_cnt++;
300 } else {
301 pr_cont("PASS\n");
302 }
303 }
304
305 if (err_cnt)
306 return -EINVAL;
307 else
308 return 0;
309}
310
311static int __init test_bpf_init(void)
312{
313 return test_bpf();
314}
315
316static void __exit test_bpf_exit(void)
317{
318}
319
320module_init(test_bpf_init);
321module_exit(test_bpf_exit);
322MODULE_LICENSE("GPL");
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 750512ba2c88..c7493b8f9b0e 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -14,6 +14,12 @@ all: $(NET_PROGS)
14run_tests: all 14run_tests: all
15 @/bin/sh ./run_netsocktests || echo "sockettests: [FAIL]" 15 @/bin/sh ./run_netsocktests || echo "sockettests: [FAIL]"
16 @/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]" 16 @/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]"
17 17 @if /sbin/modprobe test_bpf ; then \
18 /sbin/rmmod test_bpf; \
19 echo "test_bpf: ok"; \
20 else \
21 echo "test_bpf: [FAIL]"; \
22 exit 1; \
23 fi
18clean: 24clean:
19 $(RM) $(NET_PROGS) 25 $(RM) $(NET_PROGS)