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