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_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 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        struct bpf_prog_load_attr prog_load_attr = {
  89                .prog_type = BPF_PROG_TYPE_UNSPEC,
  90        };
  91        int i, ret, opt, egress_prog_fd = 0;
  92        struct bpf_devmap_val devmap_val;
  93        bool attach_egress_prog = false;
  94        unsigned char mac_addr[6];
  95        char ifname[IF_NAMESIZE];
  96        struct bpf_object *obj;
  97        unsigned int ifindex;
  98        char filename[256];
  99
 100        while ((opt = getopt(argc, argv, "SNFX")) != -1) {
 101                switch (opt) {
 102                case 'S':
 103                        xdp_flags |= XDP_FLAGS_SKB_MODE;
 104                        break;
 105                case 'N':
 106                        /* default, set below */
 107                        break;
 108                case 'F':
 109                        xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
 110                        break;
 111                case 'X':
 112                        attach_egress_prog = true;
 113                        break;
 114                default:
 115                        usage(basename(argv[0]));
 116                        return 1;
 117                }
 118        }
 119
 120        if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) {
 121                xdp_flags |= XDP_FLAGS_DRV_MODE;
 122        } else if (attach_egress_prog) {
 123                printf("Load xdp program on egress with SKB mode not supported yet\n");
 124                goto err_out;
 125        }
 126
 127        if (optind == argc) {
 128                printf("usage: %s <IFNAME|IFINDEX> <IFNAME|IFINDEX> ...\n", argv[0]);
 129                goto err_out;
 130        }
 131
 132        printf("Get interfaces");
 133        for (i = 0; i < MAX_IFACE_NUM && argv[optind + i]; i++) {
 134                ifaces[i] = if_nametoindex(argv[optind + i]);
 135                if (!ifaces[i])
 136                        ifaces[i] = strtoul(argv[optind + i], NULL, 0);
 137                if (!if_indextoname(ifaces[i], ifname)) {
 138                        perror("Invalid interface name or i");
 139                        goto err_out;
 140                }
 141                if (ifaces[i] > MAX_INDEX_NUM) {
 142                        printf("Interface index to large\n");
 143                        goto err_out;
 144                }
 145                printf(" %d", ifaces[i]);
 146        }
 147        printf("\n");
 148
 149        snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
 150        prog_load_attr.file = filename;
 151
 152        if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
 153                goto err_out;
 154
 155        if (attach_egress_prog)
 156                group_all = bpf_object__find_map_fd_by_name(obj, "map_egress");
 157        else
 158                group_all = bpf_object__find_map_fd_by_name(obj, "map_all");
 159        mac_map = bpf_object__find_map_fd_by_name(obj, "mac_map");
 160
 161        if (group_all < 0 || mac_map < 0) {
 162                printf("bpf_object__find_map_fd_by_name failed\n");
 163                goto err_out;
 164        }
 165
 166        if (attach_egress_prog) {
 167                /* Find ingress/egress prog for 2nd xdp prog */
 168                ingress_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_all_prog");
 169                egress_prog = bpf_object__find_program_by_name(obj, "xdp_devmap_prog");
 170                if (!ingress_prog || !egress_prog) {
 171                        printf("finding ingress/egress_prog in obj file failed\n");
 172                        goto err_out;
 173                }
 174                prog_fd = bpf_program__fd(ingress_prog);
 175                egress_prog_fd = bpf_program__fd(egress_prog);
 176                if (prog_fd < 0 || egress_prog_fd < 0) {
 177                        printf("find egress_prog fd failed\n");
 178                        goto err_out;
 179                }
 180        }
 181
 182        signal(SIGINT, int_exit);
 183        signal(SIGTERM, int_exit);
 184
 185        /* Init forward multicast groups and exclude group */
 186        for (i = 0; ifaces[i] > 0; i++) {
 187                ifindex = ifaces[i];
 188
 189                if (attach_egress_prog) {
 190                        ret = get_mac_addr(ifindex, mac_addr);
 191                        if (ret < 0) {
 192                                printf("get interface %d mac failed\n", ifindex);
 193                                goto err_out;
 194                        }
 195                        ret = bpf_map_update_elem(mac_map, &ifindex, mac_addr, 0);
 196                        if (ret) {
 197                                perror("bpf_update_elem mac_map failed\n");
 198                                goto err_out;
 199                        }
 200                }
 201
 202                /* Add all the interfaces to group all */
 203                devmap_val.ifindex = ifindex;
 204                devmap_val.bpf_prog.fd = egress_prog_fd;
 205                ret = bpf_map_update_elem(group_all, &ifindex, &devmap_val, 0);
 206                if (ret) {
 207                        perror("bpf_map_update_elem");
 208                        goto err_out;
 209                }
 210
 211                /* bind prog_fd to each interface */
 212                ret = bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags);
 213                if (ret) {
 214                        printf("Set xdp fd failed on %d\n", ifindex);
 215                        goto err_out;
 216                }
 217        }
 218
 219        /* sleep some time for testing */
 220        sleep(999);
 221
 222        return 0;
 223
 224err_out:
 225        return 1;
 226}
 227