Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. */
0003 
0004 #include <linux/bpf.h>
0005 #include <linux/if_link.h>
0006 #include <arpa/inet.h>
0007 #include <assert.h>
0008 #include <errno.h>
0009 #include <signal.h>
0010 #include <stdio.h>
0011 #include <stdlib.h>
0012 #include <string.h>
0013 #include <unistd.h>
0014 #include <libgen.h>
0015 #include <net/if.h>
0016 #include <sys/types.h>
0017 #include <sys/socket.h>
0018 #include <netdb.h>
0019 
0020 #include "bpf/bpf.h"
0021 #include "bpf/libbpf.h"
0022 
0023 #include "xdping.h"
0024 #include "testing_helpers.h"
0025 
0026 static int ifindex;
0027 static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
0028 
0029 static void cleanup(int sig)
0030 {
0031     bpf_xdp_detach(ifindex, xdp_flags, NULL);
0032     if (sig)
0033         exit(1);
0034 }
0035 
0036 static int get_stats(int fd, __u16 count, __u32 raddr)
0037 {
0038     struct pinginfo pinginfo = { 0 };
0039     char inaddrbuf[INET_ADDRSTRLEN];
0040     struct in_addr inaddr;
0041     __u16 i;
0042 
0043     inaddr.s_addr = raddr;
0044 
0045     printf("\nXDP RTT data:\n");
0046 
0047     if (bpf_map_lookup_elem(fd, &raddr, &pinginfo)) {
0048         perror("bpf_map_lookup elem");
0049         return 1;
0050     }
0051 
0052     for (i = 0; i < count; i++) {
0053         if (pinginfo.times[i] == 0)
0054             break;
0055 
0056         printf("64 bytes from %s: icmp_seq=%d ttl=64 time=%#.5f ms\n",
0057                inet_ntop(AF_INET, &inaddr, inaddrbuf,
0058                  sizeof(inaddrbuf)),
0059                count + i + 1,
0060                (double)pinginfo.times[i]/1000000);
0061     }
0062 
0063     if (i < count) {
0064         fprintf(stderr, "Expected %d samples, got %d.\n", count, i);
0065         return 1;
0066     }
0067 
0068     bpf_map_delete_elem(fd, &raddr);
0069 
0070     return 0;
0071 }
0072 
0073 static void show_usage(const char *prog)
0074 {
0075     fprintf(stderr,
0076         "usage: %s [OPTS] -I interface destination\n\n"
0077         "OPTS:\n"
0078         "    -c count       Stop after sending count requests\n"
0079         "           (default %d, max %d)\n"
0080         "    -I interface   interface name\n"
0081         "    -N         Run in driver mode\n"
0082         "    -s         Server mode\n"
0083         "    -S         Run in skb mode\n",
0084         prog, XDPING_DEFAULT_COUNT, XDPING_MAX_COUNT);
0085 }
0086 
0087 int main(int argc, char **argv)
0088 {
0089     __u32 mode_flags = XDP_FLAGS_DRV_MODE | XDP_FLAGS_SKB_MODE;
0090     struct addrinfo *a, hints = { .ai_family = AF_INET };
0091     __u16 count = XDPING_DEFAULT_COUNT;
0092     struct pinginfo pinginfo = { 0 };
0093     const char *optstr = "c:I:NsS";
0094     struct bpf_program *main_prog;
0095     int prog_fd = -1, map_fd = -1;
0096     struct sockaddr_in rin;
0097     struct bpf_object *obj;
0098     struct bpf_map *map;
0099     char *ifname = NULL;
0100     char filename[256];
0101     int opt, ret = 1;
0102     __u32 raddr = 0;
0103     int server = 0;
0104     char cmd[256];
0105 
0106     while ((opt = getopt(argc, argv, optstr)) != -1) {
0107         switch (opt) {
0108         case 'c':
0109             count = atoi(optarg);
0110             if (count < 1 || count > XDPING_MAX_COUNT) {
0111                 fprintf(stderr,
0112                     "min count is 1, max count is %d\n",
0113                     XDPING_MAX_COUNT);
0114                 return 1;
0115             }
0116             break;
0117         case 'I':
0118             ifname = optarg;
0119             ifindex = if_nametoindex(ifname);
0120             if (!ifindex) {
0121                 fprintf(stderr, "Could not get interface %s\n",
0122                     ifname);
0123                 return 1;
0124             }
0125             break;
0126         case 'N':
0127             xdp_flags |= XDP_FLAGS_DRV_MODE;
0128             break;
0129         case 's':
0130             /* use server program */
0131             server = 1;
0132             break;
0133         case 'S':
0134             xdp_flags |= XDP_FLAGS_SKB_MODE;
0135             break;
0136         default:
0137             show_usage(basename(argv[0]));
0138             return 1;
0139         }
0140     }
0141 
0142     if (!ifname) {
0143         show_usage(basename(argv[0]));
0144         return 1;
0145     }
0146     if (!server && optind == argc) {
0147         show_usage(basename(argv[0]));
0148         return 1;
0149     }
0150 
0151     if ((xdp_flags & mode_flags) == mode_flags) {
0152         fprintf(stderr, "-N or -S can be specified, not both.\n");
0153         show_usage(basename(argv[0]));
0154         return 1;
0155     }
0156 
0157     if (!server) {
0158         /* Only supports IPv4; see hints initiailization above. */
0159         if (getaddrinfo(argv[optind], NULL, &hints, &a) || !a) {
0160             fprintf(stderr, "Could not resolve %s\n", argv[optind]);
0161             return 1;
0162         }
0163         memcpy(&rin, a->ai_addr, sizeof(rin));
0164         raddr = rin.sin_addr.s_addr;
0165         freeaddrinfo(a);
0166     }
0167 
0168     /* Use libbpf 1.0 API mode */
0169     libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
0170 
0171     snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
0172 
0173     if (bpf_prog_test_load(filename, BPF_PROG_TYPE_XDP, &obj, &prog_fd)) {
0174         fprintf(stderr, "load of %s failed\n", filename);
0175         return 1;
0176     }
0177 
0178     main_prog = bpf_object__find_program_by_name(obj,
0179                              server ? "xdping_server" : "xdping_client");
0180     if (main_prog)
0181         prog_fd = bpf_program__fd(main_prog);
0182     if (!main_prog || prog_fd < 0) {
0183         fprintf(stderr, "could not find xdping program");
0184         return 1;
0185     }
0186 
0187     map = bpf_object__next_map(obj, NULL);
0188     if (map)
0189         map_fd = bpf_map__fd(map);
0190     if (!map || map_fd < 0) {
0191         fprintf(stderr, "Could not find ping map");
0192         goto done;
0193     }
0194 
0195     signal(SIGINT, cleanup);
0196     signal(SIGTERM, cleanup);
0197 
0198     printf("Setting up XDP for %s, please wait...\n", ifname);
0199 
0200     printf("XDP setup disrupts network connectivity, hit Ctrl+C to quit\n");
0201 
0202     if (bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL) < 0) {
0203         fprintf(stderr, "Link set xdp fd failed for %s\n", ifname);
0204         goto done;
0205     }
0206 
0207     if (server) {
0208         close(prog_fd);
0209         close(map_fd);
0210         printf("Running server on %s; press Ctrl+C to exit...\n",
0211                ifname);
0212         do { } while (1);
0213     }
0214 
0215     /* Start xdping-ing from last regular ping reply, e.g. for a count
0216      * of 10 ICMP requests, we start xdping-ing using reply with seq number
0217      * 10.  The reason the last "real" ping RTT is much higher is that
0218      * the ping program sees the ICMP reply associated with the last
0219      * XDP-generated packet, so ping doesn't get a reply until XDP is done.
0220      */
0221     pinginfo.seq = htons(count);
0222     pinginfo.count = count;
0223 
0224     if (bpf_map_update_elem(map_fd, &raddr, &pinginfo, BPF_ANY)) {
0225         fprintf(stderr, "could not communicate with BPF map: %s\n",
0226             strerror(errno));
0227         cleanup(0);
0228         goto done;
0229     }
0230 
0231     /* We need to wait for XDP setup to complete. */
0232     sleep(10);
0233 
0234     snprintf(cmd, sizeof(cmd), "ping -c %d -I %s %s",
0235          count, ifname, argv[optind]);
0236 
0237     printf("\nNormal ping RTT data\n");
0238     printf("[Ignore final RTT; it is distorted by XDP using the reply]\n");
0239 
0240     ret = system(cmd);
0241 
0242     if (!ret)
0243         ret = get_stats(map_fd, count, raddr);
0244 
0245     cleanup(0);
0246 
0247 done:
0248     if (prog_fd > 0)
0249         close(prog_fd);
0250     if (map_fd > 0)
0251         close(map_fd);
0252 
0253     return ret;
0254 }