af_xdp: AF_XDP input plugin

Type: feature

Change-Id: I85aa4ad6b68c1aa0e51938002dc691a4b11c545c
Signed-off-by: Damjan Marion <damarion@cisco.com>
Signed-off-by: Benoît Ganne <bganne@cisco.com>
diff --git a/extras/bpf/Makefile b/extras/bpf/Makefile
new file mode 100644
index 0000000..77b0643
--- /dev/null
+++ b/extras/bpf/Makefile
@@ -0,0 +1,17 @@
+CC?=clang
+# where to find bpf includes?
+BPF_ROOT?=/usr/include
+#BPF_ROOT?=/opt/vpp/external/x86_64/include
+
+CFLAGS:=-O3 -g -Wextra -Wall -target bpf
+# Workaround for Ubuntu/Debian for asm/types.h
+CFLAGS+= -I/usr/include/x86_64-linux-gnu
+CFLAGS+= -I$(BPF_ROOT)
+#CFLAGS+= -DDEBUG
+
+all: af_xdp.bpf.o
+
+clean:
+	$(RM) af_xdp.bpf.o
+
+.PHONY: all clean
diff --git a/extras/bpf/af_xdp.bpf.c b/extras/bpf/af_xdp.bpf.c
new file mode 100644
index 0000000..eddd2b0
--- /dev/null
+++ b/extras/bpf/af_xdp.bpf.c
@@ -0,0 +1,88 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0 OR Apache-2.0
+ * Dual-licensed under GPL version 2.0 or Apache License version 2.0
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ */
+#include <linux/bpf.h>
+#include <linux/in.h>
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <bpf/bpf_helpers.h>
+
+/*
+ * when compiled, debug print can be viewed with eg.
+ * sudo cat /sys/kernel/debug/tracing/trace_pipe
+ */
+#ifdef DEBUG
+#define s__(n)   # n
+#define s_(n)    s__(n)
+#define x_(fmt)  __FILE__ ":" s_(__LINE__) ": " fmt "\n"
+#define DEBUG_PRINT_(fmt, ...) do { \
+    const char fmt__[] = fmt; \
+    bpf_trace_printk(fmt__, sizeof(fmt), ## __VA_ARGS__); } while(0)
+#define DEBUG_PRINT(fmt, ...)   DEBUG_PRINT_ (x_(fmt), ## __VA_ARGS__)
+#else   /* DEBUG */
+#define DEBUG_PRINT(fmt, ...)
+#endif  /* DEBUG */
+
+#define ntohs(x)        __constant_ntohs(x)
+
+SEC("maps")
+struct bpf_map_def xsks_map = {
+    .type = BPF_MAP_TYPE_XSKMAP,
+    .key_size = sizeof(int),
+    .value_size = sizeof(int),
+    .max_entries = 64, /* max 64 queues per device */
+};
+
+SEC("xdp_sock")
+int xdp_sock_prog(struct xdp_md *ctx) {
+    const void *data = (void *)(long)ctx->data;
+    const void *data_end = (void *)(long)ctx->data_end;
+
+    DEBUG_PRINT("rx %ld bytes packet", (long)data_end - (long)data);
+
+    /* smallest packet we are interesting in is ip-ip */
+    if (data + sizeof(struct ethhdr) + 2 * sizeof(struct iphdr) > data_end) {
+        DEBUG_PRINT("packet too small");
+        return XDP_PASS;
+    }
+
+    const struct ethhdr *eth = data;
+    if (eth->h_proto != ntohs(ETH_P_IP)) {
+        DEBUG_PRINT("unsupported eth proto %x", (int)eth->h_proto);
+        return XDP_PASS;
+    }
+
+    const struct iphdr *ip = (void *)(eth + 1);
+    switch (ip->protocol) {
+      case IPPROTO_UDP: {
+            const struct udphdr *udp = (void *)(ip + 1);
+            if (udp->dest != ntohs(4789)) { /* VxLAN dest port */
+                DEBUG_PRINT("unsupported udp dst port %x", (int)udp->dest);
+                return XDP_PASS;
+            }
+          }
+      case IPPROTO_IPIP:
+      case IPPROTO_ESP:
+        break;
+      default:
+        DEBUG_PRINT("unsupported ip proto %x", (int)ip->protocol);
+        return XDP_PASS;
+    }
+
+    int qid = ctx->rx_queue_index;
+    if (!bpf_map_lookup_elem(&xsks_map, &qid))
+      {
+        DEBUG_PRINT("no socket found");
+        return XDP_PASS;
+      }
+
+    DEBUG_PRINT("going to socket %d", qid);
+    return bpf_redirect_map(&xsks_map, qid, 0);
+}
+
+/* actually Dual GPLv2/Apache2, but GPLv2 as far as kernel is concerned */
+SEC("license")
+char _license[] = "GPL";