aboutsummaryrefslogtreecommitdiffstats
path: root/src/bpfswitch/usrc
diff options
context:
space:
mode:
Diffstat (limited to 'src/bpfswitch/usrc')
-rw-r--r--src/bpfswitch/usrc/Makefile60
-rw-r--r--src/bpfswitch/usrc/libbpf_helpers.c309
-rw-r--r--src/bpfswitch/usrc/libbpf_helpers.h29
-rw-r--r--src/bpfswitch/usrc/str_utils.c159
-rw-r--r--src/bpfswitch/usrc/str_utils.h15
-rw-r--r--src/bpfswitch/usrc/xdp_dummy_user.c116
-rw-r--r--src/bpfswitch/usrc/xdp_l2fwd_user.c402
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;
+}