aboutsummaryrefslogtreecommitdiffstats
path: root/src/bpfswitch/ksrc
diff options
context:
space:
mode:
Diffstat (limited to 'src/bpfswitch/ksrc')
-rw-r--r--src/bpfswitch/ksrc/Makefile59
-rw-r--r--src/bpfswitch/ksrc/include/asm_goto_workaround.h28
-rw-r--r--src/bpfswitch/ksrc/xdp_dummy.c21
-rw-r--r--src/bpfswitch/ksrc/xdp_l2fwd.c103
4 files changed, 211 insertions, 0 deletions
diff --git a/src/bpfswitch/ksrc/Makefile b/src/bpfswitch/ksrc/Makefile
new file mode 100644
index 00000000..64bec2f8
--- /dev/null
+++ b/src/bpfswitch/ksrc/Makefile
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: GPL-2.0
+
+include ../config.mk
+
+ifneq (,$(BUILDDIR))
+OBJDIR = $(BUILDDIR)/ksrc/obj/
+else
+OBJDIR = obj/
+endif
+
+MODS += $(OBJDIR)xdp_l2fwd.o
+MODS += $(OBJDIR)xdp_dummy.o
+
+VPATH := .
+
+# rule is based on samples/bpf/Makefile
+DEFS = -D__KERNEL__ -D__BPF_TRACING__ -D__TARGET_ARCH_x86 $(EXTRA_DEFS)
+
+CFLAGS += -Wno-unused-value -Wno-pointer-sign
+CFLAGS += -Wno-compare-distinct-pointer-types
+CFLAGS += -Wno-gnu-variable-sized-type-not-at-end
+CFLAGS += -Wno-address-of-packed-member
+CFLAGS += -Wno-tautological-compare
+CFLAGS += -Wno-unknown-warning-option
+CFLAGS += -fno-stack-protector
+
+INCLUDES = -I../include
+INCLUDES += -I$(KSRC)/arch/x86/include
+INCLUDES += -I$(KBLD)/arch/x86/include/generated
+INCLUDES += -I$(KBLD)/include
+INCLUDES += -I$(KSRC)/include
+INCLUDES += -I$(KSRC)/arch/x86/include/uapi
+INCLUDES += -I$(KBLD)/arch/x86/include/generated/uapi
+INCLUDES += -I$(KSRC)/include/uapi
+INCLUDES += -I$(KBLD)/include/generated/uapi
+
+SINCLUDES = -include $(KSRC)/include/linux/kconfig.h
+SINCLUDES += -include include/asm_goto_workaround.h
+
+# this is to find stdarg.h. Ubuntu has this under x86_64-linux-gnu
+# and Fedora is under x86_64-redhat-linux. Let's try 'find'.
+GCCVER=$(shell gcc -v 2>&1 | awk '{if ($$0 ~ /gcc version/) {ver=split($$3,n,"."); print n[1]}}')
+GCC_INC=$(shell find /usr/lib/gcc/x86_64-*linux*/$(GCCVER) -name include)
+NOSTDINC_FLAGS = -nostdinc -isystem $(GCC_INC)
+
+all: build $(MODS)
+
+build:
+ @mkdir -p $(OBJDIR)
+
+$(OBJDIR)%.o: %.c
+ $(QUIET_CLANG)$(CLANG) $(NOSTDINC_FLAGS) $(INCLUDES) \
+ $(SINCLUDES) $(DEFS) $(CFLAGS) \
+ -O2 -emit-llvm $(CLANG_FLAGS) -c $< -o $@.cl
+ $(QUIET_LLC)$(LLC) -march=bpf $(LLC_FLAGS) -filetype=obj -o $@ $@.cl
+ @rm $@.cl
+
+clean:
+ @rm -rf $(OBJDIR)
diff --git a/src/bpfswitch/ksrc/include/asm_goto_workaround.h b/src/bpfswitch/ksrc/include/asm_goto_workaround.h
new file mode 100644
index 00000000..7048bb35
--- /dev/null
+++ b/src/bpfswitch/ksrc/include/asm_goto_workaround.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2019 Facebook */
+#ifndef __ASM_GOTO_WORKAROUND_H
+#define __ASM_GOTO_WORKAROUND_H
+
+/*
+ * This will bring in asm_volatile_goto and asm_inline macro definitions
+ * if enabled by compiler and config options.
+ */
+#include <linux/types.h>
+
+#ifdef asm_volatile_goto
+#undef asm_volatile_goto
+#define asm_volatile_goto(x...) asm volatile("invalid use of asm_volatile_goto")
+#endif
+
+/*
+ * asm_inline is defined as asm __inline in "include/linux/compiler_types.h"
+ * if supported by the kernel's CC (i.e CONFIG_CC_HAS_ASM_INLINE) which is not
+ * supported by CLANG.
+ */
+#ifdef asm_inline
+#undef asm_inline
+#define asm_inline asm
+#endif
+
+#define volatile(x...) volatile("")
+#endif
diff --git a/src/bpfswitch/ksrc/xdp_dummy.c b/src/bpfswitch/ksrc/xdp_dummy.c
new file mode 100644
index 00000000..d60c1c07
--- /dev/null
+++ b/src/bpfswitch/ksrc/xdp_dummy.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Dummy XDP program
+ *
+ * David Ahern <dsahern@gmail.com>
+ */
+#define KBUILD_MODNAME "xdp_dummy"
+#include <uapi/linux/bpf.h>
+#include <linux/version.h>
+#include <bpf/bpf_helpers.h>
+
+SEC("xdp_dummy")
+int xdp_dummy_prog(struct xdp_md *ctx)
+{
+ //bpf_debug("ingress: device %u queue %u\n",
+ // ctx->ingress_ifindex, ctx->rx_queue_index);
+
+ return XDP_PASS;
+}
+
+char _license[] SEC("license") = "GPL";
+int _version SEC("version") = LINUX_VERSION_CODE;
diff --git a/src/bpfswitch/ksrc/xdp_l2fwd.c b/src/bpfswitch/ksrc/xdp_l2fwd.c
new file mode 100644
index 00000000..750b580d
--- /dev/null
+++ b/src/bpfswitch/ksrc/xdp_l2fwd.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Example of L2 forwarding via XDP. FDB is a <vlan,dmac> hash table
+ * returning device index to redirect packet.
+ *
+ * Copyright (c) 2019-2020 David Ahern <dsahern@gmail.com>
+ * The code is modified to use 2 mac addresses as key instead of
+ * mac address and vlan id as key
+ *
+ */
+#define KBUILD_MODNAME "xdp_l2fwd"
+#include <uapi/linux/bpf.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/if_vlan.h>
+#include <linux/version.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_endian.h>
+
+#include "xdp_fdb.h"
+
+/* For TX-traffic redirect requires net_device ifindex to be in this devmap */
+struct bpf_map_def SEC("maps") xdp_fwd_ports = {
+ .type = BPF_MAP_TYPE_DEVMAP_HASH,
+ .key_size = sizeof(u32),
+ .value_size = sizeof(struct bpf_devmap_val),
+ .max_entries = 512,
+};
+
+/* <vlan,dmac> to device index map */
+struct bpf_map_def SEC("maps") fdb_map = {
+ .type = BPF_MAP_TYPE_HASH,
+ .key_size = sizeof(struct fdb_key),
+ .value_size = sizeof(u32),
+ .max_entries = 512,
+};
+
+SEC("xdp_l2fwd")
+int xdp_l2fwd_prog(struct xdp_md *ctx)
+{
+ void *data_end = (void *)(long)ctx->data_end;
+ void *data = (void *)(long)ctx->data;
+ struct bpf_devmap_val *entry;
+ struct vlan_hdr *vhdr = NULL;
+ struct ethhdr *eth;
+ struct fdb_key key;
+ u8 smac[ETH_ALEN];
+ u16 h_proto = 0;
+ void *nh;
+ int rc;
+
+ /* data in context points to ethernet header */
+ eth = data;
+
+ /* set pointer to header after ethernet header */
+ nh = data + sizeof(*eth);
+ if (nh > data_end)
+ return XDP_DROP; // malformed packet
+
+ __builtin_memset(&key, 0, sizeof(key));
+ __builtin_memcpy(key.dmac, eth->h_dest, ETH_ALEN);
+ __builtin_memcpy(key.smac, eth->h_source, ETH_ALEN);
+
+ if (eth->h_proto == htons(ETH_P_8021Q)) {
+ vhdr = nh;
+ if (vhdr + 1 > data_end)
+ return XDP_DROP; // malformed packet
+
+ //key.vlan = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK;
+ }
+
+ entry = bpf_map_lookup_elem(&fdb_map, &key);
+ if (!entry || entry->ifindex == 0)
+ return XDP_PASS;
+
+ /* Verify redirect index exists in port map */
+ if (!bpf_map_lookup_elem(&xdp_fwd_ports, &entry->ifindex))
+ return XDP_PASS;
+
+ if (vhdr) {
+ /* remove VLAN header before hand off to VM */
+ h_proto = vhdr->h_vlan_encapsulated_proto;
+ __builtin_memcpy(smac, eth->h_source, ETH_ALEN);
+
+ if (bpf_xdp_adjust_head(ctx, sizeof(*vhdr)))
+ return XDP_PASS;
+
+ /* reset data pointers after adjust */
+ data = (void *)(long)ctx->data;
+ data_end = (void *)(long)ctx->data_end;
+ eth = data;
+ if (eth + 1 > data_end)
+ return XDP_DROP;
+
+ __builtin_memcpy(eth->h_dest, key.dmac, ETH_ALEN);
+ __builtin_memcpy(eth->h_source, smac, ETH_ALEN);
+ eth->h_proto = h_proto;
+ }
+
+ return bpf_redirect_map(&xdp_fwd_ports, entry->ifindex, 0);
+}
+
+char _license[] SEC("license") = "GPL";
+int _version SEC("version") = LINUX_VERSION_CODE;