162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/bpf.h> 562306a36Sopenharmony_ci#include <linux/if_link.h> 662306a36Sopenharmony_ci#include <arpa/inet.h> 762306a36Sopenharmony_ci#include <assert.h> 862306a36Sopenharmony_ci#include <errno.h> 962306a36Sopenharmony_ci#include <signal.h> 1062306a36Sopenharmony_ci#include <stdio.h> 1162306a36Sopenharmony_ci#include <stdlib.h> 1262306a36Sopenharmony_ci#include <string.h> 1362306a36Sopenharmony_ci#include <unistd.h> 1462306a36Sopenharmony_ci#include <libgen.h> 1562306a36Sopenharmony_ci#include <net/if.h> 1662306a36Sopenharmony_ci#include <sys/types.h> 1762306a36Sopenharmony_ci#include <sys/socket.h> 1862306a36Sopenharmony_ci#include <netdb.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "bpf/bpf.h" 2162306a36Sopenharmony_ci#include "bpf/libbpf.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "xdping.h" 2462306a36Sopenharmony_ci#include "testing_helpers.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic int ifindex; 2762306a36Sopenharmony_cistatic __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic void cleanup(int sig) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci bpf_xdp_detach(ifindex, xdp_flags, NULL); 3262306a36Sopenharmony_ci if (sig) 3362306a36Sopenharmony_ci exit(1); 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int get_stats(int fd, __u16 count, __u32 raddr) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct pinginfo pinginfo = { 0 }; 3962306a36Sopenharmony_ci char inaddrbuf[INET_ADDRSTRLEN]; 4062306a36Sopenharmony_ci struct in_addr inaddr; 4162306a36Sopenharmony_ci __u16 i; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci inaddr.s_addr = raddr; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci printf("\nXDP RTT data:\n"); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (bpf_map_lookup_elem(fd, &raddr, &pinginfo)) { 4862306a36Sopenharmony_ci perror("bpf_map_lookup elem"); 4962306a36Sopenharmony_ci return 1; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci for (i = 0; i < count; i++) { 5362306a36Sopenharmony_ci if (pinginfo.times[i] == 0) 5462306a36Sopenharmony_ci break; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci printf("64 bytes from %s: icmp_seq=%d ttl=64 time=%#.5f ms\n", 5762306a36Sopenharmony_ci inet_ntop(AF_INET, &inaddr, inaddrbuf, 5862306a36Sopenharmony_ci sizeof(inaddrbuf)), 5962306a36Sopenharmony_ci count + i + 1, 6062306a36Sopenharmony_ci (double)pinginfo.times[i]/1000000); 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (i < count) { 6462306a36Sopenharmony_ci fprintf(stderr, "Expected %d samples, got %d.\n", count, i); 6562306a36Sopenharmony_ci return 1; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci bpf_map_delete_elem(fd, &raddr); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return 0; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void show_usage(const char *prog) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci fprintf(stderr, 7662306a36Sopenharmony_ci "usage: %s [OPTS] -I interface destination\n\n" 7762306a36Sopenharmony_ci "OPTS:\n" 7862306a36Sopenharmony_ci " -c count Stop after sending count requests\n" 7962306a36Sopenharmony_ci " (default %d, max %d)\n" 8062306a36Sopenharmony_ci " -I interface interface name\n" 8162306a36Sopenharmony_ci " -N Run in driver mode\n" 8262306a36Sopenharmony_ci " -s Server mode\n" 8362306a36Sopenharmony_ci " -S Run in skb mode\n", 8462306a36Sopenharmony_ci prog, XDPING_DEFAULT_COUNT, XDPING_MAX_COUNT); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ciint main(int argc, char **argv) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci __u32 mode_flags = XDP_FLAGS_DRV_MODE | XDP_FLAGS_SKB_MODE; 9062306a36Sopenharmony_ci struct addrinfo *a, hints = { .ai_family = AF_INET }; 9162306a36Sopenharmony_ci __u16 count = XDPING_DEFAULT_COUNT; 9262306a36Sopenharmony_ci struct pinginfo pinginfo = { 0 }; 9362306a36Sopenharmony_ci const char *optstr = "c:I:NsS"; 9462306a36Sopenharmony_ci struct bpf_program *main_prog; 9562306a36Sopenharmony_ci int prog_fd = -1, map_fd = -1; 9662306a36Sopenharmony_ci struct sockaddr_in rin; 9762306a36Sopenharmony_ci struct bpf_object *obj; 9862306a36Sopenharmony_ci struct bpf_map *map; 9962306a36Sopenharmony_ci char *ifname = NULL; 10062306a36Sopenharmony_ci char filename[256]; 10162306a36Sopenharmony_ci int opt, ret = 1; 10262306a36Sopenharmony_ci __u32 raddr = 0; 10362306a36Sopenharmony_ci int server = 0; 10462306a36Sopenharmony_ci char cmd[256]; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci while ((opt = getopt(argc, argv, optstr)) != -1) { 10762306a36Sopenharmony_ci switch (opt) { 10862306a36Sopenharmony_ci case 'c': 10962306a36Sopenharmony_ci count = atoi(optarg); 11062306a36Sopenharmony_ci if (count < 1 || count > XDPING_MAX_COUNT) { 11162306a36Sopenharmony_ci fprintf(stderr, 11262306a36Sopenharmony_ci "min count is 1, max count is %d\n", 11362306a36Sopenharmony_ci XDPING_MAX_COUNT); 11462306a36Sopenharmony_ci return 1; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci case 'I': 11862306a36Sopenharmony_ci ifname = optarg; 11962306a36Sopenharmony_ci ifindex = if_nametoindex(ifname); 12062306a36Sopenharmony_ci if (!ifindex) { 12162306a36Sopenharmony_ci fprintf(stderr, "Could not get interface %s\n", 12262306a36Sopenharmony_ci ifname); 12362306a36Sopenharmony_ci return 1; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci case 'N': 12762306a36Sopenharmony_ci xdp_flags |= XDP_FLAGS_DRV_MODE; 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci case 's': 13062306a36Sopenharmony_ci /* use server program */ 13162306a36Sopenharmony_ci server = 1; 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci case 'S': 13462306a36Sopenharmony_ci xdp_flags |= XDP_FLAGS_SKB_MODE; 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci default: 13762306a36Sopenharmony_ci show_usage(basename(argv[0])); 13862306a36Sopenharmony_ci return 1; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (!ifname) { 14362306a36Sopenharmony_ci show_usage(basename(argv[0])); 14462306a36Sopenharmony_ci return 1; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci if (!server && optind == argc) { 14762306a36Sopenharmony_ci show_usage(basename(argv[0])); 14862306a36Sopenharmony_ci return 1; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if ((xdp_flags & mode_flags) == mode_flags) { 15262306a36Sopenharmony_ci fprintf(stderr, "-N or -S can be specified, not both.\n"); 15362306a36Sopenharmony_ci show_usage(basename(argv[0])); 15462306a36Sopenharmony_ci return 1; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (!server) { 15862306a36Sopenharmony_ci /* Only supports IPv4; see hints initiailization above. */ 15962306a36Sopenharmony_ci if (getaddrinfo(argv[optind], NULL, &hints, &a) || !a) { 16062306a36Sopenharmony_ci fprintf(stderr, "Could not resolve %s\n", argv[optind]); 16162306a36Sopenharmony_ci return 1; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci memcpy(&rin, a->ai_addr, sizeof(rin)); 16462306a36Sopenharmony_ci raddr = rin.sin_addr.s_addr; 16562306a36Sopenharmony_ci freeaddrinfo(a); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Use libbpf 1.0 API mode */ 16962306a36Sopenharmony_ci libbpf_set_strict_mode(LIBBPF_STRICT_ALL); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci snprintf(filename, sizeof(filename), "%s_kern.bpf.o", argv[0]); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (bpf_prog_test_load(filename, BPF_PROG_TYPE_XDP, &obj, &prog_fd)) { 17462306a36Sopenharmony_ci fprintf(stderr, "load of %s failed\n", filename); 17562306a36Sopenharmony_ci return 1; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci main_prog = bpf_object__find_program_by_name(obj, 17962306a36Sopenharmony_ci server ? "xdping_server" : "xdping_client"); 18062306a36Sopenharmony_ci if (main_prog) 18162306a36Sopenharmony_ci prog_fd = bpf_program__fd(main_prog); 18262306a36Sopenharmony_ci if (!main_prog || prog_fd < 0) { 18362306a36Sopenharmony_ci fprintf(stderr, "could not find xdping program"); 18462306a36Sopenharmony_ci return 1; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci map = bpf_object__next_map(obj, NULL); 18862306a36Sopenharmony_ci if (map) 18962306a36Sopenharmony_ci map_fd = bpf_map__fd(map); 19062306a36Sopenharmony_ci if (!map || map_fd < 0) { 19162306a36Sopenharmony_ci fprintf(stderr, "Could not find ping map"); 19262306a36Sopenharmony_ci goto done; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci signal(SIGINT, cleanup); 19662306a36Sopenharmony_ci signal(SIGTERM, cleanup); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci printf("Setting up XDP for %s, please wait...\n", ifname); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci printf("XDP setup disrupts network connectivity, hit Ctrl+C to quit\n"); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL) < 0) { 20362306a36Sopenharmony_ci fprintf(stderr, "Link set xdp fd failed for %s\n", ifname); 20462306a36Sopenharmony_ci goto done; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (server) { 20862306a36Sopenharmony_ci close(prog_fd); 20962306a36Sopenharmony_ci close(map_fd); 21062306a36Sopenharmony_ci printf("Running server on %s; press Ctrl+C to exit...\n", 21162306a36Sopenharmony_ci ifname); 21262306a36Sopenharmony_ci do { } while (1); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Start xdping-ing from last regular ping reply, e.g. for a count 21662306a36Sopenharmony_ci * of 10 ICMP requests, we start xdping-ing using reply with seq number 21762306a36Sopenharmony_ci * 10. The reason the last "real" ping RTT is much higher is that 21862306a36Sopenharmony_ci * the ping program sees the ICMP reply associated with the last 21962306a36Sopenharmony_ci * XDP-generated packet, so ping doesn't get a reply until XDP is done. 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_ci pinginfo.seq = htons(count); 22262306a36Sopenharmony_ci pinginfo.count = count; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (bpf_map_update_elem(map_fd, &raddr, &pinginfo, BPF_ANY)) { 22562306a36Sopenharmony_ci fprintf(stderr, "could not communicate with BPF map: %s\n", 22662306a36Sopenharmony_ci strerror(errno)); 22762306a36Sopenharmony_ci cleanup(0); 22862306a36Sopenharmony_ci goto done; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* We need to wait for XDP setup to complete. */ 23262306a36Sopenharmony_ci sleep(10); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci snprintf(cmd, sizeof(cmd), "ping -c %d -I %s %s", 23562306a36Sopenharmony_ci count, ifname, argv[optind]); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci printf("\nNormal ping RTT data\n"); 23862306a36Sopenharmony_ci printf("[Ignore final RTT; it is distorted by XDP using the reply]\n"); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci ret = system(cmd); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (!ret) 24362306a36Sopenharmony_ci ret = get_stats(map_fd, count, raddr); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci cleanup(0); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cidone: 24862306a36Sopenharmony_ci if (prog_fd > 0) 24962306a36Sopenharmony_ci close(prog_fd); 25062306a36Sopenharmony_ci if (map_fd > 0) 25162306a36Sopenharmony_ci close(map_fd); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return ret; 25462306a36Sopenharmony_ci} 255