18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/bpf.h> 58c2ecf20Sopenharmony_ci#include <linux/if_link.h> 68c2ecf20Sopenharmony_ci#include <arpa/inet.h> 78c2ecf20Sopenharmony_ci#include <assert.h> 88c2ecf20Sopenharmony_ci#include <errno.h> 98c2ecf20Sopenharmony_ci#include <signal.h> 108c2ecf20Sopenharmony_ci#include <stdio.h> 118c2ecf20Sopenharmony_ci#include <stdlib.h> 128c2ecf20Sopenharmony_ci#include <string.h> 138c2ecf20Sopenharmony_ci#include <unistd.h> 148c2ecf20Sopenharmony_ci#include <libgen.h> 158c2ecf20Sopenharmony_ci#include <sys/resource.h> 168c2ecf20Sopenharmony_ci#include <net/if.h> 178c2ecf20Sopenharmony_ci#include <sys/types.h> 188c2ecf20Sopenharmony_ci#include <sys/socket.h> 198c2ecf20Sopenharmony_ci#include <netdb.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "bpf/bpf.h" 228c2ecf20Sopenharmony_ci#include "bpf/libbpf.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "xdping.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int ifindex; 278c2ecf20Sopenharmony_cistatic __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic void cleanup(int sig) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci bpf_set_link_xdp_fd(ifindex, -1, xdp_flags); 328c2ecf20Sopenharmony_ci if (sig) 338c2ecf20Sopenharmony_ci exit(1); 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int get_stats(int fd, __u16 count, __u32 raddr) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci struct pinginfo pinginfo = { 0 }; 398c2ecf20Sopenharmony_ci char inaddrbuf[INET_ADDRSTRLEN]; 408c2ecf20Sopenharmony_ci struct in_addr inaddr; 418c2ecf20Sopenharmony_ci __u16 i; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci inaddr.s_addr = raddr; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci printf("\nXDP RTT data:\n"); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (bpf_map_lookup_elem(fd, &raddr, &pinginfo)) { 488c2ecf20Sopenharmony_ci perror("bpf_map_lookup elem"); 498c2ecf20Sopenharmony_ci return 1; 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 538c2ecf20Sopenharmony_ci if (pinginfo.times[i] == 0) 548c2ecf20Sopenharmony_ci break; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci printf("64 bytes from %s: icmp_seq=%d ttl=64 time=%#.5f ms\n", 578c2ecf20Sopenharmony_ci inet_ntop(AF_INET, &inaddr, inaddrbuf, 588c2ecf20Sopenharmony_ci sizeof(inaddrbuf)), 598c2ecf20Sopenharmony_ci count + i + 1, 608c2ecf20Sopenharmony_ci (double)pinginfo.times[i]/1000000); 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (i < count) { 648c2ecf20Sopenharmony_ci fprintf(stderr, "Expected %d samples, got %d.\n", count, i); 658c2ecf20Sopenharmony_ci return 1; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci bpf_map_delete_elem(fd, &raddr); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return 0; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void show_usage(const char *prog) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci fprintf(stderr, 768c2ecf20Sopenharmony_ci "usage: %s [OPTS] -I interface destination\n\n" 778c2ecf20Sopenharmony_ci "OPTS:\n" 788c2ecf20Sopenharmony_ci " -c count Stop after sending count requests\n" 798c2ecf20Sopenharmony_ci " (default %d, max %d)\n" 808c2ecf20Sopenharmony_ci " -I interface interface name\n" 818c2ecf20Sopenharmony_ci " -N Run in driver mode\n" 828c2ecf20Sopenharmony_ci " -s Server mode\n" 838c2ecf20Sopenharmony_ci " -S Run in skb mode\n", 848c2ecf20Sopenharmony_ci prog, XDPING_DEFAULT_COUNT, XDPING_MAX_COUNT); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ciint main(int argc, char **argv) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci __u32 mode_flags = XDP_FLAGS_DRV_MODE | XDP_FLAGS_SKB_MODE; 908c2ecf20Sopenharmony_ci struct addrinfo *a, hints = { .ai_family = AF_INET }; 918c2ecf20Sopenharmony_ci struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; 928c2ecf20Sopenharmony_ci __u16 count = XDPING_DEFAULT_COUNT; 938c2ecf20Sopenharmony_ci struct pinginfo pinginfo = { 0 }; 948c2ecf20Sopenharmony_ci const char *optstr = "c:I:NsS"; 958c2ecf20Sopenharmony_ci struct bpf_program *main_prog; 968c2ecf20Sopenharmony_ci int prog_fd = -1, map_fd = -1; 978c2ecf20Sopenharmony_ci struct sockaddr_in rin; 988c2ecf20Sopenharmony_ci struct bpf_object *obj; 998c2ecf20Sopenharmony_ci struct bpf_map *map; 1008c2ecf20Sopenharmony_ci char *ifname = NULL; 1018c2ecf20Sopenharmony_ci char filename[256]; 1028c2ecf20Sopenharmony_ci int opt, ret = 1; 1038c2ecf20Sopenharmony_ci __u32 raddr = 0; 1048c2ecf20Sopenharmony_ci int server = 0; 1058c2ecf20Sopenharmony_ci char cmd[256]; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci while ((opt = getopt(argc, argv, optstr)) != -1) { 1088c2ecf20Sopenharmony_ci switch (opt) { 1098c2ecf20Sopenharmony_ci case 'c': 1108c2ecf20Sopenharmony_ci count = atoi(optarg); 1118c2ecf20Sopenharmony_ci if (count < 1 || count > XDPING_MAX_COUNT) { 1128c2ecf20Sopenharmony_ci fprintf(stderr, 1138c2ecf20Sopenharmony_ci "min count is 1, max count is %d\n", 1148c2ecf20Sopenharmony_ci XDPING_MAX_COUNT); 1158c2ecf20Sopenharmony_ci return 1; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci case 'I': 1198c2ecf20Sopenharmony_ci ifname = optarg; 1208c2ecf20Sopenharmony_ci ifindex = if_nametoindex(ifname); 1218c2ecf20Sopenharmony_ci if (!ifindex) { 1228c2ecf20Sopenharmony_ci fprintf(stderr, "Could not get interface %s\n", 1238c2ecf20Sopenharmony_ci ifname); 1248c2ecf20Sopenharmony_ci return 1; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci case 'N': 1288c2ecf20Sopenharmony_ci xdp_flags |= XDP_FLAGS_DRV_MODE; 1298c2ecf20Sopenharmony_ci break; 1308c2ecf20Sopenharmony_ci case 's': 1318c2ecf20Sopenharmony_ci /* use server program */ 1328c2ecf20Sopenharmony_ci server = 1; 1338c2ecf20Sopenharmony_ci break; 1348c2ecf20Sopenharmony_ci case 'S': 1358c2ecf20Sopenharmony_ci xdp_flags |= XDP_FLAGS_SKB_MODE; 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci default: 1388c2ecf20Sopenharmony_ci show_usage(basename(argv[0])); 1398c2ecf20Sopenharmony_ci return 1; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (!ifname) { 1448c2ecf20Sopenharmony_ci show_usage(basename(argv[0])); 1458c2ecf20Sopenharmony_ci return 1; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci if (!server && optind == argc) { 1488c2ecf20Sopenharmony_ci show_usage(basename(argv[0])); 1498c2ecf20Sopenharmony_ci return 1; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if ((xdp_flags & mode_flags) == mode_flags) { 1538c2ecf20Sopenharmony_ci fprintf(stderr, "-N or -S can be specified, not both.\n"); 1548c2ecf20Sopenharmony_ci show_usage(basename(argv[0])); 1558c2ecf20Sopenharmony_ci return 1; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (!server) { 1598c2ecf20Sopenharmony_ci /* Only supports IPv4; see hints initiailization above. */ 1608c2ecf20Sopenharmony_ci if (getaddrinfo(argv[optind], NULL, &hints, &a) || !a) { 1618c2ecf20Sopenharmony_ci fprintf(stderr, "Could not resolve %s\n", argv[optind]); 1628c2ecf20Sopenharmony_ci return 1; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci memcpy(&rin, a->ai_addr, sizeof(rin)); 1658c2ecf20Sopenharmony_ci raddr = rin.sin_addr.s_addr; 1668c2ecf20Sopenharmony_ci freeaddrinfo(a); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (setrlimit(RLIMIT_MEMLOCK, &r)) { 1708c2ecf20Sopenharmony_ci perror("setrlimit(RLIMIT_MEMLOCK)"); 1718c2ecf20Sopenharmony_ci return 1; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (bpf_prog_load(filename, BPF_PROG_TYPE_XDP, &obj, &prog_fd)) { 1778c2ecf20Sopenharmony_ci fprintf(stderr, "load of %s failed\n", filename); 1788c2ecf20Sopenharmony_ci return 1; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci main_prog = bpf_object__find_program_by_title(obj, 1828c2ecf20Sopenharmony_ci server ? "xdpserver" : 1838c2ecf20Sopenharmony_ci "xdpclient"); 1848c2ecf20Sopenharmony_ci if (main_prog) 1858c2ecf20Sopenharmony_ci prog_fd = bpf_program__fd(main_prog); 1868c2ecf20Sopenharmony_ci if (!main_prog || prog_fd < 0) { 1878c2ecf20Sopenharmony_ci fprintf(stderr, "could not find xdping program"); 1888c2ecf20Sopenharmony_ci return 1; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci map = bpf_map__next(NULL, obj); 1928c2ecf20Sopenharmony_ci if (map) 1938c2ecf20Sopenharmony_ci map_fd = bpf_map__fd(map); 1948c2ecf20Sopenharmony_ci if (!map || map_fd < 0) { 1958c2ecf20Sopenharmony_ci fprintf(stderr, "Could not find ping map"); 1968c2ecf20Sopenharmony_ci goto done; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci signal(SIGINT, cleanup); 2008c2ecf20Sopenharmony_ci signal(SIGTERM, cleanup); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci printf("Setting up XDP for %s, please wait...\n", ifname); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci printf("XDP setup disrupts network connectivity, hit Ctrl+C to quit\n"); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) { 2078c2ecf20Sopenharmony_ci fprintf(stderr, "Link set xdp fd failed for %s\n", ifname); 2088c2ecf20Sopenharmony_ci goto done; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (server) { 2128c2ecf20Sopenharmony_ci close(prog_fd); 2138c2ecf20Sopenharmony_ci close(map_fd); 2148c2ecf20Sopenharmony_ci printf("Running server on %s; press Ctrl+C to exit...\n", 2158c2ecf20Sopenharmony_ci ifname); 2168c2ecf20Sopenharmony_ci do { } while (1); 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* Start xdping-ing from last regular ping reply, e.g. for a count 2208c2ecf20Sopenharmony_ci * of 10 ICMP requests, we start xdping-ing using reply with seq number 2218c2ecf20Sopenharmony_ci * 10. The reason the last "real" ping RTT is much higher is that 2228c2ecf20Sopenharmony_ci * the ping program sees the ICMP reply associated with the last 2238c2ecf20Sopenharmony_ci * XDP-generated packet, so ping doesn't get a reply until XDP is done. 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_ci pinginfo.seq = htons(count); 2268c2ecf20Sopenharmony_ci pinginfo.count = count; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (bpf_map_update_elem(map_fd, &raddr, &pinginfo, BPF_ANY)) { 2298c2ecf20Sopenharmony_ci fprintf(stderr, "could not communicate with BPF map: %s\n", 2308c2ecf20Sopenharmony_ci strerror(errno)); 2318c2ecf20Sopenharmony_ci cleanup(0); 2328c2ecf20Sopenharmony_ci goto done; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* We need to wait for XDP setup to complete. */ 2368c2ecf20Sopenharmony_ci sleep(10); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci snprintf(cmd, sizeof(cmd), "ping -c %d -I %s %s", 2398c2ecf20Sopenharmony_ci count, ifname, argv[optind]); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci printf("\nNormal ping RTT data\n"); 2428c2ecf20Sopenharmony_ci printf("[Ignore final RTT; it is distorted by XDP using the reply]\n"); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci ret = system(cmd); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (!ret) 2478c2ecf20Sopenharmony_ci ret = get_stats(map_fd, count, raddr); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci cleanup(0); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cidone: 2528c2ecf20Sopenharmony_ci if (prog_fd > 0) 2538c2ecf20Sopenharmony_ci close(prog_fd); 2548c2ecf20Sopenharmony_ci if (map_fd > 0) 2558c2ecf20Sopenharmony_ci close(map_fd); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return ret; 2588c2ecf20Sopenharmony_ci} 259