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