xref: /linux/tools/testing/selftests/bpf/prog_tests/flow_dissector.c (revision 6fdcba32711044c35c0e1b094cbd8f3f0b4472c9)
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 
38 struct ipv4_pkt {
39 	struct ethhdr eth;
40 	struct iphdr iph;
41 	struct tcphdr tcp;
42 } __packed;
43 
44 struct ipip_pkt {
45 	struct ethhdr eth;
46 	struct iphdr iph;
47 	struct iphdr iph_inner;
48 	struct tcphdr tcp;
49 } __packed;
50 
51 struct 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 
59 struct ipv6_pkt {
60 	struct ethhdr eth;
61 	struct ipv6hdr iph;
62 	struct tcphdr tcp;
63 } __packed;
64 
65 struct 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 
77 struct 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 
87 struct 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 
103 struct 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 
387 static 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 
407 static 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 
418 static 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 
446 void 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 }
529