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>
8 #include <sys/socket.h>
9 #include <netinet/in.h>
10 #include <netinet/tcp.h>
12 #include <arpa/inet.h>
14 #include <bpf/libbpf.h>
17 #include <network_helpers.h>
19 #include "xdp_features.skel.h"
20 #include "xdp_features.h"
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"
28 char ifname[IF_NAMESIZE];
32 enum netdev_xdp_act drv_feature;
33 enum xdp_action action;
35 struct sockaddr_storage dut_ctrl_addr;
36 struct sockaddr_storage dut_addr;
37 struct sockaddr_storage tester_addr;
42 void test__fail(void) { /* for network_helpers.c */ }
44 static int libbpf_print_fn(enum libbpf_print_level level,
45 const char *format, va_list args)
47 if (level == LIBBPF_DEBUG && !env.verbosity)
49 return vfprintf(stderr, format, args);
52 static volatile bool exiting;
54 static void sig_handler(int sig)
59 const char *argp_program_version = "xdp-features 0.0";
60 const char argp_program_doc[] =
61 "XDP features detection application.\n"
63 "XDP features application checks the XDP advertised features match detected ones.\n"
65 "USAGE: ./xdp-features [-vt] [-f <xdp-feature>] [-D <dut-data-ip>] [-T <tester-data-ip>] [-C <dut-ctrl-ip>] <iface-name>\n"
67 "dut-data-ip, tester-data-ip, dut-ctrl-ip: IPv6 or IPv4-mapped-IPv6 addresses;\n"
77 static 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" },
87 static int get_xdp_feature(const char *arg)
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;
113 static char *get_xdp_feature_str(void)
115 switch (env.feature.action) {
117 return YELLOW("XDP_PASS");
119 return YELLOW("XDP_DROP");
121 return YELLOW("XDP_ABORTED");
123 return YELLOW("XDP_TX");
125 return YELLOW("XDP_REDIRECT");
130 if (env.feature.drv_feature == NETDEV_XDP_ACT_NDO_XMIT)
131 return YELLOW("XDP_NDO_XMIT");
136 static error_t parse_arg(int key, char *arg, struct argp_state *state)
140 env.verbosity = true;
143 env.is_tester = true;
146 if (get_xdp_feature(arg) < 0) {
147 fprintf(stderr, "Invalid xdp feature: %s\n", arg);
149 return ARGP_ERR_UNKNOWN;
153 if (make_sockaddr(AF_INET6, arg, DUT_ECHO_PORT,
154 &env.dut_addr, NULL)) {
156 "Invalid address assigned to the Device Under Test: %s\n",
158 return ARGP_ERR_UNKNOWN;
162 if (make_sockaddr(AF_INET6, arg, DUT_CTRL_PORT,
163 &env.dut_ctrl_addr, NULL)) {
165 "Invalid address assigned to the Device Under Test: %s\n",
167 return ARGP_ERR_UNKNOWN;
171 if (make_sockaddr(AF_INET6, arg, 0, &env.tester_addr, NULL)) {
173 "Invalid address assigned to the Tester device: %s\n",
175 return ARGP_ERR_UNKNOWN;
180 if (strlen(arg) >= IF_NAMESIZE) {
181 fprintf(stderr, "Invalid device name: %s\n", arg);
183 return ARGP_ERR_UNKNOWN;
186 env.ifindex = if_nametoindex(arg);
188 env.ifindex = strtoul(arg, NULL, 0);
189 if (!env.ifindex || !if_indextoname(env.ifindex, env.ifname)) {
191 "Bad interface index or name (%d): %s\n",
192 errno, strerror(errno));
194 return ARGP_ERR_UNKNOWN;
198 return ARGP_ERR_UNKNOWN;
204 static const struct argp argp = {
207 .doc = argp_program_doc,
210 static void set_env_default(void)
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);
223 static void *dut_echo_thread(void *arg)
225 unsigned char buf[sizeof(struct tlv_hdr)];
226 int sockfd = *(int *)arg;
229 struct tlv_hdr *tlv = (struct tlv_hdr *)buf;
230 struct sockaddr_storage addr;
234 n = recvfrom(sockfd, buf, sizeof(buf), MSG_WAITALL,
235 (struct sockaddr *)&addr, &addrlen);
236 if (n != ntohs(tlv->len))
239 if (ntohs(tlv->type) != CMD_ECHO)
242 sendto(sockfd, buf, sizeof(buf), MSG_NOSIGNAL | MSG_CONFIRM,
243 (struct sockaddr *)&addr, addrlen);
246 pthread_exit((void *)0);
252 static int dut_run_echo_thread(pthread_t *t, int *sockfd)
256 sockfd = start_reuseport_server(AF_INET6, SOCK_DGRAM, NULL,
257 DUT_ECHO_PORT, 0, 1);
260 "Failed creating data UDP socket on device %s\n",
265 /* start echo channel */
266 err = pthread_create(t, NULL, dut_echo_thread, sockfd);
269 "Failed creating data UDP thread on device %s: %s\n",
270 env.ifname, strerror(-err));
278 static int dut_attach_xdp_prog(struct xdp_features *skel, int flags)
280 enum xdp_action action = env.feature.action;
281 struct bpf_program *prog;
282 unsigned int key = 0;
285 if (env.feature.drv_feature == NETDEV_XDP_ACT_NDO_XMIT) {
286 struct bpf_devmap_val entry = {
287 .ifindex = env.ifindex,
290 err = bpf_map__update_elem(skel->maps.dev_map,
292 &entry, sizeof(entry), 0);
296 fd = bpf_program__fd(skel->progs.xdp_do_redirect_cpumap);
297 action = XDP_REDIRECT;
302 prog = skel->progs.xdp_do_tx;
305 prog = skel->progs.xdp_do_drop;
308 prog = skel->progs.xdp_do_aborted;
311 prog = skel->progs.xdp_do_pass;
314 struct bpf_cpumap_val entry = {
319 err = bpf_map__update_elem(skel->maps.cpu_map,
321 &entry, sizeof(entry), 0);
325 prog = skel->progs.xdp_do_redirect;
332 err = bpf_xdp_attach(env.ifindex, bpf_program__fd(prog), flags, NULL);
334 fprintf(stderr, "Failed attaching XDP program to device %s\n",
339 static int recv_msg(int sockfd, void *buf, size_t bufsize, void *val,
342 struct tlv_hdr *tlv = (struct tlv_hdr *)buf;
345 len = recv(sockfd, buf, bufsize, 0);
346 if (len != ntohs(tlv->len) || len < sizeof(*tlv))
354 memcpy(val, tlv->data, len);
360 static int dut_run(struct xdp_features *skel)
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;
368 sockfd = start_reuseport_server(AF_INET6, SOCK_STREAM, NULL,
369 DUT_CTRL_PORT, 0, 1);
372 "Failed creating control socket on device %s\n", env.ifname);
376 ctrl_sockfd = accept(*sockfd, (struct sockaddr *)&ctrl_addr, &addrlen);
377 if (ctrl_sockfd < 0) {
379 "Failed accepting connections on device %s control socket\n",
387 unsigned char buf[BUFSIZE] = {};
388 struct tlv_hdr *tlv = (struct tlv_hdr *)buf;
390 err = recv_msg(ctrl_sockfd, buf, BUFSIZE, NULL, 0);
394 switch (ntohs(tlv->type)) {
396 if (state == CMD_START)
400 /* Load the XDP program on the DUT */
401 err = dut_attach_xdp_prog(skel, flags);
405 err = dut_run_echo_thread(&dut_thread, &echo_sockfd);
409 tlv->type = htons(CMD_ACK);
410 tlv->len = htons(sizeof(*tlv));
411 err = send(ctrl_sockfd, buf, sizeof(*tlv), 0);
417 if (state != CMD_START)
423 bpf_xdp_detach(env.ifindex, flags, NULL);
425 tlv->type = htons(CMD_ACK);
426 tlv->len = htons(sizeof(*tlv));
427 err = send(ctrl_sockfd, buf, sizeof(*tlv), 0);
429 case CMD_GET_XDP_CAP: {
430 LIBBPF_OPTS(bpf_xdp_query_opts, opts);
431 unsigned long long val;
434 err = bpf_xdp_query(env.ifindex, XDP_FLAGS_DRV_MODE,
438 "Failed querying XDP cap for device %s\n",
443 tlv->type = htons(CMD_ACK);
444 n = sizeof(*tlv) + sizeof(opts.feature_flags);
447 val = htobe64(opts.feature_flags);
448 memcpy(tlv->data, &val, sizeof(val));
450 err = send(ctrl_sockfd, buf, n, 0);
455 case CMD_GET_STATS: {
456 unsigned int key = 0, val;
459 err = bpf_map__lookup_elem(skel->maps.dut_stats,
461 &val, sizeof(val), 0);
464 "bpf_map_lookup_elem failed (%d)\n", err);
468 tlv->type = htons(CMD_ACK);
469 n = sizeof(*tlv) + sizeof(val);
473 memcpy(tlv->data, &val, sizeof(val));
475 err = send(ctrl_sockfd, buf, n, 0);
486 pthread_join(dut_thread, NULL);
488 bpf_xdp_detach(env.ifindex, flags, NULL);
495 static bool tester_collect_detected_cap(struct xdp_features *skel,
496 unsigned int dut_stats)
498 unsigned int err, key = 0, val;
503 err = bpf_map__lookup_elem(skel->maps.stats, &key, sizeof(key),
504 &val, sizeof(val), 0);
506 fprintf(stderr, "bpf_map_lookup_elem failed (%d)\n", err);
510 switch (env.feature.action) {
522 if (env.feature.drv_feature == NETDEV_XDP_ACT_NDO_XMIT)
528 static int send_and_recv_msg(int sockfd, enum test_commands cmd, void *val,
531 unsigned char buf[BUFSIZE] = {};
532 struct tlv_hdr *tlv = (struct tlv_hdr *)buf;
535 tlv->type = htons(cmd);
536 tlv->len = htons(sizeof(*tlv));
538 err = send(sockfd, buf, sizeof(*tlv), 0);
542 err = recv_msg(sockfd, buf, BUFSIZE, val, val_size);
546 return ntohs(tlv->type) == CMD_ACK ? 0 : -EINVAL;
549 static int send_echo_msg(void)
551 unsigned char buf[sizeof(struct tlv_hdr)];
552 struct tlv_hdr *tlv = (struct tlv_hdr *)buf;
555 sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
558 "Failed creating data UDP socket on device %s\n",
563 tlv->type = htons(CMD_ECHO);
564 tlv->len = htons(sizeof(*tlv));
566 n = sendto(sockfd, buf, sizeof(*tlv), MSG_NOSIGNAL | MSG_CONFIRM,
567 (struct sockaddr *)&env.dut_addr, sizeof(env.dut_addr));
570 return n == ntohs(tlv->len) ? 0 : -EINVAL;
573 static int tester_run(struct xdp_features *skel)
575 int flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE;
576 unsigned long long advertised_feature;
577 struct bpf_program *prog;
582 sockfd = socket(AF_INET6, SOCK_STREAM, 0);
585 "Failed creating tester service control socket\n");
589 if (settimeo(sockfd, 1000) < 0)
592 err = connect(sockfd, (struct sockaddr *)&env.dut_ctrl_addr,
593 sizeof(env.dut_ctrl_addr));
596 "Failed connecting to the Device Under Test control socket\n");
600 err = send_and_recv_msg(sockfd, CMD_GET_XDP_CAP, &advertised_feature,
601 sizeof(advertised_feature));
607 advertised_feature = be64toh(advertised_feature);
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;
613 prog = skel->progs.xdp_tester_check_rx;
615 err = bpf_xdp_attach(env.ifindex, bpf_program__fd(prog), flags, NULL);
617 fprintf(stderr, "Failed attaching XDP program to device %s\n",
622 err = send_and_recv_msg(sockfd, CMD_START, NULL, 0);
626 for (i = 0; i < 10 && !exiting; i++) {
627 err = send_echo_msg();
634 err = send_and_recv_msg(sockfd, CMD_GET_STATS, &stats, sizeof(stats));
639 err = send_and_recv_msg(sockfd, CMD_STOP, NULL, 0);
640 /* send a new echo message to wake echo thread of the dut */
643 detected_cap = tester_collect_detected_cap(skel, ntohl(stats));
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"));
650 bpf_xdp_detach(env.ifindex, flags, NULL);
652 return err < 0 ? err : 0;
655 int main(int argc, char **argv)
657 struct xdp_features *skel;
660 libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
661 libbpf_set_print(libbpf_print_fn);
663 signal(SIGINT, sig_handler);
664 signal(SIGTERM, sig_handler);
668 /* Parse command line arguments */
669 err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
673 if (env.ifindex < 0) {
674 fprintf(stderr, "Invalid device name %s\n", env.ifname);
678 /* Load and verify BPF application */
679 skel = xdp_features__open();
681 fprintf(stderr, "Failed to open and load BPF skeleton\n");
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;
690 /* Load & verify BPF programs */
691 err = xdp_features__load(skel);
693 fprintf(stderr, "Failed to load and verify BPF skeleton\n");
697 err = xdp_features__attach(skel);
699 fprintf(stderr, "Failed to attach BPF skeleton\n");
705 fprintf(stdout, "Starting tester service on device %s\n",
707 err = tester_run(skel);
710 fprintf(stdout, "Starting test on device %s\n", env.ifname);
715 xdp_features__destroy(skel);
717 return err < 0 ? -err : 0;