Loading...
1// SPDX-License-Identifier: GPL-2.0
2#include <test_progs.h>
3#include <error.h>
4#include <linux/if.h>
5#include <linux/if_tun.h>
6#include <sys/uio.h>
7
8#ifndef IP_MF
9#define IP_MF 0x2000
10#endif
11
12#define CHECK_FLOW_KEYS(desc, got, expected) \
13 CHECK_ATTR(memcmp(&got, &expected, sizeof(got)) != 0, \
14 desc, \
15 "nhoff=%u/%u " \
16 "thoff=%u/%u " \
17 "addr_proto=0x%x/0x%x " \
18 "is_frag=%u/%u " \
19 "is_first_frag=%u/%u " \
20 "is_encap=%u/%u " \
21 "ip_proto=0x%x/0x%x " \
22 "n_proto=0x%x/0x%x " \
23 "flow_label=0x%x/0x%x " \
24 "sport=%u/%u " \
25 "dport=%u/%u\n", \
26 got.nhoff, expected.nhoff, \
27 got.thoff, expected.thoff, \
28 got.addr_proto, expected.addr_proto, \
29 got.is_frag, expected.is_frag, \
30 got.is_first_frag, expected.is_first_frag, \
31 got.is_encap, expected.is_encap, \
32 got.ip_proto, expected.ip_proto, \
33 got.n_proto, expected.n_proto, \
34 got.flow_label, expected.flow_label, \
35 got.sport, expected.sport, \
36 got.dport, expected.dport)
37
38struct ipv4_pkt {
39 struct ethhdr eth;
40 struct iphdr iph;
41 struct tcphdr tcp;
42} __packed;
43
44struct ipip_pkt {
45 struct ethhdr eth;
46 struct iphdr iph;
47 struct iphdr iph_inner;
48 struct tcphdr tcp;
49} __packed;
50
51struct svlan_ipv4_pkt {
52 struct ethhdr eth;
53 __u16 vlan_tci;
54 __u16 vlan_proto;
55 struct iphdr iph;
56 struct tcphdr tcp;
57} __packed;
58
59struct ipv6_pkt {
60 struct ethhdr eth;
61 struct ipv6hdr iph;
62 struct tcphdr tcp;
63} __packed;
64
65struct ipv6_frag_pkt {
66 struct ethhdr eth;
67 struct ipv6hdr iph;
68 struct frag_hdr {
69 __u8 nexthdr;
70 __u8 reserved;
71 __be16 frag_off;
72 __be32 identification;
73 } ipf;
74 struct tcphdr tcp;
75} __packed;
76
77struct dvlan_ipv6_pkt {
78 struct ethhdr eth;
79 __u16 vlan_tci;
80 __u16 vlan_proto;
81 __u16 vlan_tci2;
82 __u16 vlan_proto2;
83 struct ipv6hdr iph;
84 struct tcphdr tcp;
85} __packed;
86
87struct test {
88 const char *name;
89 union {
90 struct ipv4_pkt ipv4;
91 struct svlan_ipv4_pkt svlan_ipv4;
92 struct ipip_pkt ipip;
93 struct ipv6_pkt ipv6;
94 struct ipv6_frag_pkt ipv6_frag;
95 struct dvlan_ipv6_pkt dvlan_ipv6;
96 } pkt;
97 struct bpf_flow_keys keys;
98 __u32 flags;
99};
100
101#define VLAN_HLEN 4
102
103struct test tests[] = {
104 {
105 .name = "ipv4",
106 .pkt.ipv4 = {
107 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
108 .iph.ihl = 5,
109 .iph.protocol = IPPROTO_TCP,
110 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
111 .tcp.doff = 5,
112 .tcp.source = 80,
113 .tcp.dest = 8080,
114 },
115 .keys = {
116 .nhoff = ETH_HLEN,
117 .thoff = ETH_HLEN + sizeof(struct iphdr),
118 .addr_proto = ETH_P_IP,
119 .ip_proto = IPPROTO_TCP,
120 .n_proto = __bpf_constant_htons(ETH_P_IP),
121 .sport = 80,
122 .dport = 8080,
123 },
124 },
125 {
126 .name = "ipv6",
127 .pkt.ipv6 = {
128 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
129 .iph.nexthdr = IPPROTO_TCP,
130 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
131 .tcp.doff = 5,
132 .tcp.source = 80,
133 .tcp.dest = 8080,
134 },
135 .keys = {
136 .nhoff = ETH_HLEN,
137 .thoff = ETH_HLEN + sizeof(struct ipv6hdr),
138 .addr_proto = ETH_P_IPV6,
139 .ip_proto = IPPROTO_TCP,
140 .n_proto = __bpf_constant_htons(ETH_P_IPV6),
141 .sport = 80,
142 .dport = 8080,
143 },
144 },
145 {
146 .name = "802.1q-ipv4",
147 .pkt.svlan_ipv4 = {
148 .eth.h_proto = __bpf_constant_htons(ETH_P_8021Q),
149 .vlan_proto = __bpf_constant_htons(ETH_P_IP),
150 .iph.ihl = 5,
151 .iph.protocol = IPPROTO_TCP,
152 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
153 .tcp.doff = 5,
154 .tcp.source = 80,
155 .tcp.dest = 8080,
156 },
157 .keys = {
158 .nhoff = ETH_HLEN + VLAN_HLEN,
159 .thoff = ETH_HLEN + VLAN_HLEN + sizeof(struct iphdr),
160 .addr_proto = ETH_P_IP,
161 .ip_proto = IPPROTO_TCP,
162 .n_proto = __bpf_constant_htons(ETH_P_IP),
163 .sport = 80,
164 .dport = 8080,
165 },
166 },
167 {
168 .name = "802.1ad-ipv6",
169 .pkt.dvlan_ipv6 = {
170 .eth.h_proto = __bpf_constant_htons(ETH_P_8021AD),
171 .vlan_proto = __bpf_constant_htons(ETH_P_8021Q),
172 .vlan_proto2 = __bpf_constant_htons(ETH_P_IPV6),
173 .iph.nexthdr = IPPROTO_TCP,
174 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
175 .tcp.doff = 5,
176 .tcp.source = 80,
177 .tcp.dest = 8080,
178 },
179 .keys = {
180 .nhoff = ETH_HLEN + VLAN_HLEN * 2,
181 .thoff = ETH_HLEN + VLAN_HLEN * 2 +
182 sizeof(struct ipv6hdr),
183 .addr_proto = ETH_P_IPV6,
184 .ip_proto = IPPROTO_TCP,
185 .n_proto = __bpf_constant_htons(ETH_P_IPV6),
186 .sport = 80,
187 .dport = 8080,
188 },
189 },
190 {
191 .name = "ipv4-frag",
192 .pkt.ipv4 = {
193 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
194 .iph.ihl = 5,
195 .iph.protocol = IPPROTO_TCP,
196 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
197 .iph.frag_off = __bpf_constant_htons(IP_MF),
198 .tcp.doff = 5,
199 .tcp.source = 80,
200 .tcp.dest = 8080,
201 },
202 .keys = {
203 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
204 .nhoff = ETH_HLEN,
205 .thoff = ETH_HLEN + sizeof(struct iphdr),
206 .addr_proto = ETH_P_IP,
207 .ip_proto = IPPROTO_TCP,
208 .n_proto = __bpf_constant_htons(ETH_P_IP),
209 .is_frag = true,
210 .is_first_frag = true,
211 .sport = 80,
212 .dport = 8080,
213 },
214 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
215 },
216 {
217 .name = "ipv4-no-frag",
218 .pkt.ipv4 = {
219 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
220 .iph.ihl = 5,
221 .iph.protocol = IPPROTO_TCP,
222 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
223 .iph.frag_off = __bpf_constant_htons(IP_MF),
224 .tcp.doff = 5,
225 .tcp.source = 80,
226 .tcp.dest = 8080,
227 },
228 .keys = {
229 .nhoff = ETH_HLEN,
230 .thoff = ETH_HLEN + sizeof(struct iphdr),
231 .addr_proto = ETH_P_IP,
232 .ip_proto = IPPROTO_TCP,
233 .n_proto = __bpf_constant_htons(ETH_P_IP),
234 .is_frag = true,
235 .is_first_frag = true,
236 },
237 },
238 {
239 .name = "ipv6-frag",
240 .pkt.ipv6_frag = {
241 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
242 .iph.nexthdr = IPPROTO_FRAGMENT,
243 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
244 .ipf.nexthdr = IPPROTO_TCP,
245 .tcp.doff = 5,
246 .tcp.source = 80,
247 .tcp.dest = 8080,
248 },
249 .keys = {
250 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
251 .nhoff = ETH_HLEN,
252 .thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
253 sizeof(struct frag_hdr),
254 .addr_proto = ETH_P_IPV6,
255 .ip_proto = IPPROTO_TCP,
256 .n_proto = __bpf_constant_htons(ETH_P_IPV6),
257 .is_frag = true,
258 .is_first_frag = true,
259 .sport = 80,
260 .dport = 8080,
261 },
262 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
263 },
264 {
265 .name = "ipv6-no-frag",
266 .pkt.ipv6_frag = {
267 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
268 .iph.nexthdr = IPPROTO_FRAGMENT,
269 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
270 .ipf.nexthdr = IPPROTO_TCP,
271 .tcp.doff = 5,
272 .tcp.source = 80,
273 .tcp.dest = 8080,
274 },
275 .keys = {
276 .nhoff = ETH_HLEN,
277 .thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
278 sizeof(struct frag_hdr),
279 .addr_proto = ETH_P_IPV6,
280 .ip_proto = IPPROTO_TCP,
281 .n_proto = __bpf_constant_htons(ETH_P_IPV6),
282 .is_frag = true,
283 .is_first_frag = true,
284 },
285 },
286 {
287 .name = "ipv6-flow-label",
288 .pkt.ipv6 = {
289 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
290 .iph.nexthdr = IPPROTO_TCP,
291 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
292 .iph.flow_lbl = { 0xb, 0xee, 0xef },
293 .tcp.doff = 5,
294 .tcp.source = 80,
295 .tcp.dest = 8080,
296 },
297 .keys = {
298 .nhoff = ETH_HLEN,
299 .thoff = ETH_HLEN + sizeof(struct ipv6hdr),
300 .addr_proto = ETH_P_IPV6,
301 .ip_proto = IPPROTO_TCP,
302 .n_proto = __bpf_constant_htons(ETH_P_IPV6),
303 .sport = 80,
304 .dport = 8080,
305 .flow_label = __bpf_constant_htonl(0xbeeef),
306 },
307 },
308 {
309 .name = "ipv6-no-flow-label",
310 .pkt.ipv6 = {
311 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
312 .iph.nexthdr = IPPROTO_TCP,
313 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
314 .iph.flow_lbl = { 0xb, 0xee, 0xef },
315 .tcp.doff = 5,
316 .tcp.source = 80,
317 .tcp.dest = 8080,
318 },
319 .keys = {
320 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
321 .nhoff = ETH_HLEN,
322 .thoff = ETH_HLEN + sizeof(struct ipv6hdr),
323 .addr_proto = ETH_P_IPV6,
324 .ip_proto = IPPROTO_TCP,
325 .n_proto = __bpf_constant_htons(ETH_P_IPV6),
326 .flow_label = __bpf_constant_htonl(0xbeeef),
327 },
328 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
329 },
330 {
331 .name = "ipip-encap",
332 .pkt.ipip = {
333 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
334 .iph.ihl = 5,
335 .iph.protocol = IPPROTO_IPIP,
336 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
337 .iph_inner.ihl = 5,
338 .iph_inner.protocol = IPPROTO_TCP,
339 .iph_inner.tot_len =
340 __bpf_constant_htons(MAGIC_BYTES) -
341 sizeof(struct iphdr),
342 .tcp.doff = 5,
343 .tcp.source = 80,
344 .tcp.dest = 8080,
345 },
346 .keys = {
347 .nhoff = ETH_HLEN,
348 .thoff = ETH_HLEN + sizeof(struct iphdr) +
349 sizeof(struct iphdr),
350 .addr_proto = ETH_P_IP,
351 .ip_proto = IPPROTO_TCP,
352 .n_proto = __bpf_constant_htons(ETH_P_IP),
353 .is_encap = true,
354 .sport = 80,
355 .dport = 8080,
356 },
357 },
358 {
359 .name = "ipip-no-encap",
360 .pkt.ipip = {
361 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
362 .iph.ihl = 5,
363 .iph.protocol = IPPROTO_IPIP,
364 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
365 .iph_inner.ihl = 5,
366 .iph_inner.protocol = IPPROTO_TCP,
367 .iph_inner.tot_len =
368 __bpf_constant_htons(MAGIC_BYTES) -
369 sizeof(struct iphdr),
370 .tcp.doff = 5,
371 .tcp.source = 80,
372 .tcp.dest = 8080,
373 },
374 .keys = {
375 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
376 .nhoff = ETH_HLEN,
377 .thoff = ETH_HLEN + sizeof(struct iphdr),
378 .addr_proto = ETH_P_IP,
379 .ip_proto = IPPROTO_IPIP,
380 .n_proto = __bpf_constant_htons(ETH_P_IP),
381 .is_encap = true,
382 },
383 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
384 },
385};
386
387static int create_tap(const char *ifname)
388{
389 struct ifreq ifr = {
390 .ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS,
391 };
392 int fd, ret;
393
394 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
395
396 fd = open("/dev/net/tun", O_RDWR);
397 if (fd < 0)
398 return -1;
399
400 ret = ioctl(fd, TUNSETIFF, &ifr);
401 if (ret)
402 return -1;
403
404 return fd;
405}
406
407static int tx_tap(int fd, void *pkt, size_t len)
408{
409 struct iovec iov[] = {
410 {
411 .iov_len = len,
412 .iov_base = pkt,
413 },
414 };
415 return writev(fd, iov, ARRAY_SIZE(iov));
416}
417
418static int ifup(const char *ifname)
419{
420 struct ifreq ifr = {};
421 int sk, ret;
422
423 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
424
425 sk = socket(PF_INET, SOCK_DGRAM, 0);
426 if (sk < 0)
427 return -1;
428
429 ret = ioctl(sk, SIOCGIFFLAGS, &ifr);
430 if (ret) {
431 close(sk);
432 return -1;
433 }
434
435 ifr.ifr_flags |= IFF_UP;
436 ret = ioctl(sk, SIOCSIFFLAGS, &ifr);
437 if (ret) {
438 close(sk);
439 return -1;
440 }
441
442 close(sk);
443 return 0;
444}
445
446void test_flow_dissector(void)
447{
448 int i, err, prog_fd, keys_fd = -1, tap_fd;
449 struct bpf_object *obj;
450 __u32 duration = 0;
451
452 err = bpf_flow_load(&obj, "./bpf_flow.o", "flow_dissector",
453 "jmp_table", "last_dissection", &prog_fd, &keys_fd);
454 if (CHECK_FAIL(err))
455 return;
456
457 for (i = 0; i < ARRAY_SIZE(tests); i++) {
458 struct bpf_flow_keys flow_keys;
459 struct bpf_prog_test_run_attr tattr = {
460 .prog_fd = prog_fd,
461 .data_in = &tests[i].pkt,
462 .data_size_in = sizeof(tests[i].pkt),
463 .data_out = &flow_keys,
464 };
465 static struct bpf_flow_keys ctx = {};
466
467 if (tests[i].flags) {
468 tattr.ctx_in = &ctx;
469 tattr.ctx_size_in = sizeof(ctx);
470 ctx.flags = tests[i].flags;
471 }
472
473 err = bpf_prog_test_run_xattr(&tattr);
474 CHECK_ATTR(tattr.data_size_out != sizeof(flow_keys) ||
475 err || tattr.retval != 1,
476 tests[i].name,
477 "err %d errno %d retval %d duration %d size %u/%lu\n",
478 err, errno, tattr.retval, tattr.duration,
479 tattr.data_size_out, sizeof(flow_keys));
480 CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
481 }
482
483 /* Do the same tests but for skb-less flow dissector.
484 * We use a known path in the net/tun driver that calls
485 * eth_get_headlen and we manually export bpf_flow_keys
486 * via BPF map in this case.
487 */
488
489 err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
490 CHECK(err, "bpf_prog_attach", "err %d errno %d\n", err, errno);
491
492 tap_fd = create_tap("tap0");
493 CHECK(tap_fd < 0, "create_tap", "tap_fd %d errno %d\n", tap_fd, errno);
494 err = ifup("tap0");
495 CHECK(err, "ifup", "err %d errno %d\n", err, errno);
496
497 for (i = 0; i < ARRAY_SIZE(tests); i++) {
498 /* Keep in sync with 'flags' from eth_get_headlen. */
499 __u32 eth_get_headlen_flags =
500 BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
501 struct bpf_prog_test_run_attr tattr = {};
502 struct bpf_flow_keys flow_keys = {};
503 __u32 key = (__u32)(tests[i].keys.sport) << 16 |
504 tests[i].keys.dport;
505
506 /* For skb-less case we can't pass input flags; run
507 * only the tests that have a matching set of flags.
508 */
509
510 if (tests[i].flags != eth_get_headlen_flags)
511 continue;
512
513 err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt));
514 CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno);
515
516 err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
517 CHECK_ATTR(err, tests[i].name, "bpf_map_lookup_elem %d\n", err);
518
519 CHECK_ATTR(err, tests[i].name, "skb-less err %d\n", err);
520 CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
521
522 err = bpf_map_delete_elem(keys_fd, &key);
523 CHECK_ATTR(err, tests[i].name, "bpf_map_delete_elem %d\n", err);
524 }
525
526 bpf_prog_detach(prog_fd, BPF_FLOW_DISSECTOR);
527 bpf_object__close(obj);
528}
1// SPDX-License-Identifier: GPL-2.0
2#include <test_progs.h>
3#include <network_helpers.h>
4#include <error.h>
5#include <linux/if.h>
6#include <linux/if_tun.h>
7#include <sys/uio.h>
8
9#include "bpf_flow.skel.h"
10
11#define FLOW_CONTINUE_SADDR 0x7f00007f /* 127.0.0.127 */
12
13#ifndef IP_MF
14#define IP_MF 0x2000
15#endif
16
17#define CHECK_FLOW_KEYS(desc, got, expected) \
18 _CHECK(memcmp(&got, &expected, sizeof(got)) != 0, \
19 desc, \
20 topts.duration, \
21 "nhoff=%u/%u " \
22 "thoff=%u/%u " \
23 "addr_proto=0x%x/0x%x " \
24 "is_frag=%u/%u " \
25 "is_first_frag=%u/%u " \
26 "is_encap=%u/%u " \
27 "ip_proto=0x%x/0x%x " \
28 "n_proto=0x%x/0x%x " \
29 "flow_label=0x%x/0x%x " \
30 "sport=%u/%u " \
31 "dport=%u/%u\n", \
32 got.nhoff, expected.nhoff, \
33 got.thoff, expected.thoff, \
34 got.addr_proto, expected.addr_proto, \
35 got.is_frag, expected.is_frag, \
36 got.is_first_frag, expected.is_first_frag, \
37 got.is_encap, expected.is_encap, \
38 got.ip_proto, expected.ip_proto, \
39 got.n_proto, expected.n_proto, \
40 got.flow_label, expected.flow_label, \
41 got.sport, expected.sport, \
42 got.dport, expected.dport)
43
44struct ipv4_pkt {
45 struct ethhdr eth;
46 struct iphdr iph;
47 struct tcphdr tcp;
48} __packed;
49
50struct ipip_pkt {
51 struct ethhdr eth;
52 struct iphdr iph;
53 struct iphdr iph_inner;
54 struct tcphdr tcp;
55} __packed;
56
57struct svlan_ipv4_pkt {
58 struct ethhdr eth;
59 __u16 vlan_tci;
60 __u16 vlan_proto;
61 struct iphdr iph;
62 struct tcphdr tcp;
63} __packed;
64
65struct ipv6_pkt {
66 struct ethhdr eth;
67 struct ipv6hdr iph;
68 struct tcphdr tcp;
69} __packed;
70
71struct ipv6_frag_pkt {
72 struct ethhdr eth;
73 struct ipv6hdr iph;
74 struct frag_hdr {
75 __u8 nexthdr;
76 __u8 reserved;
77 __be16 frag_off;
78 __be32 identification;
79 } ipf;
80 struct tcphdr tcp;
81} __packed;
82
83struct dvlan_ipv6_pkt {
84 struct ethhdr eth;
85 __u16 vlan_tci;
86 __u16 vlan_proto;
87 __u16 vlan_tci2;
88 __u16 vlan_proto2;
89 struct ipv6hdr iph;
90 struct tcphdr tcp;
91} __packed;
92
93struct test {
94 const char *name;
95 union {
96 struct ipv4_pkt ipv4;
97 struct svlan_ipv4_pkt svlan_ipv4;
98 struct ipip_pkt ipip;
99 struct ipv6_pkt ipv6;
100 struct ipv6_frag_pkt ipv6_frag;
101 struct dvlan_ipv6_pkt dvlan_ipv6;
102 } pkt;
103 struct bpf_flow_keys keys;
104 __u32 flags;
105 __u32 retval;
106};
107
108#define VLAN_HLEN 4
109
110static __u32 duration;
111struct test tests[] = {
112 {
113 .name = "ipv4",
114 .pkt.ipv4 = {
115 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
116 .iph.ihl = 5,
117 .iph.protocol = IPPROTO_TCP,
118 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
119 .tcp.doff = 5,
120 .tcp.source = 80,
121 .tcp.dest = 8080,
122 },
123 .keys = {
124 .nhoff = ETH_HLEN,
125 .thoff = ETH_HLEN + sizeof(struct iphdr),
126 .addr_proto = ETH_P_IP,
127 .ip_proto = IPPROTO_TCP,
128 .n_proto = __bpf_constant_htons(ETH_P_IP),
129 .sport = 80,
130 .dport = 8080,
131 },
132 .retval = BPF_OK,
133 },
134 {
135 .name = "ipv6",
136 .pkt.ipv6 = {
137 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
138 .iph.nexthdr = IPPROTO_TCP,
139 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
140 .tcp.doff = 5,
141 .tcp.source = 80,
142 .tcp.dest = 8080,
143 },
144 .keys = {
145 .nhoff = ETH_HLEN,
146 .thoff = ETH_HLEN + sizeof(struct ipv6hdr),
147 .addr_proto = ETH_P_IPV6,
148 .ip_proto = IPPROTO_TCP,
149 .n_proto = __bpf_constant_htons(ETH_P_IPV6),
150 .sport = 80,
151 .dport = 8080,
152 },
153 .retval = BPF_OK,
154 },
155 {
156 .name = "802.1q-ipv4",
157 .pkt.svlan_ipv4 = {
158 .eth.h_proto = __bpf_constant_htons(ETH_P_8021Q),
159 .vlan_proto = __bpf_constant_htons(ETH_P_IP),
160 .iph.ihl = 5,
161 .iph.protocol = IPPROTO_TCP,
162 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
163 .tcp.doff = 5,
164 .tcp.source = 80,
165 .tcp.dest = 8080,
166 },
167 .keys = {
168 .nhoff = ETH_HLEN + VLAN_HLEN,
169 .thoff = ETH_HLEN + VLAN_HLEN + sizeof(struct iphdr),
170 .addr_proto = ETH_P_IP,
171 .ip_proto = IPPROTO_TCP,
172 .n_proto = __bpf_constant_htons(ETH_P_IP),
173 .sport = 80,
174 .dport = 8080,
175 },
176 .retval = BPF_OK,
177 },
178 {
179 .name = "802.1ad-ipv6",
180 .pkt.dvlan_ipv6 = {
181 .eth.h_proto = __bpf_constant_htons(ETH_P_8021AD),
182 .vlan_proto = __bpf_constant_htons(ETH_P_8021Q),
183 .vlan_proto2 = __bpf_constant_htons(ETH_P_IPV6),
184 .iph.nexthdr = IPPROTO_TCP,
185 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
186 .tcp.doff = 5,
187 .tcp.source = 80,
188 .tcp.dest = 8080,
189 },
190 .keys = {
191 .nhoff = ETH_HLEN + VLAN_HLEN * 2,
192 .thoff = ETH_HLEN + VLAN_HLEN * 2 +
193 sizeof(struct ipv6hdr),
194 .addr_proto = ETH_P_IPV6,
195 .ip_proto = IPPROTO_TCP,
196 .n_proto = __bpf_constant_htons(ETH_P_IPV6),
197 .sport = 80,
198 .dport = 8080,
199 },
200 .retval = BPF_OK,
201 },
202 {
203 .name = "ipv4-frag",
204 .pkt.ipv4 = {
205 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
206 .iph.ihl = 5,
207 .iph.protocol = IPPROTO_TCP,
208 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
209 .iph.frag_off = __bpf_constant_htons(IP_MF),
210 .tcp.doff = 5,
211 .tcp.source = 80,
212 .tcp.dest = 8080,
213 },
214 .keys = {
215 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
216 .nhoff = ETH_HLEN,
217 .thoff = ETH_HLEN + sizeof(struct iphdr),
218 .addr_proto = ETH_P_IP,
219 .ip_proto = IPPROTO_TCP,
220 .n_proto = __bpf_constant_htons(ETH_P_IP),
221 .is_frag = true,
222 .is_first_frag = true,
223 .sport = 80,
224 .dport = 8080,
225 },
226 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
227 .retval = BPF_OK,
228 },
229 {
230 .name = "ipv4-no-frag",
231 .pkt.ipv4 = {
232 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
233 .iph.ihl = 5,
234 .iph.protocol = IPPROTO_TCP,
235 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
236 .iph.frag_off = __bpf_constant_htons(IP_MF),
237 .tcp.doff = 5,
238 .tcp.source = 80,
239 .tcp.dest = 8080,
240 },
241 .keys = {
242 .nhoff = ETH_HLEN,
243 .thoff = ETH_HLEN + sizeof(struct iphdr),
244 .addr_proto = ETH_P_IP,
245 .ip_proto = IPPROTO_TCP,
246 .n_proto = __bpf_constant_htons(ETH_P_IP),
247 .is_frag = true,
248 .is_first_frag = true,
249 },
250 .retval = BPF_OK,
251 },
252 {
253 .name = "ipv6-frag",
254 .pkt.ipv6_frag = {
255 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
256 .iph.nexthdr = IPPROTO_FRAGMENT,
257 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
258 .ipf.nexthdr = IPPROTO_TCP,
259 .tcp.doff = 5,
260 .tcp.source = 80,
261 .tcp.dest = 8080,
262 },
263 .keys = {
264 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
265 .nhoff = ETH_HLEN,
266 .thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
267 sizeof(struct frag_hdr),
268 .addr_proto = ETH_P_IPV6,
269 .ip_proto = IPPROTO_TCP,
270 .n_proto = __bpf_constant_htons(ETH_P_IPV6),
271 .is_frag = true,
272 .is_first_frag = true,
273 .sport = 80,
274 .dport = 8080,
275 },
276 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
277 .retval = BPF_OK,
278 },
279 {
280 .name = "ipv6-no-frag",
281 .pkt.ipv6_frag = {
282 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
283 .iph.nexthdr = IPPROTO_FRAGMENT,
284 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
285 .ipf.nexthdr = IPPROTO_TCP,
286 .tcp.doff = 5,
287 .tcp.source = 80,
288 .tcp.dest = 8080,
289 },
290 .keys = {
291 .nhoff = ETH_HLEN,
292 .thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
293 sizeof(struct frag_hdr),
294 .addr_proto = ETH_P_IPV6,
295 .ip_proto = IPPROTO_TCP,
296 .n_proto = __bpf_constant_htons(ETH_P_IPV6),
297 .is_frag = true,
298 .is_first_frag = true,
299 },
300 .retval = BPF_OK,
301 },
302 {
303 .name = "ipv6-flow-label",
304 .pkt.ipv6 = {
305 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
306 .iph.nexthdr = IPPROTO_TCP,
307 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
308 .iph.flow_lbl = { 0xb, 0xee, 0xef },
309 .tcp.doff = 5,
310 .tcp.source = 80,
311 .tcp.dest = 8080,
312 },
313 .keys = {
314 .nhoff = ETH_HLEN,
315 .thoff = ETH_HLEN + sizeof(struct ipv6hdr),
316 .addr_proto = ETH_P_IPV6,
317 .ip_proto = IPPROTO_TCP,
318 .n_proto = __bpf_constant_htons(ETH_P_IPV6),
319 .sport = 80,
320 .dport = 8080,
321 .flow_label = __bpf_constant_htonl(0xbeeef),
322 },
323 .retval = BPF_OK,
324 },
325 {
326 .name = "ipv6-no-flow-label",
327 .pkt.ipv6 = {
328 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
329 .iph.nexthdr = IPPROTO_TCP,
330 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
331 .iph.flow_lbl = { 0xb, 0xee, 0xef },
332 .tcp.doff = 5,
333 .tcp.source = 80,
334 .tcp.dest = 8080,
335 },
336 .keys = {
337 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
338 .nhoff = ETH_HLEN,
339 .thoff = ETH_HLEN + sizeof(struct ipv6hdr),
340 .addr_proto = ETH_P_IPV6,
341 .ip_proto = IPPROTO_TCP,
342 .n_proto = __bpf_constant_htons(ETH_P_IPV6),
343 .flow_label = __bpf_constant_htonl(0xbeeef),
344 },
345 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
346 .retval = BPF_OK,
347 },
348 {
349 .name = "ipv6-empty-flow-label",
350 .pkt.ipv6 = {
351 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
352 .iph.nexthdr = IPPROTO_TCP,
353 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
354 .iph.flow_lbl = { 0x00, 0x00, 0x00 },
355 .tcp.doff = 5,
356 .tcp.source = 80,
357 .tcp.dest = 8080,
358 },
359 .keys = {
360 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
361 .nhoff = ETH_HLEN,
362 .thoff = ETH_HLEN + sizeof(struct ipv6hdr),
363 .addr_proto = ETH_P_IPV6,
364 .ip_proto = IPPROTO_TCP,
365 .n_proto = __bpf_constant_htons(ETH_P_IPV6),
366 .sport = 80,
367 .dport = 8080,
368 },
369 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
370 .retval = BPF_OK,
371 },
372 {
373 .name = "ipip-encap",
374 .pkt.ipip = {
375 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
376 .iph.ihl = 5,
377 .iph.protocol = IPPROTO_IPIP,
378 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
379 .iph_inner.ihl = 5,
380 .iph_inner.protocol = IPPROTO_TCP,
381 .iph_inner.tot_len =
382 __bpf_constant_htons(MAGIC_BYTES) -
383 sizeof(struct iphdr),
384 .tcp.doff = 5,
385 .tcp.source = 80,
386 .tcp.dest = 8080,
387 },
388 .keys = {
389 .nhoff = ETH_HLEN,
390 .thoff = ETH_HLEN + sizeof(struct iphdr) +
391 sizeof(struct iphdr),
392 .addr_proto = ETH_P_IP,
393 .ip_proto = IPPROTO_TCP,
394 .n_proto = __bpf_constant_htons(ETH_P_IP),
395 .is_encap = true,
396 .sport = 80,
397 .dport = 8080,
398 },
399 .retval = BPF_OK,
400 },
401 {
402 .name = "ipip-no-encap",
403 .pkt.ipip = {
404 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
405 .iph.ihl = 5,
406 .iph.protocol = IPPROTO_IPIP,
407 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
408 .iph_inner.ihl = 5,
409 .iph_inner.protocol = IPPROTO_TCP,
410 .iph_inner.tot_len =
411 __bpf_constant_htons(MAGIC_BYTES) -
412 sizeof(struct iphdr),
413 .tcp.doff = 5,
414 .tcp.source = 80,
415 .tcp.dest = 8080,
416 },
417 .keys = {
418 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
419 .nhoff = ETH_HLEN,
420 .thoff = ETH_HLEN + sizeof(struct iphdr),
421 .addr_proto = ETH_P_IP,
422 .ip_proto = IPPROTO_IPIP,
423 .n_proto = __bpf_constant_htons(ETH_P_IP),
424 .is_encap = true,
425 },
426 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
427 .retval = BPF_OK,
428 },
429 {
430 .name = "ipip-encap-dissector-continue",
431 .pkt.ipip = {
432 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
433 .iph.ihl = 5,
434 .iph.protocol = IPPROTO_IPIP,
435 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
436 .iph.saddr = __bpf_constant_htonl(FLOW_CONTINUE_SADDR),
437 .iph_inner.ihl = 5,
438 .iph_inner.protocol = IPPROTO_TCP,
439 .iph_inner.tot_len =
440 __bpf_constant_htons(MAGIC_BYTES) -
441 sizeof(struct iphdr),
442 .tcp.doff = 5,
443 .tcp.source = 99,
444 .tcp.dest = 9090,
445 },
446 .retval = BPF_FLOW_DISSECTOR_CONTINUE,
447 },
448};
449
450static int create_tap(const char *ifname)
451{
452 struct ifreq ifr = {
453 .ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS,
454 };
455 int fd, ret;
456
457 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
458
459 fd = open("/dev/net/tun", O_RDWR);
460 if (fd < 0)
461 return -1;
462
463 ret = ioctl(fd, TUNSETIFF, &ifr);
464 if (ret)
465 return -1;
466
467 return fd;
468}
469
470static int tx_tap(int fd, void *pkt, size_t len)
471{
472 struct iovec iov[] = {
473 {
474 .iov_len = len,
475 .iov_base = pkt,
476 },
477 };
478 return writev(fd, iov, ARRAY_SIZE(iov));
479}
480
481static int ifup(const char *ifname)
482{
483 struct ifreq ifr = {};
484 int sk, ret;
485
486 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
487
488 sk = socket(PF_INET, SOCK_DGRAM, 0);
489 if (sk < 0)
490 return -1;
491
492 ret = ioctl(sk, SIOCGIFFLAGS, &ifr);
493 if (ret) {
494 close(sk);
495 return -1;
496 }
497
498 ifr.ifr_flags |= IFF_UP;
499 ret = ioctl(sk, SIOCSIFFLAGS, &ifr);
500 if (ret) {
501 close(sk);
502 return -1;
503 }
504
505 close(sk);
506 return 0;
507}
508
509static int init_prog_array(struct bpf_object *obj, struct bpf_map *prog_array)
510{
511 int i, err, map_fd, prog_fd;
512 struct bpf_program *prog;
513 char prog_name[32];
514
515 map_fd = bpf_map__fd(prog_array);
516 if (map_fd < 0)
517 return -1;
518
519 for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
520 snprintf(prog_name, sizeof(prog_name), "flow_dissector_%d", i);
521
522 prog = bpf_object__find_program_by_name(obj, prog_name);
523 if (!prog)
524 return -1;
525
526 prog_fd = bpf_program__fd(prog);
527 if (prog_fd < 0)
528 return -1;
529
530 err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
531 if (err)
532 return -1;
533 }
534 return 0;
535}
536
537static void run_tests_skb_less(int tap_fd, struct bpf_map *keys)
538{
539 int i, err, keys_fd;
540
541 keys_fd = bpf_map__fd(keys);
542 if (CHECK(keys_fd < 0, "bpf_map__fd", "err %d\n", keys_fd))
543 return;
544
545 for (i = 0; i < ARRAY_SIZE(tests); i++) {
546 /* Keep in sync with 'flags' from eth_get_headlen. */
547 __u32 eth_get_headlen_flags =
548 BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
549 LIBBPF_OPTS(bpf_test_run_opts, topts);
550 struct bpf_flow_keys flow_keys = {};
551 __u32 key = (__u32)(tests[i].keys.sport) << 16 |
552 tests[i].keys.dport;
553
554 /* For skb-less case we can't pass input flags; run
555 * only the tests that have a matching set of flags.
556 */
557
558 if (tests[i].flags != eth_get_headlen_flags)
559 continue;
560
561 err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt));
562 CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno);
563
564 /* check the stored flow_keys only if BPF_OK expected */
565 if (tests[i].retval != BPF_OK)
566 continue;
567
568 err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
569 ASSERT_OK(err, "bpf_map_lookup_elem");
570
571 CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
572
573 err = bpf_map_delete_elem(keys_fd, &key);
574 ASSERT_OK(err, "bpf_map_delete_elem");
575 }
576}
577
578static void test_skb_less_prog_attach(struct bpf_flow *skel, int tap_fd)
579{
580 int err, prog_fd;
581
582 prog_fd = bpf_program__fd(skel->progs._dissect);
583 if (CHECK(prog_fd < 0, "bpf_program__fd", "err %d\n", prog_fd))
584 return;
585
586 err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
587 if (CHECK(err, "bpf_prog_attach", "err %d errno %d\n", err, errno))
588 return;
589
590 run_tests_skb_less(tap_fd, skel->maps.last_dissection);
591
592 err = bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
593 CHECK(err, "bpf_prog_detach2", "err %d errno %d\n", err, errno);
594}
595
596static void test_skb_less_link_create(struct bpf_flow *skel, int tap_fd)
597{
598 struct bpf_link *link;
599 int err, net_fd;
600
601 net_fd = open("/proc/self/ns/net", O_RDONLY);
602 if (CHECK(net_fd < 0, "open(/proc/self/ns/net)", "err %d\n", errno))
603 return;
604
605 link = bpf_program__attach_netns(skel->progs._dissect, net_fd);
606 if (!ASSERT_OK_PTR(link, "attach_netns"))
607 goto out_close;
608
609 run_tests_skb_less(tap_fd, skel->maps.last_dissection);
610
611 err = bpf_link__destroy(link);
612 CHECK(err, "bpf_link__destroy", "err %d\n", err);
613out_close:
614 close(net_fd);
615}
616
617void test_flow_dissector(void)
618{
619 int i, err, prog_fd, keys_fd = -1, tap_fd;
620 struct bpf_flow *skel;
621
622 skel = bpf_flow__open_and_load();
623 if (CHECK(!skel, "skel", "failed to open/load skeleton\n"))
624 return;
625
626 prog_fd = bpf_program__fd(skel->progs._dissect);
627 if (CHECK(prog_fd < 0, "bpf_program__fd", "err %d\n", prog_fd))
628 goto out_destroy_skel;
629 keys_fd = bpf_map__fd(skel->maps.last_dissection);
630 if (CHECK(keys_fd < 0, "bpf_map__fd", "err %d\n", keys_fd))
631 goto out_destroy_skel;
632 err = init_prog_array(skel->obj, skel->maps.jmp_table);
633 if (CHECK(err, "init_prog_array", "err %d\n", err))
634 goto out_destroy_skel;
635
636 for (i = 0; i < ARRAY_SIZE(tests); i++) {
637 struct bpf_flow_keys flow_keys;
638 LIBBPF_OPTS(bpf_test_run_opts, topts,
639 .data_in = &tests[i].pkt,
640 .data_size_in = sizeof(tests[i].pkt),
641 .data_out = &flow_keys,
642 );
643 static struct bpf_flow_keys ctx = {};
644
645 if (tests[i].flags) {
646 topts.ctx_in = &ctx;
647 topts.ctx_size_in = sizeof(ctx);
648 ctx.flags = tests[i].flags;
649 }
650
651 err = bpf_prog_test_run_opts(prog_fd, &topts);
652 ASSERT_OK(err, "test_run");
653 ASSERT_EQ(topts.retval, tests[i].retval, "test_run retval");
654
655 /* check the resulting flow_keys only if BPF_OK returned */
656 if (topts.retval != BPF_OK)
657 continue;
658 ASSERT_EQ(topts.data_size_out, sizeof(flow_keys),
659 "test_run data_size_out");
660 CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
661 }
662
663 /* Do the same tests but for skb-less flow dissector.
664 * We use a known path in the net/tun driver that calls
665 * eth_get_headlen and we manually export bpf_flow_keys
666 * via BPF map in this case.
667 */
668
669 tap_fd = create_tap("tap0");
670 CHECK(tap_fd < 0, "create_tap", "tap_fd %d errno %d\n", tap_fd, errno);
671 err = ifup("tap0");
672 CHECK(err, "ifup", "err %d errno %d\n", err, errno);
673
674 /* Test direct prog attachment */
675 test_skb_less_prog_attach(skel, tap_fd);
676 /* Test indirect prog attachment via link */
677 test_skb_less_link_create(skel, tap_fd);
678
679 close(tap_fd);
680out_destroy_skel:
681 bpf_flow__destroy(skel);
682}