xref: /linux/tools/testing/selftests/bpf/prog_tests/flow_dissector.c (revision 9406b485dea5e25bed7c81cd822747d494cc8bde)
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 #ifndef IP_MF
10 #define IP_MF 0x2000
11 #endif
12 
13 #define CHECK_FLOW_KEYS(desc, got, expected)				\
14 	CHECK_ATTR(memcmp(&got, &expected, sizeof(got)) != 0,		\
15 	      desc,							\
16 	      "nhoff=%u/%u "						\
17 	      "thoff=%u/%u "						\
18 	      "addr_proto=0x%x/0x%x "					\
19 	      "is_frag=%u/%u "						\
20 	      "is_first_frag=%u/%u "					\
21 	      "is_encap=%u/%u "						\
22 	      "ip_proto=0x%x/0x%x "					\
23 	      "n_proto=0x%x/0x%x "					\
24 	      "flow_label=0x%x/0x%x "					\
25 	      "sport=%u/%u "						\
26 	      "dport=%u/%u\n",						\
27 	      got.nhoff, expected.nhoff,				\
28 	      got.thoff, expected.thoff,				\
29 	      got.addr_proto, expected.addr_proto,			\
30 	      got.is_frag, expected.is_frag,				\
31 	      got.is_first_frag, expected.is_first_frag,		\
32 	      got.is_encap, expected.is_encap,				\
33 	      got.ip_proto, expected.ip_proto,				\
34 	      got.n_proto, expected.n_proto,				\
35 	      got.flow_label, expected.flow_label,			\
36 	      got.sport, expected.sport,				\
37 	      got.dport, expected.dport)
38 
39 struct ipv4_pkt {
40 	struct ethhdr eth;
41 	struct iphdr iph;
42 	struct tcphdr tcp;
43 } __packed;
44 
45 struct ipip_pkt {
46 	struct ethhdr eth;
47 	struct iphdr iph;
48 	struct iphdr iph_inner;
49 	struct tcphdr tcp;
50 } __packed;
51 
52 struct svlan_ipv4_pkt {
53 	struct ethhdr eth;
54 	__u16 vlan_tci;
55 	__u16 vlan_proto;
56 	struct iphdr iph;
57 	struct tcphdr tcp;
58 } __packed;
59 
60 struct ipv6_pkt {
61 	struct ethhdr eth;
62 	struct ipv6hdr iph;
63 	struct tcphdr tcp;
64 } __packed;
65 
66 struct ipv6_frag_pkt {
67 	struct ethhdr eth;
68 	struct ipv6hdr iph;
69 	struct frag_hdr {
70 		__u8 nexthdr;
71 		__u8 reserved;
72 		__be16 frag_off;
73 		__be32 identification;
74 	} ipf;
75 	struct tcphdr tcp;
76 } __packed;
77 
78 struct dvlan_ipv6_pkt {
79 	struct ethhdr eth;
80 	__u16 vlan_tci;
81 	__u16 vlan_proto;
82 	__u16 vlan_tci2;
83 	__u16 vlan_proto2;
84 	struct ipv6hdr iph;
85 	struct tcphdr tcp;
86 } __packed;
87 
88 struct test {
89 	const char *name;
90 	union {
91 		struct ipv4_pkt ipv4;
92 		struct svlan_ipv4_pkt svlan_ipv4;
93 		struct ipip_pkt ipip;
94 		struct ipv6_pkt ipv6;
95 		struct ipv6_frag_pkt ipv6_frag;
96 		struct dvlan_ipv6_pkt dvlan_ipv6;
97 	} pkt;
98 	struct bpf_flow_keys keys;
99 	__u32 flags;
100 };
101 
102 #define VLAN_HLEN	4
103 
104 struct test tests[] = {
105 	{
106 		.name = "ipv4",
107 		.pkt.ipv4 = {
108 			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
109 			.iph.ihl = 5,
110 			.iph.protocol = IPPROTO_TCP,
111 			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
112 			.tcp.doff = 5,
113 			.tcp.source = 80,
114 			.tcp.dest = 8080,
115 		},
116 		.keys = {
117 			.nhoff = ETH_HLEN,
118 			.thoff = ETH_HLEN + sizeof(struct iphdr),
119 			.addr_proto = ETH_P_IP,
120 			.ip_proto = IPPROTO_TCP,
121 			.n_proto = __bpf_constant_htons(ETH_P_IP),
122 			.sport = 80,
123 			.dport = 8080,
124 		},
125 	},
126 	{
127 		.name = "ipv6",
128 		.pkt.ipv6 = {
129 			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
130 			.iph.nexthdr = IPPROTO_TCP,
131 			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
132 			.tcp.doff = 5,
133 			.tcp.source = 80,
134 			.tcp.dest = 8080,
135 		},
136 		.keys = {
137 			.nhoff = ETH_HLEN,
138 			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
139 			.addr_proto = ETH_P_IPV6,
140 			.ip_proto = IPPROTO_TCP,
141 			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
142 			.sport = 80,
143 			.dport = 8080,
144 		},
145 	},
146 	{
147 		.name = "802.1q-ipv4",
148 		.pkt.svlan_ipv4 = {
149 			.eth.h_proto = __bpf_constant_htons(ETH_P_8021Q),
150 			.vlan_proto = __bpf_constant_htons(ETH_P_IP),
151 			.iph.ihl = 5,
152 			.iph.protocol = IPPROTO_TCP,
153 			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
154 			.tcp.doff = 5,
155 			.tcp.source = 80,
156 			.tcp.dest = 8080,
157 		},
158 		.keys = {
159 			.nhoff = ETH_HLEN + VLAN_HLEN,
160 			.thoff = ETH_HLEN + VLAN_HLEN + sizeof(struct iphdr),
161 			.addr_proto = ETH_P_IP,
162 			.ip_proto = IPPROTO_TCP,
163 			.n_proto = __bpf_constant_htons(ETH_P_IP),
164 			.sport = 80,
165 			.dport = 8080,
166 		},
167 	},
168 	{
169 		.name = "802.1ad-ipv6",
170 		.pkt.dvlan_ipv6 = {
171 			.eth.h_proto = __bpf_constant_htons(ETH_P_8021AD),
172 			.vlan_proto = __bpf_constant_htons(ETH_P_8021Q),
173 			.vlan_proto2 = __bpf_constant_htons(ETH_P_IPV6),
174 			.iph.nexthdr = IPPROTO_TCP,
175 			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
176 			.tcp.doff = 5,
177 			.tcp.source = 80,
178 			.tcp.dest = 8080,
179 		},
180 		.keys = {
181 			.nhoff = ETH_HLEN + VLAN_HLEN * 2,
182 			.thoff = ETH_HLEN + VLAN_HLEN * 2 +
183 				sizeof(struct ipv6hdr),
184 			.addr_proto = ETH_P_IPV6,
185 			.ip_proto = IPPROTO_TCP,
186 			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
187 			.sport = 80,
188 			.dport = 8080,
189 		},
190 	},
191 	{
192 		.name = "ipv4-frag",
193 		.pkt.ipv4 = {
194 			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
195 			.iph.ihl = 5,
196 			.iph.protocol = IPPROTO_TCP,
197 			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
198 			.iph.frag_off = __bpf_constant_htons(IP_MF),
199 			.tcp.doff = 5,
200 			.tcp.source = 80,
201 			.tcp.dest = 8080,
202 		},
203 		.keys = {
204 			.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
205 			.nhoff = ETH_HLEN,
206 			.thoff = ETH_HLEN + sizeof(struct iphdr),
207 			.addr_proto = ETH_P_IP,
208 			.ip_proto = IPPROTO_TCP,
209 			.n_proto = __bpf_constant_htons(ETH_P_IP),
210 			.is_frag = true,
211 			.is_first_frag = true,
212 			.sport = 80,
213 			.dport = 8080,
214 		},
215 		.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
216 	},
217 	{
218 		.name = "ipv4-no-frag",
219 		.pkt.ipv4 = {
220 			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
221 			.iph.ihl = 5,
222 			.iph.protocol = IPPROTO_TCP,
223 			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
224 			.iph.frag_off = __bpf_constant_htons(IP_MF),
225 			.tcp.doff = 5,
226 			.tcp.source = 80,
227 			.tcp.dest = 8080,
228 		},
229 		.keys = {
230 			.nhoff = ETH_HLEN,
231 			.thoff = ETH_HLEN + sizeof(struct iphdr),
232 			.addr_proto = ETH_P_IP,
233 			.ip_proto = IPPROTO_TCP,
234 			.n_proto = __bpf_constant_htons(ETH_P_IP),
235 			.is_frag = true,
236 			.is_first_frag = true,
237 		},
238 	},
239 	{
240 		.name = "ipv6-frag",
241 		.pkt.ipv6_frag = {
242 			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
243 			.iph.nexthdr = IPPROTO_FRAGMENT,
244 			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
245 			.ipf.nexthdr = IPPROTO_TCP,
246 			.tcp.doff = 5,
247 			.tcp.source = 80,
248 			.tcp.dest = 8080,
249 		},
250 		.keys = {
251 			.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
252 			.nhoff = ETH_HLEN,
253 			.thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
254 				sizeof(struct frag_hdr),
255 			.addr_proto = ETH_P_IPV6,
256 			.ip_proto = IPPROTO_TCP,
257 			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
258 			.is_frag = true,
259 			.is_first_frag = true,
260 			.sport = 80,
261 			.dport = 8080,
262 		},
263 		.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
264 	},
265 	{
266 		.name = "ipv6-no-frag",
267 		.pkt.ipv6_frag = {
268 			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
269 			.iph.nexthdr = IPPROTO_FRAGMENT,
270 			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
271 			.ipf.nexthdr = IPPROTO_TCP,
272 			.tcp.doff = 5,
273 			.tcp.source = 80,
274 			.tcp.dest = 8080,
275 		},
276 		.keys = {
277 			.nhoff = ETH_HLEN,
278 			.thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
279 				sizeof(struct frag_hdr),
280 			.addr_proto = ETH_P_IPV6,
281 			.ip_proto = IPPROTO_TCP,
282 			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
283 			.is_frag = true,
284 			.is_first_frag = true,
285 		},
286 	},
287 	{
288 		.name = "ipv6-flow-label",
289 		.pkt.ipv6 = {
290 			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
291 			.iph.nexthdr = IPPROTO_TCP,
292 			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
293 			.iph.flow_lbl = { 0xb, 0xee, 0xef },
294 			.tcp.doff = 5,
295 			.tcp.source = 80,
296 			.tcp.dest = 8080,
297 		},
298 		.keys = {
299 			.nhoff = ETH_HLEN,
300 			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
301 			.addr_proto = ETH_P_IPV6,
302 			.ip_proto = IPPROTO_TCP,
303 			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
304 			.sport = 80,
305 			.dport = 8080,
306 			.flow_label = __bpf_constant_htonl(0xbeeef),
307 		},
308 	},
309 	{
310 		.name = "ipv6-no-flow-label",
311 		.pkt.ipv6 = {
312 			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
313 			.iph.nexthdr = IPPROTO_TCP,
314 			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
315 			.iph.flow_lbl = { 0xb, 0xee, 0xef },
316 			.tcp.doff = 5,
317 			.tcp.source = 80,
318 			.tcp.dest = 8080,
319 		},
320 		.keys = {
321 			.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
322 			.nhoff = ETH_HLEN,
323 			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
324 			.addr_proto = ETH_P_IPV6,
325 			.ip_proto = IPPROTO_TCP,
326 			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
327 			.flow_label = __bpf_constant_htonl(0xbeeef),
328 		},
329 		.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
330 	},
331 	{
332 		.name = "ipip-encap",
333 		.pkt.ipip = {
334 			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
335 			.iph.ihl = 5,
336 			.iph.protocol = IPPROTO_IPIP,
337 			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
338 			.iph_inner.ihl = 5,
339 			.iph_inner.protocol = IPPROTO_TCP,
340 			.iph_inner.tot_len =
341 				__bpf_constant_htons(MAGIC_BYTES) -
342 				sizeof(struct iphdr),
343 			.tcp.doff = 5,
344 			.tcp.source = 80,
345 			.tcp.dest = 8080,
346 		},
347 		.keys = {
348 			.nhoff = ETH_HLEN,
349 			.thoff = ETH_HLEN + sizeof(struct iphdr) +
350 				sizeof(struct iphdr),
351 			.addr_proto = ETH_P_IP,
352 			.ip_proto = IPPROTO_TCP,
353 			.n_proto = __bpf_constant_htons(ETH_P_IP),
354 			.is_encap = true,
355 			.sport = 80,
356 			.dport = 8080,
357 		},
358 	},
359 	{
360 		.name = "ipip-no-encap",
361 		.pkt.ipip = {
362 			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
363 			.iph.ihl = 5,
364 			.iph.protocol = IPPROTO_IPIP,
365 			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
366 			.iph_inner.ihl = 5,
367 			.iph_inner.protocol = IPPROTO_TCP,
368 			.iph_inner.tot_len =
369 				__bpf_constant_htons(MAGIC_BYTES) -
370 				sizeof(struct iphdr),
371 			.tcp.doff = 5,
372 			.tcp.source = 80,
373 			.tcp.dest = 8080,
374 		},
375 		.keys = {
376 			.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
377 			.nhoff = ETH_HLEN,
378 			.thoff = ETH_HLEN + sizeof(struct iphdr),
379 			.addr_proto = ETH_P_IP,
380 			.ip_proto = IPPROTO_IPIP,
381 			.n_proto = __bpf_constant_htons(ETH_P_IP),
382 			.is_encap = true,
383 		},
384 		.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
385 	},
386 };
387 
388 static int create_tap(const char *ifname)
389 {
390 	struct ifreq ifr = {
391 		.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS,
392 	};
393 	int fd, ret;
394 
395 	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
396 
397 	fd = open("/dev/net/tun", O_RDWR);
398 	if (fd < 0)
399 		return -1;
400 
401 	ret = ioctl(fd, TUNSETIFF, &ifr);
402 	if (ret)
403 		return -1;
404 
405 	return fd;
406 }
407 
408 static int tx_tap(int fd, void *pkt, size_t len)
409 {
410 	struct iovec iov[] = {
411 		{
412 			.iov_len = len,
413 			.iov_base = pkt,
414 		},
415 	};
416 	return writev(fd, iov, ARRAY_SIZE(iov));
417 }
418 
419 static int ifup(const char *ifname)
420 {
421 	struct ifreq ifr = {};
422 	int sk, ret;
423 
424 	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
425 
426 	sk = socket(PF_INET, SOCK_DGRAM, 0);
427 	if (sk < 0)
428 		return -1;
429 
430 	ret = ioctl(sk, SIOCGIFFLAGS, &ifr);
431 	if (ret) {
432 		close(sk);
433 		return -1;
434 	}
435 
436 	ifr.ifr_flags |= IFF_UP;
437 	ret = ioctl(sk, SIOCSIFFLAGS, &ifr);
438 	if (ret) {
439 		close(sk);
440 		return -1;
441 	}
442 
443 	close(sk);
444 	return 0;
445 }
446 
447 void test_flow_dissector(void)
448 {
449 	int i, err, prog_fd, keys_fd = -1, tap_fd;
450 	struct bpf_object *obj;
451 	__u32 duration = 0;
452 
453 	err = bpf_flow_load(&obj, "./bpf_flow.o", "flow_dissector",
454 			    "jmp_table", "last_dissection", &prog_fd, &keys_fd);
455 	if (CHECK_FAIL(err))
456 		return;
457 
458 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
459 		struct bpf_flow_keys flow_keys;
460 		struct bpf_prog_test_run_attr tattr = {
461 			.prog_fd = prog_fd,
462 			.data_in = &tests[i].pkt,
463 			.data_size_in = sizeof(tests[i].pkt),
464 			.data_out = &flow_keys,
465 		};
466 		static struct bpf_flow_keys ctx = {};
467 
468 		if (tests[i].flags) {
469 			tattr.ctx_in = &ctx;
470 			tattr.ctx_size_in = sizeof(ctx);
471 			ctx.flags = tests[i].flags;
472 		}
473 
474 		err = bpf_prog_test_run_xattr(&tattr);
475 		CHECK_ATTR(tattr.data_size_out != sizeof(flow_keys) ||
476 			   err || tattr.retval != 1,
477 			   tests[i].name,
478 			   "err %d errno %d retval %d duration %d size %u/%lu\n",
479 			   err, errno, tattr.retval, tattr.duration,
480 			   tattr.data_size_out, sizeof(flow_keys));
481 		CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
482 	}
483 
484 	/* Do the same tests but for skb-less flow dissector.
485 	 * We use a known path in the net/tun driver that calls
486 	 * eth_get_headlen and we manually export bpf_flow_keys
487 	 * via BPF map in this case.
488 	 */
489 
490 	err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
491 	CHECK(err, "bpf_prog_attach", "err %d errno %d\n", err, errno);
492 
493 	tap_fd = create_tap("tap0");
494 	CHECK(tap_fd < 0, "create_tap", "tap_fd %d errno %d\n", tap_fd, errno);
495 	err = ifup("tap0");
496 	CHECK(err, "ifup", "err %d errno %d\n", err, errno);
497 
498 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
499 		/* Keep in sync with 'flags' from eth_get_headlen. */
500 		__u32 eth_get_headlen_flags =
501 			BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
502 		struct bpf_prog_test_run_attr tattr = {};
503 		struct bpf_flow_keys flow_keys = {};
504 		__u32 key = (__u32)(tests[i].keys.sport) << 16 |
505 			    tests[i].keys.dport;
506 
507 		/* For skb-less case we can't pass input flags; run
508 		 * only the tests that have a matching set of flags.
509 		 */
510 
511 		if (tests[i].flags != eth_get_headlen_flags)
512 			continue;
513 
514 		err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt));
515 		CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno);
516 
517 		err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
518 		CHECK_ATTR(err, tests[i].name, "bpf_map_lookup_elem %d\n", err);
519 
520 		CHECK_ATTR(err, tests[i].name, "skb-less err %d\n", err);
521 		CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
522 
523 		err = bpf_map_delete_elem(keys_fd, &key);
524 		CHECK_ATTR(err, tests[i].name, "bpf_map_delete_elem %d\n", err);
525 	}
526 
527 	bpf_prog_detach(prog_fd, BPF_FLOW_DISSECTOR);
528 	bpf_object__close(obj);
529 }
530