aboutsummaryrefslogtreecommitdiffstats
path: root/src/bpfswitch/usrc/xdp_l2fwd_user.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bpfswitch/usrc/xdp_l2fwd_user.c')
-rw-r--r--src/bpfswitch/usrc/xdp_l2fwd_user.c402
1 files changed, 402 insertions, 0 deletions
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;
+}