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