linux/tools/testing/selftests/bpf/xdp_redirect_multi.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#define MAX_INDEX_NUM 1024
  25
  26static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
  27static int ifaces[MAX_IFACE_NUM] = {};
  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_xdp_query_id(ifaces[i], xdp_flags, &prog_id)) {
  36                        printf("bpf_xdp_query_id failed\n");
  37                        exit(1);
  38                }
  39                if (prog_id)
  40                        bpf_xdp_detach(ifaces[i], xdp_flags, NULL);
  41        }
  42
  43        exit(0);
  44}
  45
  46static int get_mac_addr(unsigned int ifindex, void *mac_addr)
  47{
  48        char ifname[IF_NAMESIZE];
  49        struct ifreq ifr;
  50        int fd, ret = -1;
  51
  52        fd = socket(AF_INET, SOCK_DGRAM, 0);
  53        if (fd < 0)
  54                return ret;
  55
  56        if (!if_indextoname(ifindex, ifname))
  57                goto err_out;
  58
  59        strcpy(ifr.ifr_name, ifname);
  60
  61        if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
  62                goto err_out;
  63
  64        memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
  65        ret = 0;
  66
  67err_out:
  68        close(fd);
  69        return ret;
  70}
  71
  72static void usage(const char *prog)
  73{
  74        fprintf(stderr,
  75                "usage: %s [OPTS] <IFNAME|IFINDEX> <IFNAME|IFINDEX> ...\n"
  76                "OPTS:\n"
  77                "    -S    use skb-mode\n"
  78                "    -N    enforce native mode\n"
  79                "    -F    force loading prog\n"
  80                "    -X    load xdp program on egress\n",
  81                prog);
  82}
  83
  84int main(int argc, char **argv)
  85{
  86        int prog_fd, group_all, mac_map;
  87        struct bpf_program *ingress_prog, *egress_prog;
  88        int i, err, ret, opt, egress_prog_fd = 0;
  89        struct bpf_devmap_val devmap_val;
  90        bool attach_egress_prog = false;
  91        unsigned char mac_addr[6];
  92        char ifname[IF_NAMESIZE];
  93        struct bpf_object *obj;
  94        unsigned int ifindex;
  95        char filename[256];
  96
  97        while ((opt = getopt(argc, argv, "SNFX")) != -1) {
  98                switch (opt) {
  99                case 'S':
 100                        xdp_flags |= XDP_FLAGS_SKB_MODE;
 101                        break;
 102                case 'N':
 103                        /* default, set below */
 104                        break;
 105                case 'F':
 106                        xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
 107                        break;
 108                case 'X':
 109                        attach_egress_prog = true;
 110                        break;
 111                default:
 112                        usage(basename(argv[0]));
 113                        return 1;
 114                }
 115        }
 116
 117        if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) {
 118                xdp_flags |= XDP_FLAGS_DRV_MODE;
 119        } else if (attach_egress_prog) {
 120                printf("Load xdp program on egress with SKB mode not supported yet\n");
 121                goto err_out;
 122        }
 123
 124        if (optind == argc) {
 125                printf("usage: %s <IFNAME|IFINDEX> <IFNAME|IFINDEX> ...\n", argv[0]);
 126                goto err_out;
 127        }
 128
 129        printf("Get interfaces:");
 130        for (i = 0; i < MAX_IFACE_NUM && argv[optind + i]; i++) {
 131                ifaces[i] = if_nametoindex(argv[optind + i]);
 132                if (!ifaces[i])
 133                        ifaces[i] = strtoul(argv[optind + i], NULL, 0);
 134                if (!if_indextoname(ifaces[i], ifname)) {
 135                        perror("Invalid interface name or i");
 136                        goto err_out;
 137                }
 138                if (ifaces[i] > MAX_INDEX_NUM) {
 139                        printf(" interface index too large\n");
 140                        goto err_out;
 141                }
 142                printf(" %d", ifaces[i]);
 143        }
 144        printf("\n");
 145
 146        snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
 147        obj = bpf_object__open_file(filename, NULL);
 148        err = libbpf_get_error(obj);
 149        if (err)
 150                goto err_out;
 151        err = bpf_object__load(obj);
 152        if (err)
 153                goto err_out;
 154        prog_fd = bpf_program__fd(bpf_object__next_program(obj, NULL));
 155
 156        if (attach_egress_prog)
 157                group_all = bpf_object__find_map_fd_by_name(obj, "map_egress");
 158        else
 159                group_all = bpf_object__find_map_fd_by_name(obj, "map_all");
 160        mac_map = bpf_object__find_map_fd_by_name(obj, "mac_map");
 161
 162        if (group_all < 0 || mac_map < 0) {
 163                printf("bpf_object__find_map_fd_by_name failed\n");
 164                goto err_out;
 165        }
 166
 167        if (attach_egress_prog) {
 168                /* Find ingress/egress prog for 2nd xdp prog */
 169                ingress_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_all_prog");
 170                egress_prog = bpf_object__find_program_by_name(obj, "xdp_devmap_prog");
 171                if (!ingress_prog || !egress_prog) {
 172                        printf("finding ingress/egress_prog in obj file failed\n");
 173                        goto err_out;
 174                }
 175                prog_fd = bpf_program__fd(ingress_prog);
 176                egress_prog_fd = bpf_program__fd(egress_prog);
 177                if (prog_fd < 0 || egress_prog_fd < 0) {
 178                        printf("find egress_prog fd failed\n");
 179                        goto err_out;
 180                }
 181        }
 182
 183        signal(SIGINT, int_exit);
 184        signal(SIGTERM, int_exit);
 185
 186        /* Init forward multicast groups and exclude group */
 187        for (i = 0; ifaces[i] > 0; i++) {
 188                ifindex = ifaces[i];
 189
 190                if (attach_egress_prog) {
 191                        ret = get_mac_addr(ifindex, mac_addr);
 192                        if (ret < 0) {
 193                                printf("get interface %d mac failed\n", ifindex);
 194                                goto err_out;
 195                        }
 196                        ret = bpf_map_update_elem(mac_map, &ifindex, mac_addr, 0);
 197                        if (ret) {
 198                                perror("bpf_update_elem mac_map failed\n");
 199                                goto err_out;
 200                        }
 201                }
 202
 203                /* Add all the interfaces to group all */
 204                devmap_val.ifindex = ifindex;
 205                devmap_val.bpf_prog.fd = egress_prog_fd;
 206                ret = bpf_map_update_elem(group_all, &ifindex, &devmap_val, 0);
 207                if (ret) {
 208                        perror("bpf_map_update_elem");
 209                        goto err_out;
 210                }
 211
 212                /* bind prog_fd to each interface */
 213                ret = bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL);
 214                if (ret) {
 215                        printf("Set xdp fd failed on %d\n", ifindex);
 216                        goto err_out;
 217                }
 218        }
 219
 220        /* sleep some time for testing */
 221        sleep(999);
 222
 223        return 0;
 224
 225err_out:
 226        return 1;
 227}
 228