linux/samples/bpf/xdp_redirect_map_user.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/* Copyright (c) 2017 Covalent IO, Inc. http://covalent.io
   3 */
   4#include <linux/bpf.h>
   5#include <linux/if_link.h>
   6#include <assert.h>
   7#include <errno.h>
   8#include <signal.h>
   9#include <stdio.h>
  10#include <stdlib.h>
  11#include <stdbool.h>
  12#include <string.h>
  13#include <net/if.h>
  14#include <unistd.h>
  15#include <libgen.h>
  16#include <sys/resource.h>
  17#include <sys/ioctl.h>
  18#include <sys/types.h>
  19#include <sys/socket.h>
  20#include <netinet/in.h>
  21
  22#include "bpf_util.h"
  23#include <bpf/bpf.h>
  24#include <bpf/libbpf.h>
  25
  26static int ifindex_in;
  27static int ifindex_out;
  28static bool ifindex_out_xdp_dummy_attached = true;
  29static bool xdp_devmap_attached;
  30static __u32 prog_id;
  31static __u32 dummy_prog_id;
  32
  33static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
  34static int rxcnt_map_fd;
  35
  36static void int_exit(int sig)
  37{
  38        __u32 curr_prog_id = 0;
  39
  40        if (bpf_get_link_xdp_id(ifindex_in, &curr_prog_id, xdp_flags)) {
  41                printf("bpf_get_link_xdp_id failed\n");
  42                exit(1);
  43        }
  44        if (prog_id == curr_prog_id)
  45                bpf_set_link_xdp_fd(ifindex_in, -1, xdp_flags);
  46        else if (!curr_prog_id)
  47                printf("couldn't find a prog id on iface IN\n");
  48        else
  49                printf("program on iface IN changed, not removing\n");
  50
  51        if (ifindex_out_xdp_dummy_attached) {
  52                curr_prog_id = 0;
  53                if (bpf_get_link_xdp_id(ifindex_out, &curr_prog_id,
  54                                        xdp_flags)) {
  55                        printf("bpf_get_link_xdp_id failed\n");
  56                        exit(1);
  57                }
  58                if (dummy_prog_id == curr_prog_id)
  59                        bpf_set_link_xdp_fd(ifindex_out, -1, xdp_flags);
  60                else if (!curr_prog_id)
  61                        printf("couldn't find a prog id on iface OUT\n");
  62                else
  63                        printf("program on iface OUT changed, not removing\n");
  64        }
  65        exit(0);
  66}
  67
  68static void poll_stats(int interval, int ifindex)
  69{
  70        unsigned int nr_cpus = bpf_num_possible_cpus();
  71        __u64 values[nr_cpus], prev[nr_cpus];
  72
  73        memset(prev, 0, sizeof(prev));
  74
  75        while (1) {
  76                __u64 sum = 0;
  77                __u32 key = 0;
  78                int i;
  79
  80                sleep(interval);
  81                assert(bpf_map_lookup_elem(rxcnt_map_fd, &key, values) == 0);
  82                for (i = 0; i < nr_cpus; i++)
  83                        sum += (values[i] - prev[i]);
  84                if (sum)
  85                        printf("ifindex %i: %10llu pkt/s\n",
  86                               ifindex, sum / interval);
  87                memcpy(prev, values, sizeof(values));
  88        }
  89}
  90
  91static int get_mac_addr(unsigned int ifindex_out, void *mac_addr)
  92{
  93        char ifname[IF_NAMESIZE];
  94        struct ifreq ifr;
  95        int fd, ret = -1;
  96
  97        fd = socket(AF_INET, SOCK_DGRAM, 0);
  98        if (fd < 0)
  99                return ret;
 100
 101        if (!if_indextoname(ifindex_out, ifname))
 102                goto err_out;
 103
 104        strcpy(ifr.ifr_name, ifname);
 105
 106        if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
 107                goto err_out;
 108
 109        memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
 110        ret = 0;
 111
 112err_out:
 113        close(fd);
 114        return ret;
 115}
 116
 117static void usage(const char *prog)
 118{
 119        fprintf(stderr,
 120                "usage: %s [OPTS] <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n\n"
 121                "OPTS:\n"
 122                "    -S    use skb-mode\n"
 123                "    -N    enforce native mode\n"
 124                "    -F    force loading prog\n"
 125                "    -X    load xdp program on egress\n",
 126                prog);
 127}
 128
 129int main(int argc, char **argv)
 130{
 131        struct bpf_prog_load_attr prog_load_attr = {
 132                .prog_type      = BPF_PROG_TYPE_UNSPEC,
 133        };
 134        struct bpf_program *prog, *dummy_prog, *devmap_prog;
 135        int prog_fd, dummy_prog_fd, devmap_prog_fd = 0;
 136        int tx_port_map_fd, tx_mac_map_fd;
 137        struct bpf_devmap_val devmap_val;
 138        struct bpf_prog_info info = {};
 139        __u32 info_len = sizeof(info);
 140        const char *optstr = "FSNX";
 141        struct bpf_object *obj;
 142        int ret, opt, key = 0;
 143        char filename[256];
 144
 145        while ((opt = getopt(argc, argv, optstr)) != -1) {
 146                switch (opt) {
 147                case 'S':
 148                        xdp_flags |= XDP_FLAGS_SKB_MODE;
 149                        break;
 150                case 'N':
 151                        /* default, set below */
 152                        break;
 153                case 'F':
 154                        xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
 155                        break;
 156                case 'X':
 157                        xdp_devmap_attached = true;
 158                        break;
 159                default:
 160                        usage(basename(argv[0]));
 161                        return 1;
 162                }
 163        }
 164
 165        if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) {
 166                xdp_flags |= XDP_FLAGS_DRV_MODE;
 167        } else if (xdp_devmap_attached) {
 168                printf("Load xdp program on egress with SKB mode not supported yet\n");
 169                return 1;
 170        }
 171
 172        if (optind == argc) {
 173                printf("usage: %s <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n", argv[0]);
 174                return 1;
 175        }
 176
 177        ifindex_in = if_nametoindex(argv[optind]);
 178        if (!ifindex_in)
 179                ifindex_in = strtoul(argv[optind], NULL, 0);
 180
 181        ifindex_out = if_nametoindex(argv[optind + 1]);
 182        if (!ifindex_out)
 183                ifindex_out = strtoul(argv[optind + 1], NULL, 0);
 184
 185        printf("input: %d output: %d\n", ifindex_in, ifindex_out);
 186
 187        snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
 188        prog_load_attr.file = filename;
 189
 190        if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
 191                return 1;
 192
 193        if (xdp_flags & XDP_FLAGS_SKB_MODE) {
 194                prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_general");
 195                tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_general");
 196        } else {
 197                prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_native");
 198                tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_native");
 199        }
 200        dummy_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_dummy_prog");
 201        if (!prog || dummy_prog < 0 || tx_port_map_fd < 0) {
 202                printf("finding prog/dummy_prog/tx_port_map in obj file failed\n");
 203                goto out;
 204        }
 205        prog_fd = bpf_program__fd(prog);
 206        dummy_prog_fd = bpf_program__fd(dummy_prog);
 207        if (prog_fd < 0 || dummy_prog_fd < 0 || tx_port_map_fd < 0) {
 208                printf("bpf_prog_load_xattr: %s\n", strerror(errno));
 209                return 1;
 210        }
 211
 212        tx_mac_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_mac");
 213        rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
 214        if (tx_mac_map_fd < 0 || rxcnt_map_fd < 0) {
 215                printf("bpf_object__find_map_fd_by_name failed\n");
 216                return 1;
 217        }
 218
 219        if (bpf_set_link_xdp_fd(ifindex_in, prog_fd, xdp_flags) < 0) {
 220                printf("ERROR: link set xdp fd failed on %d\n", ifindex_in);
 221                return 1;
 222        }
 223
 224        ret = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
 225        if (ret) {
 226                printf("can't get prog info - %s\n", strerror(errno));
 227                return ret;
 228        }
 229        prog_id = info.id;
 230
 231        /* Loading dummy XDP prog on out-device */
 232        if (bpf_set_link_xdp_fd(ifindex_out, dummy_prog_fd,
 233                            (xdp_flags | XDP_FLAGS_UPDATE_IF_NOEXIST)) < 0) {
 234                printf("WARN: link set xdp fd failed on %d\n", ifindex_out);
 235                ifindex_out_xdp_dummy_attached = false;
 236        }
 237
 238        memset(&info, 0, sizeof(info));
 239        ret = bpf_obj_get_info_by_fd(dummy_prog_fd, &info, &info_len);
 240        if (ret) {
 241                printf("can't get prog info - %s\n", strerror(errno));
 242                return ret;
 243        }
 244        dummy_prog_id = info.id;
 245
 246        /* Load 2nd xdp prog on egress. */
 247        if (xdp_devmap_attached) {
 248                unsigned char mac_addr[6];
 249
 250                devmap_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_egress");
 251                if (!devmap_prog) {
 252                        printf("finding devmap_prog in obj file failed\n");
 253                        goto out;
 254                }
 255                devmap_prog_fd = bpf_program__fd(devmap_prog);
 256                if (devmap_prog_fd < 0) {
 257                        printf("finding devmap_prog fd failed\n");
 258                        goto out;
 259                }
 260
 261                if (get_mac_addr(ifindex_out, mac_addr) < 0) {
 262                        printf("get interface %d mac failed\n", ifindex_out);
 263                        goto out;
 264                }
 265
 266                ret = bpf_map_update_elem(tx_mac_map_fd, &key, mac_addr, 0);
 267                if (ret) {
 268                        perror("bpf_update_elem tx_mac_map_fd");
 269                        goto out;
 270                }
 271        }
 272
 273        signal(SIGINT, int_exit);
 274        signal(SIGTERM, int_exit);
 275
 276        devmap_val.ifindex = ifindex_out;
 277        devmap_val.bpf_prog.fd = devmap_prog_fd;
 278        ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
 279        if (ret) {
 280                perror("bpf_update_elem");
 281                goto out;
 282        }
 283
 284        poll_stats(2, ifindex_out);
 285
 286out:
 287        return 0;
 288}
 289