linux/tools/testing/selftests/bpf/xdping.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. */
   3
   4#include <linux/bpf.h>
   5#include <linux/if_link.h>
   6#include <arpa/inet.h>
   7#include <assert.h>
   8#include <errno.h>
   9#include <signal.h>
  10#include <stdio.h>
  11#include <stdlib.h>
  12#include <string.h>
  13#include <unistd.h>
  14#include <libgen.h>
  15#include <sys/resource.h>
  16#include <net/if.h>
  17#include <sys/types.h>
  18#include <sys/socket.h>
  19#include <netdb.h>
  20
  21#include "bpf/bpf.h"
  22#include "bpf/libbpf.h"
  23
  24#include "xdping.h"
  25
  26static int ifindex;
  27static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
  28
  29static void cleanup(int sig)
  30{
  31        bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
  32        if (sig)
  33                exit(1);
  34}
  35
  36static int get_stats(int fd, __u16 count, __u32 raddr)
  37{
  38        struct pinginfo pinginfo = { 0 };
  39        char inaddrbuf[INET_ADDRSTRLEN];
  40        struct in_addr inaddr;
  41        __u16 i;
  42
  43        inaddr.s_addr = raddr;
  44
  45        printf("\nXDP RTT data:\n");
  46
  47        if (bpf_map_lookup_elem(fd, &raddr, &pinginfo)) {
  48                perror("bpf_map_lookup elem");
  49                return 1;
  50        }
  51
  52        for (i = 0; i < count; i++) {
  53                if (pinginfo.times[i] == 0)
  54                        break;
  55
  56                printf("64 bytes from %s: icmp_seq=%d ttl=64 time=%#.5f ms\n",
  57                       inet_ntop(AF_INET, &inaddr, inaddrbuf,
  58                                 sizeof(inaddrbuf)),
  59                       count + i + 1,
  60                       (double)pinginfo.times[i]/1000000);
  61        }
  62
  63        if (i < count) {
  64                fprintf(stderr, "Expected %d samples, got %d.\n", count, i);
  65                return 1;
  66        }
  67
  68        bpf_map_delete_elem(fd, &raddr);
  69
  70        return 0;
  71}
  72
  73static void show_usage(const char *prog)
  74{
  75        fprintf(stderr,
  76                "usage: %s [OPTS] -I interface destination\n\n"
  77                "OPTS:\n"
  78                "    -c count           Stop after sending count requests\n"
  79                "                       (default %d, max %d)\n"
  80                "    -I interface       interface name\n"
  81                "    -N                 Run in driver mode\n"
  82                "    -s                 Server mode\n"
  83                "    -S                 Run in skb mode\n",
  84                prog, XDPING_DEFAULT_COUNT, XDPING_MAX_COUNT);
  85}
  86
  87int main(int argc, char **argv)
  88{
  89        __u32 mode_flags = XDP_FLAGS_DRV_MODE | XDP_FLAGS_SKB_MODE;
  90        struct addrinfo *a, hints = { .ai_family = AF_INET };
  91        struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
  92        __u16 count = XDPING_DEFAULT_COUNT;
  93        struct pinginfo pinginfo = { 0 };
  94        const char *optstr = "c:I:NsS";
  95        struct bpf_program *main_prog;
  96        int prog_fd = -1, map_fd = -1;
  97        struct sockaddr_in rin;
  98        struct bpf_object *obj;
  99        struct bpf_map *map;
 100        char *ifname = NULL;
 101        char filename[256];
 102        int opt, ret = 1;
 103        __u32 raddr = 0;
 104        int server = 0;
 105        char cmd[256];
 106
 107        while ((opt = getopt(argc, argv, optstr)) != -1) {
 108                switch (opt) {
 109                case 'c':
 110                        count = atoi(optarg);
 111                        if (count < 1 || count > XDPING_MAX_COUNT) {
 112                                fprintf(stderr,
 113                                        "min count is 1, max count is %d\n",
 114                                        XDPING_MAX_COUNT);
 115                                return 1;
 116                        }
 117                        break;
 118                case 'I':
 119                        ifname = optarg;
 120                        ifindex = if_nametoindex(ifname);
 121                        if (!ifindex) {
 122                                fprintf(stderr, "Could not get interface %s\n",
 123                                        ifname);
 124                                return 1;
 125                        }
 126                        break;
 127                case 'N':
 128                        xdp_flags |= XDP_FLAGS_DRV_MODE;
 129                        break;
 130                case 's':
 131                        /* use server program */
 132                        server = 1;
 133                        break;
 134                case 'S':
 135                        xdp_flags |= XDP_FLAGS_SKB_MODE;
 136                        break;
 137                default:
 138                        show_usage(basename(argv[0]));
 139                        return 1;
 140                }
 141        }
 142
 143        if (!ifname) {
 144                show_usage(basename(argv[0]));
 145                return 1;
 146        }
 147        if (!server && optind == argc) {
 148                show_usage(basename(argv[0]));
 149                return 1;
 150        }
 151
 152        if ((xdp_flags & mode_flags) == mode_flags) {
 153                fprintf(stderr, "-N or -S can be specified, not both.\n");
 154                show_usage(basename(argv[0]));
 155                return 1;
 156        }
 157
 158        if (!server) {
 159                /* Only supports IPv4; see hints initiailization above. */
 160                if (getaddrinfo(argv[optind], NULL, &hints, &a) || !a) {
 161                        fprintf(stderr, "Could not resolve %s\n", argv[optind]);
 162                        return 1;
 163                }
 164                memcpy(&rin, a->ai_addr, sizeof(rin));
 165                raddr = rin.sin_addr.s_addr;
 166                freeaddrinfo(a);
 167        }
 168
 169        if (setrlimit(RLIMIT_MEMLOCK, &r)) {
 170                perror("setrlimit(RLIMIT_MEMLOCK)");
 171                return 1;
 172        }
 173
 174        snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
 175
 176        if (bpf_prog_load(filename, BPF_PROG_TYPE_XDP, &obj, &prog_fd)) {
 177                fprintf(stderr, "load of %s failed\n", filename);
 178                return 1;
 179        }
 180
 181        main_prog = bpf_object__find_program_by_title(obj,
 182                                                      server ? "xdpserver" :
 183                                                               "xdpclient");
 184        if (main_prog)
 185                prog_fd = bpf_program__fd(main_prog);
 186        if (!main_prog || prog_fd < 0) {
 187                fprintf(stderr, "could not find xdping program");
 188                return 1;
 189        }
 190
 191        map = bpf_map__next(NULL, obj);
 192        if (map)
 193                map_fd = bpf_map__fd(map);
 194        if (!map || map_fd < 0) {
 195                fprintf(stderr, "Could not find ping map");
 196                goto done;
 197        }
 198
 199        signal(SIGINT, cleanup);
 200        signal(SIGTERM, cleanup);
 201
 202        printf("Setting up XDP for %s, please wait...\n", ifname);
 203
 204        printf("XDP setup disrupts network connectivity, hit Ctrl+C to quit\n");
 205
 206        if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) {
 207                fprintf(stderr, "Link set xdp fd failed for %s\n", ifname);
 208                goto done;
 209        }
 210
 211        if (server) {
 212                close(prog_fd);
 213                close(map_fd);
 214                printf("Running server on %s; press Ctrl+C to exit...\n",
 215                       ifname);
 216                do { } while (1);
 217        }
 218
 219        /* Start xdping-ing from last regular ping reply, e.g. for a count
 220         * of 10 ICMP requests, we start xdping-ing using reply with seq number
 221         * 10.  The reason the last "real" ping RTT is much higher is that
 222         * the ping program sees the ICMP reply associated with the last
 223         * XDP-generated packet, so ping doesn't get a reply until XDP is done.
 224         */
 225        pinginfo.seq = htons(count);
 226        pinginfo.count = count;
 227
 228        if (bpf_map_update_elem(map_fd, &raddr, &pinginfo, BPF_ANY)) {
 229                fprintf(stderr, "could not communicate with BPF map: %s\n",
 230                        strerror(errno));
 231                cleanup(0);
 232                goto done;
 233        }
 234
 235        /* We need to wait for XDP setup to complete. */
 236        sleep(10);
 237
 238        snprintf(cmd, sizeof(cmd), "ping -c %d -I %s %s",
 239                 count, ifname, argv[optind]);
 240
 241        printf("\nNormal ping RTT data\n");
 242        printf("[Ignore final RTT; it is distorted by XDP using the reply]\n");
 243
 244        ret = system(cmd);
 245
 246        if (!ret)
 247                ret = get_stats(map_fd, count, raddr);
 248
 249        cleanup(0);
 250
 251done:
 252        if (prog_fd > 0)
 253                close(prog_fd);
 254        if (map_fd > 0)
 255                close(map_fd);
 256
 257        return ret;
 258}
 259