File Index
Files and directories
Sign in
FILE

flow_dissector.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/bpf/prog_tests/flow_dissector.c C
  1// SPDX-License-Identifier: GPL-2.0
  2#define _GNU_SOURCE
  3#include <test_progs.h>
  4#include <network_helpers.h>
  5#include <linux/if_tun.h>
  6#include <sys/uio.h>
  7
  8#include "bpf_flow.skel.h"
  9
 10#define TEST_NS	"flow_dissector_ns"
 11#define FLOW_CONTINUE_SADDR 0x7f00007f /* 127.0.0.127 */
 12#define TEST_NAME_MAX_LEN	64
 13
 14#ifndef IP_MF
 15#define IP_MF 0x2000
 16#endif
 17
 18struct ipv4_pkt {
 19	struct ethhdr eth;
 20	struct iphdr iph;
 21	struct tcphdr tcp;
 22} __packed;
 23
 24struct ipip_pkt {
 25	struct ethhdr eth;
 26	struct iphdr iph;
 27	struct iphdr iph_inner;
 28	struct tcphdr tcp;
 29} __packed;
 30
 31struct svlan_ipv4_pkt {
 32	struct ethhdr eth;
 33	__u16 vlan_tci;
 34	__u16 vlan_proto;
 35	struct iphdr iph;
 36	struct tcphdr tcp;
 37} __packed;
 38
 39struct ipv6_pkt {
 40	struct ethhdr eth;
 41	struct ipv6hdr iph;
 42	struct tcphdr tcp;
 43} __packed;
 44
 45struct ipv6_frag_pkt {
 46	struct ethhdr eth;
 47	struct ipv6hdr iph;
 48	struct frag_hdr {
 49		__u8 nexthdr;
 50		__u8 reserved;
 51		__be16 frag_off;
 52		__be32 identification;
 53	} ipf;
 54	struct tcphdr tcp;
 55} __packed;
 56
 57struct dvlan_ipv6_pkt {
 58	struct ethhdr eth;
 59	__u16 vlan_tci;
 60	__u16 vlan_proto;
 61	__u16 vlan_tci2;
 62	__u16 vlan_proto2;
 63	struct ipv6hdr iph;
 64	struct tcphdr tcp;
 65} __packed;
 66
 67struct gre_base_hdr {
 68	__be16 flags;
 69	__be16 protocol;
 70} gre_base_hdr;
 71
 72struct gre_minimal_pkt {
 73	struct ethhdr eth;
 74	struct iphdr iph;
 75	struct gre_base_hdr gre_hdr;
 76	struct iphdr iph_inner;
 77	struct tcphdr tcp;
 78} __packed;
 79
 80struct test {
 81	const char *name;
 82	union {
 83		struct ipv4_pkt ipv4;
 84		struct svlan_ipv4_pkt svlan_ipv4;
 85		struct ipip_pkt ipip;
 86		struct ipv6_pkt ipv6;
 87		struct ipv6_frag_pkt ipv6_frag;
 88		struct dvlan_ipv6_pkt dvlan_ipv6;
 89		struct gre_minimal_pkt gre_minimal;
 90	} pkt;
 91	struct bpf_flow_keys keys;
 92	__u32 flags;
 93	__u32 retval;
 94};
 95
 96#define VLAN_HLEN	4
 97
 98struct test tests[] = {
 99	{
100		.name = "ipv4",
101		.pkt.ipv4 = {
102			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
103			.iph.ihl = 5,
104			.iph.protocol = IPPROTO_TCP,
105			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
106			.tcp.doff = 5,
107			.tcp.source = 80,
108			.tcp.dest = 8080,
109		},
110		.keys = {
111			.nhoff = ETH_HLEN,
112			.thoff = ETH_HLEN + sizeof(struct iphdr),
113			.addr_proto = ETH_P_IP,
114			.ip_proto = IPPROTO_TCP,
115			.n_proto = __bpf_constant_htons(ETH_P_IP),
116			.sport = 80,
117			.dport = 8080,
118		},
119		.retval = BPF_OK,
120	},
121	{
122		.name = "ipv6",
123		.pkt.ipv6 = {
124			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
125			.iph.nexthdr = IPPROTO_TCP,
126			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
127			.tcp.doff = 5,
128			.tcp.source = 80,
129			.tcp.dest = 8080,
130		},
131		.keys = {
132			.nhoff = ETH_HLEN,
133			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
134			.addr_proto = ETH_P_IPV6,
135			.ip_proto = IPPROTO_TCP,
136			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
137			.sport = 80,
138			.dport = 8080,
139		},
140		.retval = BPF_OK,
141	},
142	{
143		.name = "802.1q-ipv4",
144		.pkt.svlan_ipv4 = {
145			.eth.h_proto = __bpf_constant_htons(ETH_P_8021Q),
146			.vlan_proto = __bpf_constant_htons(ETH_P_IP),
147			.iph.ihl = 5,
148			.iph.protocol = IPPROTO_TCP,
149			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
150			.tcp.doff = 5,
151			.tcp.source = 80,
152			.tcp.dest = 8080,
153		},
154		.keys = {
155			.nhoff = ETH_HLEN + VLAN_HLEN,
156			.thoff = ETH_HLEN + VLAN_HLEN + sizeof(struct iphdr),
157			.addr_proto = ETH_P_IP,
158			.ip_proto = IPPROTO_TCP,
159			.n_proto = __bpf_constant_htons(ETH_P_IP),
160			.sport = 80,
161			.dport = 8080,
162		},
163		.retval = BPF_OK,
164	},
165	{
166		.name = "802.1ad-ipv6",
167		.pkt.dvlan_ipv6 = {
168			.eth.h_proto = __bpf_constant_htons(ETH_P_8021AD),
169			.vlan_proto = __bpf_constant_htons(ETH_P_8021Q),
170			.vlan_proto2 = __bpf_constant_htons(ETH_P_IPV6),
171			.iph.nexthdr = IPPROTO_TCP,
172			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
173			.tcp.doff = 5,
174			.tcp.source = 80,
175			.tcp.dest = 8080,
176		},
177		.keys = {
178			.nhoff = ETH_HLEN + VLAN_HLEN * 2,
179			.thoff = ETH_HLEN + VLAN_HLEN * 2 +
180				sizeof(struct ipv6hdr),
181			.addr_proto = ETH_P_IPV6,
182			.ip_proto = IPPROTO_TCP,
183			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
184			.sport = 80,
185			.dport = 8080,
186		},
187		.retval = BPF_OK,
188	},
189	{
190		.name = "ipv4-frag",
191		.pkt.ipv4 = {
192			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
193			.iph.ihl = 5,
194			.iph.protocol = IPPROTO_TCP,
195			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
196			.iph.frag_off = __bpf_constant_htons(IP_MF),
197			.tcp.doff = 5,
198			.tcp.source = 80,
199			.tcp.dest = 8080,
200		},
201		.keys = {
202			.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
203			.nhoff = ETH_HLEN,
204			.thoff = ETH_HLEN + sizeof(struct iphdr),
205			.addr_proto = ETH_P_IP,
206			.ip_proto = IPPROTO_TCP,
207			.n_proto = __bpf_constant_htons(ETH_P_IP),
208			.is_frag = true,
209			.is_first_frag = true,
210			.sport = 80,
211			.dport = 8080,
212		},
213		.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
214		.retval = BPF_OK,
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		.retval = BPF_OK,
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		.retval = BPF_OK,
265	},
266	{
267		.name = "ipv6-no-frag",
268		.pkt.ipv6_frag = {
269			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
270			.iph.nexthdr = IPPROTO_FRAGMENT,
271			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
272			.ipf.nexthdr = IPPROTO_TCP,
273			.tcp.doff = 5,
274			.tcp.source = 80,
275			.tcp.dest = 8080,
276		},
277		.keys = {
278			.nhoff = ETH_HLEN,
279			.thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
280				sizeof(struct frag_hdr),
281			.addr_proto = ETH_P_IPV6,
282			.ip_proto = IPPROTO_TCP,
283			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
284			.is_frag = true,
285			.is_first_frag = true,
286		},
287		.retval = BPF_OK,
288	},
289	{
290		.name = "ipv6-flow-label",
291		.pkt.ipv6 = {
292			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
293			.iph.nexthdr = IPPROTO_TCP,
294			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
295			.iph.flow_lbl = { 0xb, 0xee, 0xef },
296			.tcp.doff = 5,
297			.tcp.source = 80,
298			.tcp.dest = 8080,
299		},
300		.keys = {
301			.nhoff = ETH_HLEN,
302			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
303			.addr_proto = ETH_P_IPV6,
304			.ip_proto = IPPROTO_TCP,
305			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
306			.sport = 80,
307			.dport = 8080,
308			.flow_label = __bpf_constant_htonl(0xbeeef),
309		},
310		.retval = BPF_OK,
311	},
312	{
313		.name = "ipv6-no-flow-label",
314		.pkt.ipv6 = {
315			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
316			.iph.nexthdr = IPPROTO_TCP,
317			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
318			.iph.flow_lbl = { 0xb, 0xee, 0xef },
319			.tcp.doff = 5,
320			.tcp.source = 80,
321			.tcp.dest = 8080,
322		},
323		.keys = {
324			.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
325			.nhoff = ETH_HLEN,
326			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
327			.addr_proto = ETH_P_IPV6,
328			.ip_proto = IPPROTO_TCP,
329			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
330			.flow_label = __bpf_constant_htonl(0xbeeef),
331		},
332		.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
333		.retval = BPF_OK,
334	},
335	{
336		.name = "ipv6-empty-flow-label",
337		.pkt.ipv6 = {
338			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
339			.iph.nexthdr = IPPROTO_TCP,
340			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
341			.iph.flow_lbl = { 0x00, 0x00, 0x00 },
342			.tcp.doff = 5,
343			.tcp.source = 80,
344			.tcp.dest = 8080,
345		},
346		.keys = {
347			.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
348			.nhoff = ETH_HLEN,
349			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
350			.addr_proto = ETH_P_IPV6,
351			.ip_proto = IPPROTO_TCP,
352			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
353			.sport = 80,
354			.dport = 8080,
355		},
356		.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
357		.retval = BPF_OK,
358	},
359	{
360		.name = "ipip-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			.nhoff = ETH_HLEN,
377			.thoff = ETH_HLEN + sizeof(struct iphdr) +
378				sizeof(struct iphdr),
379			.addr_proto = ETH_P_IP,
380			.ip_proto = IPPROTO_TCP,
381			.n_proto = __bpf_constant_htons(ETH_P_IP),
382			.is_encap = true,
383			.sport = 80,
384			.dport = 8080,
385		},
386		.retval = BPF_OK,
387	},
388	{
389		.name = "ipip-no-encap",
390		.pkt.ipip = {
391			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
392			.iph.ihl = 5,
393			.iph.protocol = IPPROTO_IPIP,
394			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
395			.iph_inner.ihl = 5,
396			.iph_inner.protocol = IPPROTO_TCP,
397			.iph_inner.tot_len =
398				__bpf_constant_htons(MAGIC_BYTES -
399				sizeof(struct iphdr)),
400			.tcp.doff = 5,
401			.tcp.source = 80,
402			.tcp.dest = 8080,
403		},
404		.keys = {
405			.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
406			.nhoff = ETH_HLEN,
407			.thoff = ETH_HLEN + sizeof(struct iphdr),
408			.addr_proto = ETH_P_IP,
409			.ip_proto = IPPROTO_IPIP,
410			.n_proto = __bpf_constant_htons(ETH_P_IP),
411			.is_encap = true,
412		},
413		.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
414		.retval = BPF_OK,
415	},
416	{
417		.name = "ipip-encap-dissector-continue",
418		.pkt.ipip = {
419			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
420			.iph.ihl = 5,
421			.iph.protocol = IPPROTO_IPIP,
422			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
423			.iph.saddr = __bpf_constant_htonl(FLOW_CONTINUE_SADDR),
424			.iph_inner.ihl = 5,
425			.iph_inner.protocol = IPPROTO_TCP,
426			.iph_inner.tot_len =
427				__bpf_constant_htons(MAGIC_BYTES -
428				sizeof(struct iphdr)),
429			.tcp.doff = 5,
430			.tcp.source = 99,
431			.tcp.dest = 9090,
432		},
433		.retval = BPF_FLOW_DISSECTOR_CONTINUE,
434	},
435	{
436		.name = "ip-gre",
437		.pkt.gre_minimal = {
438			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
439			.iph.ihl = 5,
440			.iph.protocol = IPPROTO_GRE,
441			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
442			.gre_hdr = {
443				.flags = 0,
444				.protocol = __bpf_constant_htons(ETH_P_IP),
445			},
446			.iph_inner.ihl = 5,
447			.iph_inner.protocol = IPPROTO_TCP,
448			.iph_inner.tot_len =
449				__bpf_constant_htons(MAGIC_BYTES -
450				sizeof(struct iphdr)),
451			.tcp.doff = 5,
452			.tcp.source = 80,
453			.tcp.dest = 8080,
454		},
455		.keys = {
456			.nhoff = ETH_HLEN,
457			.thoff = ETH_HLEN + sizeof(struct iphdr) * 2 +
458				 sizeof(struct gre_base_hdr),
459			.addr_proto = ETH_P_IP,
460			.ip_proto = IPPROTO_TCP,
461			.n_proto = __bpf_constant_htons(ETH_P_IP),
462			.is_encap = true,
463			.sport = 80,
464			.dport = 8080,
465		},
466		.retval = BPF_OK,
467	},
468	{
469		.name = "ip-gre-no-encap",
470		.pkt.ipip = {
471			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
472			.iph.ihl = 5,
473			.iph.protocol = IPPROTO_GRE,
474			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
475			.iph_inner.ihl = 5,
476			.iph_inner.protocol = IPPROTO_TCP,
477			.iph_inner.tot_len =
478				__bpf_constant_htons(MAGIC_BYTES -
479				sizeof(struct iphdr)),
480			.tcp.doff = 5,
481			.tcp.source = 80,
482			.tcp.dest = 8080,
483		},
484		.keys = {
485			.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
486			.nhoff = ETH_HLEN,
487			.thoff = ETH_HLEN + sizeof(struct iphdr)
488				 + sizeof(struct gre_base_hdr),
489			.addr_proto = ETH_P_IP,
490			.ip_proto = IPPROTO_GRE,
491			.n_proto = __bpf_constant_htons(ETH_P_IP),
492			.is_encap = true,
493		},
494		.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
495		.retval = BPF_OK,
496	},
497};
498
499void serial_test_flow_dissector_namespace(void)
500{
501	struct bpf_flow *skel;
502	struct nstoken *ns;
503	int err, prog_fd;
504
505	skel = bpf_flow__open_and_load();
506	if (!ASSERT_OK_PTR(skel, "open/load skeleton"))
507		return;
508
509	prog_fd = bpf_program__fd(skel->progs._dissect);
510	if (!ASSERT_OK_FD(prog_fd, "get dissector fd"))
511		goto out_destroy_skel;
512
513	/* We must be able to attach a flow dissector to root namespace */
514	err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
515	if (!ASSERT_OK(err, "attach on root namespace ok"))
516		goto out_destroy_skel;
517
518	err = make_netns(TEST_NS);
519	if (!ASSERT_OK(err, "create non-root net namespace"))
520		goto out_destroy_skel;
521
522	/* We must not be able to additionally attach a flow dissector to a
523	 * non-root net namespace
524	 */
525	ns = open_netns(TEST_NS);
526	if (!ASSERT_OK_PTR(ns, "enter non-root net namespace"))
527		goto out_clean_ns;
528	err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
529	if (!ASSERT_ERR(err,
530			"refuse new flow dissector in non-root net namespace"))
531		bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
532	else
533		ASSERT_EQ(errno, EEXIST,
534			  "refused because of already attached prog");
535	close_netns(ns);
536
537	/* If no flow dissector is attached to the root namespace, we must
538	 * be able to attach one to a non-root net namespace
539	 */
540	bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
541	ns = open_netns(TEST_NS);
542	ASSERT_OK_PTR(ns, "enter non-root net namespace");
543	err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
544	close_netns(ns);
545	ASSERT_OK(err, "accept new flow dissector in non-root net namespace");
546
547	/* If a flow dissector is attached to non-root net namespace, attaching
548	 * a flow dissector to root namespace must fail
549	 */
550	err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
551	if (!ASSERT_ERR(err, "refuse new flow dissector on root namespace"))
552		bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
553	else
554		ASSERT_EQ(errno, EEXIST,
555			  "refused because of already attached prog");
556
557	ns = open_netns(TEST_NS);
558	bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
559	close_netns(ns);
560out_clean_ns:
561	remove_netns(TEST_NS);
562out_destroy_skel:
563	bpf_flow__destroy(skel);
564}
565
566static int create_tap(const char *ifname)
567{
568	struct ifreq ifr = {
569		.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS,
570	};
571	int fd, ret;
572
573	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
574
575	fd = open("/dev/net/tun", O_RDWR);
576	if (fd < 0)
577		return -1;
578
579	ret = ioctl(fd, TUNSETIFF, &ifr);
580	if (ret)
581		return -1;
582
583	return fd;
584}
585
586static int tx_tap(int fd, void *pkt, size_t len)
587{
588	struct iovec iov[] = {
589		{
590			.iov_len = len,
591			.iov_base = pkt,
592		},
593	};
594	return writev(fd, iov, ARRAY_SIZE(iov));
595}
596
597static int ifup(const char *ifname)
598{
599	struct ifreq ifr = {};
600	int sk, ret;
601
602	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
603
604	sk = socket(PF_INET, SOCK_DGRAM, 0);
605	if (sk < 0)
606		return -1;
607
608	ret = ioctl(sk, SIOCGIFFLAGS, &ifr);
609	if (ret) {
610		close(sk);
611		return -1;
612	}
613
614	ifr.ifr_flags |= IFF_UP;
615	ret = ioctl(sk, SIOCSIFFLAGS, &ifr);
616	if (ret) {
617		close(sk);
618		return -1;
619	}
620
621	close(sk);
622	return 0;
623}
624
625static int init_prog_array(struct bpf_object *obj, struct bpf_map *prog_array)
626{
627	int i, err, map_fd, prog_fd;
628	struct bpf_program *prog;
629	char prog_name[32];
630
631	map_fd = bpf_map__fd(prog_array);
632	if (map_fd < 0)
633		return -1;
634
635	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
636		snprintf(prog_name, sizeof(prog_name), "flow_dissector_%d", i);
637
638		prog = bpf_object__find_program_by_name(obj, prog_name);
639		if (!prog)
640			return -1;
641
642		prog_fd = bpf_program__fd(prog);
643		if (prog_fd < 0)
644			return -1;
645
646		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
647		if (err)
648			return -1;
649	}
650	return 0;
651}
652
653static void run_tests_skb_less(int tap_fd, struct bpf_map *keys,
654			       char *test_suffix)
655{
656	char test_name[TEST_NAME_MAX_LEN];
657	int i, err, keys_fd;
658
659	keys_fd = bpf_map__fd(keys);
660	if (!ASSERT_OK_FD(keys_fd, "bpf_map__fd"))
661		return;
662
663	for (i = 0; i < ARRAY_SIZE(tests); i++) {
664		/* Keep in sync with 'flags' from eth_get_headlen. */
665		__u32 eth_get_headlen_flags =
666			BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
667		struct bpf_flow_keys flow_keys = {};
668		__u32 key = (__u32)(tests[i].keys.sport) << 16 |
669			    tests[i].keys.dport;
670		snprintf(test_name, TEST_NAME_MAX_LEN, "%s-%s", tests[i].name,
671			 test_suffix);
672		if (!test__start_subtest(test_name))
673			continue;
674
675		/* For skb-less case we can't pass input flags; run
676		 * only the tests that have a matching set of flags.
677		 */
678
679		if (tests[i].flags != eth_get_headlen_flags)
680			continue;
681
682		err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt));
683		if (!ASSERT_EQ(err, sizeof(tests[i].pkt), "tx_tap"))
684			continue;
685
686		/* check the stored flow_keys only if BPF_OK expected */
687		if (tests[i].retval != BPF_OK)
688			continue;
689
690		err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
691		if (!ASSERT_OK(err, "bpf_map_lookup_elem"))
692			continue;
693
694		ASSERT_MEMEQ(&flow_keys, &tests[i].keys,
695			     sizeof(struct bpf_flow_keys),
696			     "returned flow keys");
697
698		err = bpf_map_delete_elem(keys_fd, &key);
699		ASSERT_OK(err, "bpf_map_delete_elem");
700	}
701}
702
703void test_flow_dissector_skb_less_direct_attach(void)
704{
705	int err, prog_fd, tap_fd;
706	struct bpf_flow *skel;
707	struct netns_obj *ns;
708
709	ns = netns_new("flow_dissector_skb_less_indirect_attach_ns", true);
710	if (!ASSERT_OK_PTR(ns, "create and open netns"))
711		return;
712
713	skel = bpf_flow__open_and_load();
714	if (!ASSERT_OK_PTR(skel, "open/load skeleton"))
715		goto out_clean_ns;
716
717	err = init_prog_array(skel->obj, skel->maps.jmp_table);
718	if (!ASSERT_OK(err, "init_prog_array"))
719		goto out_destroy_skel;
720
721	prog_fd = bpf_program__fd(skel->progs._dissect);
722	if (!ASSERT_OK_FD(prog_fd, "bpf_program__fd"))
723		goto out_destroy_skel;
724
725	err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
726	if (!ASSERT_OK(err, "bpf_prog_attach"))
727		goto out_destroy_skel;
728
729	tap_fd = create_tap("tap0");
730	if (!ASSERT_OK_FD(tap_fd, "create_tap"))
731		goto out_destroy_skel;
732	err = ifup("tap0");
733	if (!ASSERT_OK(err, "ifup"))
734		goto out_close_tap;
735
736	run_tests_skb_less(tap_fd, skel->maps.last_dissection,
737			   "non-skb-direct-attach");
738
739	err = bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
740	ASSERT_OK(err, "bpf_prog_detach2");
741
742out_close_tap:
743	close(tap_fd);
744out_destroy_skel:
745	bpf_flow__destroy(skel);
746out_clean_ns:
747	netns_free(ns);
748}
749
750void test_flow_dissector_skb_less_indirect_attach(void)
751{
752	int err, net_fd, tap_fd;
753	struct bpf_flow *skel;
754	struct bpf_link *link;
755	struct netns_obj *ns;
756
757	ns = netns_new("flow_dissector_skb_less_indirect_attach_ns", true);
758	if (!ASSERT_OK_PTR(ns, "create and open netns"))
759		return;
760
761	skel = bpf_flow__open_and_load();
762	if (!ASSERT_OK_PTR(skel, "open/load skeleton"))
763		goto out_clean_ns;
764
765	net_fd = open("/proc/self/ns/net", O_RDONLY);
766	if (!ASSERT_OK_FD(net_fd, "open(/proc/self/ns/net"))
767		goto out_destroy_skel;
768
769	err = init_prog_array(skel->obj, skel->maps.jmp_table);
770	if (!ASSERT_OK(err, "init_prog_array"))
771		goto out_destroy_skel;
772
773	tap_fd = create_tap("tap0");
774	if (!ASSERT_OK_FD(tap_fd, "create_tap"))
775		goto out_close_ns;
776	err = ifup("tap0");
777	if (!ASSERT_OK(err, "ifup"))
778		goto out_close_tap;
779
780	link = bpf_program__attach_netns(skel->progs._dissect, net_fd);
781	if (!ASSERT_OK_PTR(link, "attach_netns"))
782		goto out_close_tap;
783
784	run_tests_skb_less(tap_fd, skel->maps.last_dissection,
785			   "non-skb-indirect-attach");
786
787	err = bpf_link__destroy(link);
788	ASSERT_OK(err, "bpf_link__destroy");
789
790out_close_tap:
791	close(tap_fd);
792out_close_ns:
793	close(net_fd);
794out_destroy_skel:
795	bpf_flow__destroy(skel);
796out_clean_ns:
797	netns_free(ns);
798}
799
800void test_flow_dissector_skb(void)
801{
802	char test_name[TEST_NAME_MAX_LEN];
803	struct bpf_flow *skel;
804	int i, err, prog_fd;
805
806	skel = bpf_flow__open_and_load();
807	if (!ASSERT_OK_PTR(skel, "open/load skeleton"))
808		return;
809
810	err = init_prog_array(skel->obj, skel->maps.jmp_table);
811	if (!ASSERT_OK(err, "init_prog_array"))
812		goto out_destroy_skel;
813
814	prog_fd = bpf_program__fd(skel->progs._dissect);
815	if (!ASSERT_OK_FD(prog_fd, "bpf_program__fd"))
816		goto out_destroy_skel;
817
818	for (i = 0; i < ARRAY_SIZE(tests); i++) {
819		struct bpf_flow_keys flow_keys;
820		LIBBPF_OPTS(bpf_test_run_opts, topts,
821			.data_in = &tests[i].pkt,
822			.data_size_in = sizeof(tests[i].pkt),
823			.data_out = &flow_keys,
824		);
825		static struct bpf_flow_keys ctx = {};
826
827		snprintf(test_name, TEST_NAME_MAX_LEN, "%s-skb", tests[i].name);
828		if (!test__start_subtest(test_name))
829			continue;
830
831		if (tests[i].flags) {
832			topts.ctx_in = &ctx;
833			topts.ctx_size_in = sizeof(ctx);
834			ctx.flags = tests[i].flags;
835		}
836
837		err = bpf_prog_test_run_opts(prog_fd, &topts);
838		ASSERT_OK(err, "test_run");
839		ASSERT_EQ(topts.retval, tests[i].retval, "test_run retval");
840
841		/* check the resulting flow_keys only if BPF_OK returned */
842		if (topts.retval != BPF_OK)
843			continue;
844		ASSERT_EQ(topts.data_size_out, sizeof(flow_keys),
845			  "test_run data_size_out");
846		ASSERT_MEMEQ(&flow_keys, &tests[i].keys,
847			     sizeof(struct bpf_flow_keys),
848			     "returned flow keys");
849	}
850
851out_destroy_skel:
852	bpf_flow__destroy(skel);
853}
854
~/ Read-only file preview.