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 = 0,
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 (err) {
456 		error_cnt++;
457 		return;
458 	}
459 
460 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
461 		struct bpf_flow_keys flow_keys;
462 		struct bpf_prog_test_run_attr tattr = {
463 			.prog_fd = prog_fd,
464 			.data_in = &tests[i].pkt,
465 			.data_size_in = sizeof(tests[i].pkt),
466 			.data_out = &flow_keys,
467 		};
468 		static struct bpf_flow_keys ctx = {};
469 
470 		if (tests[i].flags) {
471 			tattr.ctx_in = &ctx;
472 			tattr.ctx_size_in = sizeof(ctx);
473 			ctx.flags = tests[i].flags;
474 		}
475 
476 		err = bpf_prog_test_run_xattr(&tattr);
477 		CHECK_ATTR(tattr.data_size_out != sizeof(flow_keys) ||
478 			   err || tattr.retval != 1,
479 			   tests[i].name,
480 			   "err %d errno %d retval %d duration %d size %u/%lu\n",
481 			   err, errno, tattr.retval, tattr.duration,
482 			   tattr.data_size_out, sizeof(flow_keys));
483 		CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
484 	}
485 
486 	/* Do the same tests but for skb-less flow dissector.
487 	 * We use a known path in the net/tun driver that calls
488 	 * eth_get_headlen and we manually export bpf_flow_keys
489 	 * via BPF map in this case.
490 	 */
491 
492 	err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
493 	CHECK(err, "bpf_prog_attach", "err %d errno %d\n", err, errno);
494 
495 	tap_fd = create_tap("tap0");
496 	CHECK(tap_fd < 0, "create_tap", "tap_fd %d errno %d\n", tap_fd, errno);
497 	err = ifup("tap0");
498 	CHECK(err, "ifup", "err %d errno %d\n", err, errno);
499 
500 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
501 		/* Keep in sync with 'flags' from eth_get_headlen. */
502 		__u32 eth_get_headlen_flags =
503 			BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
504 		struct bpf_prog_test_run_attr tattr = {};
505 		struct bpf_flow_keys flow_keys = {};
506 		__u32 key = (__u32)(tests[i].keys.sport) << 16 |
507 			    tests[i].keys.dport;
508 
509 		/* For skb-less case we can't pass input flags; run
510 		 * only the tests that have a matching set of flags.
511 		 */
512 
513 		if (tests[i].flags != eth_get_headlen_flags)
514 			continue;
515 
516 		err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt));
517 		CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno);
518 
519 		err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
520 		CHECK_ATTR(err, tests[i].name, "bpf_map_lookup_elem %d\n", err);
521 
522 		CHECK_ATTR(err, tests[i].name, "skb-less err %d\n", err);
523 		CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
524 
525 		err = bpf_map_delete_elem(keys_fd, &key);
526 		CHECK_ATTR(err, tests[i].name, "bpf_map_delete_elem %d\n", err);
527 	}
528 
529 	bpf_prog_detach(prog_fd, BPF_FLOW_DISSECTOR);
530 	bpf_object__close(obj);
531 }
532