xref: /openbmc/linux/tools/testing/selftests/bpf/xdp_features.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
14dba3e78SLorenzo Bianconi // SPDX-License-Identifier: GPL-2.0
24dba3e78SLorenzo Bianconi #include <uapi/linux/bpf.h>
34dba3e78SLorenzo Bianconi #include <uapi/linux/netdev.h>
44dba3e78SLorenzo Bianconi #include <linux/if_link.h>
54dba3e78SLorenzo Bianconi #include <signal.h>
64dba3e78SLorenzo Bianconi #include <argp.h>
74dba3e78SLorenzo Bianconi #include <net/if.h>
84dba3e78SLorenzo Bianconi #include <sys/socket.h>
94dba3e78SLorenzo Bianconi #include <netinet/in.h>
104dba3e78SLorenzo Bianconi #include <netinet/tcp.h>
114dba3e78SLorenzo Bianconi #include <unistd.h>
124dba3e78SLorenzo Bianconi #include <arpa/inet.h>
134dba3e78SLorenzo Bianconi #include <bpf/bpf.h>
144dba3e78SLorenzo Bianconi #include <bpf/libbpf.h>
154dba3e78SLorenzo Bianconi #include <pthread.h>
164dba3e78SLorenzo Bianconi 
174dba3e78SLorenzo Bianconi #include <network_helpers.h>
184dba3e78SLorenzo Bianconi 
194dba3e78SLorenzo Bianconi #include "xdp_features.skel.h"
204dba3e78SLorenzo Bianconi #include "xdp_features.h"
214dba3e78SLorenzo Bianconi 
224dba3e78SLorenzo Bianconi #define RED(str)	"\033[0;31m" str "\033[0m"
234dba3e78SLorenzo Bianconi #define GREEN(str)	"\033[0;32m" str "\033[0m"
244dba3e78SLorenzo Bianconi #define YELLOW(str)	"\033[0;33m" str "\033[0m"
254dba3e78SLorenzo Bianconi 
264dba3e78SLorenzo Bianconi static struct env {
274dba3e78SLorenzo Bianconi 	bool verbosity;
2827a36bc3SLorenzo Bianconi 	char ifname[IF_NAMESIZE];
294dba3e78SLorenzo Bianconi 	int ifindex;
304dba3e78SLorenzo Bianconi 	bool is_tester;
314dba3e78SLorenzo Bianconi 	struct {
324dba3e78SLorenzo Bianconi 		enum netdev_xdp_act drv_feature;
334dba3e78SLorenzo Bianconi 		enum xdp_action action;
344dba3e78SLorenzo Bianconi 	} feature;
354dba3e78SLorenzo Bianconi 	struct sockaddr_storage dut_ctrl_addr;
364dba3e78SLorenzo Bianconi 	struct sockaddr_storage dut_addr;
374dba3e78SLorenzo Bianconi 	struct sockaddr_storage tester_addr;
384dba3e78SLorenzo Bianconi } env;
394dba3e78SLorenzo Bianconi 
404dba3e78SLorenzo Bianconi #define BUFSIZE		128
414dba3e78SLorenzo Bianconi 
test__fail(void)424dba3e78SLorenzo Bianconi void test__fail(void) { /* for network_helpers.c */ }
434dba3e78SLorenzo Bianconi 
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)444dba3e78SLorenzo Bianconi static int libbpf_print_fn(enum libbpf_print_level level,
454dba3e78SLorenzo Bianconi 			   const char *format, va_list args)
464dba3e78SLorenzo Bianconi {
474dba3e78SLorenzo Bianconi 	if (level == LIBBPF_DEBUG && !env.verbosity)
484dba3e78SLorenzo Bianconi 		return 0;
494dba3e78SLorenzo Bianconi 	return vfprintf(stderr, format, args);
504dba3e78SLorenzo Bianconi }
514dba3e78SLorenzo Bianconi 
524dba3e78SLorenzo Bianconi static volatile bool exiting;
534dba3e78SLorenzo Bianconi 
sig_handler(int sig)544dba3e78SLorenzo Bianconi static void sig_handler(int sig)
554dba3e78SLorenzo Bianconi {
564dba3e78SLorenzo Bianconi 	exiting = true;
574dba3e78SLorenzo Bianconi }
584dba3e78SLorenzo Bianconi 
594dba3e78SLorenzo Bianconi const char *argp_program_version = "xdp-features 0.0";
604dba3e78SLorenzo Bianconi const char argp_program_doc[] =
618306829bSColin Ian King "XDP features detection application.\n"
624dba3e78SLorenzo Bianconi "\n"
634dba3e78SLorenzo Bianconi "XDP features application checks the XDP advertised features match detected ones.\n"
644dba3e78SLorenzo Bianconi "\n"
654dba3e78SLorenzo Bianconi "USAGE: ./xdp-features [-vt] [-f <xdp-feature>] [-D <dut-data-ip>] [-T <tester-data-ip>] [-C <dut-ctrl-ip>] <iface-name>\n"
664dba3e78SLorenzo Bianconi "\n"
674dba3e78SLorenzo Bianconi "dut-data-ip, tester-data-ip, dut-ctrl-ip: IPv6 or IPv4-mapped-IPv6 addresses;\n"
684dba3e78SLorenzo Bianconi "\n"
694dba3e78SLorenzo Bianconi "XDP features\n:"
704dba3e78SLorenzo Bianconi "- XDP_PASS\n"
714dba3e78SLorenzo Bianconi "- XDP_DROP\n"
724dba3e78SLorenzo Bianconi "- XDP_ABORTED\n"
734dba3e78SLorenzo Bianconi "- XDP_REDIRECT\n"
744dba3e78SLorenzo Bianconi "- XDP_NDO_XMIT\n"
754dba3e78SLorenzo Bianconi "- XDP_TX\n";
764dba3e78SLorenzo Bianconi 
774dba3e78SLorenzo Bianconi static const struct argp_option opts[] = {
784dba3e78SLorenzo Bianconi 	{ "verbose", 'v', NULL, 0, "Verbose debug output" },
794dba3e78SLorenzo Bianconi 	{ "tester", 't', NULL, 0, "Tester mode" },
804dba3e78SLorenzo Bianconi 	{ "feature", 'f', "XDP-FEATURE", 0, "XDP feature to test" },
814dba3e78SLorenzo Bianconi 	{ "dut_data_ip", 'D', "DUT-DATA-IP", 0, "DUT IP data channel" },
824dba3e78SLorenzo Bianconi 	{ "dut_ctrl_ip", 'C', "DUT-CTRL-IP", 0, "DUT IP control channel" },
834dba3e78SLorenzo Bianconi 	{ "tester_data_ip", 'T', "TESTER-DATA-IP", 0, "Tester IP data channel" },
844dba3e78SLorenzo Bianconi 	{},
854dba3e78SLorenzo Bianconi };
864dba3e78SLorenzo Bianconi 
get_xdp_feature(const char * arg)874dba3e78SLorenzo Bianconi static int get_xdp_feature(const char *arg)
884dba3e78SLorenzo Bianconi {
894dba3e78SLorenzo Bianconi 	if (!strcmp(arg, "XDP_PASS")) {
904dba3e78SLorenzo Bianconi 		env.feature.action = XDP_PASS;
914dba3e78SLorenzo Bianconi 		env.feature.drv_feature = NETDEV_XDP_ACT_BASIC;
924dba3e78SLorenzo Bianconi 	} else if (!strcmp(arg, "XDP_DROP")) {
934dba3e78SLorenzo Bianconi 		env.feature.drv_feature = NETDEV_XDP_ACT_BASIC;
944dba3e78SLorenzo Bianconi 		env.feature.action = XDP_DROP;
954dba3e78SLorenzo Bianconi 	} else if (!strcmp(arg, "XDP_ABORTED")) {
964dba3e78SLorenzo Bianconi 		env.feature.drv_feature = NETDEV_XDP_ACT_BASIC;
974dba3e78SLorenzo Bianconi 		env.feature.action = XDP_ABORTED;
984dba3e78SLorenzo Bianconi 	} else if (!strcmp(arg, "XDP_TX")) {
994dba3e78SLorenzo Bianconi 		env.feature.drv_feature = NETDEV_XDP_ACT_BASIC;
1004dba3e78SLorenzo Bianconi 		env.feature.action = XDP_TX;
1014dba3e78SLorenzo Bianconi 	} else if (!strcmp(arg, "XDP_REDIRECT")) {
1024dba3e78SLorenzo Bianconi 		env.feature.drv_feature = NETDEV_XDP_ACT_REDIRECT;
1034dba3e78SLorenzo Bianconi 		env.feature.action = XDP_REDIRECT;
1044dba3e78SLorenzo Bianconi 	} else if (!strcmp(arg, "XDP_NDO_XMIT")) {
1054dba3e78SLorenzo Bianconi 		env.feature.drv_feature = NETDEV_XDP_ACT_NDO_XMIT;
1064dba3e78SLorenzo Bianconi 	} else {
1074dba3e78SLorenzo Bianconi 		return -EINVAL;
1084dba3e78SLorenzo Bianconi 	}
1094dba3e78SLorenzo Bianconi 
1104dba3e78SLorenzo Bianconi 	return 0;
1114dba3e78SLorenzo Bianconi }
1124dba3e78SLorenzo Bianconi 
get_xdp_feature_str(void)1134dba3e78SLorenzo Bianconi static char *get_xdp_feature_str(void)
1144dba3e78SLorenzo Bianconi {
1154dba3e78SLorenzo Bianconi 	switch (env.feature.action) {
1164dba3e78SLorenzo Bianconi 	case XDP_PASS:
1174dba3e78SLorenzo Bianconi 		return YELLOW("XDP_PASS");
1184dba3e78SLorenzo Bianconi 	case XDP_DROP:
1194dba3e78SLorenzo Bianconi 		return YELLOW("XDP_DROP");
1204dba3e78SLorenzo Bianconi 	case XDP_ABORTED:
1214dba3e78SLorenzo Bianconi 		return YELLOW("XDP_ABORTED");
1224dba3e78SLorenzo Bianconi 	case XDP_TX:
1234dba3e78SLorenzo Bianconi 		return YELLOW("XDP_TX");
1244dba3e78SLorenzo Bianconi 	case XDP_REDIRECT:
1254dba3e78SLorenzo Bianconi 		return YELLOW("XDP_REDIRECT");
1264dba3e78SLorenzo Bianconi 	default:
1274dba3e78SLorenzo Bianconi 		break;
1284dba3e78SLorenzo Bianconi 	}
1294dba3e78SLorenzo Bianconi 
1304dba3e78SLorenzo Bianconi 	if (env.feature.drv_feature == NETDEV_XDP_ACT_NDO_XMIT)
1314dba3e78SLorenzo Bianconi 		return YELLOW("XDP_NDO_XMIT");
1324dba3e78SLorenzo Bianconi 
1334dba3e78SLorenzo Bianconi 	return "";
1344dba3e78SLorenzo Bianconi }
1354dba3e78SLorenzo Bianconi 
parse_arg(int key,char * arg,struct argp_state * state)1364dba3e78SLorenzo Bianconi static error_t parse_arg(int key, char *arg, struct argp_state *state)
1374dba3e78SLorenzo Bianconi {
1384dba3e78SLorenzo Bianconi 	switch (key) {
1394dba3e78SLorenzo Bianconi 	case 'v':
1404dba3e78SLorenzo Bianconi 		env.verbosity = true;
1414dba3e78SLorenzo Bianconi 		break;
1424dba3e78SLorenzo Bianconi 	case 't':
1434dba3e78SLorenzo Bianconi 		env.is_tester = true;
1444dba3e78SLorenzo Bianconi 		break;
1454dba3e78SLorenzo Bianconi 	case 'f':
1464dba3e78SLorenzo Bianconi 		if (get_xdp_feature(arg) < 0) {
1474dba3e78SLorenzo Bianconi 			fprintf(stderr, "Invalid xdp feature: %s\n", arg);
1484dba3e78SLorenzo Bianconi 			argp_usage(state);
1494dba3e78SLorenzo Bianconi 			return ARGP_ERR_UNKNOWN;
1504dba3e78SLorenzo Bianconi 		}
1514dba3e78SLorenzo Bianconi 		break;
1524dba3e78SLorenzo Bianconi 	case 'D':
1534dba3e78SLorenzo Bianconi 		if (make_sockaddr(AF_INET6, arg, DUT_ECHO_PORT,
1544dba3e78SLorenzo Bianconi 				  &env.dut_addr, NULL)) {
155*c1cd734cSLorenzo Bianconi 			fprintf(stderr,
156*c1cd734cSLorenzo Bianconi 				"Invalid address assigned to the Device Under Test: %s\n",
157*c1cd734cSLorenzo Bianconi 				arg);
1584dba3e78SLorenzo Bianconi 			return ARGP_ERR_UNKNOWN;
1594dba3e78SLorenzo Bianconi 		}
1604dba3e78SLorenzo Bianconi 		break;
1614dba3e78SLorenzo Bianconi 	case 'C':
1624dba3e78SLorenzo Bianconi 		if (make_sockaddr(AF_INET6, arg, DUT_CTRL_PORT,
1634dba3e78SLorenzo Bianconi 				  &env.dut_ctrl_addr, NULL)) {
164*c1cd734cSLorenzo Bianconi 			fprintf(stderr,
165*c1cd734cSLorenzo Bianconi 				"Invalid address assigned to the Device Under Test: %s\n",
166*c1cd734cSLorenzo Bianconi 				arg);
1674dba3e78SLorenzo Bianconi 			return ARGP_ERR_UNKNOWN;
1684dba3e78SLorenzo Bianconi 		}
1694dba3e78SLorenzo Bianconi 		break;
1704dba3e78SLorenzo Bianconi 	case 'T':
1714dba3e78SLorenzo Bianconi 		if (make_sockaddr(AF_INET6, arg, 0, &env.tester_addr, NULL)) {
172*c1cd734cSLorenzo Bianconi 			fprintf(stderr,
173*c1cd734cSLorenzo Bianconi 				"Invalid address assigned to the Tester device: %s\n",
174*c1cd734cSLorenzo Bianconi 				arg);
1754dba3e78SLorenzo Bianconi 			return ARGP_ERR_UNKNOWN;
1764dba3e78SLorenzo Bianconi 		}
1774dba3e78SLorenzo Bianconi 		break;
1784dba3e78SLorenzo Bianconi 	case ARGP_KEY_ARG:
1794dba3e78SLorenzo Bianconi 		errno = 0;
1804dba3e78SLorenzo Bianconi 		if (strlen(arg) >= IF_NAMESIZE) {
1814dba3e78SLorenzo Bianconi 			fprintf(stderr, "Invalid device name: %s\n", arg);
1824dba3e78SLorenzo Bianconi 			argp_usage(state);
1834dba3e78SLorenzo Bianconi 			return ARGP_ERR_UNKNOWN;
1844dba3e78SLorenzo Bianconi 		}
1854dba3e78SLorenzo Bianconi 
1864dba3e78SLorenzo Bianconi 		env.ifindex = if_nametoindex(arg);
1874dba3e78SLorenzo Bianconi 		if (!env.ifindex)
1884dba3e78SLorenzo Bianconi 			env.ifindex = strtoul(arg, NULL, 0);
18927a36bc3SLorenzo Bianconi 		if (!env.ifindex || !if_indextoname(env.ifindex, env.ifname)) {
1904dba3e78SLorenzo Bianconi 			fprintf(stderr,
1914dba3e78SLorenzo Bianconi 				"Bad interface index or name (%d): %s\n",
1924dba3e78SLorenzo Bianconi 				errno, strerror(errno));
1934dba3e78SLorenzo Bianconi 			argp_usage(state);
1944dba3e78SLorenzo Bianconi 			return ARGP_ERR_UNKNOWN;
1954dba3e78SLorenzo Bianconi 		}
1964dba3e78SLorenzo Bianconi 		break;
1974dba3e78SLorenzo Bianconi 	default:
1984dba3e78SLorenzo Bianconi 		return ARGP_ERR_UNKNOWN;
1994dba3e78SLorenzo Bianconi 	}
2004dba3e78SLorenzo Bianconi 
2014dba3e78SLorenzo Bianconi 	return 0;
2024dba3e78SLorenzo Bianconi }
2034dba3e78SLorenzo Bianconi 
2044dba3e78SLorenzo Bianconi static const struct argp argp = {
2054dba3e78SLorenzo Bianconi 	.options = opts,
2064dba3e78SLorenzo Bianconi 	.parser = parse_arg,
2074dba3e78SLorenzo Bianconi 	.doc = argp_program_doc,
2084dba3e78SLorenzo Bianconi };
2094dba3e78SLorenzo Bianconi 
set_env_default(void)2104dba3e78SLorenzo Bianconi static void set_env_default(void)
2114dba3e78SLorenzo Bianconi {
2124dba3e78SLorenzo Bianconi 	env.feature.drv_feature = NETDEV_XDP_ACT_NDO_XMIT;
2134dba3e78SLorenzo Bianconi 	env.feature.action = -EINVAL;
2144dba3e78SLorenzo Bianconi 	env.ifindex = -ENODEV;
21527a36bc3SLorenzo Bianconi 	strcpy(env.ifname, "unknown");
2164dba3e78SLorenzo Bianconi 	make_sockaddr(AF_INET6, "::ffff:127.0.0.1", DUT_CTRL_PORT,
2174dba3e78SLorenzo Bianconi 		      &env.dut_ctrl_addr, NULL);
2184dba3e78SLorenzo Bianconi 	make_sockaddr(AF_INET6, "::ffff:127.0.0.1", DUT_ECHO_PORT,
2194dba3e78SLorenzo Bianconi 		      &env.dut_addr, NULL);
2204dba3e78SLorenzo Bianconi 	make_sockaddr(AF_INET6, "::ffff:127.0.0.1", 0, &env.tester_addr, NULL);
2214dba3e78SLorenzo Bianconi }
2224dba3e78SLorenzo Bianconi 
dut_echo_thread(void * arg)2234dba3e78SLorenzo Bianconi static void *dut_echo_thread(void *arg)
2244dba3e78SLorenzo Bianconi {
2254dba3e78SLorenzo Bianconi 	unsigned char buf[sizeof(struct tlv_hdr)];
2264dba3e78SLorenzo Bianconi 	int sockfd = *(int *)arg;
2274dba3e78SLorenzo Bianconi 
2284dba3e78SLorenzo Bianconi 	while (!exiting) {
2294dba3e78SLorenzo Bianconi 		struct tlv_hdr *tlv = (struct tlv_hdr *)buf;
2304dba3e78SLorenzo Bianconi 		struct sockaddr_storage addr;
2314dba3e78SLorenzo Bianconi 		socklen_t addrlen;
2324dba3e78SLorenzo Bianconi 		size_t n;
2334dba3e78SLorenzo Bianconi 
2344dba3e78SLorenzo Bianconi 		n = recvfrom(sockfd, buf, sizeof(buf), MSG_WAITALL,
2354dba3e78SLorenzo Bianconi 			     (struct sockaddr *)&addr, &addrlen);
2364dba3e78SLorenzo Bianconi 		if (n != ntohs(tlv->len))
2374dba3e78SLorenzo Bianconi 			continue;
2384dba3e78SLorenzo Bianconi 
2394dba3e78SLorenzo Bianconi 		if (ntohs(tlv->type) != CMD_ECHO)
2404dba3e78SLorenzo Bianconi 			continue;
2414dba3e78SLorenzo Bianconi 
2424dba3e78SLorenzo Bianconi 		sendto(sockfd, buf, sizeof(buf), MSG_NOSIGNAL | MSG_CONFIRM,
2434dba3e78SLorenzo Bianconi 		       (struct sockaddr *)&addr, addrlen);
2444dba3e78SLorenzo Bianconi 	}
2454dba3e78SLorenzo Bianconi 
2464dba3e78SLorenzo Bianconi 	pthread_exit((void *)0);
2474dba3e78SLorenzo Bianconi 	close(sockfd);
2484dba3e78SLorenzo Bianconi 
2494dba3e78SLorenzo Bianconi 	return NULL;
2504dba3e78SLorenzo Bianconi }
2514dba3e78SLorenzo Bianconi 
dut_run_echo_thread(pthread_t * t,int * sockfd)2524dba3e78SLorenzo Bianconi static int dut_run_echo_thread(pthread_t *t, int *sockfd)
2534dba3e78SLorenzo Bianconi {
2544dba3e78SLorenzo Bianconi 	int err;
2554dba3e78SLorenzo Bianconi 
2564dba3e78SLorenzo Bianconi 	sockfd = start_reuseport_server(AF_INET6, SOCK_DGRAM, NULL,
2574dba3e78SLorenzo Bianconi 					DUT_ECHO_PORT, 0, 1);
2584dba3e78SLorenzo Bianconi 	if (!sockfd) {
25927a36bc3SLorenzo Bianconi 		fprintf(stderr,
26027a36bc3SLorenzo Bianconi 			"Failed creating data UDP socket on device %s\n",
26127a36bc3SLorenzo Bianconi 			env.ifname);
2624dba3e78SLorenzo Bianconi 		return -errno;
2634dba3e78SLorenzo Bianconi 	}
2644dba3e78SLorenzo Bianconi 
2654dba3e78SLorenzo Bianconi 	/* start echo channel */
2664dba3e78SLorenzo Bianconi 	err = pthread_create(t, NULL, dut_echo_thread, sockfd);
2674dba3e78SLorenzo Bianconi 	if (err) {
26827a36bc3SLorenzo Bianconi 		fprintf(stderr,
26927a36bc3SLorenzo Bianconi 			"Failed creating data UDP thread on device %s: %s\n",
27027a36bc3SLorenzo Bianconi 			env.ifname, strerror(-err));
2714dba3e78SLorenzo Bianconi 		free_fds(sockfd, 1);
2724dba3e78SLorenzo Bianconi 		return -EINVAL;
2734dba3e78SLorenzo Bianconi 	}
2744dba3e78SLorenzo Bianconi 
2754dba3e78SLorenzo Bianconi 	return 0;
2764dba3e78SLorenzo Bianconi }
2774dba3e78SLorenzo Bianconi 
dut_attach_xdp_prog(struct xdp_features * skel,int flags)2784dba3e78SLorenzo Bianconi static int dut_attach_xdp_prog(struct xdp_features *skel, int flags)
2794dba3e78SLorenzo Bianconi {
2804dba3e78SLorenzo Bianconi 	enum xdp_action action = env.feature.action;
2814dba3e78SLorenzo Bianconi 	struct bpf_program *prog;
2824dba3e78SLorenzo Bianconi 	unsigned int key = 0;
2834dba3e78SLorenzo Bianconi 	int err, fd = 0;
2844dba3e78SLorenzo Bianconi 
2854dba3e78SLorenzo Bianconi 	if (env.feature.drv_feature == NETDEV_XDP_ACT_NDO_XMIT) {
2864dba3e78SLorenzo Bianconi 		struct bpf_devmap_val entry = {
2874dba3e78SLorenzo Bianconi 			.ifindex = env.ifindex,
2884dba3e78SLorenzo Bianconi 		};
2894dba3e78SLorenzo Bianconi 
2904dba3e78SLorenzo Bianconi 		err = bpf_map__update_elem(skel->maps.dev_map,
2914dba3e78SLorenzo Bianconi 					   &key, sizeof(key),
2924dba3e78SLorenzo Bianconi 					   &entry, sizeof(entry), 0);
2934dba3e78SLorenzo Bianconi 		if (err < 0)
2944dba3e78SLorenzo Bianconi 			return err;
2954dba3e78SLorenzo Bianconi 
2964dba3e78SLorenzo Bianconi 		fd = bpf_program__fd(skel->progs.xdp_do_redirect_cpumap);
2974dba3e78SLorenzo Bianconi 		action = XDP_REDIRECT;
2984dba3e78SLorenzo Bianconi 	}
2994dba3e78SLorenzo Bianconi 
3004dba3e78SLorenzo Bianconi 	switch (action) {
3014dba3e78SLorenzo Bianconi 	case XDP_TX:
3024dba3e78SLorenzo Bianconi 		prog = skel->progs.xdp_do_tx;
3034dba3e78SLorenzo Bianconi 		break;
3044dba3e78SLorenzo Bianconi 	case XDP_DROP:
3054dba3e78SLorenzo Bianconi 		prog = skel->progs.xdp_do_drop;
3064dba3e78SLorenzo Bianconi 		break;
3074dba3e78SLorenzo Bianconi 	case XDP_ABORTED:
3084dba3e78SLorenzo Bianconi 		prog = skel->progs.xdp_do_aborted;
3094dba3e78SLorenzo Bianconi 		break;
3104dba3e78SLorenzo Bianconi 	case XDP_PASS:
3114dba3e78SLorenzo Bianconi 		prog = skel->progs.xdp_do_pass;
3124dba3e78SLorenzo Bianconi 		break;
3134dba3e78SLorenzo Bianconi 	case XDP_REDIRECT: {
3144dba3e78SLorenzo Bianconi 		struct bpf_cpumap_val entry = {
3154dba3e78SLorenzo Bianconi 			.qsize = 2048,
3164dba3e78SLorenzo Bianconi 			.bpf_prog.fd = fd,
3174dba3e78SLorenzo Bianconi 		};
3184dba3e78SLorenzo Bianconi 
3194dba3e78SLorenzo Bianconi 		err = bpf_map__update_elem(skel->maps.cpu_map,
3204dba3e78SLorenzo Bianconi 					   &key, sizeof(key),
3214dba3e78SLorenzo Bianconi 					   &entry, sizeof(entry), 0);
3224dba3e78SLorenzo Bianconi 		if (err < 0)
3234dba3e78SLorenzo Bianconi 			return err;
3244dba3e78SLorenzo Bianconi 
3254dba3e78SLorenzo Bianconi 		prog = skel->progs.xdp_do_redirect;
3264dba3e78SLorenzo Bianconi 		break;
3274dba3e78SLorenzo Bianconi 	}
3284dba3e78SLorenzo Bianconi 	default:
3294dba3e78SLorenzo Bianconi 		return -EINVAL;
3304dba3e78SLorenzo Bianconi 	}
3314dba3e78SLorenzo Bianconi 
3324dba3e78SLorenzo Bianconi 	err = bpf_xdp_attach(env.ifindex, bpf_program__fd(prog), flags, NULL);
3334dba3e78SLorenzo Bianconi 	if (err)
33427a36bc3SLorenzo Bianconi 		fprintf(stderr, "Failed attaching XDP program to device %s\n",
33527a36bc3SLorenzo Bianconi 			env.ifname);
3364dba3e78SLorenzo Bianconi 	return err;
3374dba3e78SLorenzo Bianconi }
3384dba3e78SLorenzo Bianconi 
recv_msg(int sockfd,void * buf,size_t bufsize,void * val,size_t val_size)3394dba3e78SLorenzo Bianconi static int recv_msg(int sockfd, void *buf, size_t bufsize, void *val,
3404dba3e78SLorenzo Bianconi 		    size_t val_size)
3414dba3e78SLorenzo Bianconi {
3424dba3e78SLorenzo Bianconi 	struct tlv_hdr *tlv = (struct tlv_hdr *)buf;
3434dba3e78SLorenzo Bianconi 	size_t len;
3444dba3e78SLorenzo Bianconi 
3454dba3e78SLorenzo Bianconi 	len = recv(sockfd, buf, bufsize, 0);
3464dba3e78SLorenzo Bianconi 	if (len != ntohs(tlv->len) || len < sizeof(*tlv))
3474dba3e78SLorenzo Bianconi 		return -EINVAL;
3484dba3e78SLorenzo Bianconi 
3494dba3e78SLorenzo Bianconi 	if (val) {
3504dba3e78SLorenzo Bianconi 		len -= sizeof(*tlv);
3514dba3e78SLorenzo Bianconi 		if (len > val_size)
3524dba3e78SLorenzo Bianconi 			return -ENOMEM;
3534dba3e78SLorenzo Bianconi 
3544dba3e78SLorenzo Bianconi 		memcpy(val, tlv->data, len);
3554dba3e78SLorenzo Bianconi 	}
3564dba3e78SLorenzo Bianconi 
3574dba3e78SLorenzo Bianconi 	return 0;
3584dba3e78SLorenzo Bianconi }
3594dba3e78SLorenzo Bianconi 
dut_run(struct xdp_features * skel)3604dba3e78SLorenzo Bianconi static int dut_run(struct xdp_features *skel)
3614dba3e78SLorenzo Bianconi {
3624dba3e78SLorenzo Bianconi 	int flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE;
3634dba3e78SLorenzo Bianconi 	int state, err, *sockfd, ctrl_sockfd, echo_sockfd;
3644dba3e78SLorenzo Bianconi 	struct sockaddr_storage ctrl_addr;
3654dba3e78SLorenzo Bianconi 	pthread_t dut_thread;
3664dba3e78SLorenzo Bianconi 	socklen_t addrlen;
3674dba3e78SLorenzo Bianconi 
3684dba3e78SLorenzo Bianconi 	sockfd = start_reuseport_server(AF_INET6, SOCK_STREAM, NULL,
3694dba3e78SLorenzo Bianconi 					DUT_CTRL_PORT, 0, 1);
3704dba3e78SLorenzo Bianconi 	if (!sockfd) {
37127a36bc3SLorenzo Bianconi 		fprintf(stderr,
37227a36bc3SLorenzo Bianconi 			"Failed creating control socket on device %s\n", env.ifname);
3734dba3e78SLorenzo Bianconi 		return -errno;
3744dba3e78SLorenzo Bianconi 	}
3754dba3e78SLorenzo Bianconi 
3764dba3e78SLorenzo Bianconi 	ctrl_sockfd = accept(*sockfd, (struct sockaddr *)&ctrl_addr, &addrlen);
3774dba3e78SLorenzo Bianconi 	if (ctrl_sockfd < 0) {
37827a36bc3SLorenzo Bianconi 		fprintf(stderr,
37927a36bc3SLorenzo Bianconi 			"Failed accepting connections on device %s control socket\n",
38027a36bc3SLorenzo Bianconi 			env.ifname);
3814dba3e78SLorenzo Bianconi 		free_fds(sockfd, 1);
3824dba3e78SLorenzo Bianconi 		return -errno;
3834dba3e78SLorenzo Bianconi 	}
3844dba3e78SLorenzo Bianconi 
3854dba3e78SLorenzo Bianconi 	/* CTRL loop */
3864dba3e78SLorenzo Bianconi 	while (!exiting) {
3874dba3e78SLorenzo Bianconi 		unsigned char buf[BUFSIZE] = {};
3884dba3e78SLorenzo Bianconi 		struct tlv_hdr *tlv = (struct tlv_hdr *)buf;
3894dba3e78SLorenzo Bianconi 
3904dba3e78SLorenzo Bianconi 		err = recv_msg(ctrl_sockfd, buf, BUFSIZE, NULL, 0);
3914dba3e78SLorenzo Bianconi 		if (err)
3924dba3e78SLorenzo Bianconi 			continue;
3934dba3e78SLorenzo Bianconi 
3944dba3e78SLorenzo Bianconi 		switch (ntohs(tlv->type)) {
3954dba3e78SLorenzo Bianconi 		case CMD_START: {
3964dba3e78SLorenzo Bianconi 			if (state == CMD_START)
3974dba3e78SLorenzo Bianconi 				continue;
3984dba3e78SLorenzo Bianconi 
3994dba3e78SLorenzo Bianconi 			state = CMD_START;
4004dba3e78SLorenzo Bianconi 			/* Load the XDP program on the DUT */
4014dba3e78SLorenzo Bianconi 			err = dut_attach_xdp_prog(skel, flags);
4024dba3e78SLorenzo Bianconi 			if (err)
4034dba3e78SLorenzo Bianconi 				goto out;
4044dba3e78SLorenzo Bianconi 
4054dba3e78SLorenzo Bianconi 			err = dut_run_echo_thread(&dut_thread, &echo_sockfd);
4064dba3e78SLorenzo Bianconi 			if (err < 0)
4074dba3e78SLorenzo Bianconi 				goto out;
4084dba3e78SLorenzo Bianconi 
4094dba3e78SLorenzo Bianconi 			tlv->type = htons(CMD_ACK);
4104dba3e78SLorenzo Bianconi 			tlv->len = htons(sizeof(*tlv));
4114dba3e78SLorenzo Bianconi 			err = send(ctrl_sockfd, buf, sizeof(*tlv), 0);
4124dba3e78SLorenzo Bianconi 			if (err < 0)
4134dba3e78SLorenzo Bianconi 				goto end_thread;
4144dba3e78SLorenzo Bianconi 			break;
4154dba3e78SLorenzo Bianconi 		}
4164dba3e78SLorenzo Bianconi 		case CMD_STOP:
4174dba3e78SLorenzo Bianconi 			if (state != CMD_START)
4184dba3e78SLorenzo Bianconi 				break;
4194dba3e78SLorenzo Bianconi 
4204dba3e78SLorenzo Bianconi 			state = CMD_STOP;
4214dba3e78SLorenzo Bianconi 
4224dba3e78SLorenzo Bianconi 			exiting = true;
4234dba3e78SLorenzo Bianconi 			bpf_xdp_detach(env.ifindex, flags, NULL);
4244dba3e78SLorenzo Bianconi 
4254dba3e78SLorenzo Bianconi 			tlv->type = htons(CMD_ACK);
4264dba3e78SLorenzo Bianconi 			tlv->len = htons(sizeof(*tlv));
4274dba3e78SLorenzo Bianconi 			err = send(ctrl_sockfd, buf, sizeof(*tlv), 0);
4284dba3e78SLorenzo Bianconi 			goto end_thread;
4294dba3e78SLorenzo Bianconi 		case CMD_GET_XDP_CAP: {
4304dba3e78SLorenzo Bianconi 			LIBBPF_OPTS(bpf_xdp_query_opts, opts);
4314dba3e78SLorenzo Bianconi 			unsigned long long val;
4324dba3e78SLorenzo Bianconi 			size_t n;
4334dba3e78SLorenzo Bianconi 
4344dba3e78SLorenzo Bianconi 			err = bpf_xdp_query(env.ifindex, XDP_FLAGS_DRV_MODE,
4354dba3e78SLorenzo Bianconi 					    &opts);
4364dba3e78SLorenzo Bianconi 			if (err) {
4374dba3e78SLorenzo Bianconi 				fprintf(stderr,
43827a36bc3SLorenzo Bianconi 					"Failed querying XDP cap for device %s\n",
43927a36bc3SLorenzo Bianconi 					env.ifname);
4404dba3e78SLorenzo Bianconi 				goto end_thread;
4414dba3e78SLorenzo Bianconi 			}
4424dba3e78SLorenzo Bianconi 
4434dba3e78SLorenzo Bianconi 			tlv->type = htons(CMD_ACK);
4444dba3e78SLorenzo Bianconi 			n = sizeof(*tlv) + sizeof(opts.feature_flags);
4454dba3e78SLorenzo Bianconi 			tlv->len = htons(n);
4464dba3e78SLorenzo Bianconi 
4474dba3e78SLorenzo Bianconi 			val = htobe64(opts.feature_flags);
4484dba3e78SLorenzo Bianconi 			memcpy(tlv->data, &val, sizeof(val));
4494dba3e78SLorenzo Bianconi 
4504dba3e78SLorenzo Bianconi 			err = send(ctrl_sockfd, buf, n, 0);
4514dba3e78SLorenzo Bianconi 			if (err < 0)
4524dba3e78SLorenzo Bianconi 				goto end_thread;
4534dba3e78SLorenzo Bianconi 			break;
4544dba3e78SLorenzo Bianconi 		}
4554dba3e78SLorenzo Bianconi 		case CMD_GET_STATS: {
4564dba3e78SLorenzo Bianconi 			unsigned int key = 0, val;
4574dba3e78SLorenzo Bianconi 			size_t n;
4584dba3e78SLorenzo Bianconi 
4594dba3e78SLorenzo Bianconi 			err = bpf_map__lookup_elem(skel->maps.dut_stats,
4604dba3e78SLorenzo Bianconi 						   &key, sizeof(key),
4614dba3e78SLorenzo Bianconi 						   &val, sizeof(val), 0);
4624dba3e78SLorenzo Bianconi 			if (err) {
463*c1cd734cSLorenzo Bianconi 				fprintf(stderr,
464*c1cd734cSLorenzo Bianconi 					"bpf_map_lookup_elem failed (%d)\n", err);
4654dba3e78SLorenzo Bianconi 				goto end_thread;
4664dba3e78SLorenzo Bianconi 			}
4674dba3e78SLorenzo Bianconi 
4684dba3e78SLorenzo Bianconi 			tlv->type = htons(CMD_ACK);
4694dba3e78SLorenzo Bianconi 			n = sizeof(*tlv) + sizeof(val);
4704dba3e78SLorenzo Bianconi 			tlv->len = htons(n);
4714dba3e78SLorenzo Bianconi 
4724dba3e78SLorenzo Bianconi 			val = htonl(val);
4734dba3e78SLorenzo Bianconi 			memcpy(tlv->data, &val, sizeof(val));
4744dba3e78SLorenzo Bianconi 
4754dba3e78SLorenzo Bianconi 			err = send(ctrl_sockfd, buf, n, 0);
4764dba3e78SLorenzo Bianconi 			if (err < 0)
4774dba3e78SLorenzo Bianconi 				goto end_thread;
4784dba3e78SLorenzo Bianconi 			break;
4794dba3e78SLorenzo Bianconi 		}
4804dba3e78SLorenzo Bianconi 		default:
4814dba3e78SLorenzo Bianconi 			break;
4824dba3e78SLorenzo Bianconi 		}
4834dba3e78SLorenzo Bianconi 	}
4844dba3e78SLorenzo Bianconi 
4854dba3e78SLorenzo Bianconi end_thread:
4864dba3e78SLorenzo Bianconi 	pthread_join(dut_thread, NULL);
4874dba3e78SLorenzo Bianconi out:
4884dba3e78SLorenzo Bianconi 	bpf_xdp_detach(env.ifindex, flags, NULL);
4894dba3e78SLorenzo Bianconi 	close(ctrl_sockfd);
4904dba3e78SLorenzo Bianconi 	free_fds(sockfd, 1);
4914dba3e78SLorenzo Bianconi 
4924dba3e78SLorenzo Bianconi 	return err;
4934dba3e78SLorenzo Bianconi }
4944dba3e78SLorenzo Bianconi 
tester_collect_detected_cap(struct xdp_features * skel,unsigned int dut_stats)4954dba3e78SLorenzo Bianconi static bool tester_collect_detected_cap(struct xdp_features *skel,
4964dba3e78SLorenzo Bianconi 					unsigned int dut_stats)
4974dba3e78SLorenzo Bianconi {
4984dba3e78SLorenzo Bianconi 	unsigned int err, key = 0, val;
4994dba3e78SLorenzo Bianconi 
5004dba3e78SLorenzo Bianconi 	if (!dut_stats)
5014dba3e78SLorenzo Bianconi 		return false;
5024dba3e78SLorenzo Bianconi 
5034dba3e78SLorenzo Bianconi 	err = bpf_map__lookup_elem(skel->maps.stats, &key, sizeof(key),
5044dba3e78SLorenzo Bianconi 				   &val, sizeof(val), 0);
5054dba3e78SLorenzo Bianconi 	if (err) {
506*c1cd734cSLorenzo Bianconi 		fprintf(stderr, "bpf_map_lookup_elem failed (%d)\n", err);
5074dba3e78SLorenzo Bianconi 		return false;
5084dba3e78SLorenzo Bianconi 	}
5094dba3e78SLorenzo Bianconi 
5104dba3e78SLorenzo Bianconi 	switch (env.feature.action) {
5114dba3e78SLorenzo Bianconi 	case XDP_PASS:
5124dba3e78SLorenzo Bianconi 	case XDP_TX:
5134dba3e78SLorenzo Bianconi 	case XDP_REDIRECT:
5144dba3e78SLorenzo Bianconi 		return val > 0;
5154dba3e78SLorenzo Bianconi 	case XDP_DROP:
5164dba3e78SLorenzo Bianconi 	case XDP_ABORTED:
5174dba3e78SLorenzo Bianconi 		return val == 0;
5184dba3e78SLorenzo Bianconi 	default:
5194dba3e78SLorenzo Bianconi 		break;
5204dba3e78SLorenzo Bianconi 	}
5214dba3e78SLorenzo Bianconi 
5224dba3e78SLorenzo Bianconi 	if (env.feature.drv_feature == NETDEV_XDP_ACT_NDO_XMIT)
5234dba3e78SLorenzo Bianconi 		return val > 0;
5244dba3e78SLorenzo Bianconi 
5254dba3e78SLorenzo Bianconi 	return false;
5264dba3e78SLorenzo Bianconi }
5274dba3e78SLorenzo Bianconi 
send_and_recv_msg(int sockfd,enum test_commands cmd,void * val,size_t val_size)5284dba3e78SLorenzo Bianconi static int send_and_recv_msg(int sockfd, enum test_commands cmd, void *val,
5294dba3e78SLorenzo Bianconi 			     size_t val_size)
5304dba3e78SLorenzo Bianconi {
5314dba3e78SLorenzo Bianconi 	unsigned char buf[BUFSIZE] = {};
5324dba3e78SLorenzo Bianconi 	struct tlv_hdr *tlv = (struct tlv_hdr *)buf;
5334dba3e78SLorenzo Bianconi 	int err;
5344dba3e78SLorenzo Bianconi 
5354dba3e78SLorenzo Bianconi 	tlv->type = htons(cmd);
5364dba3e78SLorenzo Bianconi 	tlv->len = htons(sizeof(*tlv));
5374dba3e78SLorenzo Bianconi 
5384dba3e78SLorenzo Bianconi 	err = send(sockfd, buf, sizeof(*tlv), 0);
5394dba3e78SLorenzo Bianconi 	if (err < 0)
5404dba3e78SLorenzo Bianconi 		return err;
5414dba3e78SLorenzo Bianconi 
5424dba3e78SLorenzo Bianconi 	err = recv_msg(sockfd, buf, BUFSIZE, val, val_size);
5434dba3e78SLorenzo Bianconi 	if (err < 0)
5444dba3e78SLorenzo Bianconi 		return err;
5454dba3e78SLorenzo Bianconi 
5464dba3e78SLorenzo Bianconi 	return ntohs(tlv->type) == CMD_ACK ? 0 : -EINVAL;
5474dba3e78SLorenzo Bianconi }
5484dba3e78SLorenzo Bianconi 
send_echo_msg(void)5494dba3e78SLorenzo Bianconi static int send_echo_msg(void)
5504dba3e78SLorenzo Bianconi {
5514dba3e78SLorenzo Bianconi 	unsigned char buf[sizeof(struct tlv_hdr)];
5524dba3e78SLorenzo Bianconi 	struct tlv_hdr *tlv = (struct tlv_hdr *)buf;
5534dba3e78SLorenzo Bianconi 	int sockfd, n;
5544dba3e78SLorenzo Bianconi 
5554dba3e78SLorenzo Bianconi 	sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
5564dba3e78SLorenzo Bianconi 	if (sockfd < 0) {
55727a36bc3SLorenzo Bianconi 		fprintf(stderr,
55827a36bc3SLorenzo Bianconi 			"Failed creating data UDP socket on device %s\n",
55927a36bc3SLorenzo Bianconi 			env.ifname);
5604dba3e78SLorenzo Bianconi 		return -errno;
5614dba3e78SLorenzo Bianconi 	}
5624dba3e78SLorenzo Bianconi 
5634dba3e78SLorenzo Bianconi 	tlv->type = htons(CMD_ECHO);
5644dba3e78SLorenzo Bianconi 	tlv->len = htons(sizeof(*tlv));
5654dba3e78SLorenzo Bianconi 
5664dba3e78SLorenzo Bianconi 	n = sendto(sockfd, buf, sizeof(*tlv), MSG_NOSIGNAL | MSG_CONFIRM,
5674dba3e78SLorenzo Bianconi 		   (struct sockaddr *)&env.dut_addr, sizeof(env.dut_addr));
5684dba3e78SLorenzo Bianconi 	close(sockfd);
5694dba3e78SLorenzo Bianconi 
5704dba3e78SLorenzo Bianconi 	return n == ntohs(tlv->len) ? 0 : -EINVAL;
5714dba3e78SLorenzo Bianconi }
5724dba3e78SLorenzo Bianconi 
tester_run(struct xdp_features * skel)5734dba3e78SLorenzo Bianconi static int tester_run(struct xdp_features *skel)
5744dba3e78SLorenzo Bianconi {
5754dba3e78SLorenzo Bianconi 	int flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE;
5764dba3e78SLorenzo Bianconi 	unsigned long long advertised_feature;
5774dba3e78SLorenzo Bianconi 	struct bpf_program *prog;
5784dba3e78SLorenzo Bianconi 	unsigned int stats;
5794dba3e78SLorenzo Bianconi 	int i, err, sockfd;
5804dba3e78SLorenzo Bianconi 	bool detected_cap;
5814dba3e78SLorenzo Bianconi 
5824dba3e78SLorenzo Bianconi 	sockfd = socket(AF_INET6, SOCK_STREAM, 0);
5834dba3e78SLorenzo Bianconi 	if (sockfd < 0) {
584*c1cd734cSLorenzo Bianconi 		fprintf(stderr,
585*c1cd734cSLorenzo Bianconi 			"Failed creating tester service control socket\n");
5864dba3e78SLorenzo Bianconi 		return -errno;
5874dba3e78SLorenzo Bianconi 	}
5884dba3e78SLorenzo Bianconi 
5894dba3e78SLorenzo Bianconi 	if (settimeo(sockfd, 1000) < 0)
5904dba3e78SLorenzo Bianconi 		return -EINVAL;
5914dba3e78SLorenzo Bianconi 
5924dba3e78SLorenzo Bianconi 	err = connect(sockfd, (struct sockaddr *)&env.dut_ctrl_addr,
5934dba3e78SLorenzo Bianconi 		      sizeof(env.dut_ctrl_addr));
5944dba3e78SLorenzo Bianconi 	if (err) {
595*c1cd734cSLorenzo Bianconi 		fprintf(stderr,
596*c1cd734cSLorenzo Bianconi 			"Failed connecting to the Device Under Test control socket\n");
5974dba3e78SLorenzo Bianconi 		return -errno;
5984dba3e78SLorenzo Bianconi 	}
5994dba3e78SLorenzo Bianconi 
6004dba3e78SLorenzo Bianconi 	err = send_and_recv_msg(sockfd, CMD_GET_XDP_CAP, &advertised_feature,
6014dba3e78SLorenzo Bianconi 				sizeof(advertised_feature));
6024dba3e78SLorenzo Bianconi 	if (err < 0) {
6034dba3e78SLorenzo Bianconi 		close(sockfd);
6044dba3e78SLorenzo Bianconi 		return err;
6054dba3e78SLorenzo Bianconi 	}
6064dba3e78SLorenzo Bianconi 
6074dba3e78SLorenzo Bianconi 	advertised_feature = be64toh(advertised_feature);
6084dba3e78SLorenzo Bianconi 
6094dba3e78SLorenzo Bianconi 	if (env.feature.drv_feature == NETDEV_XDP_ACT_NDO_XMIT ||
6104dba3e78SLorenzo Bianconi 	    env.feature.action == XDP_TX)
6114dba3e78SLorenzo Bianconi 		prog = skel->progs.xdp_tester_check_tx;
6124dba3e78SLorenzo Bianconi 	else
6134dba3e78SLorenzo Bianconi 		prog = skel->progs.xdp_tester_check_rx;
6144dba3e78SLorenzo Bianconi 
6154dba3e78SLorenzo Bianconi 	err = bpf_xdp_attach(env.ifindex, bpf_program__fd(prog), flags, NULL);
6164dba3e78SLorenzo Bianconi 	if (err) {
61727a36bc3SLorenzo Bianconi 		fprintf(stderr, "Failed attaching XDP program to device %s\n",
61827a36bc3SLorenzo Bianconi 			env.ifname);
6194dba3e78SLorenzo Bianconi 		goto out;
6204dba3e78SLorenzo Bianconi 	}
6214dba3e78SLorenzo Bianconi 
6224dba3e78SLorenzo Bianconi 	err = send_and_recv_msg(sockfd, CMD_START, NULL, 0);
6234dba3e78SLorenzo Bianconi 	if (err)
6244dba3e78SLorenzo Bianconi 		goto out;
6254dba3e78SLorenzo Bianconi 
6264dba3e78SLorenzo Bianconi 	for (i = 0; i < 10 && !exiting; i++) {
6274dba3e78SLorenzo Bianconi 		err = send_echo_msg();
6284dba3e78SLorenzo Bianconi 		if (err < 0)
6294dba3e78SLorenzo Bianconi 			goto out;
6304dba3e78SLorenzo Bianconi 
6314dba3e78SLorenzo Bianconi 		sleep(1);
6324dba3e78SLorenzo Bianconi 	}
6334dba3e78SLorenzo Bianconi 
6344dba3e78SLorenzo Bianconi 	err = send_and_recv_msg(sockfd, CMD_GET_STATS, &stats, sizeof(stats));
6354dba3e78SLorenzo Bianconi 	if (err)
6364dba3e78SLorenzo Bianconi 		goto out;
6374dba3e78SLorenzo Bianconi 
6384dba3e78SLorenzo Bianconi 	/* stop the test */
6394dba3e78SLorenzo Bianconi 	err = send_and_recv_msg(sockfd, CMD_STOP, NULL, 0);
6404dba3e78SLorenzo Bianconi 	/* send a new echo message to wake echo thread of the dut */
6414dba3e78SLorenzo Bianconi 	send_echo_msg();
6424dba3e78SLorenzo Bianconi 
6434dba3e78SLorenzo Bianconi 	detected_cap = tester_collect_detected_cap(skel, ntohl(stats));
6444dba3e78SLorenzo Bianconi 
6454dba3e78SLorenzo Bianconi 	fprintf(stdout, "Feature %s: [%s][%s]\n", get_xdp_feature_str(),
6464dba3e78SLorenzo Bianconi 		detected_cap ? GREEN("DETECTED") : RED("NOT DETECTED"),
6474dba3e78SLorenzo Bianconi 		env.feature.drv_feature & advertised_feature ? GREEN("ADVERTISED")
6484dba3e78SLorenzo Bianconi 							     : RED("NOT ADVERTISED"));
6494dba3e78SLorenzo Bianconi out:
6504dba3e78SLorenzo Bianconi 	bpf_xdp_detach(env.ifindex, flags, NULL);
6514dba3e78SLorenzo Bianconi 	close(sockfd);
6524dba3e78SLorenzo Bianconi 	return err < 0 ? err : 0;
6534dba3e78SLorenzo Bianconi }
6544dba3e78SLorenzo Bianconi 
main(int argc,char ** argv)6554dba3e78SLorenzo Bianconi int main(int argc, char **argv)
6564dba3e78SLorenzo Bianconi {
6574dba3e78SLorenzo Bianconi 	struct xdp_features *skel;
6584dba3e78SLorenzo Bianconi 	int err;
6594dba3e78SLorenzo Bianconi 
6604dba3e78SLorenzo Bianconi 	libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
6614dba3e78SLorenzo Bianconi 	libbpf_set_print(libbpf_print_fn);
6624dba3e78SLorenzo Bianconi 
6634dba3e78SLorenzo Bianconi 	signal(SIGINT, sig_handler);
6644dba3e78SLorenzo Bianconi 	signal(SIGTERM, sig_handler);
6654dba3e78SLorenzo Bianconi 
6664dba3e78SLorenzo Bianconi 	set_env_default();
6674dba3e78SLorenzo Bianconi 
6684dba3e78SLorenzo Bianconi 	/* Parse command line arguments */
6694dba3e78SLorenzo Bianconi 	err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
6704dba3e78SLorenzo Bianconi 	if (err)
6714dba3e78SLorenzo Bianconi 		return err;
6724dba3e78SLorenzo Bianconi 
6734dba3e78SLorenzo Bianconi 	if (env.ifindex < 0) {
67427a36bc3SLorenzo Bianconi 		fprintf(stderr, "Invalid device name %s\n", env.ifname);
6754dba3e78SLorenzo Bianconi 		return -ENODEV;
6764dba3e78SLorenzo Bianconi 	}
6774dba3e78SLorenzo Bianconi 
6784dba3e78SLorenzo Bianconi 	/* Load and verify BPF application */
6794dba3e78SLorenzo Bianconi 	skel = xdp_features__open();
6804dba3e78SLorenzo Bianconi 	if (!skel) {
6814dba3e78SLorenzo Bianconi 		fprintf(stderr, "Failed to open and load BPF skeleton\n");
6824dba3e78SLorenzo Bianconi 		return -EINVAL;
6834dba3e78SLorenzo Bianconi 	}
6844dba3e78SLorenzo Bianconi 
6854dba3e78SLorenzo Bianconi 	skel->rodata->tester_addr =
6864dba3e78SLorenzo Bianconi 		((struct sockaddr_in6 *)&env.tester_addr)->sin6_addr;
6874dba3e78SLorenzo Bianconi 	skel->rodata->dut_addr =
6884dba3e78SLorenzo Bianconi 		((struct sockaddr_in6 *)&env.dut_addr)->sin6_addr;
6894dba3e78SLorenzo Bianconi 
6904dba3e78SLorenzo Bianconi 	/* Load & verify BPF programs */
6914dba3e78SLorenzo Bianconi 	err = xdp_features__load(skel);
6924dba3e78SLorenzo Bianconi 	if (err) {
6934dba3e78SLorenzo Bianconi 		fprintf(stderr, "Failed to load and verify BPF skeleton\n");
6944dba3e78SLorenzo Bianconi 		goto cleanup;
6954dba3e78SLorenzo Bianconi 	}
6964dba3e78SLorenzo Bianconi 
6974dba3e78SLorenzo Bianconi 	err = xdp_features__attach(skel);
6984dba3e78SLorenzo Bianconi 	if (err) {
6994dba3e78SLorenzo Bianconi 		fprintf(stderr, "Failed to attach BPF skeleton\n");
7004dba3e78SLorenzo Bianconi 		goto cleanup;
7014dba3e78SLorenzo Bianconi 	}
7024dba3e78SLorenzo Bianconi 
7034dba3e78SLorenzo Bianconi 	if (env.is_tester) {
7044dba3e78SLorenzo Bianconi 		/* Tester */
70527a36bc3SLorenzo Bianconi 		fprintf(stdout, "Starting tester service on device %s\n",
70627a36bc3SLorenzo Bianconi 			env.ifname);
7074dba3e78SLorenzo Bianconi 		err = tester_run(skel);
7084dba3e78SLorenzo Bianconi 	} else {
7094dba3e78SLorenzo Bianconi 		/* DUT */
71027a36bc3SLorenzo Bianconi 		fprintf(stdout, "Starting test on device %s\n", env.ifname);
7114dba3e78SLorenzo Bianconi 		err = dut_run(skel);
7124dba3e78SLorenzo Bianconi 	}
7134dba3e78SLorenzo Bianconi 
7144dba3e78SLorenzo Bianconi cleanup:
7154dba3e78SLorenzo Bianconi 	xdp_features__destroy(skel);
7164dba3e78SLorenzo Bianconi 
7174dba3e78SLorenzo Bianconi 	return err < 0 ? -err : 0;
7184dba3e78SLorenzo Bianconi }
719