linux/samples/bpf/xdp_redirect_map_multi_user.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/bpf.h>
   3#include <linux/if_link.h>
   4#include <assert.h>
   5#include <errno.h>
   6#include <signal.h>
   7#include <stdio.h>
   8#include <stdlib.h>
   9#include <string.h>
  10#include <net/if.h>
  11#include <unistd.h>
  12#include <libgen.h>
  13#include <sys/resource.h>
  14#include <sys/ioctl.h>
  15#include <sys/types.h>
  16#include <sys/socket.h>
  17#include <netinet/in.h>
  18
  19#include "bpf_util.h"
  20#include <bpf/bpf.h>
  21#include <bpf/libbpf.h>
  22
  23#define MAX_IFACE_NUM 32
  24
  25static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
  26static int ifaces[MAX_IFACE_NUM] = {};
  27static int rxcnt_map_fd;
  28
  29static void int_exit(int sig)
  30{
  31        __u32 prog_id = 0;
  32        int i;
  33
  34        for (i = 0; ifaces[i] > 0; i++) {
  35                if (bpf_get_link_xdp_id(ifaces[i], &prog_id, xdp_flags)) {
  36                        printf("bpf_get_link_xdp_id failed\n");
  37                        exit(1);
  38                }
  39                if (prog_id)
  40                        bpf_set_link_xdp_fd(ifaces[i], -1, xdp_flags);
  41        }
  42
  43        exit(0);
  44}
  45
  46static void poll_stats(int interval)
  47{
  48        unsigned int nr_cpus = bpf_num_possible_cpus();
  49        __u64 values[nr_cpus], prev[nr_cpus];
  50
  51        memset(prev, 0, sizeof(prev));
  52
  53        while (1) {
  54                __u64 sum = 0;
  55                __u32 key = 0;
  56                int i;
  57
  58                sleep(interval);
  59                assert(bpf_map_lookup_elem(rxcnt_map_fd, &key, values) == 0);
  60                for (i = 0; i < nr_cpus; i++)
  61                        sum += (values[i] - prev[i]);
  62                if (sum)
  63                        printf("Forwarding %10llu pkt/s\n", sum / interval);
  64                memcpy(prev, values, sizeof(values));
  65        }
  66}
  67
  68static int get_mac_addr(unsigned int ifindex, void *mac_addr)
  69{
  70        char ifname[IF_NAMESIZE];
  71        struct ifreq ifr;
  72        int fd, ret = -1;
  73
  74        fd = socket(AF_INET, SOCK_DGRAM, 0);
  75        if (fd < 0)
  76                return ret;
  77
  78        if (!if_indextoname(ifindex, ifname))
  79                goto err_out;
  80
  81        strcpy(ifr.ifr_name, ifname);
  82
  83        if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
  84                goto err_out;
  85
  86        memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
  87        ret = 0;
  88
  89err_out:
  90        close(fd);
  91        return ret;
  92}
  93
  94static int update_mac_map(struct bpf_object *obj)
  95{
  96        int i, ret = -1, mac_map_fd;
  97        unsigned char mac_addr[6];
  98        unsigned int ifindex;
  99
 100        mac_map_fd = bpf_object__find_map_fd_by_name(obj, "mac_map");
 101        if (mac_map_fd < 0) {
 102                printf("find mac map fd failed\n");
 103                return ret;
 104        }
 105
 106        for (i = 0; ifaces[i] > 0; i++) {
 107                ifindex = ifaces[i];
 108
 109                ret = get_mac_addr(ifindex, mac_addr);
 110                if (ret < 0) {
 111                        printf("get interface %d mac failed\n", ifindex);
 112                        return ret;
 113                }
 114
 115                ret = bpf_map_update_elem(mac_map_fd, &ifindex, mac_addr, 0);
 116                if (ret) {
 117                        perror("bpf_update_elem mac_map_fd");
 118                        return ret;
 119                }
 120        }
 121
 122        return 0;
 123}
 124
 125static void usage(const char *prog)
 126{
 127        fprintf(stderr,
 128                "usage: %s [OPTS] <IFNAME|IFINDEX> <IFNAME|IFINDEX> ...\n"
 129                "OPTS:\n"
 130                "    -S    use skb-mode\n"
 131                "    -N    enforce native mode\n"
 132                "    -F    force loading prog\n"
 133                "    -X    load xdp program on egress\n",
 134                prog);
 135}
 136
 137int main(int argc, char **argv)
 138{
 139        int i, ret, opt, forward_map_fd, max_ifindex = 0;
 140        struct bpf_program *ingress_prog, *egress_prog;
 141        int ingress_prog_fd, egress_prog_fd = 0;
 142        struct bpf_devmap_val devmap_val;
 143        bool attach_egress_prog = false;
 144        char ifname[IF_NAMESIZE];
 145        struct bpf_map *mac_map;
 146        struct bpf_object *obj;
 147        unsigned int ifindex;
 148        char filename[256];
 149
 150        while ((opt = getopt(argc, argv, "SNFX")) != -1) {
 151                switch (opt) {
 152                case 'S':
 153                        xdp_flags |= XDP_FLAGS_SKB_MODE;
 154                        break;
 155                case 'N':
 156                        /* default, set below */
 157                        break;
 158                case 'F':
 159                        xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
 160                        break;
 161                case 'X':
 162                        attach_egress_prog = true;
 163                        break;
 164                default:
 165                        usage(basename(argv[0]));
 166                        return 1;
 167                }
 168        }
 169
 170        if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) {
 171                xdp_flags |= XDP_FLAGS_DRV_MODE;
 172        } else if (attach_egress_prog) {
 173                printf("Load xdp program on egress with SKB mode not supported yet\n");
 174                return 1;
 175        }
 176
 177        if (optind == argc) {
 178                printf("usage: %s <IFNAME|IFINDEX> <IFNAME|IFINDEX> ...\n", argv[0]);
 179                return 1;
 180        }
 181
 182        printf("Get interfaces");
 183        for (i = 0; i < MAX_IFACE_NUM && argv[optind + i]; i++) {
 184                ifaces[i] = if_nametoindex(argv[optind + i]);
 185                if (!ifaces[i])
 186                        ifaces[i] = strtoul(argv[optind + i], NULL, 0);
 187                if (!if_indextoname(ifaces[i], ifname)) {
 188                        perror("Invalid interface name or i");
 189                        return 1;
 190                }
 191
 192                /* Find the largest index number */
 193                if (ifaces[i] > max_ifindex)
 194                        max_ifindex = ifaces[i];
 195
 196                printf(" %d", ifaces[i]);
 197        }
 198        printf("\n");
 199
 200        snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
 201
 202        obj = bpf_object__open(filename);
 203        if (libbpf_get_error(obj)) {
 204                printf("ERROR: opening BPF object file failed\n");
 205                obj = NULL;
 206                goto err_out;
 207        }
 208
 209        /* Reset the map size to max ifindex + 1 */
 210        if (attach_egress_prog) {
 211                mac_map = bpf_object__find_map_by_name(obj, "mac_map");
 212                ret = bpf_map__resize(mac_map, max_ifindex + 1);
 213                if (ret < 0) {
 214                        printf("ERROR: reset mac map size failed\n");
 215                        goto err_out;
 216                }
 217        }
 218
 219        /* load BPF program */
 220        if (bpf_object__load(obj)) {
 221                printf("ERROR: loading BPF object file failed\n");
 222                goto err_out;
 223        }
 224
 225        if (xdp_flags & XDP_FLAGS_SKB_MODE) {
 226                ingress_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_general");
 227                forward_map_fd = bpf_object__find_map_fd_by_name(obj, "forward_map_general");
 228        } else {
 229                ingress_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_native");
 230                forward_map_fd = bpf_object__find_map_fd_by_name(obj, "forward_map_native");
 231        }
 232        if (!ingress_prog || forward_map_fd < 0) {
 233                printf("finding ingress_prog/forward_map in obj file failed\n");
 234                goto err_out;
 235        }
 236
 237        ingress_prog_fd = bpf_program__fd(ingress_prog);
 238        if (ingress_prog_fd < 0) {
 239                printf("find ingress_prog fd failed\n");
 240                goto err_out;
 241        }
 242
 243        rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
 244        if (rxcnt_map_fd < 0) {
 245                printf("bpf_object__find_map_fd_by_name failed\n");
 246                goto err_out;
 247        }
 248
 249        if (attach_egress_prog) {
 250                /* Update mac_map with all egress interfaces' mac addr */
 251                if (update_mac_map(obj) < 0) {
 252                        printf("Error: update mac map failed");
 253                        goto err_out;
 254                }
 255
 256                /* Find egress prog fd */
 257                egress_prog = bpf_object__find_program_by_name(obj, "xdp_devmap_prog");
 258                if (!egress_prog) {
 259                        printf("finding egress_prog in obj file failed\n");
 260                        goto err_out;
 261                }
 262                egress_prog_fd = bpf_program__fd(egress_prog);
 263                if (egress_prog_fd < 0) {
 264                        printf("find egress_prog fd failed\n");
 265                        goto err_out;
 266                }
 267        }
 268
 269        /* Remove attached program when program is interrupted or killed */
 270        signal(SIGINT, int_exit);
 271        signal(SIGTERM, int_exit);
 272
 273        /* Init forward multicast groups */
 274        for (i = 0; ifaces[i] > 0; i++) {
 275                ifindex = ifaces[i];
 276
 277                /* bind prog_fd to each interface */
 278                ret = bpf_set_link_xdp_fd(ifindex, ingress_prog_fd, xdp_flags);
 279                if (ret) {
 280                        printf("Set xdp fd failed on %d\n", ifindex);
 281                        goto err_out;
 282                }
 283
 284                /* Add all the interfaces to forward group and attach
 285                 * egress devmap programe if exist
 286                 */
 287                devmap_val.ifindex = ifindex;
 288                devmap_val.bpf_prog.fd = egress_prog_fd;
 289                ret = bpf_map_update_elem(forward_map_fd, &ifindex, &devmap_val, 0);
 290                if (ret) {
 291                        perror("bpf_map_update_elem forward_map");
 292                        goto err_out;
 293                }
 294        }
 295
 296        poll_stats(2);
 297
 298        return 0;
 299
 300err_out:
 301        return 1;
 302}
 303