brintos

brintos / linux-shallow public Read only

0
0
Text · 16.1 KiB · 595c791 Raw
719 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <uapi/linux/bpf.h>3#include <uapi/linux/netdev.h>4#include <linux/if_link.h>5#include <signal.h>6#include <argp.h>7#include <net/if.h>8#include <sys/socket.h>9#include <netinet/in.h>10#include <netinet/tcp.h>11#include <unistd.h>12#include <arpa/inet.h>13#include <bpf/bpf.h>14#include <bpf/libbpf.h>15#include <pthread.h>16 17#include <network_helpers.h>18 19#include "xdp_features.skel.h"20#include "xdp_features.h"21 22#define RED(str)	"\033[0;31m" str "\033[0m"23#define GREEN(str)	"\033[0;32m" str "\033[0m"24#define YELLOW(str)	"\033[0;33m" str "\033[0m"25 26static struct env {27	bool verbosity;28	char ifname[IF_NAMESIZE];29	int ifindex;30	bool is_tester;31	struct {32		enum netdev_xdp_act drv_feature;33		enum xdp_action action;34	} feature;35	struct sockaddr_storage dut_ctrl_addr;36	struct sockaddr_storage dut_addr;37	struct sockaddr_storage tester_addr;38} env;39 40#define BUFSIZE		12841 42void test__fail(void) { /* for network_helpers.c */ }43 44static int libbpf_print_fn(enum libbpf_print_level level,45			   const char *format, va_list args)46{47	if (level == LIBBPF_DEBUG && !env.verbosity)48		return 0;49	return vfprintf(stderr, format, args);50}51 52static volatile bool exiting;53 54static void sig_handler(int sig)55{56	exiting = true;57}58 59const char *argp_program_version = "xdp-features 0.0";60const char argp_program_doc[] =61"XDP features detection application.\n"62"\n"63"XDP features application checks the XDP advertised features match detected ones.\n"64"\n"65"USAGE: ./xdp-features [-vt] [-f <xdp-feature>] [-D <dut-data-ip>] [-T <tester-data-ip>] [-C <dut-ctrl-ip>] <iface-name>\n"66"\n"67"dut-data-ip, tester-data-ip, dut-ctrl-ip: IPv6 or IPv4-mapped-IPv6 addresses;\n"68"\n"69"XDP features\n:"70"- XDP_PASS\n"71"- XDP_DROP\n"72"- XDP_ABORTED\n"73"- XDP_REDIRECT\n"74"- XDP_NDO_XMIT\n"75"- XDP_TX\n";76 77static const struct argp_option opts[] = {78	{ "verbose", 'v', NULL, 0, "Verbose debug output" },79	{ "tester", 't', NULL, 0, "Tester mode" },80	{ "feature", 'f', "XDP-FEATURE", 0, "XDP feature to test" },81	{ "dut_data_ip", 'D', "DUT-DATA-IP", 0, "DUT IP data channel" },82	{ "dut_ctrl_ip", 'C', "DUT-CTRL-IP", 0, "DUT IP control channel" },83	{ "tester_data_ip", 'T', "TESTER-DATA-IP", 0, "Tester IP data channel" },84	{},85};86 87static int get_xdp_feature(const char *arg)88{89	if (!strcmp(arg, "XDP_PASS")) {90		env.feature.action = XDP_PASS;91		env.feature.drv_feature = NETDEV_XDP_ACT_BASIC;92	} else if (!strcmp(arg, "XDP_DROP")) {93		env.feature.drv_feature = NETDEV_XDP_ACT_BASIC;94		env.feature.action = XDP_DROP;95	} else if (!strcmp(arg, "XDP_ABORTED")) {96		env.feature.drv_feature = NETDEV_XDP_ACT_BASIC;97		env.feature.action = XDP_ABORTED;98	} else if (!strcmp(arg, "XDP_TX")) {99		env.feature.drv_feature = NETDEV_XDP_ACT_BASIC;100		env.feature.action = XDP_TX;101	} else if (!strcmp(arg, "XDP_REDIRECT")) {102		env.feature.drv_feature = NETDEV_XDP_ACT_REDIRECT;103		env.feature.action = XDP_REDIRECT;104	} else if (!strcmp(arg, "XDP_NDO_XMIT")) {105		env.feature.drv_feature = NETDEV_XDP_ACT_NDO_XMIT;106	} else {107		return -EINVAL;108	}109 110	return 0;111}112 113static char *get_xdp_feature_str(void)114{115	switch (env.feature.action) {116	case XDP_PASS:117		return YELLOW("XDP_PASS");118	case XDP_DROP:119		return YELLOW("XDP_DROP");120	case XDP_ABORTED:121		return YELLOW("XDP_ABORTED");122	case XDP_TX:123		return YELLOW("XDP_TX");124	case XDP_REDIRECT:125		return YELLOW("XDP_REDIRECT");126	default:127		break;128	}129 130	if (env.feature.drv_feature == NETDEV_XDP_ACT_NDO_XMIT)131		return YELLOW("XDP_NDO_XMIT");132 133	return "";134}135 136static error_t parse_arg(int key, char *arg, struct argp_state *state)137{138	switch (key) {139	case 'v':140		env.verbosity = true;141		break;142	case 't':143		env.is_tester = true;144		break;145	case 'f':146		if (get_xdp_feature(arg) < 0) {147			fprintf(stderr, "Invalid xdp feature: %s\n", arg);148			argp_usage(state);149			return ARGP_ERR_UNKNOWN;150		}151		break;152	case 'D':153		if (make_sockaddr(AF_INET6, arg, DUT_ECHO_PORT,154				  &env.dut_addr, NULL)) {155			fprintf(stderr,156				"Invalid address assigned to the Device Under Test: %s\n",157				arg);158			return ARGP_ERR_UNKNOWN;159		}160		break;161	case 'C':162		if (make_sockaddr(AF_INET6, arg, DUT_CTRL_PORT,163				  &env.dut_ctrl_addr, NULL)) {164			fprintf(stderr,165				"Invalid address assigned to the Device Under Test: %s\n",166				arg);167			return ARGP_ERR_UNKNOWN;168		}169		break;170	case 'T':171		if (make_sockaddr(AF_INET6, arg, 0, &env.tester_addr, NULL)) {172			fprintf(stderr,173				"Invalid address assigned to the Tester device: %s\n",174				arg);175			return ARGP_ERR_UNKNOWN;176		}177		break;178	case ARGP_KEY_ARG:179		errno = 0;180		if (strlen(arg) >= IF_NAMESIZE) {181			fprintf(stderr, "Invalid device name: %s\n", arg);182			argp_usage(state);183			return ARGP_ERR_UNKNOWN;184		}185 186		env.ifindex = if_nametoindex(arg);187		if (!env.ifindex)188			env.ifindex = strtoul(arg, NULL, 0);189		if (!env.ifindex || !if_indextoname(env.ifindex, env.ifname)) {190			fprintf(stderr,191				"Bad interface index or name (%d): %s\n",192				errno, strerror(errno));193			argp_usage(state);194			return ARGP_ERR_UNKNOWN;195		}196		break;197	default:198		return ARGP_ERR_UNKNOWN;199	}200 201	return 0;202}203 204static const struct argp argp = {205	.options = opts,206	.parser = parse_arg,207	.doc = argp_program_doc,208};209 210static void set_env_default(void)211{212	env.feature.drv_feature = NETDEV_XDP_ACT_NDO_XMIT;213	env.feature.action = -EINVAL;214	env.ifindex = -ENODEV;215	strcpy(env.ifname, "unknown");216	make_sockaddr(AF_INET6, "::ffff:127.0.0.1", DUT_CTRL_PORT,217		      &env.dut_ctrl_addr, NULL);218	make_sockaddr(AF_INET6, "::ffff:127.0.0.1", DUT_ECHO_PORT,219		      &env.dut_addr, NULL);220	make_sockaddr(AF_INET6, "::ffff:127.0.0.1", 0, &env.tester_addr, NULL);221}222 223static void *dut_echo_thread(void *arg)224{225	unsigned char buf[sizeof(struct tlv_hdr)];226	int sockfd = *(int *)arg;227 228	while (!exiting) {229		struct tlv_hdr *tlv = (struct tlv_hdr *)buf;230		struct sockaddr_storage addr;231		socklen_t addrlen;232		size_t n;233 234		n = recvfrom(sockfd, buf, sizeof(buf), MSG_WAITALL,235			     (struct sockaddr *)&addr, &addrlen);236		if (n != ntohs(tlv->len))237			continue;238 239		if (ntohs(tlv->type) != CMD_ECHO)240			continue;241 242		sendto(sockfd, buf, sizeof(buf), MSG_NOSIGNAL | MSG_CONFIRM,243		       (struct sockaddr *)&addr, addrlen);244	}245 246	pthread_exit((void *)0);247	close(sockfd);248 249	return NULL;250}251 252static int dut_run_echo_thread(pthread_t *t, int *sockfd)253{254	int err;255 256	sockfd = start_reuseport_server(AF_INET6, SOCK_DGRAM, NULL,257					DUT_ECHO_PORT, 0, 1);258	if (!sockfd) {259		fprintf(stderr,260			"Failed creating data UDP socket on device %s\n",261			env.ifname);262		return -errno;263	}264 265	/* start echo channel */266	err = pthread_create(t, NULL, dut_echo_thread, sockfd);267	if (err) {268		fprintf(stderr,269			"Failed creating data UDP thread on device %s: %s\n",270			env.ifname, strerror(-err));271		free_fds(sockfd, 1);272		return -EINVAL;273	}274 275	return 0;276}277 278static int dut_attach_xdp_prog(struct xdp_features *skel, int flags)279{280	enum xdp_action action = env.feature.action;281	struct bpf_program *prog;282	unsigned int key = 0;283	int err, fd = 0;284 285	if (env.feature.drv_feature == NETDEV_XDP_ACT_NDO_XMIT) {286		struct bpf_devmap_val entry = {287			.ifindex = env.ifindex,288		};289 290		err = bpf_map__update_elem(skel->maps.dev_map,291					   &key, sizeof(key),292					   &entry, sizeof(entry), 0);293		if (err < 0)294			return err;295 296		fd = bpf_program__fd(skel->progs.xdp_do_redirect_cpumap);297		action = XDP_REDIRECT;298	}299 300	switch (action) {301	case XDP_TX:302		prog = skel->progs.xdp_do_tx;303		break;304	case XDP_DROP:305		prog = skel->progs.xdp_do_drop;306		break;307	case XDP_ABORTED:308		prog = skel->progs.xdp_do_aborted;309		break;310	case XDP_PASS:311		prog = skel->progs.xdp_do_pass;312		break;313	case XDP_REDIRECT: {314		struct bpf_cpumap_val entry = {315			.qsize = 2048,316			.bpf_prog.fd = fd,317		};318 319		err = bpf_map__update_elem(skel->maps.cpu_map,320					   &key, sizeof(key),321					   &entry, sizeof(entry), 0);322		if (err < 0)323			return err;324 325		prog = skel->progs.xdp_do_redirect;326		break;327	}328	default:329		return -EINVAL;330	}331 332	err = bpf_xdp_attach(env.ifindex, bpf_program__fd(prog), flags, NULL);333	if (err)334		fprintf(stderr, "Failed attaching XDP program to device %s\n",335			env.ifname);336	return err;337}338 339static int recv_msg(int sockfd, void *buf, size_t bufsize, void *val,340		    size_t val_size)341{342	struct tlv_hdr *tlv = (struct tlv_hdr *)buf;343	size_t len;344 345	len = recv(sockfd, buf, bufsize, 0);346	if (len != ntohs(tlv->len) || len < sizeof(*tlv))347		return -EINVAL;348 349	if (val) {350		len -= sizeof(*tlv);351		if (len > val_size)352			return -ENOMEM;353 354		memcpy(val, tlv->data, len);355	}356 357	return 0;358}359 360static int dut_run(struct xdp_features *skel)361{362	int flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE;363	int state, err = 0, *sockfd, ctrl_sockfd, echo_sockfd;364	struct sockaddr_storage ctrl_addr;365	pthread_t dut_thread = 0;366	socklen_t addrlen;367 368	sockfd = start_reuseport_server(AF_INET6, SOCK_STREAM, NULL,369					DUT_CTRL_PORT, 0, 1);370	if (!sockfd) {371		fprintf(stderr,372			"Failed creating control socket on device %s\n", env.ifname);373		return -errno;374	}375 376	ctrl_sockfd = accept(*sockfd, (struct sockaddr *)&ctrl_addr, &addrlen);377	if (ctrl_sockfd < 0) {378		fprintf(stderr,379			"Failed accepting connections on device %s control socket\n",380			env.ifname);381		free_fds(sockfd, 1);382		return -errno;383	}384 385	/* CTRL loop */386	while (!exiting) {387		unsigned char buf[BUFSIZE] = {};388		struct tlv_hdr *tlv = (struct tlv_hdr *)buf;389 390		err = recv_msg(ctrl_sockfd, buf, BUFSIZE, NULL, 0);391		if (err)392			continue;393 394		switch (ntohs(tlv->type)) {395		case CMD_START: {396			if (state == CMD_START)397				continue;398 399			state = CMD_START;400			/* Load the XDP program on the DUT */401			err = dut_attach_xdp_prog(skel, flags);402			if (err)403				goto out;404 405			err = dut_run_echo_thread(&dut_thread, &echo_sockfd);406			if (err < 0)407				goto out;408 409			tlv->type = htons(CMD_ACK);410			tlv->len = htons(sizeof(*tlv));411			err = send(ctrl_sockfd, buf, sizeof(*tlv), 0);412			if (err < 0)413				goto end_thread;414			break;415		}416		case CMD_STOP:417			if (state != CMD_START)418				break;419 420			state = CMD_STOP;421 422			exiting = true;423			bpf_xdp_detach(env.ifindex, flags, NULL);424 425			tlv->type = htons(CMD_ACK);426			tlv->len = htons(sizeof(*tlv));427			err = send(ctrl_sockfd, buf, sizeof(*tlv), 0);428			goto end_thread;429		case CMD_GET_XDP_CAP: {430			LIBBPF_OPTS(bpf_xdp_query_opts, opts);431			unsigned long long val;432			size_t n;433 434			err = bpf_xdp_query(env.ifindex, XDP_FLAGS_DRV_MODE,435					    &opts);436			if (err) {437				fprintf(stderr,438					"Failed querying XDP cap for device %s\n",439					env.ifname);440				goto end_thread;441			}442 443			tlv->type = htons(CMD_ACK);444			n = sizeof(*tlv) + sizeof(opts.feature_flags);445			tlv->len = htons(n);446 447			val = htobe64(opts.feature_flags);448			memcpy(tlv->data, &val, sizeof(val));449 450			err = send(ctrl_sockfd, buf, n, 0);451			if (err < 0)452				goto end_thread;453			break;454		}455		case CMD_GET_STATS: {456			unsigned int key = 0, val;457			size_t n;458 459			err = bpf_map__lookup_elem(skel->maps.dut_stats,460						   &key, sizeof(key),461						   &val, sizeof(val), 0);462			if (err) {463				fprintf(stderr,464					"bpf_map_lookup_elem failed (%d)\n", err);465				goto end_thread;466			}467 468			tlv->type = htons(CMD_ACK);469			n = sizeof(*tlv) + sizeof(val);470			tlv->len = htons(n);471 472			val = htonl(val);473			memcpy(tlv->data, &val, sizeof(val));474 475			err = send(ctrl_sockfd, buf, n, 0);476			if (err < 0)477				goto end_thread;478			break;479		}480		default:481			break;482		}483	}484 485end_thread:486	pthread_join(dut_thread, NULL);487out:488	bpf_xdp_detach(env.ifindex, flags, NULL);489	close(ctrl_sockfd);490	free_fds(sockfd, 1);491 492	return err;493}494 495static bool tester_collect_detected_cap(struct xdp_features *skel,496					unsigned int dut_stats)497{498	unsigned int err, key = 0, val;499 500	if (!dut_stats)501		return false;502 503	err = bpf_map__lookup_elem(skel->maps.stats, &key, sizeof(key),504				   &val, sizeof(val), 0);505	if (err) {506		fprintf(stderr, "bpf_map_lookup_elem failed (%d)\n", err);507		return false;508	}509 510	switch (env.feature.action) {511	case XDP_PASS:512	case XDP_TX:513	case XDP_REDIRECT:514		return val > 0;515	case XDP_DROP:516	case XDP_ABORTED:517		return val == 0;518	default:519		break;520	}521 522	if (env.feature.drv_feature == NETDEV_XDP_ACT_NDO_XMIT)523		return val > 0;524 525	return false;526}527 528static int send_and_recv_msg(int sockfd, enum test_commands cmd, void *val,529			     size_t val_size)530{531	unsigned char buf[BUFSIZE] = {};532	struct tlv_hdr *tlv = (struct tlv_hdr *)buf;533	int err;534 535	tlv->type = htons(cmd);536	tlv->len = htons(sizeof(*tlv));537 538	err = send(sockfd, buf, sizeof(*tlv), 0);539	if (err < 0)540		return err;541 542	err = recv_msg(sockfd, buf, BUFSIZE, val, val_size);543	if (err < 0)544		return err;545 546	return ntohs(tlv->type) == CMD_ACK ? 0 : -EINVAL;547}548 549static int send_echo_msg(void)550{551	unsigned char buf[sizeof(struct tlv_hdr)];552	struct tlv_hdr *tlv = (struct tlv_hdr *)buf;553	int sockfd, n;554 555	sockfd = socket(AF_INET6, SOCK_DGRAM, 0);556	if (sockfd < 0) {557		fprintf(stderr,558			"Failed creating data UDP socket on device %s\n",559			env.ifname);560		return -errno;561	}562 563	tlv->type = htons(CMD_ECHO);564	tlv->len = htons(sizeof(*tlv));565 566	n = sendto(sockfd, buf, sizeof(*tlv), MSG_NOSIGNAL | MSG_CONFIRM,567		   (struct sockaddr *)&env.dut_addr, sizeof(env.dut_addr));568	close(sockfd);569 570	return n == ntohs(tlv->len) ? 0 : -EINVAL;571}572 573static int tester_run(struct xdp_features *skel)574{575	int flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE;576	unsigned long long advertised_feature;577	struct bpf_program *prog;578	unsigned int stats;579	int i, err, sockfd;580	bool detected_cap;581 582	sockfd = socket(AF_INET6, SOCK_STREAM, 0);583	if (sockfd < 0) {584		fprintf(stderr,585			"Failed creating tester service control socket\n");586		return -errno;587	}588 589	if (settimeo(sockfd, 1000) < 0)590		return -EINVAL;591 592	err = connect(sockfd, (struct sockaddr *)&env.dut_ctrl_addr,593		      sizeof(env.dut_ctrl_addr));594	if (err) {595		fprintf(stderr,596			"Failed connecting to the Device Under Test control socket\n");597		return -errno;598	}599 600	err = send_and_recv_msg(sockfd, CMD_GET_XDP_CAP, &advertised_feature,601				sizeof(advertised_feature));602	if (err < 0) {603		close(sockfd);604		return err;605	}606 607	advertised_feature = be64toh(advertised_feature);608 609	if (env.feature.drv_feature == NETDEV_XDP_ACT_NDO_XMIT ||610	    env.feature.action == XDP_TX)611		prog = skel->progs.xdp_tester_check_tx;612	else613		prog = skel->progs.xdp_tester_check_rx;614 615	err = bpf_xdp_attach(env.ifindex, bpf_program__fd(prog), flags, NULL);616	if (err) {617		fprintf(stderr, "Failed attaching XDP program to device %s\n",618			env.ifname);619		goto out;620	}621 622	err = send_and_recv_msg(sockfd, CMD_START, NULL, 0);623	if (err)624		goto out;625 626	for (i = 0; i < 10 && !exiting; i++) {627		err = send_echo_msg();628		if (err < 0)629			goto out;630 631		sleep(1);632	}633 634	err = send_and_recv_msg(sockfd, CMD_GET_STATS, &stats, sizeof(stats));635	if (err)636		goto out;637 638	/* stop the test */639	err = send_and_recv_msg(sockfd, CMD_STOP, NULL, 0);640	/* send a new echo message to wake echo thread of the dut */641	send_echo_msg();642 643	detected_cap = tester_collect_detected_cap(skel, ntohl(stats));644 645	fprintf(stdout, "Feature %s: [%s][%s]\n", get_xdp_feature_str(),646		detected_cap ? GREEN("DETECTED") : RED("NOT DETECTED"),647		env.feature.drv_feature & advertised_feature ? GREEN("ADVERTISED")648							     : RED("NOT ADVERTISED"));649out:650	bpf_xdp_detach(env.ifindex, flags, NULL);651	close(sockfd);652	return err < 0 ? err : 0;653}654 655int main(int argc, char **argv)656{657	struct xdp_features *skel;658	int err;659 660	libbpf_set_strict_mode(LIBBPF_STRICT_ALL);661	libbpf_set_print(libbpf_print_fn);662 663	signal(SIGINT, sig_handler);664	signal(SIGTERM, sig_handler);665 666	set_env_default();667 668	/* Parse command line arguments */669	err = argp_parse(&argp, argc, argv, 0, NULL, NULL);670	if (err)671		return err;672 673	if (env.ifindex < 0) {674		fprintf(stderr, "Invalid device name %s\n", env.ifname);675		return -ENODEV;676	}677 678	/* Load and verify BPF application */679	skel = xdp_features__open();680	if (!skel) {681		fprintf(stderr, "Failed to open and load BPF skeleton\n");682		return -EINVAL;683	}684 685	skel->rodata->tester_addr =686		((struct sockaddr_in6 *)&env.tester_addr)->sin6_addr;687	skel->rodata->dut_addr =688		((struct sockaddr_in6 *)&env.dut_addr)->sin6_addr;689 690	/* Load & verify BPF programs */691	err = xdp_features__load(skel);692	if (err) {693		fprintf(stderr, "Failed to load and verify BPF skeleton\n");694		goto cleanup;695	}696 697	err = xdp_features__attach(skel);698	if (err) {699		fprintf(stderr, "Failed to attach BPF skeleton\n");700		goto cleanup;701	}702 703	if (env.is_tester) {704		/* Tester */705		fprintf(stdout, "Starting tester service on device %s\n",706			env.ifname);707		err = tester_run(skel);708	} else {709		/* DUT */710		fprintf(stdout, "Starting test on device %s\n", env.ifname);711		err = dut_run(skel);712	}713 714cleanup:715	xdp_features__destroy(skel);716 717	return err < 0 ? -err : 0;718}719