162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <uapi/linux/bpf.h>
362306a36Sopenharmony_ci#include <uapi/linux/netdev.h>
462306a36Sopenharmony_ci#include <linux/if_link.h>
562306a36Sopenharmony_ci#include <signal.h>
662306a36Sopenharmony_ci#include <argp.h>
762306a36Sopenharmony_ci#include <net/if.h>
862306a36Sopenharmony_ci#include <sys/socket.h>
962306a36Sopenharmony_ci#include <netinet/in.h>
1062306a36Sopenharmony_ci#include <netinet/tcp.h>
1162306a36Sopenharmony_ci#include <unistd.h>
1262306a36Sopenharmony_ci#include <arpa/inet.h>
1362306a36Sopenharmony_ci#include <bpf/bpf.h>
1462306a36Sopenharmony_ci#include <bpf/libbpf.h>
1562306a36Sopenharmony_ci#include <pthread.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <network_helpers.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "xdp_features.skel.h"
2062306a36Sopenharmony_ci#include "xdp_features.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define RED(str)	"\033[0;31m" str "\033[0m"
2362306a36Sopenharmony_ci#define GREEN(str)	"\033[0;32m" str "\033[0m"
2462306a36Sopenharmony_ci#define YELLOW(str)	"\033[0;33m" str "\033[0m"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic struct env {
2762306a36Sopenharmony_ci	bool verbosity;
2862306a36Sopenharmony_ci	char ifname[IF_NAMESIZE];
2962306a36Sopenharmony_ci	int ifindex;
3062306a36Sopenharmony_ci	bool is_tester;
3162306a36Sopenharmony_ci	struct {
3262306a36Sopenharmony_ci		enum netdev_xdp_act drv_feature;
3362306a36Sopenharmony_ci		enum xdp_action action;
3462306a36Sopenharmony_ci	} feature;
3562306a36Sopenharmony_ci	struct sockaddr_storage dut_ctrl_addr;
3662306a36Sopenharmony_ci	struct sockaddr_storage dut_addr;
3762306a36Sopenharmony_ci	struct sockaddr_storage tester_addr;
3862306a36Sopenharmony_ci} env;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define BUFSIZE		128
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_civoid test__fail(void) { /* for network_helpers.c */ }
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int libbpf_print_fn(enum libbpf_print_level level,
4562306a36Sopenharmony_ci			   const char *format, va_list args)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	if (level == LIBBPF_DEBUG && !env.verbosity)
4862306a36Sopenharmony_ci		return 0;
4962306a36Sopenharmony_ci	return vfprintf(stderr, format, args);
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic volatile bool exiting;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic void sig_handler(int sig)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	exiting = true;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ciconst char *argp_program_version = "xdp-features 0.0";
6062306a36Sopenharmony_ciconst char argp_program_doc[] =
6162306a36Sopenharmony_ci"XDP features detection application.\n"
6262306a36Sopenharmony_ci"\n"
6362306a36Sopenharmony_ci"XDP features application checks the XDP advertised features match detected ones.\n"
6462306a36Sopenharmony_ci"\n"
6562306a36Sopenharmony_ci"USAGE: ./xdp-features [-vt] [-f <xdp-feature>] [-D <dut-data-ip>] [-T <tester-data-ip>] [-C <dut-ctrl-ip>] <iface-name>\n"
6662306a36Sopenharmony_ci"\n"
6762306a36Sopenharmony_ci"dut-data-ip, tester-data-ip, dut-ctrl-ip: IPv6 or IPv4-mapped-IPv6 addresses;\n"
6862306a36Sopenharmony_ci"\n"
6962306a36Sopenharmony_ci"XDP features\n:"
7062306a36Sopenharmony_ci"- XDP_PASS\n"
7162306a36Sopenharmony_ci"- XDP_DROP\n"
7262306a36Sopenharmony_ci"- XDP_ABORTED\n"
7362306a36Sopenharmony_ci"- XDP_REDIRECT\n"
7462306a36Sopenharmony_ci"- XDP_NDO_XMIT\n"
7562306a36Sopenharmony_ci"- XDP_TX\n";
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic const struct argp_option opts[] = {
7862306a36Sopenharmony_ci	{ "verbose", 'v', NULL, 0, "Verbose debug output" },
7962306a36Sopenharmony_ci	{ "tester", 't', NULL, 0, "Tester mode" },
8062306a36Sopenharmony_ci	{ "feature", 'f', "XDP-FEATURE", 0, "XDP feature to test" },
8162306a36Sopenharmony_ci	{ "dut_data_ip", 'D', "DUT-DATA-IP", 0, "DUT IP data channel" },
8262306a36Sopenharmony_ci	{ "dut_ctrl_ip", 'C', "DUT-CTRL-IP", 0, "DUT IP control channel" },
8362306a36Sopenharmony_ci	{ "tester_data_ip", 'T', "TESTER-DATA-IP", 0, "Tester IP data channel" },
8462306a36Sopenharmony_ci	{},
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int get_xdp_feature(const char *arg)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	if (!strcmp(arg, "XDP_PASS")) {
9062306a36Sopenharmony_ci		env.feature.action = XDP_PASS;
9162306a36Sopenharmony_ci		env.feature.drv_feature = NETDEV_XDP_ACT_BASIC;
9262306a36Sopenharmony_ci	} else if (!strcmp(arg, "XDP_DROP")) {
9362306a36Sopenharmony_ci		env.feature.drv_feature = NETDEV_XDP_ACT_BASIC;
9462306a36Sopenharmony_ci		env.feature.action = XDP_DROP;
9562306a36Sopenharmony_ci	} else if (!strcmp(arg, "XDP_ABORTED")) {
9662306a36Sopenharmony_ci		env.feature.drv_feature = NETDEV_XDP_ACT_BASIC;
9762306a36Sopenharmony_ci		env.feature.action = XDP_ABORTED;
9862306a36Sopenharmony_ci	} else if (!strcmp(arg, "XDP_TX")) {
9962306a36Sopenharmony_ci		env.feature.drv_feature = NETDEV_XDP_ACT_BASIC;
10062306a36Sopenharmony_ci		env.feature.action = XDP_TX;
10162306a36Sopenharmony_ci	} else if (!strcmp(arg, "XDP_REDIRECT")) {
10262306a36Sopenharmony_ci		env.feature.drv_feature = NETDEV_XDP_ACT_REDIRECT;
10362306a36Sopenharmony_ci		env.feature.action = XDP_REDIRECT;
10462306a36Sopenharmony_ci	} else if (!strcmp(arg, "XDP_NDO_XMIT")) {
10562306a36Sopenharmony_ci		env.feature.drv_feature = NETDEV_XDP_ACT_NDO_XMIT;
10662306a36Sopenharmony_ci	} else {
10762306a36Sopenharmony_ci		return -EINVAL;
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return 0;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic char *get_xdp_feature_str(void)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	switch (env.feature.action) {
11662306a36Sopenharmony_ci	case XDP_PASS:
11762306a36Sopenharmony_ci		return YELLOW("XDP_PASS");
11862306a36Sopenharmony_ci	case XDP_DROP:
11962306a36Sopenharmony_ci		return YELLOW("XDP_DROP");
12062306a36Sopenharmony_ci	case XDP_ABORTED:
12162306a36Sopenharmony_ci		return YELLOW("XDP_ABORTED");
12262306a36Sopenharmony_ci	case XDP_TX:
12362306a36Sopenharmony_ci		return YELLOW("XDP_TX");
12462306a36Sopenharmony_ci	case XDP_REDIRECT:
12562306a36Sopenharmony_ci		return YELLOW("XDP_REDIRECT");
12662306a36Sopenharmony_ci	default:
12762306a36Sopenharmony_ci		break;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (env.feature.drv_feature == NETDEV_XDP_ACT_NDO_XMIT)
13162306a36Sopenharmony_ci		return YELLOW("XDP_NDO_XMIT");
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return "";
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic error_t parse_arg(int key, char *arg, struct argp_state *state)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	switch (key) {
13962306a36Sopenharmony_ci	case 'v':
14062306a36Sopenharmony_ci		env.verbosity = true;
14162306a36Sopenharmony_ci		break;
14262306a36Sopenharmony_ci	case 't':
14362306a36Sopenharmony_ci		env.is_tester = true;
14462306a36Sopenharmony_ci		break;
14562306a36Sopenharmony_ci	case 'f':
14662306a36Sopenharmony_ci		if (get_xdp_feature(arg) < 0) {
14762306a36Sopenharmony_ci			fprintf(stderr, "Invalid xdp feature: %s\n", arg);
14862306a36Sopenharmony_ci			argp_usage(state);
14962306a36Sopenharmony_ci			return ARGP_ERR_UNKNOWN;
15062306a36Sopenharmony_ci		}
15162306a36Sopenharmony_ci		break;
15262306a36Sopenharmony_ci	case 'D':
15362306a36Sopenharmony_ci		if (make_sockaddr(AF_INET6, arg, DUT_ECHO_PORT,
15462306a36Sopenharmony_ci				  &env.dut_addr, NULL)) {
15562306a36Sopenharmony_ci			fprintf(stderr,
15662306a36Sopenharmony_ci				"Invalid address assigned to the Device Under Test: %s\n",
15762306a36Sopenharmony_ci				arg);
15862306a36Sopenharmony_ci			return ARGP_ERR_UNKNOWN;
15962306a36Sopenharmony_ci		}
16062306a36Sopenharmony_ci		break;
16162306a36Sopenharmony_ci	case 'C':
16262306a36Sopenharmony_ci		if (make_sockaddr(AF_INET6, arg, DUT_CTRL_PORT,
16362306a36Sopenharmony_ci				  &env.dut_ctrl_addr, NULL)) {
16462306a36Sopenharmony_ci			fprintf(stderr,
16562306a36Sopenharmony_ci				"Invalid address assigned to the Device Under Test: %s\n",
16662306a36Sopenharmony_ci				arg);
16762306a36Sopenharmony_ci			return ARGP_ERR_UNKNOWN;
16862306a36Sopenharmony_ci		}
16962306a36Sopenharmony_ci		break;
17062306a36Sopenharmony_ci	case 'T':
17162306a36Sopenharmony_ci		if (make_sockaddr(AF_INET6, arg, 0, &env.tester_addr, NULL)) {
17262306a36Sopenharmony_ci			fprintf(stderr,
17362306a36Sopenharmony_ci				"Invalid address assigned to the Tester device: %s\n",
17462306a36Sopenharmony_ci				arg);
17562306a36Sopenharmony_ci			return ARGP_ERR_UNKNOWN;
17662306a36Sopenharmony_ci		}
17762306a36Sopenharmony_ci		break;
17862306a36Sopenharmony_ci	case ARGP_KEY_ARG:
17962306a36Sopenharmony_ci		errno = 0;
18062306a36Sopenharmony_ci		if (strlen(arg) >= IF_NAMESIZE) {
18162306a36Sopenharmony_ci			fprintf(stderr, "Invalid device name: %s\n", arg);
18262306a36Sopenharmony_ci			argp_usage(state);
18362306a36Sopenharmony_ci			return ARGP_ERR_UNKNOWN;
18462306a36Sopenharmony_ci		}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci		env.ifindex = if_nametoindex(arg);
18762306a36Sopenharmony_ci		if (!env.ifindex)
18862306a36Sopenharmony_ci			env.ifindex = strtoul(arg, NULL, 0);
18962306a36Sopenharmony_ci		if (!env.ifindex || !if_indextoname(env.ifindex, env.ifname)) {
19062306a36Sopenharmony_ci			fprintf(stderr,
19162306a36Sopenharmony_ci				"Bad interface index or name (%d): %s\n",
19262306a36Sopenharmony_ci				errno, strerror(errno));
19362306a36Sopenharmony_ci			argp_usage(state);
19462306a36Sopenharmony_ci			return ARGP_ERR_UNKNOWN;
19562306a36Sopenharmony_ci		}
19662306a36Sopenharmony_ci		break;
19762306a36Sopenharmony_ci	default:
19862306a36Sopenharmony_ci		return ARGP_ERR_UNKNOWN;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	return 0;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic const struct argp argp = {
20562306a36Sopenharmony_ci	.options = opts,
20662306a36Sopenharmony_ci	.parser = parse_arg,
20762306a36Sopenharmony_ci	.doc = argp_program_doc,
20862306a36Sopenharmony_ci};
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic void set_env_default(void)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	env.feature.drv_feature = NETDEV_XDP_ACT_NDO_XMIT;
21362306a36Sopenharmony_ci	env.feature.action = -EINVAL;
21462306a36Sopenharmony_ci	env.ifindex = -ENODEV;
21562306a36Sopenharmony_ci	strcpy(env.ifname, "unknown");
21662306a36Sopenharmony_ci	make_sockaddr(AF_INET6, "::ffff:127.0.0.1", DUT_CTRL_PORT,
21762306a36Sopenharmony_ci		      &env.dut_ctrl_addr, NULL);
21862306a36Sopenharmony_ci	make_sockaddr(AF_INET6, "::ffff:127.0.0.1", DUT_ECHO_PORT,
21962306a36Sopenharmony_ci		      &env.dut_addr, NULL);
22062306a36Sopenharmony_ci	make_sockaddr(AF_INET6, "::ffff:127.0.0.1", 0, &env.tester_addr, NULL);
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic void *dut_echo_thread(void *arg)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	unsigned char buf[sizeof(struct tlv_hdr)];
22662306a36Sopenharmony_ci	int sockfd = *(int *)arg;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	while (!exiting) {
22962306a36Sopenharmony_ci		struct tlv_hdr *tlv = (struct tlv_hdr *)buf;
23062306a36Sopenharmony_ci		struct sockaddr_storage addr;
23162306a36Sopenharmony_ci		socklen_t addrlen;
23262306a36Sopenharmony_ci		size_t n;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		n = recvfrom(sockfd, buf, sizeof(buf), MSG_WAITALL,
23562306a36Sopenharmony_ci			     (struct sockaddr *)&addr, &addrlen);
23662306a36Sopenharmony_ci		if (n != ntohs(tlv->len))
23762306a36Sopenharmony_ci			continue;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		if (ntohs(tlv->type) != CMD_ECHO)
24062306a36Sopenharmony_ci			continue;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		sendto(sockfd, buf, sizeof(buf), MSG_NOSIGNAL | MSG_CONFIRM,
24362306a36Sopenharmony_ci		       (struct sockaddr *)&addr, addrlen);
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	pthread_exit((void *)0);
24762306a36Sopenharmony_ci	close(sockfd);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	return NULL;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic int dut_run_echo_thread(pthread_t *t, int *sockfd)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	int err;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	sockfd = start_reuseport_server(AF_INET6, SOCK_DGRAM, NULL,
25762306a36Sopenharmony_ci					DUT_ECHO_PORT, 0, 1);
25862306a36Sopenharmony_ci	if (!sockfd) {
25962306a36Sopenharmony_ci		fprintf(stderr,
26062306a36Sopenharmony_ci			"Failed creating data UDP socket on device %s\n",
26162306a36Sopenharmony_ci			env.ifname);
26262306a36Sopenharmony_ci		return -errno;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* start echo channel */
26662306a36Sopenharmony_ci	err = pthread_create(t, NULL, dut_echo_thread, sockfd);
26762306a36Sopenharmony_ci	if (err) {
26862306a36Sopenharmony_ci		fprintf(stderr,
26962306a36Sopenharmony_ci			"Failed creating data UDP thread on device %s: %s\n",
27062306a36Sopenharmony_ci			env.ifname, strerror(-err));
27162306a36Sopenharmony_ci		free_fds(sockfd, 1);
27262306a36Sopenharmony_ci		return -EINVAL;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return 0;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic int dut_attach_xdp_prog(struct xdp_features *skel, int flags)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	enum xdp_action action = env.feature.action;
28162306a36Sopenharmony_ci	struct bpf_program *prog;
28262306a36Sopenharmony_ci	unsigned int key = 0;
28362306a36Sopenharmony_ci	int err, fd = 0;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (env.feature.drv_feature == NETDEV_XDP_ACT_NDO_XMIT) {
28662306a36Sopenharmony_ci		struct bpf_devmap_val entry = {
28762306a36Sopenharmony_ci			.ifindex = env.ifindex,
28862306a36Sopenharmony_ci		};
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		err = bpf_map__update_elem(skel->maps.dev_map,
29162306a36Sopenharmony_ci					   &key, sizeof(key),
29262306a36Sopenharmony_ci					   &entry, sizeof(entry), 0);
29362306a36Sopenharmony_ci		if (err < 0)
29462306a36Sopenharmony_ci			return err;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci		fd = bpf_program__fd(skel->progs.xdp_do_redirect_cpumap);
29762306a36Sopenharmony_ci		action = XDP_REDIRECT;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	switch (action) {
30162306a36Sopenharmony_ci	case XDP_TX:
30262306a36Sopenharmony_ci		prog = skel->progs.xdp_do_tx;
30362306a36Sopenharmony_ci		break;
30462306a36Sopenharmony_ci	case XDP_DROP:
30562306a36Sopenharmony_ci		prog = skel->progs.xdp_do_drop;
30662306a36Sopenharmony_ci		break;
30762306a36Sopenharmony_ci	case XDP_ABORTED:
30862306a36Sopenharmony_ci		prog = skel->progs.xdp_do_aborted;
30962306a36Sopenharmony_ci		break;
31062306a36Sopenharmony_ci	case XDP_PASS:
31162306a36Sopenharmony_ci		prog = skel->progs.xdp_do_pass;
31262306a36Sopenharmony_ci		break;
31362306a36Sopenharmony_ci	case XDP_REDIRECT: {
31462306a36Sopenharmony_ci		struct bpf_cpumap_val entry = {
31562306a36Sopenharmony_ci			.qsize = 2048,
31662306a36Sopenharmony_ci			.bpf_prog.fd = fd,
31762306a36Sopenharmony_ci		};
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		err = bpf_map__update_elem(skel->maps.cpu_map,
32062306a36Sopenharmony_ci					   &key, sizeof(key),
32162306a36Sopenharmony_ci					   &entry, sizeof(entry), 0);
32262306a36Sopenharmony_ci		if (err < 0)
32362306a36Sopenharmony_ci			return err;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		prog = skel->progs.xdp_do_redirect;
32662306a36Sopenharmony_ci		break;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci	default:
32962306a36Sopenharmony_ci		return -EINVAL;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	err = bpf_xdp_attach(env.ifindex, bpf_program__fd(prog), flags, NULL);
33362306a36Sopenharmony_ci	if (err)
33462306a36Sopenharmony_ci		fprintf(stderr, "Failed attaching XDP program to device %s\n",
33562306a36Sopenharmony_ci			env.ifname);
33662306a36Sopenharmony_ci	return err;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic int recv_msg(int sockfd, void *buf, size_t bufsize, void *val,
34062306a36Sopenharmony_ci		    size_t val_size)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	struct tlv_hdr *tlv = (struct tlv_hdr *)buf;
34362306a36Sopenharmony_ci	size_t len;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	len = recv(sockfd, buf, bufsize, 0);
34662306a36Sopenharmony_ci	if (len != ntohs(tlv->len) || len < sizeof(*tlv))
34762306a36Sopenharmony_ci		return -EINVAL;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	if (val) {
35062306a36Sopenharmony_ci		len -= sizeof(*tlv);
35162306a36Sopenharmony_ci		if (len > val_size)
35262306a36Sopenharmony_ci			return -ENOMEM;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		memcpy(val, tlv->data, len);
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	return 0;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic int dut_run(struct xdp_features *skel)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	int flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE;
36362306a36Sopenharmony_ci	int state, err, *sockfd, ctrl_sockfd, echo_sockfd;
36462306a36Sopenharmony_ci	struct sockaddr_storage ctrl_addr;
36562306a36Sopenharmony_ci	pthread_t dut_thread;
36662306a36Sopenharmony_ci	socklen_t addrlen;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	sockfd = start_reuseport_server(AF_INET6, SOCK_STREAM, NULL,
36962306a36Sopenharmony_ci					DUT_CTRL_PORT, 0, 1);
37062306a36Sopenharmony_ci	if (!sockfd) {
37162306a36Sopenharmony_ci		fprintf(stderr,
37262306a36Sopenharmony_ci			"Failed creating control socket on device %s\n", env.ifname);
37362306a36Sopenharmony_ci		return -errno;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	ctrl_sockfd = accept(*sockfd, (struct sockaddr *)&ctrl_addr, &addrlen);
37762306a36Sopenharmony_ci	if (ctrl_sockfd < 0) {
37862306a36Sopenharmony_ci		fprintf(stderr,
37962306a36Sopenharmony_ci			"Failed accepting connections on device %s control socket\n",
38062306a36Sopenharmony_ci			env.ifname);
38162306a36Sopenharmony_ci		free_fds(sockfd, 1);
38262306a36Sopenharmony_ci		return -errno;
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	/* CTRL loop */
38662306a36Sopenharmony_ci	while (!exiting) {
38762306a36Sopenharmony_ci		unsigned char buf[BUFSIZE] = {};
38862306a36Sopenharmony_ci		struct tlv_hdr *tlv = (struct tlv_hdr *)buf;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		err = recv_msg(ctrl_sockfd, buf, BUFSIZE, NULL, 0);
39162306a36Sopenharmony_ci		if (err)
39262306a36Sopenharmony_ci			continue;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		switch (ntohs(tlv->type)) {
39562306a36Sopenharmony_ci		case CMD_START: {
39662306a36Sopenharmony_ci			if (state == CMD_START)
39762306a36Sopenharmony_ci				continue;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci			state = CMD_START;
40062306a36Sopenharmony_ci			/* Load the XDP program on the DUT */
40162306a36Sopenharmony_ci			err = dut_attach_xdp_prog(skel, flags);
40262306a36Sopenharmony_ci			if (err)
40362306a36Sopenharmony_ci				goto out;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci			err = dut_run_echo_thread(&dut_thread, &echo_sockfd);
40662306a36Sopenharmony_ci			if (err < 0)
40762306a36Sopenharmony_ci				goto out;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci			tlv->type = htons(CMD_ACK);
41062306a36Sopenharmony_ci			tlv->len = htons(sizeof(*tlv));
41162306a36Sopenharmony_ci			err = send(ctrl_sockfd, buf, sizeof(*tlv), 0);
41262306a36Sopenharmony_ci			if (err < 0)
41362306a36Sopenharmony_ci				goto end_thread;
41462306a36Sopenharmony_ci			break;
41562306a36Sopenharmony_ci		}
41662306a36Sopenharmony_ci		case CMD_STOP:
41762306a36Sopenharmony_ci			if (state != CMD_START)
41862306a36Sopenharmony_ci				break;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci			state = CMD_STOP;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci			exiting = true;
42362306a36Sopenharmony_ci			bpf_xdp_detach(env.ifindex, flags, NULL);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci			tlv->type = htons(CMD_ACK);
42662306a36Sopenharmony_ci			tlv->len = htons(sizeof(*tlv));
42762306a36Sopenharmony_ci			err = send(ctrl_sockfd, buf, sizeof(*tlv), 0);
42862306a36Sopenharmony_ci			goto end_thread;
42962306a36Sopenharmony_ci		case CMD_GET_XDP_CAP: {
43062306a36Sopenharmony_ci			LIBBPF_OPTS(bpf_xdp_query_opts, opts);
43162306a36Sopenharmony_ci			unsigned long long val;
43262306a36Sopenharmony_ci			size_t n;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci			err = bpf_xdp_query(env.ifindex, XDP_FLAGS_DRV_MODE,
43562306a36Sopenharmony_ci					    &opts);
43662306a36Sopenharmony_ci			if (err) {
43762306a36Sopenharmony_ci				fprintf(stderr,
43862306a36Sopenharmony_ci					"Failed querying XDP cap for device %s\n",
43962306a36Sopenharmony_ci					env.ifname);
44062306a36Sopenharmony_ci				goto end_thread;
44162306a36Sopenharmony_ci			}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci			tlv->type = htons(CMD_ACK);
44462306a36Sopenharmony_ci			n = sizeof(*tlv) + sizeof(opts.feature_flags);
44562306a36Sopenharmony_ci			tlv->len = htons(n);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci			val = htobe64(opts.feature_flags);
44862306a36Sopenharmony_ci			memcpy(tlv->data, &val, sizeof(val));
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci			err = send(ctrl_sockfd, buf, n, 0);
45162306a36Sopenharmony_ci			if (err < 0)
45262306a36Sopenharmony_ci				goto end_thread;
45362306a36Sopenharmony_ci			break;
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci		case CMD_GET_STATS: {
45662306a36Sopenharmony_ci			unsigned int key = 0, val;
45762306a36Sopenharmony_ci			size_t n;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci			err = bpf_map__lookup_elem(skel->maps.dut_stats,
46062306a36Sopenharmony_ci						   &key, sizeof(key),
46162306a36Sopenharmony_ci						   &val, sizeof(val), 0);
46262306a36Sopenharmony_ci			if (err) {
46362306a36Sopenharmony_ci				fprintf(stderr,
46462306a36Sopenharmony_ci					"bpf_map_lookup_elem failed (%d)\n", err);
46562306a36Sopenharmony_ci				goto end_thread;
46662306a36Sopenharmony_ci			}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci			tlv->type = htons(CMD_ACK);
46962306a36Sopenharmony_ci			n = sizeof(*tlv) + sizeof(val);
47062306a36Sopenharmony_ci			tlv->len = htons(n);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci			val = htonl(val);
47362306a36Sopenharmony_ci			memcpy(tlv->data, &val, sizeof(val));
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci			err = send(ctrl_sockfd, buf, n, 0);
47662306a36Sopenharmony_ci			if (err < 0)
47762306a36Sopenharmony_ci				goto end_thread;
47862306a36Sopenharmony_ci			break;
47962306a36Sopenharmony_ci		}
48062306a36Sopenharmony_ci		default:
48162306a36Sopenharmony_ci			break;
48262306a36Sopenharmony_ci		}
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ciend_thread:
48662306a36Sopenharmony_ci	pthread_join(dut_thread, NULL);
48762306a36Sopenharmony_ciout:
48862306a36Sopenharmony_ci	bpf_xdp_detach(env.ifindex, flags, NULL);
48962306a36Sopenharmony_ci	close(ctrl_sockfd);
49062306a36Sopenharmony_ci	free_fds(sockfd, 1);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	return err;
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic bool tester_collect_detected_cap(struct xdp_features *skel,
49662306a36Sopenharmony_ci					unsigned int dut_stats)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	unsigned int err, key = 0, val;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	if (!dut_stats)
50162306a36Sopenharmony_ci		return false;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	err = bpf_map__lookup_elem(skel->maps.stats, &key, sizeof(key),
50462306a36Sopenharmony_ci				   &val, sizeof(val), 0);
50562306a36Sopenharmony_ci	if (err) {
50662306a36Sopenharmony_ci		fprintf(stderr, "bpf_map_lookup_elem failed (%d)\n", err);
50762306a36Sopenharmony_ci		return false;
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	switch (env.feature.action) {
51162306a36Sopenharmony_ci	case XDP_PASS:
51262306a36Sopenharmony_ci	case XDP_TX:
51362306a36Sopenharmony_ci	case XDP_REDIRECT:
51462306a36Sopenharmony_ci		return val > 0;
51562306a36Sopenharmony_ci	case XDP_DROP:
51662306a36Sopenharmony_ci	case XDP_ABORTED:
51762306a36Sopenharmony_ci		return val == 0;
51862306a36Sopenharmony_ci	default:
51962306a36Sopenharmony_ci		break;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (env.feature.drv_feature == NETDEV_XDP_ACT_NDO_XMIT)
52362306a36Sopenharmony_ci		return val > 0;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	return false;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistatic int send_and_recv_msg(int sockfd, enum test_commands cmd, void *val,
52962306a36Sopenharmony_ci			     size_t val_size)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	unsigned char buf[BUFSIZE] = {};
53262306a36Sopenharmony_ci	struct tlv_hdr *tlv = (struct tlv_hdr *)buf;
53362306a36Sopenharmony_ci	int err;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	tlv->type = htons(cmd);
53662306a36Sopenharmony_ci	tlv->len = htons(sizeof(*tlv));
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	err = send(sockfd, buf, sizeof(*tlv), 0);
53962306a36Sopenharmony_ci	if (err < 0)
54062306a36Sopenharmony_ci		return err;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	err = recv_msg(sockfd, buf, BUFSIZE, val, val_size);
54362306a36Sopenharmony_ci	if (err < 0)
54462306a36Sopenharmony_ci		return err;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	return ntohs(tlv->type) == CMD_ACK ? 0 : -EINVAL;
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic int send_echo_msg(void)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	unsigned char buf[sizeof(struct tlv_hdr)];
55262306a36Sopenharmony_ci	struct tlv_hdr *tlv = (struct tlv_hdr *)buf;
55362306a36Sopenharmony_ci	int sockfd, n;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
55662306a36Sopenharmony_ci	if (sockfd < 0) {
55762306a36Sopenharmony_ci		fprintf(stderr,
55862306a36Sopenharmony_ci			"Failed creating data UDP socket on device %s\n",
55962306a36Sopenharmony_ci			env.ifname);
56062306a36Sopenharmony_ci		return -errno;
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	tlv->type = htons(CMD_ECHO);
56462306a36Sopenharmony_ci	tlv->len = htons(sizeof(*tlv));
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	n = sendto(sockfd, buf, sizeof(*tlv), MSG_NOSIGNAL | MSG_CONFIRM,
56762306a36Sopenharmony_ci		   (struct sockaddr *)&env.dut_addr, sizeof(env.dut_addr));
56862306a36Sopenharmony_ci	close(sockfd);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	return n == ntohs(tlv->len) ? 0 : -EINVAL;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic int tester_run(struct xdp_features *skel)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	int flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE;
57662306a36Sopenharmony_ci	unsigned long long advertised_feature;
57762306a36Sopenharmony_ci	struct bpf_program *prog;
57862306a36Sopenharmony_ci	unsigned int stats;
57962306a36Sopenharmony_ci	int i, err, sockfd;
58062306a36Sopenharmony_ci	bool detected_cap;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	sockfd = socket(AF_INET6, SOCK_STREAM, 0);
58362306a36Sopenharmony_ci	if (sockfd < 0) {
58462306a36Sopenharmony_ci		fprintf(stderr,
58562306a36Sopenharmony_ci			"Failed creating tester service control socket\n");
58662306a36Sopenharmony_ci		return -errno;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	if (settimeo(sockfd, 1000) < 0)
59062306a36Sopenharmony_ci		return -EINVAL;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	err = connect(sockfd, (struct sockaddr *)&env.dut_ctrl_addr,
59362306a36Sopenharmony_ci		      sizeof(env.dut_ctrl_addr));
59462306a36Sopenharmony_ci	if (err) {
59562306a36Sopenharmony_ci		fprintf(stderr,
59662306a36Sopenharmony_ci			"Failed connecting to the Device Under Test control socket\n");
59762306a36Sopenharmony_ci		return -errno;
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	err = send_and_recv_msg(sockfd, CMD_GET_XDP_CAP, &advertised_feature,
60162306a36Sopenharmony_ci				sizeof(advertised_feature));
60262306a36Sopenharmony_ci	if (err < 0) {
60362306a36Sopenharmony_ci		close(sockfd);
60462306a36Sopenharmony_ci		return err;
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	advertised_feature = be64toh(advertised_feature);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	if (env.feature.drv_feature == NETDEV_XDP_ACT_NDO_XMIT ||
61062306a36Sopenharmony_ci	    env.feature.action == XDP_TX)
61162306a36Sopenharmony_ci		prog = skel->progs.xdp_tester_check_tx;
61262306a36Sopenharmony_ci	else
61362306a36Sopenharmony_ci		prog = skel->progs.xdp_tester_check_rx;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	err = bpf_xdp_attach(env.ifindex, bpf_program__fd(prog), flags, NULL);
61662306a36Sopenharmony_ci	if (err) {
61762306a36Sopenharmony_ci		fprintf(stderr, "Failed attaching XDP program to device %s\n",
61862306a36Sopenharmony_ci			env.ifname);
61962306a36Sopenharmony_ci		goto out;
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	err = send_and_recv_msg(sockfd, CMD_START, NULL, 0);
62362306a36Sopenharmony_ci	if (err)
62462306a36Sopenharmony_ci		goto out;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	for (i = 0; i < 10 && !exiting; i++) {
62762306a36Sopenharmony_ci		err = send_echo_msg();
62862306a36Sopenharmony_ci		if (err < 0)
62962306a36Sopenharmony_ci			goto out;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci		sleep(1);
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	err = send_and_recv_msg(sockfd, CMD_GET_STATS, &stats, sizeof(stats));
63562306a36Sopenharmony_ci	if (err)
63662306a36Sopenharmony_ci		goto out;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	/* stop the test */
63962306a36Sopenharmony_ci	err = send_and_recv_msg(sockfd, CMD_STOP, NULL, 0);
64062306a36Sopenharmony_ci	/* send a new echo message to wake echo thread of the dut */
64162306a36Sopenharmony_ci	send_echo_msg();
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	detected_cap = tester_collect_detected_cap(skel, ntohl(stats));
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	fprintf(stdout, "Feature %s: [%s][%s]\n", get_xdp_feature_str(),
64662306a36Sopenharmony_ci		detected_cap ? GREEN("DETECTED") : RED("NOT DETECTED"),
64762306a36Sopenharmony_ci		env.feature.drv_feature & advertised_feature ? GREEN("ADVERTISED")
64862306a36Sopenharmony_ci							     : RED("NOT ADVERTISED"));
64962306a36Sopenharmony_ciout:
65062306a36Sopenharmony_ci	bpf_xdp_detach(env.ifindex, flags, NULL);
65162306a36Sopenharmony_ci	close(sockfd);
65262306a36Sopenharmony_ci	return err < 0 ? err : 0;
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ciint main(int argc, char **argv)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	struct xdp_features *skel;
65862306a36Sopenharmony_ci	int err;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
66162306a36Sopenharmony_ci	libbpf_set_print(libbpf_print_fn);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	signal(SIGINT, sig_handler);
66462306a36Sopenharmony_ci	signal(SIGTERM, sig_handler);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	set_env_default();
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	/* Parse command line arguments */
66962306a36Sopenharmony_ci	err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
67062306a36Sopenharmony_ci	if (err)
67162306a36Sopenharmony_ci		return err;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	if (env.ifindex < 0) {
67462306a36Sopenharmony_ci		fprintf(stderr, "Invalid device name %s\n", env.ifname);
67562306a36Sopenharmony_ci		return -ENODEV;
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	/* Load and verify BPF application */
67962306a36Sopenharmony_ci	skel = xdp_features__open();
68062306a36Sopenharmony_ci	if (!skel) {
68162306a36Sopenharmony_ci		fprintf(stderr, "Failed to open and load BPF skeleton\n");
68262306a36Sopenharmony_ci		return -EINVAL;
68362306a36Sopenharmony_ci	}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	skel->rodata->tester_addr =
68662306a36Sopenharmony_ci		((struct sockaddr_in6 *)&env.tester_addr)->sin6_addr;
68762306a36Sopenharmony_ci	skel->rodata->dut_addr =
68862306a36Sopenharmony_ci		((struct sockaddr_in6 *)&env.dut_addr)->sin6_addr;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	/* Load & verify BPF programs */
69162306a36Sopenharmony_ci	err = xdp_features__load(skel);
69262306a36Sopenharmony_ci	if (err) {
69362306a36Sopenharmony_ci		fprintf(stderr, "Failed to load and verify BPF skeleton\n");
69462306a36Sopenharmony_ci		goto cleanup;
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	err = xdp_features__attach(skel);
69862306a36Sopenharmony_ci	if (err) {
69962306a36Sopenharmony_ci		fprintf(stderr, "Failed to attach BPF skeleton\n");
70062306a36Sopenharmony_ci		goto cleanup;
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	if (env.is_tester) {
70462306a36Sopenharmony_ci		/* Tester */
70562306a36Sopenharmony_ci		fprintf(stdout, "Starting tester service on device %s\n",
70662306a36Sopenharmony_ci			env.ifname);
70762306a36Sopenharmony_ci		err = tester_run(skel);
70862306a36Sopenharmony_ci	} else {
70962306a36Sopenharmony_ci		/* DUT */
71062306a36Sopenharmony_ci		fprintf(stdout, "Starting test on device %s\n", env.ifname);
71162306a36Sopenharmony_ci		err = dut_run(skel);
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cicleanup:
71562306a36Sopenharmony_ci	xdp_features__destroy(skel);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	return err < 0 ? -err : 0;
71862306a36Sopenharmony_ci}
719