diff options
Diffstat (limited to 'src/bpfswitch/usrc')
-rw-r--r-- | src/bpfswitch/usrc/Makefile | 60 | ||||
-rw-r--r-- | src/bpfswitch/usrc/libbpf_helpers.c | 309 | ||||
-rw-r--r-- | src/bpfswitch/usrc/libbpf_helpers.h | 29 | ||||
-rw-r--r-- | src/bpfswitch/usrc/str_utils.c | 159 | ||||
-rw-r--r-- | src/bpfswitch/usrc/str_utils.h | 15 | ||||
-rw-r--r-- | src/bpfswitch/usrc/xdp_dummy_user.c | 116 | ||||
-rw-r--r-- | src/bpfswitch/usrc/xdp_l2fwd_user.c | 402 |
7 files changed, 1090 insertions, 0 deletions
diff --git a/src/bpfswitch/usrc/Makefile b/src/bpfswitch/usrc/Makefile new file mode 100644 index 00000000..fc15a6b0 --- /dev/null +++ b/src/bpfswitch/usrc/Makefile @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: GPL-2.0 + +include ../config.mk + +ifneq (,$(BUILDDIR)) +OBJDIR = $(BUILDDIR)/src/obj/ +else +OBJDIR = obj/ +endif + +ifneq (,$(BUILDDIR)) +BINDIR = $(BUILDDIR)/src/bin/ +else +BINDIR = bin/ +endif + +MODS += $(BINDIR)xdp_l2fwd +MODS += $(BINDIR)xdp_dummy + +VPATH := . + +CC = gcc +CFLAGS += -O2 -g -Wall + +INCLUDES = -I../include -I../include/uapi +INCLUDES += -I../include/tools + +ifneq (,$(LIBBPF_DIR)) +BPF_LINK_FEAT := $(shell egrep 'bpf_link_create' $(LIBBPF_PATH)/usr/include/bpf/libbpf.h) +ifneq (,$(BPF_LINK_FEAT)) +CFLAGS += -DHAVE_BPF_LINK_CREATE +endif +else +LIBBPF=-lbpf +endif + +#COMMON += $(OBJDIR)timestamps.o +COMMON += $(OBJDIR)libbpf_helpers.o +COMMON += $(OBJDIR)str_utils.o +#COMMON += $(OBJDIR)rbtree.o +#COMMON += $(OBJDIR)parse_pkt.o +#COMMON += $(OBJDIR)print_pkt.o +#COMMON += $(OBJDIR)ksyms.o + +all: build $(MODS) + +build: + @mkdir -p $(OBJDIR) $(BINDIR) + +$(BINDIR)%: $(OBJDIR)%.o $(COMMON) + $(QUIET_LINK)$(CC) $(INCLUDES) $(DEFS) $(CFLAGS) $^ -o $@ $(LDLIBS) + +$(BINDIR)xdp_%: $(OBJDIR)xdp_%_user.o $(COMMON) + $(QUIET_LINK)$(CC) $(INCLUDES) $(DEFS) $(CFLAGS) $^ -o $@ $(LDLIBS) + +$(OBJDIR)%.o: %.c + $(QUIET_CC)$(CC) $(INCLUDES) $(DEFS) $(CFLAGS) -c $^ -o $@ + +clean: + @rm -rf $(OBJDIR) $(BINDIR) diff --git a/src/bpfswitch/usrc/libbpf_helpers.c b/src/bpfswitch/usrc/libbpf_helpers.c new file mode 100644 index 00000000..522f5586 --- /dev/null +++ b/src/bpfswitch/usrc/libbpf_helpers.c @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * convenience wrappers around libbpf functions + * + * Copyright (c) 2019-2021 David Ahern <dsahern@gmail.com> + */ + +#include <linux/if_link.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <limits.h> +#include <unistd.h> +#include <bpf/bpf.h> +#include <bpf/libbpf.h> +#include <errno.h> + +#include "libbpf_helpers.h" + +enum bpf_obj_type { + BPF_OBJ_UNKNOWN, + BPF_OBJ_PROG, + BPF_OBJ_MAP, + BPF_OBJ_LINK, + BPF_OBJ_BTF, +}; + +int load_obj_file(struct bpf_prog_load_attr *attr, + struct bpf_object **obj, + const char *objfile, bool user_set) +{ + static char *expected_paths[] = { + "bin", + "ksrc/obj", /* path in git tree */ + "bpf-obj", + ".", /* cwd */ + NULL, + }; + char path[PATH_MAX]; + int prog_fd, i = 0; + + if (user_set) { + attr->file = objfile; + return bpf_prog_load_xattr(attr, obj, &prog_fd); + } + + attr->file = path; + while (expected_paths[i]) { + struct stat sbuf; + + snprintf(path, sizeof(path), "%s/%s", + expected_paths[i], objfile); + + if (stat(path, &sbuf) == 0) { + if (!bpf_prog_load_xattr(attr, obj, &prog_fd)) + return 0; + + if (errno != ENOENT) + break; + } + ++i; + } + + fprintf(stderr, "Failed to find object file; nothing to load\n"); + return 1; +} + +int bpf_map_get_fd_by_name(const char *name) +{ + struct bpf_map_info info = {}; + __u32 len = sizeof(info); + __u32 id = 0; + int err, fd; + + while (1) { + err = bpf_map_get_next_id(id, &id); + if (err) + break; + + fd = bpf_map_get_fd_by_id(id); + if (fd < 0) + continue; + + err = bpf_obj_get_info_by_fd(fd, &info, &len); + if (!err && strcmp(info.name, name) == 0) + return fd; + + close(fd); + } + + return -1; +} + +/* from bpftool */ +static int get_fd_type(int fd) +{ + char path[PATH_MAX]; + char buf[512]; + ssize_t n; + + snprintf(path, sizeof(path), "/proc/self/fd/%d", fd); + + n = readlink(path, buf, sizeof(buf)); + if (n < 0) { + fprintf(stderr, "can't read link type: %s\n", strerror(errno)); + return -1; + } + if (n == sizeof(path)) { + fprintf(stderr, "can't read link type: path too long!\n"); + return -1; + } + + if (strstr(buf, "bpf-map")) + return BPF_OBJ_MAP; + + if (strstr(buf, "bpf-prog")) + return BPF_OBJ_PROG; + + if (strstr(buf, "bpf-link")) + return BPF_OBJ_LINK; + + return BPF_OBJ_UNKNOWN; +} + +int bpf_map_get_fd_by_path(const char *path) +{ + enum bpf_obj_type objtype; + int fd; + + fd = bpf_obj_get(path); + if (fd < 0) { + fprintf(stderr, "Failed to get bpf object (%s): %s\n", + path, strerror(errno)); + return -1; + } + + objtype = get_fd_type(fd); + if (objtype != BPF_OBJ_MAP) { + fprintf(stderr, "Path is not to a BPF map\n"); + close(fd); + return -1; + } + + return fd; +} + +int bpf_map_get_fd(__u32 id, const char *path, const char *name, + const char *desc) +{ + int fd = -1; + + if (id) { + fd = bpf_map_get_fd_by_id(id); + if (fd < 0 && errno != ENOENT) { + fprintf(stderr, + "Failed to get fd for %s by id: %s: %d\n", + desc, strerror(errno), errno); + return -1; + } + } else if (path) { + fd = bpf_map_get_fd_by_path(path); + if (fd < 0) { + fprintf(stderr, + "Failed to get fd for %s by path: %s: %d\n", + desc, strerror(errno), errno); + return -1; + } + } else if (name) { + fd = bpf_map_get_fd_by_name(name); + if (fd < 0 && errno != ENOENT) { + fprintf(stderr, + "Failed to get fd for %s by expected name: %s: %d\n", + desc, strerror(errno), errno); + return -1; + } + } + + return fd; +} + +int bpf_prog_get_fd_by_path(const char *path) +{ + enum bpf_obj_type objtype; + int fd; + + fd = bpf_obj_get(path); + if (fd < 0) { + fprintf(stderr, "Failed to get bpf object (%s): %s\n", + path, strerror(errno)); + return -1; + } + + objtype = get_fd_type(fd); + if (objtype != BPF_OBJ_PROG) { + fprintf(stderr, "Path is not to a BPF program\n"); + close(fd); + return -1; + } + + return fd; +} + +int bpf_prog_get_fd_by_name(const char *name) +{ + struct bpf_prog_info info = {}; + __u32 len = sizeof(info); + __u32 id = 0; + int err, fd; + + while (1) { + err = bpf_prog_get_next_id(id, &id); + if (err) + break; + + fd = bpf_prog_get_fd_by_id(id); + if (fd < 0) + continue; + + err = bpf_obj_get_info_by_fd(fd, &info, &len); + if (!err && strcmp(info.name, name) == 0) + return fd; + + close(fd); + } + + return -1; +} + +int bpf_prog_get_fd(__u32 id, const char *path, const char *name, + const char *desc) +{ + int fd = -1; + + if (id) { + fd = bpf_prog_get_fd_by_id(id); + if (fd < 0 && errno != ENOENT) { + fprintf(stderr, + "Failed to get fd for %s by id: %s: %d\n", + desc, strerror(errno), errno); + return -1; + } + } else if (path) { + fd = bpf_prog_get_fd_by_path(path); + if (fd < 0) { + fprintf(stderr, + "Failed to get fd for %s by path: %s: %d\n", + desc, strerror(errno), errno); + return -1; + } + } else if (name) { + fd = bpf_prog_get_fd_by_name(name); + if (fd < 0 && errno != ENOENT) { + fprintf(stderr, + "Failed to get fd for %s by expected name: %s: %d\n", + desc, strerror(errno), errno); + return -1; + } + } + + return fd; +} + +int attach_to_dev_generic(int idx, int prog_fd, const char *dev) +{ + int err; + + err = bpf_set_link_xdp_fd(idx, prog_fd, XDP_FLAGS_SKB_MODE); + if (err < 0) { + printf("ERROR: failed to attach program to %s\n", dev); + return err; + } + + return 0; +} + +int detach_from_dev_generic(int idx, const char *dev) +{ + int err; + + err = bpf_set_link_xdp_fd(idx, -1, XDP_FLAGS_SKB_MODE); + if (err < 0) + printf("ERROR: failed to detach program from %s\n", dev); + + return 0; +} + + +int attach_to_dev(int idx, int prog_fd, const char *dev) +{ + int err; + + err = bpf_set_link_xdp_fd(idx, prog_fd, XDP_FLAGS_DRV_MODE); + if (err < 0) { + printf("ERROR: failed to attach program to %s\n", dev); + return err; + } + + return 0; +} + +int detach_from_dev(int idx, const char *dev) +{ + int err; + + err = bpf_set_link_xdp_fd(idx, -1, 0); + if (err < 0) + printf("ERROR: failed to detach program from %s\n", dev); + + return 0; +} diff --git a/src/bpfswitch/usrc/libbpf_helpers.h b/src/bpfswitch/usrc/libbpf_helpers.h new file mode 100644 index 00000000..9be0513c --- /dev/null +++ b/src/bpfswitch/usrc/libbpf_helpers.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBBPF_HELPERS_H +#define __LIBBPF_HELPERS_H + +#include <bpf/libbpf.h> + +int load_obj_file(struct bpf_prog_load_attr *attr, + struct bpf_object **obj, + const char *objfile, bool user_set); + +int bpf_map_get_fd_by_name(const char *name); +int bpf_map_get_fd_by_path(const char *path); +int bpf_map_get_fd(__u32 id, const char *path, const char *name, + const char *desc); + +int bpf_prog_get_fd_by_path(const char *path); +int bpf_prog_get_fd(__u32 id, const char *path, const char *name, + const char *desc); + +int attach_to_dev_generic(int idx, int prog_fd, const char *dev); +int detach_from_dev_generic(int idx, const char *dev); + +int attach_to_dev(int idx, int prog_fd, const char *dev); +int detach_from_dev(int idx, const char *dev); + +int attach_to_dev_tx(int idx, int prog_fd, const char *dev); +int detach_from_dev_tx(int idx, const char *dev); + +#endif diff --git a/src/bpfswitch/usrc/str_utils.c b/src/bpfswitch/usrc/str_utils.c new file mode 100644 index 00000000..8650cdfb --- /dev/null +++ b/src/bpfswitch/usrc/str_utils.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * String conversion and parsing functions. + * + * David Ahern <dsahern@gmail.com> + */ +#include <linux/if_ether.h> +#include <net/if.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <errno.h> + +#include "str_utils.h" + +static int str_to_int_base(const char *str, int min, int max, int *value, int base) +{ + int number; + char *end; + + errno = 0; + number = (int) strtol(str, &end, base); + + if ( ((*end == '\0') || (*end == '\n')) && (end != str) && + (errno != ERANGE) && (min <= number) && (number <= max)) { + *value = number; + return 0; + } + + return -1; +} + +int str_to_int(const char *str, int min, int max, int *value) +{ + return str_to_int_base(str, min, max, value, 0); +} + +int str_to_ushort(const char *str, unsigned short *us) +{ + int i; + + if (str_to_int(str, 0, 0xFFFF, &i) != 0) + return -1; + + *us = (unsigned short) (i); + + return 0; +} + +int str_to_ulong_base(const char *str, unsigned long *ul, int base) +{ + char *end; + + *ul= strtoul(str, &end, base); + if (*end != '\0') + return -1; + + return 0; +} + +int str_to_ulong(const char *str, unsigned long *ul) +{ + return str_to_ulong_base(str, ul, 0); +} + +int str_to_ullong(const char *str, unsigned long long *ul) +{ + char *end; + + *ul= strtoull(str, &end, 0); + if (*end != '\0') + return -1; + + return 0; +} + +int str_to_mac(const char *str, unsigned char *mac) +{ + int rc = -1, m, i; + char *s = strdup(str), *p, *d, tmp[3]; + + if (!s) + return -1; + + p = s; + tmp[2] = '\0'; + for (i = 0; i < ETH_ALEN; ++i) { + if (*p == '\0') + goto out; + + d = strchr(p, ':'); + if (d) { + *d = '\0'; + if (strlen(p) > 2) + goto out; + + strcpy(tmp, p); + p = d + 1; + } else { + strncpy(tmp, p, 2); + p += 2; + } + + if (str_to_int_base(tmp, 0, 0xFF, &m, 16) != 0) + goto out; + + mac[i] = m; + } + + if (*p == '\0') + rc = 0; +out: + free(s); + + return rc; +} + +int get_ifidx(const char *arg) +{ + int idx; + + idx = if_nametoindex(arg); + if (!idx) + idx = strtoul(arg, NULL, 0); + + return idx; +} + +/* find parameters in a string -- based on Harbison and Steele, p. 291 */ +int parsestr(char *str, char *delims, char *fields[], int nmax) +{ + int n; + + if (!str || (*str == '\0')) + return 0; + + n = 0; + fields[0] = strtok(str, delims); + while ((fields[n] != (char *) NULL) && (n < (nmax-1))) { + ++n; + fields[n] = strtok(NULL, delims); + } + + if ((n == (nmax - 1)) && (fields[n] != (char *) NULL)) + ++n; + + return n; +} + +void print_mac(const __u8 *mac, bool reverse) +{ + if (reverse) + printf("%.02x:%.02x:%.02x:%.02x:%.02x:%.02x", + mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]); + else + printf("%.02x:%.02x:%.02x:%.02x:%.02x:%.02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +} diff --git a/src/bpfswitch/usrc/str_utils.h b/src/bpfswitch/usrc/str_utils.h new file mode 100644 index 00000000..817eb5f6 --- /dev/null +++ b/src/bpfswitch/usrc/str_utils.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __STR_UTILS_H +#define __STR_UTILS_H + +int str_to_int(const char *str, int min, int max, int *value); +int str_to_ushort(const char *str, unsigned short *us); +int str_to_ulong(const char *str, unsigned long *ul); +int str_to_ulong_base(const char *str, unsigned long *ul, int base); +int str_to_ullong(const char *str, unsigned long long *ul); +int str_to_mac(const char *str, unsigned char *mac); +int get_ifidx(const char *arg); + +int parsestr(char *str, char *delims, char *fields[], int nmax); +void print_mac(const __u8 *mac, bool reverse); +#endif diff --git a/src/bpfswitch/usrc/xdp_dummy_user.c b/src/bpfswitch/usrc/xdp_dummy_user.c new file mode 100644 index 00000000..7741f330 --- /dev/null +++ b/src/bpfswitch/usrc/xdp_dummy_user.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/bpf.h> +#include <linux/if_link.h> +#include <linux/limits.h> +#include <net/if.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <libgen.h> + +#include <bpf/bpf.h> +#include <bpf/libbpf.h> + +#include "libbpf_helpers.h" + +static void usage(const char *prog) +{ + fprintf(stderr, + "usage: %s [OPTS] interface-list\n" + "\nOPTS:\n" + " -d detach program\n" + " -f bpf-file bpf filename to load\n" + " -g skb mode\n" + , prog); +} + +int main(int argc, char **argv) +{ + int (*attach_fn)(int idx, int prog_fd, const char *dev) = attach_to_dev; + int (*detach_fn)(int idx, const char *dev) = detach_from_dev; + struct bpf_prog_load_attr prog_load_attr = { }; + const char *objfile = "xdp_dummy_kern.o"; + const char *pname = "xdp_dummy"; + bool filename_set = false; + struct bpf_program *prog; + struct bpf_object *obj; + int opt, i, prog_fd; + bool attach = true; + int ret = 0; + + while ((opt = getopt(argc, argv, ":df:g")) != -1) { + switch (opt) { + case 'f': + objfile = optarg; + filename_set = true; + break; + case 'd': + attach = false; + break; + case 'g': + attach_fn = attach_to_dev_generic; + detach_fn = detach_from_dev_generic; + break; + default: + usage(basename(argv[0])); + return 1; + } + } + + if (optind == argc) { + usage(basename(argv[0])); + return 1; + } + + if (!attach) { + for (i = optind; i < argc; ++i) { + int idx, err; + + idx = if_nametoindex(argv[i]); + if (!idx) + idx = strtoul(argv[i], NULL, 0); + + if (!idx) { + fprintf(stderr, "Invalid device argument\n"); + return 1; + } + err = detach_fn(idx, argv[i]); + if (err) + ret = err; + } + return ret; + } + + if (load_obj_file(&prog_load_attr, &obj, objfile, filename_set)) + return 1; + + prog = bpf_object__find_program_by_title(obj, pname); + prog_fd = bpf_program__fd(prog); + if (prog_fd < 0) { + printf("program not found: %s\n", strerror(prog_fd)); + return 1; + } + + for (i = optind; i < argc; ++i) { + int idx, err; + + idx = if_nametoindex(argv[i]); + if (!idx) + idx = strtoul(argv[i], NULL, 0); + + if (!idx) { + fprintf(stderr, "Invalid device argument\n"); + return 1; + } + + err = attach_fn(idx, prog_fd, argv[i]); + if (err) + ret = err; + } + + return ret; +} diff --git a/src/bpfswitch/usrc/xdp_l2fwd_user.c b/src/bpfswitch/usrc/xdp_l2fwd_user.c new file mode 100644 index 00000000..13437621 --- /dev/null +++ b/src/bpfswitch/usrc/xdp_l2fwd_user.c @@ -0,0 +1,402 @@ +/* Copyright (c) 2019-22 David Ahern <dsahern@gmail.com> + * + * The code is modifed to use 2 mac addresses as key instead of + * mac-address and vland-id as key + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <limits.h> +#include <net/if.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <libgen.h> +#include <bpf/bpf.h> + +#include "xdp_fdb.h" +#include "str_utils.h" +#include "libbpf_helpers.h" + +static bool fdb_map_verify(int map_fd) +{ + struct bpf_map_info info = {}; + __u32 len = sizeof(info); + + if (bpf_obj_get_info_by_fd(map_fd, &info, &len)) { + fprintf(stderr, "Failed to get map info: %s: %d", + strerror(errno), errno); + return false; + } + + if (info.type != BPF_MAP_TYPE_HASH || + info.key_size != sizeof(struct fdb_key) || + info.value_size != sizeof(__u32)) { + fprintf(stderr, "Incompatible map\n"); + return false; + } + + return true; +} + +static bool ports_map_verify(int ports_fd, bool *with_prog) +{ + struct bpf_map_info info = {}; + __u32 len = sizeof(info); + + if (bpf_obj_get_info_by_fd(ports_fd, &info, &len)) { + fprintf(stderr, "Failed to get map info: %s: %d", + strerror(errno), errno); + return false; + } + + if (info.value_size == sizeof(struct bpf_devmap_val)) + *with_prog = true; + + if (info.type != BPF_MAP_TYPE_DEVMAP_HASH || + info.key_size != sizeof(__u32) || + (info.value_size != sizeof(__u32) && !with_prog)) { + fprintf(stderr, "Incompatible map\n"); + return false; + } + + return true; +} + +static int show_entries_cli(int fdb_fd, int ports_fd) +{ + struct fdb_key *key, *prev_key = NULL; + struct bpf_devmap_val pval; + bool with_prog = false; + char buf[IFNAMSIZ]; + int err, i; + __u32 fval; + + + if (!fdb_map_verify(fdb_fd) || + !ports_map_verify(ports_fd, &with_prog)) + return 1; + + key = calloc(1, sizeof(*key)); + if (!key) { + fprintf(stderr, "Failed to allocate memory for key\n"); + return 1; + } + + for (i = 0; ; ++i) { + err = bpf_map_get_next_key(fdb_fd, prev_key, key); + if (err) { + if (errno == ENOENT) + err = 0; + break; + } + + fval = 0; + memset(&pval, 0, sizeof(pval)); + if (bpf_map_lookup_elem(fdb_fd, key, &fval)) + goto next_key; + + if (if_indextoname(fval, buf) == NULL) { + fprintf(stderr, "WARNING: stale device index\n"); + snprintf(buf, IFNAMSIZ, "-"); + } + + //printf("-v %u -m ", key->vlan); + print_mac(key->smac, false); + print_mac(key->dmac, false); + printf(" -d %s", buf); + + if (bpf_map_lookup_elem(ports_fd, &fval, &pval)) { + fprintf(stderr, + "No ports entry for device %s/%d\n", buf, fval); + goto end_entry; + } + + if (with_prog) + printf(" -p %u", pval.bpf_prog.id); +end_entry: + printf("\n"); +next_key: + prev_key = key; + } + + free(key); + return err; +} + +static int show_ports_entries(int map_fd) +{ + __u32 *key, *prev_key = NULL; + struct bpf_devmap_val val; + bool with_prog = false; + int err; + + if (!ports_map_verify(map_fd, &with_prog)) + return 1; + + key = calloc(1, sizeof(*key)); + if (!key) { + fprintf(stderr, "Failed to allocate memory for key\n"); + return 1; + } + + printf("\nPorts map:\n"); + while(1) { + err = bpf_map_get_next_key(map_fd, prev_key, key); + if (err) { + if (errno == ENOENT) + err = 0; + break; + } + + memset(&val, 0, sizeof(val)); + if (!bpf_map_lookup_elem(map_fd, key, &val)) { + char buf[IFNAMSIZ]; + + if (if_indextoname(val.ifindex, buf) == NULL) { + fprintf(stderr, "WARNING: stale device index\n"); + snprintf(buf, IFNAMSIZ, "-"); + } + + printf("index %u -> device %s/%u", + *key, buf, val.ifindex); + if (with_prog && val.bpf_prog.id) + printf(", prog id %u", val.bpf_prog.id); + printf("\n"); + } + + prev_key = key; + } + + free(key); + return err; +} + +static int show_fdb_entries(int map_fd) +{ + struct fdb_key *key, *prev_key = NULL; + int err, i; + __u32 val; + + if (!fdb_map_verify(map_fd)) + return 1; + + key = calloc(1, sizeof(*key)); + if (!key) { + fprintf(stderr, "Failed to allocate memory for key\n"); + return 1; + } + + printf("FDB map:\n"); + for (i = 0; ; ++i) { + err = bpf_map_get_next_key(map_fd, prev_key, key); + if (err) { + if (errno == ENOENT) + err = 0; + break; + } + + val = 0; + if (!bpf_map_lookup_elem(map_fd, key, &val)) { + char buf[IFNAMSIZ]; + + if (if_indextoname(val, buf) == NULL) { + fprintf(stderr, "WARNING: stale device index\n"); + snprintf(buf, IFNAMSIZ, "-"); + } + + //printf("entry %d: < %u, ", i, key->vlan); + print_mac(key->smac, false); + print_mac(key->dmac, false); + printf(" > --> device %s/%u\n", buf, val); + } + + prev_key = key; + } + + free(key); + return err; +} + +/* remove from fdb then remove device */ +static int remove_entries(int fdb_fd, struct fdb_key *key, + int ports_fd, int idx) +{ + int rc; + + rc = bpf_map_delete_elem(fdb_fd, key); + if (rc) + fprintf(stderr, "Failed to delete fdb entry\n"); + + rc = bpf_map_delete_elem(ports_fd, &idx); + if (rc) + fprintf(stderr, "Failed to delete ports entry\n"); + return rc; +} + +static void usage(const char *prog) +{ + fprintf(stderr, + "usage: %s [OPTS]\n" + "\nOPTS:\n" + " -f id fdb map id or pinned path\n" + " -t id devmap id or pinned path for tx ports\n" + " -d device device to redirect\n" + " -m mac Destination mac address for entry\n" + " -s mac Source mac address for entry\n" + " -r remove entries\n" + " -p progid bpf program id or pinned path to attach to entry\n" + " -P print map entries\n" + " -C print map entries in command line format\n" + , prog); +} + +int main(int argc, char **argv) +{ + struct bpf_devmap_val pval = { .bpf_prog.fd = -1 }; + __u32 fdb_id = 0, ports_id = 0, bpf_prog_id = 0; + const char *ports_map_path = NULL; + const char *bpf_prog_path = NULL; + const char *fdb_map_path = NULL; + bool print_entries = false; + struct fdb_key key = {}; + int fdb_fd, ports_fd; + bool delete = false; + bool cli_arg = false; + unsigned long tmp; + int opt, ret; + + while ((opt = getopt(argc, argv, ":f:t:d:m:s:p:rPC")) != -1) { + switch (opt) { + case 'f': + if (str_to_ulong(optarg, &tmp) == 0) { + fdb_id = (__u32)tmp; + } else if (*optarg == '/') { + fdb_map_path = optarg; + } else { + fprintf(stderr, "Invalid fdb map id\n"); + return 1; + } + break; + case 't': + if (str_to_ulong(optarg, &tmp) == 0) { + ports_id = (__u32)tmp; + } else if (*optarg == '/') { + ports_map_path = optarg; + } else { + fprintf(stderr, "Invalid ports map id\n"); + return 1; + } + break; + case 'd': + pval.ifindex = if_nametoindex(optarg); + if (!pval.ifindex) { + if (str_to_int(optarg, 0, INT_MAX, &ret)) { + fprintf(stderr, "Invalid device\n"); + return 1; + } + pval.ifindex = (__u32)ret; + } + break; + case 'm': + if (str_to_mac(optarg, key.dmac)) { + fprintf(stderr, "Invalid Dest-mac address\n"); + return 1; + } + break; + case 's': + /* + if (str_to_int(optarg, 0, 4095, &ret)) { + fprintf(stderr, "Invalid vlan\n"); + return 1; + } + key.vlan = (__u16)ret; + */ + if (str_to_mac(optarg, key.smac)) { + fprintf(stderr, "Invalid Source-mac address\n"); + return 1; + } + break; + case 'p': + if (str_to_ulong(optarg, &tmp) == 0) { + bpf_prog_id = (__u32)tmp; + } else if (*optarg == '/') { + bpf_prog_path = optarg; + } else { + fprintf(stderr, "Invalid program id\n"); + return 1; + } + break; + case 'r': + delete = true; + break; + case 'C': + cli_arg = true; + break; + case 'P': + print_entries = true; + break; + default: + usage(basename(argv[0])); + return 1; + } + } + + fdb_fd = bpf_map_get_fd(fdb_id, fdb_map_path, "fdb_map", "fdb map"); + if (fdb_fd < 0) + return 1; + + ports_fd = bpf_map_get_fd(ports_id, ports_map_path, "xdp_fwd_ports", + "ports map"); + if (ports_fd < 0) + return 1; + + if (cli_arg) + return show_entries_cli(fdb_fd, ports_fd); + + if (print_entries) { + ret = show_fdb_entries(fdb_fd); + return show_ports_entries(ports_fd) ? : ret; + } + + pval.bpf_prog.fd = bpf_prog_get_fd(bpf_prog_id, bpf_prog_path, NULL, + "redirect program"); + if (pval.bpf_prog.fd < 0) + return 1; + + if (!pval.ifindex) { + fprintf(stderr, "Device index not given\n"); + return 1; + } + + if (delete) + return remove_entries(fdb_fd, &key, ports_fd, pval.ifindex); + + /* add device to port map and then add fdb entry */ + ret = bpf_map_update_elem(ports_fd, &pval.ifindex, &pval, 0); + if (ret) { + fprintf(stderr, "Failed to add ports entry: %s: %d\n", + strerror(errno), errno); + remove_entries(fdb_fd, &key, ports_fd, pval.ifindex); + return ret; + } + + ret = bpf_map_update_elem(fdb_fd, &key, &pval.ifindex, BPF_ANY); + if (ret) { + fprintf(stderr, "Failed to add fdb entry: %s: %d\n", + strerror(errno), errno); + remove_entries(fdb_fd, &key, ports_fd, pval.ifindex); + return ret; + } + + return 0; +} |