File Index
Files and directories
Sign in
FILE

flow_dissector_classification.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/bpf/prog_tests/flow_dissector_classification.c C
  1// SPDX-License-Identifier: GPL-2.0
  2
  3#define _GNU_SOURCE
  4#include <stdbool.h>
  5#include <stdlib.h>
  6#include <stdio.h>
  7#include <bpf/bpf.h>
  8#include <linux/bpf.h>
  9#include <bpf/libbpf.h>
 10#include <arpa/inet.h>
 11#include <asm/byteorder.h>
 12#include <netinet/udp.h>
 13#include <poll.h>
 14#include <string.h>
 15#include <sys/ioctl.h>
 16#include <sys/socket.h>
 17#include <sys/time.h>
 18#include <unistd.h>
 19#include "test_progs.h"
 20#include "network_helpers.h"
 21#include "bpf_util.h"
 22#include "bpf_flow.skel.h"
 23
 24#define CFG_PORT_INNER 8000
 25#define CFG_PORT_GUE 6080
 26#define SUBTEST_NAME_MAX_LEN 32
 27#define TEST_NAME_MAX_LEN (32 + SUBTEST_NAME_MAX_LEN)
 28#define MAX_SOURCE_PORTS 3
 29#define TEST_PACKETS_COUNT 10
 30#define TEST_PACKET_LEN 100
 31#define TEST_PACKET_PATTERN 'a'
 32#define TEST_IPV4 "192.168.0.1/32"
 33#define TEST_IPV6 "100::a/128"
 34#define TEST_TUNNEL_REMOTE "127.0.0.2"
 35#define TEST_TUNNEL_LOCAL "127.0.0.1"
 36
 37#define INIT_ADDR4(addr4, port)					\
 38	{							\
 39		.sin_family = AF_INET,				\
 40		.sin_port = __constant_htons(port),		\
 41		.sin_addr.s_addr = __constant_htonl(addr4),	\
 42	}
 43
 44#define INIT_ADDR6(addr6, port)				\
 45	{						\
 46		.sin6_family = AF_INET6,		\
 47		.sin6_port = __constant_htons(port),	\
 48		.sin6_addr = addr6,			\
 49	}
 50#define TEST_IN4_SRC_ADDR_DEFAULT INIT_ADDR4(INADDR_LOOPBACK + 2, 0)
 51#define TEST_IN4_DST_ADDR_DEFAULT INIT_ADDR4(INADDR_LOOPBACK, CFG_PORT_INNER)
 52#define TEST_OUT4_SRC_ADDR_DEFAULT INIT_ADDR4(INADDR_LOOPBACK + 1, 0)
 53#define TEST_OUT4_DST_ADDR_DEFAULT INIT_ADDR4(INADDR_LOOPBACK, 0)
 54
 55#define TEST_IN6_SRC_ADDR_DEFAULT INIT_ADDR6(IN6ADDR_LOOPBACK_INIT, 0)
 56#define TEST_IN6_DST_ADDR_DEFAULT \
 57	INIT_ADDR6(IN6ADDR_LOOPBACK_INIT, CFG_PORT_INNER)
 58#define TEST_OUT6_SRC_ADDR_DEFAULT INIT_ADDR6(IN6ADDR_LOOPBACK_INIT, 0)
 59#define TEST_OUT6_DST_ADDR_DEFAULT INIT_ADDR6(IN6ADDR_LOOPBACK_INIT, 0)
 60
 61#define TEST_IN4_SRC_ADDR_DISSECT_CONTINUE INIT_ADDR4(INADDR_LOOPBACK + 126, 0)
 62#define TEST_IN4_SRC_ADDR_IPIP INIT_ADDR4((in_addr_t)0x01010101, 0)
 63#define TEST_IN4_DST_ADDR_IPIP INIT_ADDR4((in_addr_t)0xC0A80001, CFG_PORT_INNER)
 64
 65struct grehdr {
 66	uint16_t unused;
 67	uint16_t protocol;
 68} __packed;
 69
 70struct guehdr {
 71	union {
 72		struct {
 73#if defined(__LITTLE_ENDIAN_BITFIELD)
 74			__u8 hlen : 5, control : 1, version : 2;
 75#elif defined(__BIG_ENDIAN_BITFIELD)
 76			__u8 version : 2, control : 1, hlen : 5;
 77#else
 78#error "Please fix <asm/byteorder.h>"
 79#endif
 80			__u8 proto_ctype;
 81			__be16 flags;
 82		};
 83		__be32 word;
 84	};
 85};
 86
 87static char buf[ETH_DATA_LEN];
 88
 89struct test_configuration {
 90	char name[SUBTEST_NAME_MAX_LEN];
 91	int (*test_setup)(void);
 92	void (*test_teardown)(void);
 93	int source_ports[MAX_SOURCE_PORTS];
 94	int cfg_l3_inner;
 95	struct sockaddr_in in_saddr4;
 96	struct sockaddr_in in_daddr4;
 97	struct sockaddr_in6 in_saddr6;
 98	struct sockaddr_in6 in_daddr6;
 99	int cfg_l3_outer;
100	struct sockaddr_in out_saddr4;
101	struct sockaddr_in out_daddr4;
102	struct sockaddr_in6 out_saddr6;
103	struct sockaddr_in6 out_daddr6;
104	int cfg_encap_proto;
105	uint8_t cfg_dsfield_inner;
106	uint8_t cfg_dsfield_outer;
107	int cfg_l3_extra;
108	struct sockaddr_in extra_saddr4;
109	struct sockaddr_in extra_daddr4;
110	struct sockaddr_in6 extra_saddr6;
111	struct sockaddr_in6 extra_daddr6;
112};
113
114static unsigned long util_gettime(void)
115{
116	struct timeval tv;
117
118	gettimeofday(&tv, NULL);
119	return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
120}
121
122static void build_ipv4_header(void *header, uint8_t proto, uint32_t src,
123			      uint32_t dst, int payload_len, uint8_t tos)
124{
125	struct iphdr *iph = header;
126
127	iph->ihl = 5;
128	iph->version = 4;
129	iph->tos = tos;
130	iph->ttl = 8;
131	iph->tot_len = htons(sizeof(*iph) + payload_len);
132	iph->id = htons(1337);
133	iph->protocol = proto;
134	iph->saddr = src;
135	iph->daddr = dst;
136	iph->check = build_ip_csum((void *)iph);
137}
138
139static void ipv6_set_dsfield(struct ipv6hdr *ip6h, uint8_t dsfield)
140{
141	uint16_t val, *ptr = (uint16_t *)ip6h;
142
143	val = ntohs(*ptr);
144	val &= 0xF00F;
145	val |= ((uint16_t)dsfield) << 4;
146	*ptr = htons(val);
147}
148
149static void build_ipv6_header(void *header, uint8_t proto,
150			      const struct sockaddr_in6 *src,
151			      const struct sockaddr_in6 *dst, int payload_len,
152			      uint8_t dsfield)
153{
154	struct ipv6hdr *ip6h = header;
155
156	ip6h->version = 6;
157	ip6h->payload_len = htons(payload_len);
158	ip6h->nexthdr = proto;
159	ip6h->hop_limit = 8;
160	ipv6_set_dsfield(ip6h, dsfield);
161
162	memcpy(&ip6h->saddr, &src->sin6_addr, sizeof(ip6h->saddr));
163	memcpy(&ip6h->daddr, &dst->sin6_addr, sizeof(ip6h->daddr));
164}
165
166static void build_udp_header(void *header, int payload_len, uint16_t sport,
167			     uint16_t dport, int family)
168{
169	struct udphdr *udph = header;
170	int len = sizeof(*udph) + payload_len;
171
172	udph->source = htons(sport);
173	udph->dest = htons(dport);
174	udph->len = htons(len);
175	udph->check = 0;
176	if (family == AF_INET)
177		udph->check = build_udp_v4_csum(header - sizeof(struct iphdr),
178						udph);
179	else
180		udph->check = build_udp_v6_csum(header - sizeof(struct ipv6hdr),
181						udph);
182}
183
184static void build_gue_header(void *header, uint8_t proto)
185{
186	struct guehdr *gueh = header;
187
188	gueh->proto_ctype = proto;
189}
190
191static void build_gre_header(void *header, uint16_t proto)
192{
193	struct grehdr *greh = header;
194
195	greh->protocol = htons(proto);
196}
197
198static int l3_length(int family)
199{
200	if (family == AF_INET)
201		return sizeof(struct iphdr);
202	else
203		return sizeof(struct ipv6hdr);
204}
205
206static int build_packet(const struct test_configuration *test, uint16_t sport)
207{
208	int ol3_len = 0, ol4_len = 0, il3_len = 0, il4_len = 0;
209	int el3_len = 0, packet_len;
210
211	memset(buf, 0, ETH_DATA_LEN);
212
213	if (test->cfg_l3_extra)
214		el3_len = l3_length(test->cfg_l3_extra);
215
216	/* calculate header offsets */
217	if (test->cfg_encap_proto) {
218		ol3_len = l3_length(test->cfg_l3_outer);
219
220		if (test->cfg_encap_proto == IPPROTO_GRE)
221			ol4_len = sizeof(struct grehdr);
222		else if (test->cfg_encap_proto == IPPROTO_UDP)
223			ol4_len = sizeof(struct udphdr) + sizeof(struct guehdr);
224	}
225
226	il3_len = l3_length(test->cfg_l3_inner);
227	il4_len = sizeof(struct udphdr);
228
229	packet_len = el3_len + ol3_len + ol4_len + il3_len + il4_len +
230		     TEST_PACKET_LEN;
231	if (!ASSERT_LE(packet_len, sizeof(buf), "check packet size"))
232		return -1;
233
234	/*
235	 * Fill packet from inside out, to calculate correct checksums.
236	 * But create ip before udp headers, as udp uses ip for pseudo-sum.
237	 */
238	memset(buf + el3_len + ol3_len + ol4_len + il3_len + il4_len,
239	       TEST_PACKET_PATTERN, TEST_PACKET_LEN);
240
241	/* add zero byte for udp csum padding */
242	buf[el3_len + ol3_len + ol4_len + il3_len + il4_len + TEST_PACKET_LEN] =
243		0;
244
245	switch (test->cfg_l3_inner) {
246	case PF_INET:
247		build_ipv4_header(buf + el3_len + ol3_len + ol4_len,
248				  IPPROTO_UDP, test->in_saddr4.sin_addr.s_addr,
249				  test->in_daddr4.sin_addr.s_addr,
250				  il4_len + TEST_PACKET_LEN,
251				  test->cfg_dsfield_inner);
252		break;
253	case PF_INET6:
254		build_ipv6_header(buf + el3_len + ol3_len + ol4_len,
255				  IPPROTO_UDP, &test->in_saddr6,
256				  &test->in_daddr6, il4_len + TEST_PACKET_LEN,
257				  test->cfg_dsfield_inner);
258		break;
259	}
260
261	build_udp_header(buf + el3_len + ol3_len + ol4_len + il3_len,
262			 TEST_PACKET_LEN, sport, CFG_PORT_INNER,
263			 test->cfg_l3_inner);
264
265	if (!test->cfg_encap_proto)
266		return il3_len + il4_len + TEST_PACKET_LEN;
267
268	switch (test->cfg_l3_outer) {
269	case PF_INET:
270		build_ipv4_header(buf + el3_len, test->cfg_encap_proto,
271				  test->out_saddr4.sin_addr.s_addr,
272				  test->out_daddr4.sin_addr.s_addr,
273				  ol4_len + il3_len + il4_len + TEST_PACKET_LEN,
274				  test->cfg_dsfield_outer);
275		break;
276	case PF_INET6:
277		build_ipv6_header(buf + el3_len, test->cfg_encap_proto,
278				  &test->out_saddr6, &test->out_daddr6,
279				  ol4_len + il3_len + il4_len + TEST_PACKET_LEN,
280				  test->cfg_dsfield_outer);
281		break;
282	}
283
284	switch (test->cfg_encap_proto) {
285	case IPPROTO_UDP:
286		build_gue_header(buf + el3_len + ol3_len + ol4_len -
287					 sizeof(struct guehdr),
288				 test->cfg_l3_inner == PF_INET ? IPPROTO_IPIP :
289								 IPPROTO_IPV6);
290		build_udp_header(buf + el3_len + ol3_len,
291				 sizeof(struct guehdr) + il3_len + il4_len +
292					 TEST_PACKET_LEN,
293				 sport, CFG_PORT_GUE, test->cfg_l3_outer);
294		break;
295	case IPPROTO_GRE:
296		build_gre_header(buf + el3_len + ol3_len,
297				 test->cfg_l3_inner == PF_INET ? ETH_P_IP :
298								 ETH_P_IPV6);
299		break;
300	}
301
302	switch (test->cfg_l3_extra) {
303	case PF_INET:
304		build_ipv4_header(buf,
305				  test->cfg_l3_outer == PF_INET ? IPPROTO_IPIP :
306								  IPPROTO_IPV6,
307				  test->extra_saddr4.sin_addr.s_addr,
308				  test->extra_daddr4.sin_addr.s_addr,
309				  ol3_len + ol4_len + il3_len + il4_len +
310					  TEST_PACKET_LEN,
311				  0);
312		break;
313	case PF_INET6:
314		build_ipv6_header(buf,
315				  test->cfg_l3_outer == PF_INET ? IPPROTO_IPIP :
316								  IPPROTO_IPV6,
317				  &test->extra_saddr6, &test->extra_daddr6,
318				  ol3_len + ol4_len + il3_len + il4_len +
319					  TEST_PACKET_LEN,
320				  0);
321		break;
322	}
323
324	return el3_len + ol3_len + ol4_len + il3_len + il4_len +
325	       TEST_PACKET_LEN;
326}
327
328/* sender transmits encapsulated over RAW or unencap'd over UDP */
329static int setup_tx(const struct test_configuration *test)
330{
331	int family, fd, ret;
332
333	if (test->cfg_l3_extra)
334		family = test->cfg_l3_extra;
335	else if (test->cfg_l3_outer)
336		family = test->cfg_l3_outer;
337	else
338		family = test->cfg_l3_inner;
339
340	fd = socket(family, SOCK_RAW, IPPROTO_RAW);
341	if (!ASSERT_OK_FD(fd, "setup tx socket"))
342		return fd;
343
344	if (test->cfg_l3_extra) {
345		if (test->cfg_l3_extra == PF_INET)
346			ret = connect(fd, (void *)&test->extra_daddr4,
347				      sizeof(test->extra_daddr4));
348		else
349			ret = connect(fd, (void *)&test->extra_daddr6,
350				      sizeof(test->extra_daddr6));
351		if (!ASSERT_OK(ret, "connect")) {
352			close(fd);
353			return ret;
354		}
355	} else if (test->cfg_l3_outer) {
356		/* connect to destination if not encapsulated */
357		if (test->cfg_l3_outer == PF_INET)
358			ret = connect(fd, (void *)&test->out_daddr4,
359				      sizeof(test->out_daddr4));
360		else
361			ret = connect(fd, (void *)&test->out_daddr6,
362				      sizeof(test->out_daddr6));
363		if (!ASSERT_OK(ret, "connect")) {
364			close(fd);
365			return ret;
366		}
367	} else {
368		/* otherwise using loopback */
369		if (test->cfg_l3_inner == PF_INET)
370			ret = connect(fd, (void *)&test->in_daddr4,
371				      sizeof(test->in_daddr4));
372		else
373			ret = connect(fd, (void *)&test->in_daddr6,
374				      sizeof(test->in_daddr6));
375		if (!ASSERT_OK(ret, "connect")) {
376			close(fd);
377			return ret;
378		}
379	}
380
381	return fd;
382}
383
384/* receiver reads unencapsulated UDP */
385static int setup_rx(const struct test_configuration *test)
386{
387	int fd, ret;
388
389	fd = socket(test->cfg_l3_inner, SOCK_DGRAM, 0);
390	if (!ASSERT_OK_FD(fd, "socket rx"))
391		return fd;
392
393	if (test->cfg_l3_inner == PF_INET)
394		ret = bind(fd, (void *)&test->in_daddr4,
395			   sizeof(test->in_daddr4));
396	else
397		ret = bind(fd, (void *)&test->in_daddr6,
398			   sizeof(test->in_daddr6));
399	if (!ASSERT_OK(ret, "bind rx")) {
400		close(fd);
401		return ret;
402	}
403
404	return fd;
405}
406
407static int do_tx(int fd, const char *pkt, int len)
408{
409	int ret;
410
411	ret = write(fd, pkt, len);
412	return ret != len;
413}
414
415static int do_poll(int fd, short events, int timeout)
416{
417	struct pollfd pfd;
418	int ret;
419
420	pfd.fd = fd;
421	pfd.events = events;
422
423	ret = poll(&pfd, 1, timeout);
424	return ret;
425}
426
427static int do_rx(int fd)
428{
429	char rbuf;
430	int ret, num = 0;
431
432	while (1) {
433		ret = recv(fd, &rbuf, 1, MSG_DONTWAIT);
434		if (ret == -1 && errno == EAGAIN)
435			break;
436		if (ret < 0)
437			return -1;
438		if (!ASSERT_EQ(rbuf, TEST_PACKET_PATTERN, "check pkt pattern"))
439			return -1;
440		num++;
441	}
442
443	return num;
444}
445
446static int run_test(const struct test_configuration *test,
447		    int source_port_index)
448{
449	int fdt = -1, fdr = -1, len, tx = 0, rx = 0, err;
450	unsigned long tstop, tcur;
451
452	fdr = setup_rx(test);
453	fdt = setup_tx(test);
454	if (!ASSERT_OK_FD(fdr, "setup rx") || !ASSERT_OK_FD(fdt, "setup tx")) {
455		err = -1;
456		goto out_close_sockets;
457	}
458
459	len = build_packet(test,
460			   (uint16_t)test->source_ports[source_port_index]);
461	if (!ASSERT_GT(len, 0, "build test packet"))
462		return -1;
463
464	tcur = util_gettime();
465	tstop = tcur;
466
467	while (tx < TEST_PACKETS_COUNT) {
468		if (!ASSERT_OK(do_tx(fdt, buf, len), "do_tx"))
469			break;
470		tx++;
471		err = do_rx(fdr);
472		if (!ASSERT_GE(err, 0, "do_rx"))
473			break;
474		rx += err;
475	}
476
477	/* read straggler packets, if any */
478	if (rx < tx) {
479		tstop = util_gettime() + 100;
480		while (rx < tx) {
481			tcur = util_gettime();
482			if (tcur >= tstop)
483				break;
484
485			err = do_poll(fdr, POLLIN, tstop - tcur);
486			if (err < 0)
487				break;
488			err = do_rx(fdr);
489			if (err >= 0)
490				rx += err;
491		}
492	}
493
494out_close_sockets:
495	close(fdt);
496	close(fdr);
497	return rx;
498}
499
500static int attach_and_configure_program(struct bpf_flow *skel)
501{
502	struct bpf_map *prog_array = skel->maps.jmp_table;
503	int main_prog_fd, sub_prog_fd, map_fd, i, err;
504	struct bpf_program *prog;
505	char prog_name[32];
506
507	main_prog_fd = bpf_program__fd(skel->progs._dissect);
508	if (main_prog_fd < 0)
509		return main_prog_fd;
510
511	err = bpf_prog_attach(main_prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
512	if (err)
513		return err;
514
515	map_fd = bpf_map__fd(prog_array);
516	if (map_fd < 0)
517		return map_fd;
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(skel->obj, prog_name);
523		if (!prog)
524			return -1;
525
526		sub_prog_fd = bpf_program__fd(prog);
527		if (sub_prog_fd < 0)
528			return -1;
529
530		err = bpf_map_update_elem(map_fd, &i, &sub_prog_fd, BPF_ANY);
531		if (err)
532			return -1;
533	}
534
535	return main_prog_fd;
536}
537
538static void detach_program(struct bpf_flow *skel, int prog_fd)
539{
540	bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
541}
542
543static int set_port_drop(int pf, bool multi_port)
544{
545	char dst_port[16];
546
547	snprintf(dst_port, sizeof(dst_port), "%d", CFG_PORT_INNER);
548
549	SYS(fail, "tc qdisc add dev lo ingress");
550	SYS(fail_delete_qdisc, "tc filter add %s %s %s %s %s %s %s %s %s %s %s %s",
551	    "dev lo",
552	    "parent FFFF:",
553	    "protocol", pf == PF_INET6 ? "ipv6" : "ip",
554	    "pref 1337",
555	    "flower",
556	    "ip_proto udp",
557	    "src_port", multi_port ? "8-10" : "9",
558	    "dst_port", dst_port,
559	    "action drop");
560	return 0;
561
562fail_delete_qdisc:
563	SYS_NOFAIL("tc qdisc del dev lo ingress");
564fail:
565	return 1;
566}
567
568static void remove_filter(void)
569{
570	SYS_NOFAIL("tc filter del dev lo ingress");
571	SYS_NOFAIL("tc qdisc del dev lo ingress");
572}
573
574static int ipv4_setup(void)
575{
576	return set_port_drop(PF_INET, false);
577}
578
579static int ipv6_setup(void)
580{
581	return set_port_drop(PF_INET6, false);
582}
583
584static int port_range_setup(void)
585{
586	return set_port_drop(PF_INET, true);
587}
588
589static int set_addresses(void)
590{
591	SYS(out, "ip -4 addr add  %s dev lo", TEST_IPV4);
592	SYS(out_remove_ipv4, "ip -6 addr add %s dev lo", TEST_IPV6);
593	return 0;
594out_remove_ipv4:
595	SYS_NOFAIL("ip -4 addr del %s dev lo", TEST_IPV4);
596out:
597	return -1;
598}
599
600static void unset_addresses(void)
601{
602	SYS_NOFAIL("ip -4 addr del %s dev lo", TEST_IPV4);
603	SYS_NOFAIL("ip -6 addr del %s dev lo", TEST_IPV6);
604}
605
606static int ipip_setup(void)
607{
608	if (!ASSERT_OK(set_addresses(), "configure addresses"))
609		return -1;
610	if (!ASSERT_OK(set_port_drop(PF_INET, false), "set filter"))
611		goto out_unset_addresses;
612	SYS(out_remove_filter,
613	    "ip link add ipip_test type ipip remote %s local %s dev lo",
614	    TEST_TUNNEL_REMOTE, TEST_TUNNEL_LOCAL);
615	SYS(out_clean_netif, "ip link set ipip_test up");
616	return 0;
617
618out_clean_netif:
619	SYS_NOFAIL("ip link del ipip_test");
620out_remove_filter:
621	remove_filter();
622out_unset_addresses:
623	unset_addresses();
624	return -1;
625}
626
627static void ipip_shutdown(void)
628{
629	SYS_NOFAIL("ip link del ipip_test");
630	remove_filter();
631	unset_addresses();
632}
633
634static int gre_setup(void)
635{
636	if (!ASSERT_OK(set_addresses(), "configure addresses"))
637		return -1;
638	if (!ASSERT_OK(set_port_drop(PF_INET, false), "set filter"))
639		goto out_unset_addresses;
640	SYS(out_remove_filter,
641	    "ip link add gre_test type gre remote %s local %s dev lo",
642	    TEST_TUNNEL_REMOTE, TEST_TUNNEL_LOCAL);
643	SYS(out_clean_netif, "ip link set gre_test up");
644	return 0;
645
646out_clean_netif:
647	SYS_NOFAIL("ip link del ipip_test");
648out_remove_filter:
649	remove_filter();
650out_unset_addresses:
651	unset_addresses();
652	return -1;
653}
654
655static void gre_shutdown(void)
656{
657	SYS_NOFAIL("ip link del gre_test");
658	remove_filter();
659	unset_addresses();
660}
661
662static const struct test_configuration tests_input[] = {
663	{
664		.name = "ipv4",
665		.test_setup = ipv4_setup,
666		.test_teardown = remove_filter,
667		.source_ports = { 8, 9, 10 },
668		.cfg_l3_inner = PF_INET,
669		.in_saddr4 = TEST_IN4_SRC_ADDR_DEFAULT,
670		.in_daddr4 = TEST_IN4_DST_ADDR_DEFAULT
671	},
672	{
673		.name = "ipv4_continue_dissect",
674		.test_setup = ipv4_setup,
675		.test_teardown = remove_filter,
676		.source_ports = { 8, 9, 10 },
677		.cfg_l3_inner = PF_INET,
678		.in_saddr4 = TEST_IN4_SRC_ADDR_DISSECT_CONTINUE,
679		.in_daddr4 = TEST_IN4_DST_ADDR_DEFAULT },
680	{
681		.name = "ipip",
682		.test_setup = ipip_setup,
683		.test_teardown = ipip_shutdown,
684		.source_ports = { 8, 9, 10 },
685		.cfg_l3_inner = PF_INET,
686		.in_saddr4 = TEST_IN4_SRC_ADDR_IPIP,
687		.in_daddr4 = TEST_IN4_DST_ADDR_IPIP,
688		.out_saddr4 = TEST_OUT4_SRC_ADDR_DEFAULT,
689		.out_daddr4 = TEST_OUT4_DST_ADDR_DEFAULT,
690		.cfg_l3_outer = PF_INET,
691		.cfg_encap_proto = IPPROTO_IPIP,
692
693	},
694	{
695		.name = "gre",
696		.test_setup = gre_setup,
697		.test_teardown = gre_shutdown,
698		.source_ports = { 8, 9, 10 },
699		.cfg_l3_inner = PF_INET,
700		.in_saddr4 = TEST_IN4_SRC_ADDR_IPIP,
701		.in_daddr4 = TEST_IN4_DST_ADDR_IPIP,
702		.out_saddr4 = TEST_OUT4_SRC_ADDR_DEFAULT,
703		.out_daddr4 = TEST_OUT4_DST_ADDR_DEFAULT,
704		.cfg_l3_outer = PF_INET,
705		.cfg_encap_proto = IPPROTO_GRE,
706	},
707	{
708		.name = "port_range",
709		.test_setup = port_range_setup,
710		.test_teardown = remove_filter,
711		.source_ports = { 7, 9, 11 },
712		.cfg_l3_inner = PF_INET,
713		.in_saddr4 = TEST_IN4_SRC_ADDR_DEFAULT,
714		.in_daddr4 = TEST_IN4_DST_ADDR_DEFAULT },
715	{
716		.name = "ipv6",
717		.test_setup = ipv6_setup,
718		.test_teardown = remove_filter,
719		.source_ports = { 8, 9, 10 },
720		.cfg_l3_inner = PF_INET6,
721		.in_saddr6 = TEST_IN6_SRC_ADDR_DEFAULT,
722		.in_daddr6 = TEST_IN6_DST_ADDR_DEFAULT
723	},
724};
725
726struct test_ctx {
727	struct bpf_flow *skel;
728	struct netns_obj *ns;
729	int prog_fd;
730};
731
732static int test_global_init(struct test_ctx *ctx)
733{
734	int err;
735
736	ctx->skel = bpf_flow__open_and_load();
737	if (!ASSERT_OK_PTR(ctx->skel, "open and load flow_dissector"))
738		return -1;
739
740	ctx->ns = netns_new("flow_dissector_classification", true);
741	if (!ASSERT_OK_PTR(ctx->ns, "switch ns"))
742		goto out_destroy_skel;
743
744	err = write_sysctl("/proc/sys/net/ipv4/conf/default/rp_filter", "0");
745	err |= write_sysctl("/proc/sys/net/ipv4/conf/all/rp_filter", "0");
746	err |= write_sysctl("/proc/sys/net/ipv4/conf/lo/rp_filter", "0");
747	if (!ASSERT_OK(err, "configure net tunables"))
748		goto out_clean_ns;
749
750	ctx->prog_fd = attach_and_configure_program(ctx->skel);
751	if (!ASSERT_OK_FD(ctx->prog_fd, "attach and configure program"))
752		goto out_clean_ns;
753	return 0;
754out_clean_ns:
755	netns_free(ctx->ns);
756out_destroy_skel:
757	bpf_flow__destroy(ctx->skel);
758	return -1;
759}
760
761static void test_global_shutdown(struct test_ctx *ctx)
762{
763	detach_program(ctx->skel, ctx->prog_fd);
764	netns_free(ctx->ns);
765	bpf_flow__destroy(ctx->skel);
766}
767
768void test_flow_dissector_classification(void)
769{
770	struct test_ctx ctx;
771	const struct test_configuration *test;
772	int i;
773
774	if (test_global_init(&ctx))
775		return;
776
777	for (i = 0; i < ARRAY_SIZE(tests_input); i++) {
778		if (!test__start_subtest(tests_input[i].name))
779			continue;
780		test = &tests_input[i];
781		/* All tests are expected to have one rx-ok port first,
782		 * then a non-working rx port, and finally a rx-ok port
783		 */
784		if (test->test_setup &&
785		    !ASSERT_OK(test->test_setup(), "init filter"))
786			continue;
787
788		ASSERT_EQ(run_test(test, 0), TEST_PACKETS_COUNT,
789			  "test first port");
790		ASSERT_EQ(run_test(test, 1), 0, "test second port");
791		ASSERT_EQ(run_test(test, 2), TEST_PACKETS_COUNT,
792			  "test third port");
793		if (test->test_teardown)
794			test->test_teardown();
795	}
796	test_global_shutdown(&ctx);
797}
~/ Read-only file preview.