linux/samples/bpf/xdp_tx_iptunnel_user.c
<<
>>
Prefs
   1/* Copyright (c) 2016 Facebook
   2 *
   3 * This program is free software; you can redistribute it and/or
   4 * modify it under the terms of version 2 of the GNU General Public
   5 * License as published by the Free Software Foundation.
   6 */
   7#include <linux/bpf.h>
   8#include <linux/if_link.h>
   9#include <assert.h>
  10#include <errno.h>
  11#include <signal.h>
  12#include <stdio.h>
  13#include <stdlib.h>
  14#include <string.h>
  15#include <sys/resource.h>
  16#include <arpa/inet.h>
  17#include <netinet/ether.h>
  18#include <unistd.h>
  19#include <time.h>
  20#include "bpf_load.h"
  21#include "libbpf.h"
  22#include "bpf_util.h"
  23#include "xdp_tx_iptunnel_common.h"
  24
  25#define STATS_INTERVAL_S 2U
  26
  27static int ifindex = -1;
  28static __u32 xdp_flags = 0;
  29
  30static void int_exit(int sig)
  31{
  32        if (ifindex > -1)
  33                bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
  34        exit(0);
  35}
  36
  37/* simple per-protocol drop counter
  38 */
  39static void poll_stats(unsigned int kill_after_s)
  40{
  41        const unsigned int nr_protos = 256;
  42        unsigned int nr_cpus = bpf_num_possible_cpus();
  43        time_t started_at = time(NULL);
  44        __u64 values[nr_cpus], prev[nr_protos][nr_cpus];
  45        __u32 proto;
  46        int i;
  47
  48        memset(prev, 0, sizeof(prev));
  49
  50        while (!kill_after_s || time(NULL) - started_at <= kill_after_s) {
  51                sleep(STATS_INTERVAL_S);
  52
  53                for (proto = 0; proto < nr_protos; proto++) {
  54                        __u64 sum = 0;
  55
  56                        assert(bpf_map_lookup_elem(map_fd[0], &proto, values) == 0);
  57                        for (i = 0; i < nr_cpus; i++)
  58                                sum += (values[i] - prev[proto][i]);
  59
  60                        if (sum)
  61                                printf("proto %u: sum:%10llu pkts, rate:%10llu pkts/s\n",
  62                                       proto, sum, sum / STATS_INTERVAL_S);
  63                        memcpy(prev[proto], values, sizeof(values));
  64                }
  65        }
  66}
  67
  68static void usage(const char *cmd)
  69{
  70        printf("Start a XDP prog which encapsulates incoming packets\n"
  71               "in an IPv4/v6 header and XDP_TX it out.  The dst <VIP:PORT>\n"
  72               "is used to select packets to encapsulate\n\n");
  73        printf("Usage: %s [...]\n", cmd);
  74        printf("    -i <ifindex> Interface Index\n");
  75        printf("    -a <vip-service-address> IPv4 or IPv6\n");
  76        printf("    -p <vip-service-port> A port range (e.g. 433-444) is also allowed\n");
  77        printf("    -s <source-ip> Used in the IPTunnel header\n");
  78        printf("    -d <dest-ip> Used in the IPTunnel header\n");
  79        printf("    -m <dest-MAC> Used in sending the IP Tunneled pkt\n");
  80        printf("    -T <stop-after-X-seconds> Default: 0 (forever)\n");
  81        printf("    -P <IP-Protocol> Default is TCP\n");
  82        printf("    -S use skb-mode\n");
  83        printf("    -N enforce native mode\n");
  84        printf("    -h Display this help\n");
  85}
  86
  87static int parse_ipstr(const char *ipstr, unsigned int *addr)
  88{
  89        if (inet_pton(AF_INET6, ipstr, addr) == 1) {
  90                return AF_INET6;
  91        } else if (inet_pton(AF_INET, ipstr, addr) == 1) {
  92                addr[1] = addr[2] = addr[3] = 0;
  93                return AF_INET;
  94        }
  95
  96        fprintf(stderr, "%s is an invalid IP\n", ipstr);
  97        return AF_UNSPEC;
  98}
  99
 100static int parse_ports(const char *port_str, int *min_port, int *max_port)
 101{
 102        char *end;
 103        long tmp_min_port;
 104        long tmp_max_port;
 105
 106        tmp_min_port = strtol(optarg, &end, 10);
 107        if (tmp_min_port < 1 || tmp_min_port > 65535) {
 108                fprintf(stderr, "Invalid port(s):%s\n", optarg);
 109                return 1;
 110        }
 111
 112        if (*end == '-') {
 113                end++;
 114                tmp_max_port = strtol(end, NULL, 10);
 115                if (tmp_max_port < 1 || tmp_max_port > 65535) {
 116                        fprintf(stderr, "Invalid port(s):%s\n", optarg);
 117                        return 1;
 118                }
 119        } else {
 120                tmp_max_port = tmp_min_port;
 121        }
 122
 123        if (tmp_min_port > tmp_max_port) {
 124                fprintf(stderr, "Invalid port(s):%s\n", optarg);
 125                return 1;
 126        }
 127
 128        if (tmp_max_port - tmp_min_port + 1 > MAX_IPTNL_ENTRIES) {
 129                fprintf(stderr, "Port range (%s) is larger than %u\n",
 130                        port_str, MAX_IPTNL_ENTRIES);
 131                return 1;
 132        }
 133        *min_port = tmp_min_port;
 134        *max_port = tmp_max_port;
 135
 136        return 0;
 137}
 138
 139int main(int argc, char **argv)
 140{
 141        unsigned char opt_flags[256] = {};
 142        unsigned int kill_after_s = 0;
 143        const char *optstr = "i:a:p:s:d:m:T:P:SNh";
 144        int min_port = 0, max_port = 0;
 145        struct iptnl_info tnl = {};
 146        struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
 147        struct vip vip = {};
 148        char filename[256];
 149        int opt;
 150        int i;
 151
 152        tnl.family = AF_UNSPEC;
 153        vip.protocol = IPPROTO_TCP;
 154
 155        for (i = 0; i < strlen(optstr); i++)
 156                if (optstr[i] != 'h' && 'a' <= optstr[i] && optstr[i] <= 'z')
 157                        opt_flags[(unsigned char)optstr[i]] = 1;
 158
 159        while ((opt = getopt(argc, argv, optstr)) != -1) {
 160                unsigned short family;
 161                unsigned int *v6;
 162
 163                switch (opt) {
 164                case 'i':
 165                        ifindex = atoi(optarg);
 166                        break;
 167                case 'a':
 168                        vip.family = parse_ipstr(optarg, vip.daddr.v6);
 169                        if (vip.family == AF_UNSPEC)
 170                                return 1;
 171                        break;
 172                case 'p':
 173                        if (parse_ports(optarg, &min_port, &max_port))
 174                                return 1;
 175                        break;
 176                case 'P':
 177                        vip.protocol = atoi(optarg);
 178                        break;
 179                case 's':
 180                case 'd':
 181                        if (opt == 's')
 182                                v6 = tnl.saddr.v6;
 183                        else
 184                                v6 = tnl.daddr.v6;
 185
 186                        family = parse_ipstr(optarg, v6);
 187                        if (family == AF_UNSPEC)
 188                                return 1;
 189                        if (tnl.family == AF_UNSPEC) {
 190                                tnl.family = family;
 191                        } else if (tnl.family != family) {
 192                                fprintf(stderr,
 193                                        "The IP version of the src and dst addresses used in the IP encapsulation does not match\n");
 194                                return 1;
 195                        }
 196                        break;
 197                case 'm':
 198                        if (!ether_aton_r(optarg,
 199                                          (struct ether_addr *)tnl.dmac)) {
 200                                fprintf(stderr, "Invalid mac address:%s\n",
 201                                        optarg);
 202                                return 1;
 203                        }
 204                        break;
 205                case 'T':
 206                        kill_after_s = atoi(optarg);
 207                        break;
 208                case 'S':
 209                        xdp_flags |= XDP_FLAGS_SKB_MODE;
 210                        break;
 211                case 'N':
 212                        xdp_flags |= XDP_FLAGS_DRV_MODE;
 213                        break;
 214                default:
 215                        usage(argv[0]);
 216                        return 1;
 217                }
 218                opt_flags[opt] = 0;
 219        }
 220
 221        for (i = 0; i < strlen(optstr); i++) {
 222                if (opt_flags[(unsigned int)optstr[i]]) {
 223                        fprintf(stderr, "Missing argument -%c\n", optstr[i]);
 224                        usage(argv[0]);
 225                        return 1;
 226                }
 227        }
 228
 229        if (setrlimit(RLIMIT_MEMLOCK, &r)) {
 230                perror("setrlimit(RLIMIT_MEMLOCK, RLIM_INFINITY)");
 231                return 1;
 232        }
 233
 234        snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
 235
 236        if (load_bpf_file(filename)) {
 237                printf("%s", bpf_log_buf);
 238                return 1;
 239        }
 240
 241        if (!prog_fd[0]) {
 242                printf("load_bpf_file: %s\n", strerror(errno));
 243                return 1;
 244        }
 245
 246        signal(SIGINT, int_exit);
 247        signal(SIGTERM, int_exit);
 248
 249        while (min_port <= max_port) {
 250                vip.dport = htons(min_port++);
 251                if (bpf_map_update_elem(map_fd[1], &vip, &tnl, BPF_NOEXIST)) {
 252                        perror("bpf_map_update_elem(&vip2tnl)");
 253                        return 1;
 254                }
 255        }
 256
 257        if (bpf_set_link_xdp_fd(ifindex, prog_fd[0], xdp_flags) < 0) {
 258                printf("link set xdp fd failed\n");
 259                return 1;
 260        }
 261
 262        poll_stats(kill_after_s);
 263
 264        bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
 265
 266        return 0;
 267}
 268