mirror of
https://github.com/xdp-project/bpf-examples.git
synced 2024-05-06 15:54:53 +00:00
Initial import with encap-forward example
Signed-off-by: Toke Høiland-Jørgensen <[email protected]>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
config.mk
|
||||
.ccls-cache
|
||||
compile_commands.json
|
||||
*.ll
|
||||
*.o
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "lib/libbpf"]
|
||||
path = lib/libbpf
|
||||
url = https://github.com/xdp-project/libbpf.git
|
||||
@@ -0,0 +1,62 @@
|
||||
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Top level Makefile for xdp-tools
|
||||
|
||||
ifeq ("$(origin V)", "command line")
|
||||
VERBOSE = $(V)
|
||||
endif
|
||||
ifndef VERBOSE
|
||||
VERBOSE = 0
|
||||
endif
|
||||
|
||||
ifeq ($(VERBOSE),0)
|
||||
MAKEFLAGS += --no-print-directory
|
||||
endif
|
||||
|
||||
SUBDIRS := encap-forward
|
||||
.PHONY: check_submodule help clobber distclean clean $(SUBDIRS)
|
||||
|
||||
all: $(SUBDIRS)
|
||||
|
||||
lib: config.mk check_submodule
|
||||
@echo; echo $@; $(MAKE) -C $@
|
||||
|
||||
$(SUBDIRS):
|
||||
@echo; echo $@; $(MAKE) -C $@
|
||||
|
||||
help:
|
||||
@echo "Make Targets:"
|
||||
@echo " all - build binaries"
|
||||
@echo " clean - remove products of build"
|
||||
@echo " distclean - remove configuration and build"
|
||||
@echo " install - install binaries on local machine"
|
||||
@echo " test - run test suite"
|
||||
@echo " archive - create tarball of all sources"
|
||||
@echo ""
|
||||
@echo "Make Arguments:"
|
||||
@echo " V=[0|1] - set build verbosity level"
|
||||
|
||||
config.mk:
|
||||
sh configure
|
||||
|
||||
check_submodule:
|
||||
@if [ -d .git ] && `git submodule status lib/libbpf | grep -q '^+'`; then \
|
||||
echo "" ;\
|
||||
echo "** WARNING **: git submodule SHA-1 out-of-sync" ;\
|
||||
echo " consider running: git submodule update" ;\
|
||||
echo "" ;\
|
||||
fi\
|
||||
|
||||
clobber:
|
||||
touch config.mk
|
||||
$(MAKE) clean
|
||||
rm -f config.mk cscope.* compile_commands.json
|
||||
|
||||
distclean: clobber
|
||||
|
||||
clean: check_submodule
|
||||
@for i in $(SUBDIRS); \
|
||||
do $(MAKE) -C $$i clean; done
|
||||
|
||||
compile_commands.json: clean
|
||||
compiledb make V=1
|
||||
@@ -0,0 +1,206 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# This is not an autoconf generated configure
|
||||
#
|
||||
|
||||
# Output file which is input to Makefile
|
||||
CONFIG_FINAL=config.mk
|
||||
CONFIG=".${CONFIG}.tmp"
|
||||
|
||||
# Make a temp directory in build tree.
|
||||
TMPDIR=$(mktemp -d config.XXXXXX)
|
||||
trap 'status=$?; rm -rf $TMPDIR; rm -f $CONFIG; exit $status' EXIT HUP INT QUIT TERM
|
||||
|
||||
check_toolchain()
|
||||
{
|
||||
local emacs_version
|
||||
|
||||
: ${PKG_CONFIG:=pkg-config}
|
||||
: ${CC=gcc}
|
||||
: ${CLANG=clang}
|
||||
: ${LLC=llc}
|
||||
: ${M4=m4}
|
||||
: ${EMACS=emacs}
|
||||
|
||||
for TOOL in $PKG_CONFIG $CC $CLANG $LLC $M4; do
|
||||
if [ ! $(command -v ${TOOL} 2>/dev/null) ]; then
|
||||
echo "*** ERROR: Cannot find tool ${TOOL}" ;
|
||||
exit 1;
|
||||
fi;
|
||||
done
|
||||
|
||||
if ! command -v $EMACS >/dev/null 2>&1; then
|
||||
EMACS=""
|
||||
else
|
||||
emacs_version=$($EMACS --version 2>/dev/null | head -n 1)
|
||||
if echo $emacs_version | grep -Eq 'GNU Emacs 2[67]'; then
|
||||
echo "using emacs: $emacs_version"
|
||||
else
|
||||
echo "not using emacs: $emacs_version"
|
||||
EMACS=""
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$EMACS" ] && [ "${FORCE_EMACS:-0}" -eq "1" ]; then
|
||||
echo "FORCE_EMACS is set, but no usable emacs found on system"
|
||||
rm -f "$CONFIG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "PKG_CONFIG:=${PKG_CONFIG}" >>$CONFIG
|
||||
echo "CC:=${CC}" >>$CONFIG
|
||||
echo "CLANG:=${CLANG}" >>$CONFIG
|
||||
echo "LLC:=${LLC}" >>$CONFIG
|
||||
echo "M4:=${M4}" >>$CONFIG
|
||||
echo "EMACS:=${EMACS}" >>$CONFIG
|
||||
}
|
||||
|
||||
check_elf()
|
||||
{
|
||||
if ${PKG_CONFIG} libelf --exists; then
|
||||
echo "HAVE_ELF:=y" >>$CONFIG
|
||||
echo "yes"
|
||||
|
||||
echo 'CFLAGS += -DHAVE_ELF' `${PKG_CONFIG} libelf --cflags` >> $CONFIG
|
||||
echo 'LDLIBS += ' `${PKG_CONFIG} libelf --libs` >>$CONFIG
|
||||
else
|
||||
echo "missing - this is required"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_zlib()
|
||||
{
|
||||
if ${PKG_CONFIG} zlib --exists; then
|
||||
echo "HAVE_ZLIB:=y" >>$CONFIG
|
||||
echo "yes"
|
||||
|
||||
echo 'CFLAGS += -DHAVE_ZLIB' `${PKG_CONFIG} zlib --cflags` >> $CONFIG
|
||||
echo 'LDLIBS += ' `${PKG_CONFIG} zlib --libs` >>$CONFIG
|
||||
else
|
||||
echo "missing - this is required"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_libbpf()
|
||||
{
|
||||
local libbpf_err
|
||||
|
||||
if ${PKG_CONFIG} libbpf --exists || [ -n "$LIBBPF_DIR" ]; then
|
||||
|
||||
if [ -n "$LIBBPF_DIR" ]; then
|
||||
LIBBPF_CFLAGS="-I${LIBBPF_DIR}/include -L${LIBBPF_DIR}/lib"
|
||||
LIBBPF_LDLIBS="-lbpf"
|
||||
else
|
||||
LIBBPF_CFLAGS=$(${PKG_CONFIG} libbpf --cflags)
|
||||
LIBBPF_LDLIBS=$(${PKG_CONFIG} libbpf --libs)
|
||||
fi
|
||||
|
||||
cat >$TMPDIR/libbpftest.c <<EOF
|
||||
#include <bpf/libbpf.h>
|
||||
int main(int argc, char **argv) {
|
||||
void *ptr;
|
||||
DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts, .pin_root_path = "/path");
|
||||
DECLARE_LIBBPF_OPTS(bpf_xdp_set_link_opts, lopts, .old_fd = -1);
|
||||
(void) bpf_object__open_file("file", &opts);
|
||||
(void) bpf_program__name(ptr);
|
||||
(void) bpf_map__set_initial_value(ptr, ptr, 0);
|
||||
(void) bpf_set_link_xdp_fd_opts(0, 0, 0, &lopts);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
libbpf_err=$($CC -o $TMPDIR/libbpftest $TMPDIR/libbpftest.c $LIBBPF_CFLAGS -lbpf 2>&1)
|
||||
if [ "$?" -eq "0" ]; then
|
||||
echo "SYSTEM_LIBBPF:=y" >>$CONFIG
|
||||
echo 'CFLAGS += ' $LIBBPF_CFLAGS >> $CONFIG
|
||||
echo 'LDLIBS += ' $LIBBPF_LDLIBS >>$CONFIG
|
||||
echo 'OBJECT_LIBBPF = ' >>$CONFIG
|
||||
echo system
|
||||
|
||||
echo -n "perf_buffer__consume support: "
|
||||
check_perf_consume
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
libbpf_err="${PKG_CONFIG} couldn't find libbpf"
|
||||
fi
|
||||
|
||||
if [ "${FORCE_SYSTEM_LIBBPF:-0}" -eq "1" ]; then
|
||||
echo "FORCE_SYSTEM_LIBBPF is set, but no usable libbpf found on system"
|
||||
echo "error: $libbpf_err"
|
||||
rm -f "$CONFIG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo submodule
|
||||
echo "SYSTEM_LIBBPF:=n" >> $CONFIG
|
||||
echo 'CFLAGS += -I$(LIB_DIR)/libbpf/src/root/usr/include' >>$CONFIG
|
||||
echo 'BPF_CFLAGS += -I$(LIB_DIR)/libbpf/src/root/usr/include' >>$CONFIG
|
||||
echo 'LDFLAGS += -L$(LIB_DIR)/libbpf/src' >>$CONFIG
|
||||
echo 'LDLIBS += -l:libbpf.a' >>$CONFIG
|
||||
echo 'OBJECT_LIBBPF = $(LIB_DIR)/libbpf/src/libbpf.a' >>$CONFIG
|
||||
if ! [ -d "lib/libbpf/src" ] && [ -f ".gitmodules" ] && [ -e ".git" ]; then
|
||||
git submodule init && git submodule update
|
||||
fi
|
||||
|
||||
echo -n "ELF support: "
|
||||
check_elf || exit 1
|
||||
|
||||
echo -n "zlib support: "
|
||||
check_zlib || exit 1
|
||||
|
||||
# For the build submodule library we know it does support this API, so we
|
||||
# hard code it. Also due to the fact it's hard to build a test app as
|
||||
# libbpf.a has not been build at configure time.
|
||||
echo "HAVE_LIBBPF_PERF_BUFFER__CONSUME:=y" >>"$CONFIG"
|
||||
}
|
||||
|
||||
quiet_config()
|
||||
{
|
||||
cat <<EOF
|
||||
# user can control verbosity similar to kernel builds (e.g., V=1)
|
||||
ifeq ("\$(origin V)", "command line")
|
||||
VERBOSE = \$(V)
|
||||
endif
|
||||
ifndef VERBOSE
|
||||
VERBOSE = 0
|
||||
endif
|
||||
ifeq (\$(VERBOSE),1)
|
||||
Q =
|
||||
else
|
||||
Q = @
|
||||
endif
|
||||
ifeq (\$(VERBOSE),0)
|
||||
MAKEFLAGS += --no-print-directory
|
||||
endif
|
||||
|
||||
|
||||
ifeq (\$(VERBOSE), 0)
|
||||
QUIET_CC = @echo ' CC '\$@;
|
||||
QUIET_CLANG = @echo ' CLANG '\$@;
|
||||
QUIET_LLC = @echo ' LLC '\$@;
|
||||
QUIET_LINK = @echo ' LINK '\$@;
|
||||
QUIET_INSTALL = @echo ' INSTALL '\$@;
|
||||
QUIET_M4 = @echo ' M4 '\$@;
|
||||
QUIET_GEN = @echo ' GEN '\$@;
|
||||
endif
|
||||
EOF
|
||||
}
|
||||
|
||||
echo "# Generated config" >$CONFIG
|
||||
quiet_config >> $CONFIG
|
||||
|
||||
check_toolchain
|
||||
|
||||
echo -n "libbpf support: "
|
||||
check_libbpf
|
||||
|
||||
if [ -n "$KERNEL_HEADERS" ]; then
|
||||
echo "kernel headers: $KERNEL_HEADERS"
|
||||
echo "CFLAGS += -I$KERNEL_HEADERS" >>$CONFIG
|
||||
echo "BPF_CFLAGS += -I$KERNEL_HEADERS" >>$CONFIG
|
||||
fi
|
||||
|
||||
mv $CONFIG $CONFIG_FINAL
|
||||
@@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
|
||||
USER_TARGETS :=
|
||||
BPF_TARGETS := xdp_encap tc_bpf_encap
|
||||
EXTRA_DEPS := encap.h
|
||||
|
||||
LIB_DIR = ../lib
|
||||
|
||||
include $(LIB_DIR)/common.mk
|
||||
@@ -0,0 +1,62 @@
|
||||
#define NEXTHDR_IPV6 41
|
||||
|
||||
|
||||
static void encap_ipv6(volatile void *data, volatile void *data_end)
|
||||
{
|
||||
volatile struct ipv6hdr *ip6h;
|
||||
size_t len;
|
||||
|
||||
struct ipv6hdr encap_hdr = {
|
||||
.version = 6,
|
||||
.nexthdr = NEXTHDR_IPV6,
|
||||
.hop_limit = 16,
|
||||
.saddr = { .s6_addr = { 0xfc, 0x00, 0xde, 0xad, 0xca, 0xfe,
|
||||
0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02 } },
|
||||
.daddr = { .s6_addr = { 0xfc, 0x00, 0xde, 0xad, 0xca, 0xfe,
|
||||
0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01 } },
|
||||
};
|
||||
|
||||
ip6h = data + sizeof(struct ethhdr);
|
||||
if (ip6h + 1 > data_end)
|
||||
return;
|
||||
|
||||
*ip6h = encap_hdr;
|
||||
|
||||
len = (data_end - data);
|
||||
ip6h->payload_len = bpf_htons(len - sizeof(struct ethhdr) - sizeof(*ip6h));
|
||||
}
|
||||
|
||||
static __always_inline __u16 csum_fold_helper(__u32 csum)
|
||||
{
|
||||
__u32 sum;
|
||||
sum = (csum >> 16) + (csum & 0xffff);
|
||||
sum += (sum >> 16);
|
||||
return ~sum;
|
||||
}
|
||||
|
||||
static void encap_ipv4(volatile void *data, volatile void *data_end)
|
||||
{
|
||||
volatile struct iphdr *iph;
|
||||
size_t len;
|
||||
|
||||
struct iphdr encap_hdr = {
|
||||
.version = 4,
|
||||
.ihl = 5,
|
||||
.protocol = NEXTHDR_IPV6,
|
||||
.ttl = 16,
|
||||
.saddr = bpf_htonl(0x0a0b0202),
|
||||
.daddr = bpf_htonl(0x0a0b0201),
|
||||
};
|
||||
|
||||
iph = data + sizeof(struct ethhdr);
|
||||
if (iph + 1 > data_end)
|
||||
return;
|
||||
|
||||
*iph = encap_hdr;
|
||||
|
||||
len = (data_end - data);
|
||||
iph->tot_len = bpf_htons(len - sizeof(struct ethhdr));
|
||||
iph->check = csum_fold_helper(bpf_csum_diff((__be32 *)iph, 0, (__be32 *)iph, sizeof(*iph), 0));
|
||||
}
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
|
||||
TESTENV=../lib/testenv/testenv.sh
|
||||
|
||||
if [[ "$1" == "teardown" ]] || [[ "$1" == "reset" ]]; then
|
||||
$TESTENV teardown -n bpf-encap
|
||||
[[ "$1" == "teardown" ]] && exit 0
|
||||
fi
|
||||
|
||||
$TESTENV setup -n bpf-encap --legacy-ip
|
||||
ip link add dev bpf-encap2 type veth peer name veth1 netns bpf-encap
|
||||
ip link set dev bpf-encap2 up
|
||||
ip a add dev bpf-encap2 10.11.2.1/24
|
||||
ip a add dev bpf-encap2 fc00:dead:cafe:2::1/64
|
||||
$TESTENV exec ip link set dev veth1 up
|
||||
$TESTENV exec ip a add 10.11.2.2/24 dev veth1
|
||||
$TESTENV exec ip a add fc00:dead:cafe:2::2/64 dev veth1
|
||||
$TESTENV exec -- sysctl -w net.ipv4.ip_forward=1 net.ipv6.conf.all.forwarding=1 net.ipv4.conf.veth0.rp_filter=2 net.ipv4.conf.veth0.accept_local=1
|
||||
#$TESTENV exec -- ip link set dev veth0 xdp object xdp_encap.o
|
||||
$TESTENV exec -- tc qdisc add dev veth0 clsact
|
||||
$TESTENV exec -- tc filter add dev veth0 ingress bpf da obj tc_bpf_encap.o
|
||||
@@ -0,0 +1,27 @@
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/in6.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <xdp/parsing_helpers.h>
|
||||
#include "encap.h"
|
||||
|
||||
SEC("classifier") int tc_encap(struct __sk_buff *skb)
|
||||
{
|
||||
volatile void *data, *data_end;
|
||||
int ret = BPF_DROP;
|
||||
size_t offset = sizeof(struct iphdr);
|
||||
|
||||
|
||||
if (bpf_skb_adjust_room(skb, offset, BPF_ADJ_ROOM_MAC, BPF_F_ADJ_ROOM_ENCAP_L3_IPV4))
|
||||
goto out;
|
||||
|
||||
data = (void *)(long)skb->data;
|
||||
data_end = (void *)(long)skb->data_end;
|
||||
|
||||
// encap_ipv6(ctx);
|
||||
encap_ipv4(data, data_end);
|
||||
ret = BPF_OK;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -0,0 +1,43 @@
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/in6.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <xdp/parsing_helpers.h>
|
||||
#include "encap.h"
|
||||
|
||||
|
||||
SEC("prog") int xdp_encap(struct xdp_md *ctx)
|
||||
{
|
||||
volatile struct ethhdr *ehdr, old_ehdr = {};
|
||||
volatile void *data, *data_end;
|
||||
int ret = XDP_ABORTED;
|
||||
size_t offset = sizeof(struct iphdr);
|
||||
|
||||
data = (void *)(long)ctx->data;
|
||||
data_end = (void *)(long)ctx->data_end;
|
||||
|
||||
ehdr = data;
|
||||
if (ehdr + 1 > data_end)
|
||||
goto out;
|
||||
old_ehdr = *ehdr;
|
||||
|
||||
if (bpf_xdp_adjust_head(ctx, -offset))
|
||||
goto out;
|
||||
|
||||
data = (void *)(long)ctx->data;
|
||||
data_end = (void *)(long)ctx->data_end;
|
||||
|
||||
ehdr = data;
|
||||
|
||||
if (ehdr + 1 > data_end)
|
||||
goto out;
|
||||
*ehdr = old_ehdr;
|
||||
ehdr->h_proto = bpf_htons(ETH_P_IP);
|
||||
|
||||
// encap_ipv6(data, data_end);
|
||||
encap_ipv4(data, data_end);
|
||||
ret = XDP_PASS;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -0,0 +1,120 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
#ifndef __BPF_TRACE_HELPERS_H
|
||||
#define __BPF_TRACE_HELPERS_H
|
||||
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
#define ___bpf_concat(a, b) a ## b
|
||||
#define ___bpf_apply(fn, n) ___bpf_concat(fn, n)
|
||||
#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N
|
||||
#define ___bpf_narg(...) \
|
||||
___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
#define ___bpf_empty(...) \
|
||||
___bpf_nth(_, ##__VA_ARGS__, N, N, N, N, N, N, N, N, N, N, 0)
|
||||
|
||||
#define ___bpf_ctx_cast0() ctx
|
||||
#define ___bpf_ctx_cast1(x) ___bpf_ctx_cast0(), (void *)ctx[0]
|
||||
#define ___bpf_ctx_cast2(x, args...) ___bpf_ctx_cast1(args), (void *)ctx[1]
|
||||
#define ___bpf_ctx_cast3(x, args...) ___bpf_ctx_cast2(args), (void *)ctx[2]
|
||||
#define ___bpf_ctx_cast4(x, args...) ___bpf_ctx_cast3(args), (void *)ctx[3]
|
||||
#define ___bpf_ctx_cast5(x, args...) ___bpf_ctx_cast4(args), (void *)ctx[4]
|
||||
#define ___bpf_ctx_cast6(x, args...) ___bpf_ctx_cast5(args), (void *)ctx[5]
|
||||
#define ___bpf_ctx_cast7(x, args...) ___bpf_ctx_cast6(args), (void *)ctx[6]
|
||||
#define ___bpf_ctx_cast8(x, args...) ___bpf_ctx_cast7(args), (void *)ctx[7]
|
||||
#define ___bpf_ctx_cast9(x, args...) ___bpf_ctx_cast8(args), (void *)ctx[8]
|
||||
#define ___bpf_ctx_cast10(x, args...) ___bpf_ctx_cast9(args), (void *)ctx[9]
|
||||
#define ___bpf_ctx_cast11(x, args...) ___bpf_ctx_cast10(args), (void *)ctx[10]
|
||||
#define ___bpf_ctx_cast12(x, args...) ___bpf_ctx_cast11(args), (void *)ctx[11]
|
||||
#define ___bpf_ctx_cast(args...) \
|
||||
___bpf_apply(___bpf_ctx_cast, ___bpf_narg(args))(args)
|
||||
|
||||
/*
|
||||
* BPF_PROG is a convenience wrapper for generic tp_btf/fentry/fexit and
|
||||
* similar kinds of BPF programs, that accept input arguments as a single
|
||||
* pointer to untyped u64 array, where each u64 can actually be a typed
|
||||
* pointer or integer of different size. Instead of requring user to write
|
||||
* manual casts and work with array elements by index, BPF_PROG macro
|
||||
* allows user to declare a list of named and typed input arguments in the
|
||||
* same syntax as for normal C function. All the casting is hidden and
|
||||
* performed transparently, while user code can just assume working with
|
||||
* function arguments of specified type and name.
|
||||
*
|
||||
* Original raw context argument is preserved as well as 'ctx' argument.
|
||||
* This is useful when using BPF helpers that expect original context
|
||||
* as one of the parameters (e.g., for bpf_perf_event_output()).
|
||||
*/
|
||||
#define BPF_PROG(name, args...) \
|
||||
name(unsigned long long *ctx); \
|
||||
static __always_inline typeof(name(0)) \
|
||||
____##name(unsigned long long *ctx, ##args); \
|
||||
typeof(name(0)) name(unsigned long long *ctx) \
|
||||
{ \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
return ____##name(___bpf_ctx_cast(args)); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
} \
|
||||
static __always_inline typeof(name(0)) \
|
||||
____##name(unsigned long long *ctx, ##args)
|
||||
|
||||
struct pt_regs;
|
||||
|
||||
#define ___bpf_kprobe_args0() ctx
|
||||
#define ___bpf_kprobe_args1(x) \
|
||||
___bpf_kprobe_args0(), (void *)PT_REGS_PARM1(ctx)
|
||||
#define ___bpf_kprobe_args2(x, args...) \
|
||||
___bpf_kprobe_args1(args), (void *)PT_REGS_PARM2(ctx)
|
||||
#define ___bpf_kprobe_args3(x, args...) \
|
||||
___bpf_kprobe_args2(args), (void *)PT_REGS_PARM3(ctx)
|
||||
#define ___bpf_kprobe_args4(x, args...) \
|
||||
___bpf_kprobe_args3(args), (void *)PT_REGS_PARM4(ctx)
|
||||
#define ___bpf_kprobe_args5(x, args...) \
|
||||
___bpf_kprobe_args4(args), (void *)PT_REGS_PARM5(ctx)
|
||||
#define ___bpf_kprobe_args(args...) \
|
||||
___bpf_apply(___bpf_kprobe_args, ___bpf_narg(args))(args)
|
||||
|
||||
/*
|
||||
* BPF_KPROBE serves the same purpose for kprobes as BPF_PROG for
|
||||
* tp_btf/fentry/fexit BPF programs. It hides the underlying platform-specific
|
||||
* low-level way of getting kprobe input arguments from struct pt_regs, and
|
||||
* provides a familiar typed and named function arguments syntax and
|
||||
* semantics of accessing kprobe input paremeters.
|
||||
*
|
||||
* Original struct pt_regs* context is preserved as 'ctx' argument. This might
|
||||
* be necessary when using BPF helpers like bpf_perf_event_output().
|
||||
*/
|
||||
#define BPF_KPROBE(name, args...) \
|
||||
name(struct pt_regs *ctx); \
|
||||
static __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args);\
|
||||
typeof(name(0)) name(struct pt_regs *ctx) \
|
||||
{ \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
return ____##name(___bpf_kprobe_args(args)); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
} \
|
||||
static __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args)
|
||||
|
||||
#define ___bpf_kretprobe_args0() ctx
|
||||
#define ___bpf_kretprobe_argsN(x, args...) \
|
||||
___bpf_kprobe_args(args), (void *)PT_REGS_RET(ctx)
|
||||
#define ___bpf_kretprobe_args(args...) \
|
||||
___bpf_apply(___bpf_kretprobe_args, ___bpf_empty(args))(args)
|
||||
|
||||
/*
|
||||
* BPF_KRETPROBE is similar to BPF_KPROBE, except, in addition to listing all
|
||||
* input kprobe arguments, one last extra argument has to be specified, which
|
||||
* captures kprobe return value.
|
||||
*/
|
||||
#define BPF_KRETPROBE(name, args...) \
|
||||
name(struct pt_regs *ctx); \
|
||||
static __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args);\
|
||||
typeof(name(0)) name(struct pt_regs *ctx) \
|
||||
{ \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
return ____##name(___bpf_kretprobe_args(args)); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
} \
|
||||
static __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args)
|
||||
#endif
|
||||
+4201
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,172 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/* Copyright (c) 2018 Facebook */
|
||||
#ifndef __LINUX_BTF_H__
|
||||
#define __LINUX_BTF_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define BTF_MAGIC 0xeB9F
|
||||
#define BTF_VERSION 1
|
||||
|
||||
struct btf_header {
|
||||
__u16 magic;
|
||||
__u8 version;
|
||||
__u8 flags;
|
||||
__u32 hdr_len;
|
||||
|
||||
/* All offsets are in bytes relative to the end of this header */
|
||||
__u32 type_off; /* offset of type section */
|
||||
__u32 type_len; /* length of type section */
|
||||
__u32 str_off; /* offset of string section */
|
||||
__u32 str_len; /* length of string section */
|
||||
};
|
||||
|
||||
/* Max # of type identifier */
|
||||
#define BTF_MAX_TYPE 0x000fffff
|
||||
/* Max offset into the string section */
|
||||
#define BTF_MAX_NAME_OFFSET 0x00ffffff
|
||||
/* Max # of struct/union/enum members or func args */
|
||||
#define BTF_MAX_VLEN 0xffff
|
||||
|
||||
struct btf_type {
|
||||
__u32 name_off;
|
||||
/* "info" bits arrangement
|
||||
* bits 0-15: vlen (e.g. # of struct's members)
|
||||
* bits 16-23: unused
|
||||
* bits 24-27: kind (e.g. int, ptr, array...etc)
|
||||
* bits 28-30: unused
|
||||
* bit 31: kind_flag, currently used by
|
||||
* struct, union and fwd
|
||||
*/
|
||||
__u32 info;
|
||||
/* "size" is used by INT, ENUM, STRUCT, UNION and DATASEC.
|
||||
* "size" tells the size of the type it is describing.
|
||||
*
|
||||
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
|
||||
* FUNC, FUNC_PROTO and VAR.
|
||||
* "type" is a type_id referring to another type.
|
||||
*/
|
||||
union {
|
||||
__u32 size;
|
||||
__u32 type;
|
||||
};
|
||||
};
|
||||
|
||||
#define BTF_INFO_KIND(info) (((info) >> 24) & 0x0f)
|
||||
#define BTF_INFO_VLEN(info) ((info) & 0xffff)
|
||||
#define BTF_INFO_KFLAG(info) ((info) >> 31)
|
||||
|
||||
#define BTF_KIND_UNKN 0 /* Unknown */
|
||||
#define BTF_KIND_INT 1 /* Integer */
|
||||
#define BTF_KIND_PTR 2 /* Pointer */
|
||||
#define BTF_KIND_ARRAY 3 /* Array */
|
||||
#define BTF_KIND_STRUCT 4 /* Struct */
|
||||
#define BTF_KIND_UNION 5 /* Union */
|
||||
#define BTF_KIND_ENUM 6 /* Enumeration */
|
||||
#define BTF_KIND_FWD 7 /* Forward */
|
||||
#define BTF_KIND_TYPEDEF 8 /* Typedef */
|
||||
#define BTF_KIND_VOLATILE 9 /* Volatile */
|
||||
#define BTF_KIND_CONST 10 /* Const */
|
||||
#define BTF_KIND_RESTRICT 11 /* Restrict */
|
||||
#define BTF_KIND_FUNC 12 /* Function */
|
||||
#define BTF_KIND_FUNC_PROTO 13 /* Function Proto */
|
||||
#define BTF_KIND_VAR 14 /* Variable */
|
||||
#define BTF_KIND_DATASEC 15 /* Section */
|
||||
#define BTF_KIND_MAX BTF_KIND_DATASEC
|
||||
#define NR_BTF_KINDS (BTF_KIND_MAX + 1)
|
||||
|
||||
/* For some specific BTF_KIND, "struct btf_type" is immediately
|
||||
* followed by extra data.
|
||||
*/
|
||||
|
||||
/* BTF_KIND_INT is followed by a u32 and the following
|
||||
* is the 32 bits arrangement:
|
||||
*/
|
||||
#define BTF_INT_ENCODING(VAL) (((VAL) & 0x0f000000) >> 24)
|
||||
#define BTF_INT_OFFSET(VAL) (((VAL) & 0x00ff0000) >> 16)
|
||||
#define BTF_INT_BITS(VAL) ((VAL) & 0x000000ff)
|
||||
|
||||
/* Attributes stored in the BTF_INT_ENCODING */
|
||||
#define BTF_INT_SIGNED (1 << 0)
|
||||
#define BTF_INT_CHAR (1 << 1)
|
||||
#define BTF_INT_BOOL (1 << 2)
|
||||
|
||||
/* BTF_KIND_ENUM is followed by multiple "struct btf_enum".
|
||||
* The exact number of btf_enum is stored in the vlen (of the
|
||||
* info in "struct btf_type").
|
||||
*/
|
||||
struct btf_enum {
|
||||
__u32 name_off;
|
||||
__s32 val;
|
||||
};
|
||||
|
||||
/* BTF_KIND_ARRAY is followed by one "struct btf_array" */
|
||||
struct btf_array {
|
||||
__u32 type;
|
||||
__u32 index_type;
|
||||
__u32 nelems;
|
||||
};
|
||||
|
||||
/* BTF_KIND_STRUCT and BTF_KIND_UNION are followed
|
||||
* by multiple "struct btf_member". The exact number
|
||||
* of btf_member is stored in the vlen (of the info in
|
||||
* "struct btf_type").
|
||||
*/
|
||||
struct btf_member {
|
||||
__u32 name_off;
|
||||
__u32 type;
|
||||
/* If the type info kind_flag is set, the btf_member offset
|
||||
* contains both member bitfield size and bit offset. The
|
||||
* bitfield size is set for bitfield members. If the type
|
||||
* info kind_flag is not set, the offset contains only bit
|
||||
* offset.
|
||||
*/
|
||||
__u32 offset;
|
||||
};
|
||||
|
||||
/* If the struct/union type info kind_flag is set, the
|
||||
* following two macros are used to access bitfield_size
|
||||
* and bit_offset from btf_member.offset.
|
||||
*/
|
||||
#define BTF_MEMBER_BITFIELD_SIZE(val) ((val) >> 24)
|
||||
#define BTF_MEMBER_BIT_OFFSET(val) ((val) & 0xffffff)
|
||||
|
||||
/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param".
|
||||
* The exact number of btf_param is stored in the vlen (of the
|
||||
* info in "struct btf_type").
|
||||
*/
|
||||
struct btf_param {
|
||||
__u32 name_off;
|
||||
__u32 type;
|
||||
};
|
||||
|
||||
enum {
|
||||
BTF_VAR_STATIC = 0,
|
||||
BTF_VAR_GLOBAL_ALLOCATED = 1,
|
||||
BTF_VAR_GLOBAL_EXTERN = 2,
|
||||
};
|
||||
|
||||
enum btf_func_linkage {
|
||||
BTF_FUNC_STATIC = 0,
|
||||
BTF_FUNC_GLOBAL = 1,
|
||||
BTF_FUNC_EXTERN = 2,
|
||||
};
|
||||
|
||||
/* BTF_KIND_VAR is followed by a single "struct btf_var" to describe
|
||||
* additional information related to the variable such as its linkage.
|
||||
*/
|
||||
struct btf_var {
|
||||
__u32 linkage;
|
||||
};
|
||||
|
||||
/* BTF_KIND_DATASEC is followed by multiple "struct btf_var_secinfo"
|
||||
* to describe all BTF_KIND_VAR types it contains along with it's
|
||||
* in-section offset as well as size.
|
||||
*/
|
||||
struct btf_var_secinfo {
|
||||
__u32 type;
|
||||
__u32 offset;
|
||||
__u32 size;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_BTF_H__ */
|
||||
@@ -0,0 +1,34 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
|
||||
#ifndef __LINUX_ERR_H
|
||||
#define __LINUX_ERR_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/errno.h>
|
||||
|
||||
#define MAX_ERRNO 4095
|
||||
|
||||
#define IS_ERR_VALUE(x) ((x) >= (unsigned long)-MAX_ERRNO)
|
||||
|
||||
static inline void * ERR_PTR(long error_)
|
||||
{
|
||||
return (void *) error_;
|
||||
}
|
||||
|
||||
static inline long PTR_ERR(const void *ptr)
|
||||
{
|
||||
return (long) ptr;
|
||||
}
|
||||
|
||||
static inline bool IS_ERR(const void *ptr)
|
||||
{
|
||||
return IS_ERR_VALUE((unsigned long)ptr);
|
||||
}
|
||||
|
||||
static inline bool IS_ERR_OR_NULL(const void *ptr)
|
||||
{
|
||||
return (!ptr) || IS_ERR_VALUE((unsigned long)ptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,108 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* if_xdp: XDP socket user-space interface
|
||||
* Copyright(c) 2018 Intel Corporation.
|
||||
*
|
||||
* Author(s): Björn Töpel <[email protected]>
|
||||
* Magnus Karlsson <[email protected]>
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_IF_XDP_H
|
||||
#define _LINUX_IF_XDP_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Options for the sxdp_flags field */
|
||||
#define XDP_SHARED_UMEM (1 << 0)
|
||||
#define XDP_COPY (1 << 1) /* Force copy-mode */
|
||||
#define XDP_ZEROCOPY (1 << 2) /* Force zero-copy mode */
|
||||
/* If this option is set, the driver might go sleep and in that case
|
||||
* the XDP_RING_NEED_WAKEUP flag in the fill and/or Tx rings will be
|
||||
* set. If it is set, the application need to explicitly wake up the
|
||||
* driver with a poll() (Rx and Tx) or sendto() (Tx only). If you are
|
||||
* running the driver and the application on the same core, you should
|
||||
* use this option so that the kernel will yield to the user space
|
||||
* application.
|
||||
*/
|
||||
#define XDP_USE_NEED_WAKEUP (1 << 3)
|
||||
|
||||
/* Flags for xsk_umem_config flags */
|
||||
#define XDP_UMEM_UNALIGNED_CHUNK_FLAG (1 << 0)
|
||||
|
||||
struct sockaddr_xdp {
|
||||
__u16 sxdp_family;
|
||||
__u16 sxdp_flags;
|
||||
__u32 sxdp_ifindex;
|
||||
__u32 sxdp_queue_id;
|
||||
__u32 sxdp_shared_umem_fd;
|
||||
};
|
||||
|
||||
/* XDP_RING flags */
|
||||
#define XDP_RING_NEED_WAKEUP (1 << 0)
|
||||
|
||||
struct xdp_ring_offset {
|
||||
__u64 producer;
|
||||
__u64 consumer;
|
||||
__u64 desc;
|
||||
__u64 flags;
|
||||
};
|
||||
|
||||
struct xdp_mmap_offsets {
|
||||
struct xdp_ring_offset rx;
|
||||
struct xdp_ring_offset tx;
|
||||
struct xdp_ring_offset fr; /* Fill */
|
||||
struct xdp_ring_offset cr; /* Completion */
|
||||
};
|
||||
|
||||
/* XDP socket options */
|
||||
#define XDP_MMAP_OFFSETS 1
|
||||
#define XDP_RX_RING 2
|
||||
#define XDP_TX_RING 3
|
||||
#define XDP_UMEM_REG 4
|
||||
#define XDP_UMEM_FILL_RING 5
|
||||
#define XDP_UMEM_COMPLETION_RING 6
|
||||
#define XDP_STATISTICS 7
|
||||
#define XDP_OPTIONS 8
|
||||
|
||||
struct xdp_umem_reg {
|
||||
__u64 addr; /* Start of packet data area */
|
||||
__u64 len; /* Length of packet data area */
|
||||
__u32 chunk_size;
|
||||
__u32 headroom;
|
||||
__u32 flags;
|
||||
};
|
||||
|
||||
struct xdp_statistics {
|
||||
__u64 rx_dropped; /* Dropped for reasons other than invalid desc */
|
||||
__u64 rx_invalid_descs; /* Dropped due to invalid descriptor */
|
||||
__u64 tx_invalid_descs; /* Dropped due to invalid descriptor */
|
||||
};
|
||||
|
||||
struct xdp_options {
|
||||
__u32 flags;
|
||||
};
|
||||
|
||||
/* Flags for the flags field of struct xdp_options */
|
||||
#define XDP_OPTIONS_ZEROCOPY (1 << 0)
|
||||
|
||||
/* Pgoff for mmaping the rings */
|
||||
#define XDP_PGOFF_RX_RING 0
|
||||
#define XDP_PGOFF_TX_RING 0x80000000
|
||||
#define XDP_UMEM_PGOFF_FILL_RING 0x100000000ULL
|
||||
#define XDP_UMEM_PGOFF_COMPLETION_RING 0x180000000ULL
|
||||
|
||||
/* Masks for unaligned chunks mode */
|
||||
#define XSK_UNALIGNED_BUF_OFFSET_SHIFT 48
|
||||
#define XSK_UNALIGNED_BUF_ADDR_MASK \
|
||||
((1ULL << XSK_UNALIGNED_BUF_OFFSET_SHIFT) - 1)
|
||||
|
||||
/* Rx/Tx descriptor */
|
||||
struct xdp_desc {
|
||||
__u64 addr;
|
||||
__u32 len;
|
||||
__u32 options;
|
||||
};
|
||||
|
||||
/* UMEM descriptor is __u64 */
|
||||
|
||||
#endif /* _LINUX_IF_XDP_H */
|
||||
@@ -0,0 +1,172 @@
|
||||
#ifndef _LINUX_JHASH_H
|
||||
#define _LINUX_JHASH_H
|
||||
|
||||
/* Copied from $(LINUX)/include/linux/jhash.h (kernel 4.18) */
|
||||
|
||||
/* jhash.h: Jenkins hash support.
|
||||
*
|
||||
* Copyright (C) 2006. Bob Jenkins ([email protected])
|
||||
*
|
||||
* http://burtleburtle.net/bob/hash/
|
||||
*
|
||||
* These are the credits from Bob's sources:
|
||||
*
|
||||
* lookup3.c, by Bob Jenkins, May 2006, Public Domain.
|
||||
*
|
||||
* These are functions for producing 32-bit hashes for hash table lookup.
|
||||
* hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
|
||||
* are externally useful functions. Routines to test the hash are included
|
||||
* if SELF_TEST is defined. You can use this free for any purpose. It's in
|
||||
* the public domain. It has no warranty.
|
||||
*
|
||||
* Copyright (C) 2009-2010 Jozsef Kadlecsik ([email protected])
|
||||
*/
|
||||
|
||||
static inline __u32 rol32(__u32 word, unsigned int shift)
|
||||
{
|
||||
return (word << shift) | (word >> ((-shift) & 31));
|
||||
}
|
||||
|
||||
/* copy paste of jhash from kernel sources (include/linux/jhash.h) to make sure
|
||||
* LLVM can compile it into valid sequence of BPF instructions
|
||||
*/
|
||||
#define __jhash_mix(a, b, c) \
|
||||
{ \
|
||||
a -= c; a ^= rol32(c, 4); c += b; \
|
||||
b -= a; b ^= rol32(a, 6); a += c; \
|
||||
c -= b; c ^= rol32(b, 8); b += a; \
|
||||
a -= c; a ^= rol32(c, 16); c += b; \
|
||||
b -= a; b ^= rol32(a, 19); a += c; \
|
||||
c -= b; c ^= rol32(b, 4); b += a; \
|
||||
}
|
||||
|
||||
#define __jhash_final(a, b, c) \
|
||||
{ \
|
||||
c ^= b; c -= rol32(b, 14); \
|
||||
a ^= c; a -= rol32(c, 11); \
|
||||
b ^= a; b -= rol32(a, 25); \
|
||||
c ^= b; c -= rol32(b, 16); \
|
||||
a ^= c; a -= rol32(c, 4); \
|
||||
b ^= a; b -= rol32(a, 14); \
|
||||
c ^= b; c -= rol32(b, 24); \
|
||||
}
|
||||
|
||||
#define JHASH_INITVAL 0xdeadbeef
|
||||
|
||||
typedef unsigned int u32;
|
||||
|
||||
/* jhash - hash an arbitrary key
|
||||
* @k: sequence of bytes as key
|
||||
* @length: the length of the key
|
||||
* @initval: the previous hash, or an arbitray value
|
||||
*
|
||||
* The generic version, hashes an arbitrary sequence of bytes.
|
||||
* No alignment or length assumptions are made about the input key.
|
||||
*
|
||||
* Returns the hash value of the key. The result depends on endianness.
|
||||
*/
|
||||
static inline u32 jhash(const void *key, u32 length, u32 initval)
|
||||
{
|
||||
u32 a, b, c;
|
||||
const unsigned char *k = key;
|
||||
|
||||
/* Set up the internal state */
|
||||
a = b = c = JHASH_INITVAL + length + initval;
|
||||
|
||||
/* All but the last block: affect some 32 bits of (a,b,c) */
|
||||
while (length > 12) {
|
||||
a += *(u32 *)(k);
|
||||
b += *(u32 *)(k + 4);
|
||||
c += *(u32 *)(k + 8);
|
||||
__jhash_mix(a, b, c);
|
||||
length -= 12;
|
||||
k += 12;
|
||||
}
|
||||
/* Last block: affect all 32 bits of (c) */
|
||||
switch (length) {
|
||||
case 12: c += (u32)k[11]<<24; /* fall through */
|
||||
case 11: c += (u32)k[10]<<16; /* fall through */
|
||||
case 10: c += (u32)k[9]<<8; /* fall through */
|
||||
case 9: c += k[8]; /* fall through */
|
||||
case 8: b += (u32)k[7]<<24; /* fall through */
|
||||
case 7: b += (u32)k[6]<<16; /* fall through */
|
||||
case 6: b += (u32)k[5]<<8; /* fall through */
|
||||
case 5: b += k[4]; /* fall through */
|
||||
case 4: a += (u32)k[3]<<24; /* fall through */
|
||||
case 3: a += (u32)k[2]<<16; /* fall through */
|
||||
case 2: a += (u32)k[1]<<8; /* fall through */
|
||||
case 1: a += k[0];
|
||||
__jhash_final(a, b, c);
|
||||
case 0: /* Nothing left to add */
|
||||
break;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/* jhash2 - hash an array of u32's
|
||||
* @k: the key which must be an array of u32's
|
||||
* @length: the number of u32's in the key
|
||||
* @initval: the previous hash, or an arbitray value
|
||||
*
|
||||
* Returns the hash value of the key.
|
||||
*/
|
||||
static inline u32 jhash2(const u32 *k, u32 length, u32 initval)
|
||||
{
|
||||
u32 a, b, c;
|
||||
|
||||
/* Set up the internal state */
|
||||
a = b = c = JHASH_INITVAL + (length<<2) + initval;
|
||||
|
||||
/* Handle most of the key */
|
||||
while (length > 3) {
|
||||
a += k[0];
|
||||
b += k[1];
|
||||
c += k[2];
|
||||
__jhash_mix(a, b, c);
|
||||
length -= 3;
|
||||
k += 3;
|
||||
}
|
||||
|
||||
/* Handle the last 3 u32's */
|
||||
switch (length) {
|
||||
case 3: c += k[2]; /* fall through */
|
||||
case 2: b += k[1]; /* fall through */
|
||||
case 1: a += k[0];
|
||||
__jhash_final(a, b, c);
|
||||
case 0: /* Nothing left to add */
|
||||
break;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/* __jhash_nwords - hash exactly 3, 2 or 1 word(s) */
|
||||
static inline u32 __jhash_nwords(u32 a, u32 b, u32 c, u32 initval)
|
||||
{
|
||||
a += initval;
|
||||
b += initval;
|
||||
c += initval;
|
||||
|
||||
__jhash_final(a, b, c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
|
||||
{
|
||||
return __jhash_nwords(a, b, c, initval + JHASH_INITVAL + (3 << 2));
|
||||
}
|
||||
|
||||
static inline u32 jhash_2words(u32 a, u32 b, u32 initval)
|
||||
{
|
||||
return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2));
|
||||
}
|
||||
|
||||
static inline u32 jhash_1word(u32 a, u32 initval)
|
||||
{
|
||||
return __jhash_nwords(a, 0, 0, initval + JHASH_INITVAL + (1 << 2));
|
||||
}
|
||||
|
||||
#endif /* _LINUX_JHASH_H */
|
||||
@@ -0,0 +1,77 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copied from $(LINUX)/tools/perf/perf-sys.h (kernel 4.18) */
|
||||
#ifndef _PERF_SYS_H
|
||||
#define _PERF_SYS_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/perf_event.h>
|
||||
/*
|
||||
* remove the following headers to allow for userspace program compilation
|
||||
* #include <linux/compiler.h>
|
||||
* #include <asm/barrier.h>
|
||||
*/
|
||||
#ifdef __powerpc__
|
||||
#define CPUINFO_PROC {"cpu"}
|
||||
#endif
|
||||
|
||||
#ifdef __s390__
|
||||
#define CPUINFO_PROC {"vendor_id"}
|
||||
#endif
|
||||
|
||||
#ifdef __sh__
|
||||
#define CPUINFO_PROC {"cpu type"}
|
||||
#endif
|
||||
|
||||
#ifdef __hppa__
|
||||
#define CPUINFO_PROC {"cpu"}
|
||||
#endif
|
||||
|
||||
#ifdef __sparc__
|
||||
#define CPUINFO_PROC {"cpu"}
|
||||
#endif
|
||||
|
||||
#ifdef __alpha__
|
||||
#define CPUINFO_PROC {"cpu model"}
|
||||
#endif
|
||||
|
||||
#ifdef __arm__
|
||||
#define CPUINFO_PROC {"model name", "Processor"}
|
||||
#endif
|
||||
|
||||
#ifdef __mips__
|
||||
#define CPUINFO_PROC {"cpu model"}
|
||||
#endif
|
||||
|
||||
#ifdef __arc__
|
||||
#define CPUINFO_PROC {"Processor"}
|
||||
#endif
|
||||
|
||||
#ifdef __xtensa__
|
||||
#define CPUINFO_PROC {"core ID"}
|
||||
#endif
|
||||
|
||||
#ifndef CPUINFO_PROC
|
||||
#define CPUINFO_PROC { "model name", }
|
||||
#endif
|
||||
|
||||
static inline int
|
||||
sys_perf_event_open(struct perf_event_attr *attr,
|
||||
pid_t pid, int cpu, int group_fd,
|
||||
unsigned long flags)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = syscall(__NR_perf_event_open, attr, pid, cpu,
|
||||
group_fd, flags);
|
||||
|
||||
#ifdef HAVE_ATTR_TEST
|
||||
if (unlikely(test_attr__enabled))
|
||||
test_attr__open(attr, pid, cpu, fd, group_fd, flags);
|
||||
#endif
|
||||
return fd;
|
||||
}
|
||||
|
||||
#endif /* _PERF_SYS_H */
|
||||
@@ -0,0 +1,102 @@
|
||||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
|
||||
/*
|
||||
* XDP management utility functions
|
||||
*
|
||||
* Copyright (C) 2020 Toke Høiland-Jørgensen <[email protected]>
|
||||
*/
|
||||
|
||||
#ifndef __LIBXDP_LIBXDP_H
|
||||
#define __LIBXDP_LIBXDP_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include "xdp_helpers.h"
|
||||
|
||||
#define XDP_BPFFS_ENVVAR "LIBXDP_BPFFS"
|
||||
#define XDP_OBJECT_ENVVAR "LIBXDP_OBJECT_PATH"
|
||||
|
||||
enum xdp_attach_mode {
|
||||
XDP_MODE_UNSPEC = 0,
|
||||
XDP_MODE_NATIVE,
|
||||
XDP_MODE_SKB,
|
||||
XDP_MODE_HW
|
||||
};
|
||||
|
||||
/* This is compatible with libbpf logging levels */
|
||||
enum libxdp_print_level {
|
||||
LIBXDP_WARN,
|
||||
LIBXDP_INFO,
|
||||
LIBXDP_DEBUG,
|
||||
};
|
||||
typedef int (*libxdp_print_fn_t)(enum libxdp_print_level level,
|
||||
const char *, va_list ap);
|
||||
|
||||
libxdp_print_fn_t libxdp_set_print(libxdp_print_fn_t fn);
|
||||
|
||||
|
||||
struct xdp_program;
|
||||
struct xdp_multiprog;
|
||||
|
||||
long libxdp_get_error(const void *ptr);
|
||||
int libxdp_strerror(int err, char *buf, size_t size);
|
||||
|
||||
|
||||
struct xdp_program *xdp_program__from_bpf_obj(struct bpf_object *obj,
|
||||
const char *section_name);
|
||||
struct xdp_program *xdp_program__find_file(const char *filename,
|
||||
const char *section_name,
|
||||
struct bpf_object_open_opts *opts);
|
||||
struct xdp_program *xdp_program__open_file(const char *filename,
|
||||
const char *section_name,
|
||||
struct bpf_object_open_opts *opts);
|
||||
struct xdp_program *xdp_program__from_fd(int fd);
|
||||
struct xdp_program *xdp_program__from_id(__u32 prog_id);
|
||||
struct xdp_program *xdp_program__from_pin(const char *pin_path);
|
||||
|
||||
void xdp_program__close(struct xdp_program *xdp_prog);
|
||||
|
||||
enum xdp_attach_mode xdp_program__is_attached(const struct xdp_program *xdp_prog,
|
||||
int ifindex);
|
||||
const char *xdp_program__name(const struct xdp_program *xdp_prog);
|
||||
const unsigned char *xdp_program__tag(const struct xdp_program *xdp_prog);
|
||||
struct bpf_object *xdp_program__bpf_obj(struct xdp_program *xdp_prog);
|
||||
const struct btf *xdp_program__btf(struct xdp_program *xdp_prog);
|
||||
uint32_t xdp_program__id(const struct xdp_program *xdp_prog);
|
||||
int xdp_program__fd(const struct xdp_program *xdp_prog);
|
||||
unsigned int xdp_program__run_prio(const struct xdp_program *xdp_prog);
|
||||
int xdp_program__set_run_prio(struct xdp_program *xdp_prog,
|
||||
unsigned int run_prio);
|
||||
bool xdp_program__chain_call_enabled(const struct xdp_program *xdp_prog,
|
||||
enum xdp_action action);
|
||||
int xdp_program__set_chain_call_enabled(struct xdp_program *prog,
|
||||
unsigned int action,
|
||||
bool enabled);
|
||||
int xdp_program__print_chain_call_actions(const struct xdp_program *prog,
|
||||
char *buf,
|
||||
size_t buf_len);
|
||||
int xdp_program__pin(struct xdp_program *xdp_prog, const char *pin_path);
|
||||
int xdp_program__attach(struct xdp_program *xdp_prog,
|
||||
int ifindex, enum xdp_attach_mode mode,
|
||||
unsigned int flags);
|
||||
int xdp_program__attach_multi(struct xdp_program **progs, size_t num_progs,
|
||||
int ifindex, enum xdp_attach_mode mode,
|
||||
unsigned int flags);
|
||||
int xdp_program__detach(struct xdp_program *xdp_prog,
|
||||
int ifindex, enum xdp_attach_mode mode,
|
||||
unsigned int flags);
|
||||
int xdp_program__detach_multi(struct xdp_program **progs, size_t num_progs,
|
||||
int ifindex, enum xdp_attach_mode mode,
|
||||
unsigned int flags);
|
||||
|
||||
struct xdp_multiprog *xdp_multiprog__get_from_ifindex(int ifindex);
|
||||
struct xdp_program *xdp_multiprog__next_prog(const struct xdp_program *prog,
|
||||
const struct xdp_multiprog *mp);
|
||||
void xdp_multiprog__close(struct xdp_multiprog *mp);
|
||||
int xdp_multiprog__detach(struct xdp_multiprog *mp);
|
||||
enum xdp_attach_mode xdp_multiprog__attach_mode(const struct xdp_multiprog *mp);
|
||||
struct xdp_program *xdp_multiprog__main_prog(const struct xdp_multiprog *mp);
|
||||
bool xdp_multiprog__is_legacy(const struct xdp_multiprog *mp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,279 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-clause) */
|
||||
/*
|
||||
* This file contains parsing functions that can be used in eXDP programs. The
|
||||
* functions are marked as __always_inline, and fully defined in this header
|
||||
* file to be included in the BPF program.
|
||||
*
|
||||
* Each helper parses a packet header, including doing bounds checking, and
|
||||
* returns the type of its contents if successful, and -1 otherwise.
|
||||
*
|
||||
* For Ethernet and IP headers, the content type is the type of the payload
|
||||
* (h_proto for Ethernet, nexthdr for IPv6), for ICMP it is the ICMP type field.
|
||||
* All return values are in host byte order.
|
||||
*/
|
||||
|
||||
#ifndef __PARSING_HELPERS_H
|
||||
#define __PARSING_HELPERS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/icmpv6.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/in.h>
|
||||
#include <bpf/bpf_endian.h>
|
||||
|
||||
/* Header cursor to keep track of current parsing position */
|
||||
struct hdr_cursor {
|
||||
void *pos;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct vlan_hdr - vlan header
|
||||
* @h_vlan_TCI: priority and VLAN ID
|
||||
* @h_vlan_encapsulated_proto: packet type ID or len
|
||||
*/
|
||||
struct vlan_hdr {
|
||||
__be16 h_vlan_TCI;
|
||||
__be16 h_vlan_encapsulated_proto;
|
||||
};
|
||||
|
||||
/*
|
||||
* Struct icmphdr_common represents the common part of the icmphdr and icmp6hdr
|
||||
* structures.
|
||||
*/
|
||||
struct icmphdr_common {
|
||||
__u8 type;
|
||||
__u8 code;
|
||||
__sum16 cksum;
|
||||
};
|
||||
|
||||
/* Allow users of header file to redefine VLAN max depth */
|
||||
#ifndef VLAN_MAX_DEPTH
|
||||
#define VLAN_MAX_DEPTH 4
|
||||
#endif
|
||||
|
||||
/* Longest chain of IPv6 extension headers to resolve */
|
||||
#ifndef IPV6_EXT_MAX_CHAIN
|
||||
#define IPV6_EXT_MAX_CHAIN 6
|
||||
#endif
|
||||
|
||||
|
||||
static __always_inline int proto_is_vlan(__u16 h_proto)
|
||||
{
|
||||
return !!(h_proto == bpf_htons(ETH_P_8021Q) ||
|
||||
h_proto == bpf_htons(ETH_P_8021AD));
|
||||
}
|
||||
|
||||
/* Notice, parse_ethhdr() will skip VLAN tags, by advancing nh->pos and returns
|
||||
* next header EtherType, BUT the ethhdr pointer supplied still points to the
|
||||
* Ethernet header. Thus, caller can look at eth->h_proto to see if this was a
|
||||
* VLAN tagged packet.
|
||||
*/
|
||||
static __always_inline int parse_ethhdr(struct hdr_cursor *nh, void *data_end,
|
||||
struct ethhdr **ethhdr)
|
||||
{
|
||||
struct ethhdr *eth = nh->pos;
|
||||
struct vlan_hdr *vlh;
|
||||
__u16 h_proto;
|
||||
int i;
|
||||
|
||||
if (eth + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
nh->pos = eth + 1;
|
||||
*ethhdr = eth;
|
||||
vlh = nh->pos;
|
||||
h_proto = eth->h_proto;
|
||||
|
||||
/* Use loop unrolling to avoid the verifier restriction on loops;
|
||||
* support up to VLAN_MAX_DEPTH layers of VLAN encapsulation.
|
||||
*/
|
||||
#pragma unroll
|
||||
for (i = 0; i < VLAN_MAX_DEPTH; i++) {
|
||||
if (!proto_is_vlan(h_proto))
|
||||
break;
|
||||
|
||||
if (vlh + 1 > data_end)
|
||||
break;
|
||||
|
||||
h_proto = vlh->h_vlan_encapsulated_proto;
|
||||
vlh++;
|
||||
}
|
||||
|
||||
nh->pos = vlh;
|
||||
return h_proto; /* network-byte-order */
|
||||
}
|
||||
|
||||
static __always_inline int skip_ip6hdrext(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
__u8 next_hdr_type)
|
||||
{
|
||||
for (int i = 0; i < IPV6_EXT_MAX_CHAIN; ++i) {
|
||||
struct ipv6_opt_hdr *hdr = nh->pos;
|
||||
|
||||
if (hdr + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
switch (next_hdr_type) {
|
||||
case IPPROTO_HOPOPTS:
|
||||
case IPPROTO_DSTOPTS:
|
||||
case IPPROTO_ROUTING:
|
||||
case IPPROTO_MH:
|
||||
nh->pos = (char *)hdr + (hdr->hdrlen + 1) * 8;
|
||||
next_hdr_type = hdr->nexthdr;
|
||||
break;
|
||||
case IPPROTO_AH:
|
||||
nh->pos = (char *)hdr + (hdr->hdrlen + 2) * 4;
|
||||
next_hdr_type = hdr->nexthdr;
|
||||
break;
|
||||
case IPPROTO_FRAGMENT:
|
||||
nh->pos = (char *)hdr + 8;
|
||||
next_hdr_type = hdr->nexthdr;
|
||||
break;
|
||||
default:
|
||||
/* Found a header that is not an IPv6 extension header */
|
||||
return next_hdr_type;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static __always_inline int parse_ip6hdr(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
struct ipv6hdr **ip6hdr)
|
||||
{
|
||||
struct ipv6hdr *ip6h = nh->pos;
|
||||
|
||||
/* Pointer-arithmetic bounds check; pointer +1 points to after end of
|
||||
* thing being pointed to. We will be using this style in the remainder
|
||||
* of the tutorial.
|
||||
*/
|
||||
if (ip6h + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
nh->pos = ip6h + 1;
|
||||
*ip6hdr = ip6h;
|
||||
|
||||
return skip_ip6hdrext(nh, data_end, ip6h->nexthdr);
|
||||
}
|
||||
|
||||
static __always_inline int parse_iphdr(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
struct iphdr **iphdr)
|
||||
{
|
||||
struct iphdr *iph = nh->pos;
|
||||
int hdrsize;
|
||||
|
||||
if (iph + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
hdrsize = iph->ihl * 4;
|
||||
|
||||
/* Variable-length IPv4 header, need to use byte-based arithmetic */
|
||||
if (nh->pos + hdrsize > data_end)
|
||||
return -1;
|
||||
|
||||
nh->pos += hdrsize;
|
||||
*iphdr = iph;
|
||||
|
||||
return iph->protocol;
|
||||
}
|
||||
|
||||
static __always_inline int parse_icmp6hdr(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
struct icmp6hdr **icmp6hdr)
|
||||
{
|
||||
struct icmp6hdr *icmp6h = nh->pos;
|
||||
|
||||
if (icmp6h + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
nh->pos = icmp6h + 1;
|
||||
*icmp6hdr = icmp6h;
|
||||
|
||||
return icmp6h->icmp6_type;
|
||||
}
|
||||
|
||||
static __always_inline int parse_icmphdr(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
struct icmphdr **icmphdr)
|
||||
{
|
||||
struct icmphdr *icmph = nh->pos;
|
||||
|
||||
if (icmph + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
nh->pos = icmph + 1;
|
||||
*icmphdr = icmph;
|
||||
|
||||
return icmph->type;
|
||||
}
|
||||
|
||||
static __always_inline int parse_icmphdr_common(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
struct icmphdr_common **icmphdr)
|
||||
{
|
||||
struct icmphdr_common *h = nh->pos;
|
||||
|
||||
if (h + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
nh->pos = h + 1;
|
||||
*icmphdr = h;
|
||||
|
||||
return h->type;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse_udphdr: parse the udp header and return the length of the udp payload
|
||||
*/
|
||||
static __always_inline int parse_udphdr(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
struct udphdr **udphdr)
|
||||
{
|
||||
int len;
|
||||
struct udphdr *h = nh->pos;
|
||||
|
||||
if (h + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
nh->pos = h + 1;
|
||||
*udphdr = h;
|
||||
|
||||
len = bpf_ntohs(h->len) - sizeof(struct udphdr);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse_tcphdr: parse and return the length of the tcp header
|
||||
*/
|
||||
static __always_inline int parse_tcphdr(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
struct tcphdr **tcphdr)
|
||||
{
|
||||
int len;
|
||||
struct tcphdr *h = nh->pos;
|
||||
|
||||
if (h + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
len = h->doff * 4;
|
||||
if ((void *) h + len > data_end)
|
||||
return -1;
|
||||
|
||||
nh->pos = h + 1;
|
||||
*tcphdr = h;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
#endif /* __PARSING_HELPERS_H */
|
||||
@@ -0,0 +1,27 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-clause) */
|
||||
|
||||
#ifndef __PROG_DISPATCHER_H
|
||||
#define __PROG_DISPATCHER_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define XDP_METADATA_SECTION "xdp_metadata"
|
||||
#define XDP_DISPATCHER_VERSION 1
|
||||
/* default retval for dispatcher corresponds to the highest bit in the
|
||||
* chain_call_actions bitmap; we use this to make sure the dispatcher always
|
||||
* continues the calls chain if a function does not have an freplace program
|
||||
* attached.
|
||||
*/
|
||||
#define XDP_DISPATCHER_RETVAL 31
|
||||
|
||||
#ifndef MAX_DISPATCHER_ACTIONS
|
||||
#define MAX_DISPATCHER_ACTIONS 10
|
||||
#endif
|
||||
|
||||
struct xdp_dispatcher_config {
|
||||
__u8 num_progs_enabled;
|
||||
__u32 chain_call_actions[MAX_DISPATCHER_ACTIONS];
|
||||
__u32 run_prios[MAX_DISPATCHER_ACTIONS];
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-clause) */
|
||||
|
||||
#ifndef __XDP_HELPERS_H
|
||||
#define __XDP_HELPERS_H
|
||||
|
||||
#define _CONCAT(x,y) x ## y
|
||||
#define XDP_RUN_CONFIG(f) _CONCAT(_,f) SEC(".xdp_run_config")
|
||||
|
||||
#define XDP_DEFAULT_RUN_PRIO 50
|
||||
#define XDP_DEFAULT_CHAIN_CALL_ACTIONS (1<<XDP_PASS)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/* Used *ONLY* by BPF-prog running kernel side. */
|
||||
#ifndef __XDP_STATS_KERN_H
|
||||
#define __XDP_STATS_KERN_H
|
||||
|
||||
/* Data record type 'struct datarec' is defined in common/xdp_stats_kern_user.h,
|
||||
* programs using this header must first include that file.
|
||||
*/
|
||||
#ifndef __XDP_STATS_KERN_USER_H
|
||||
#warning "You forgot to #include <../common/xdp_stats_kern_user.h>"
|
||||
#include <../common/xdp_stats_kern_user.h>
|
||||
#endif
|
||||
|
||||
/* Keeps stats per (enum) xdp_action */
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||
__uint(max_entries, XDP_ACTION_MAX);
|
||||
__type(key, __u32);
|
||||
__type(value, struct datarec);
|
||||
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
||||
} XDP_STATS_MAP_NAME SEC(".maps");
|
||||
|
||||
|
||||
static __always_inline
|
||||
__u32 xdp_stats_record_action(struct xdp_md *ctx, __u32 action)
|
||||
{
|
||||
if (action >= XDP_ACTION_MAX)
|
||||
return XDP_ABORTED;
|
||||
|
||||
/* Lookup in kernel BPF-side return pointer to actual data record */
|
||||
struct datarec *rec = bpf_map_lookup_elem(&xdp_stats_map, &action);
|
||||
if (!rec)
|
||||
return XDP_ABORTED;
|
||||
|
||||
/* BPF_MAP_TYPE_PERCPU_ARRAY returns a data record specific to current
|
||||
* CPU and XDP hooks runs under Softirq, which makes it safe to update
|
||||
* without atomic operations.
|
||||
*/
|
||||
rec->rx_packets++;
|
||||
rec->rx_bytes += (ctx->data_end - ctx->data);
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
#endif /* __XDP_STATS_KERN_H */
|
||||
@@ -0,0 +1,21 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/* Used by BPF-prog kernel side BPF-progs and userspace programs,
|
||||
* for sharing xdp_stats common struct and DEFINEs.
|
||||
*/
|
||||
#ifndef __XDP_STATS_KERN_USER_H
|
||||
#define __XDP_STATS_KERN_USER_H
|
||||
|
||||
/* This is the data record stored in the map */
|
||||
struct datarec {
|
||||
__u64 rx_packets;
|
||||
__u64 rx_bytes;
|
||||
};
|
||||
|
||||
#ifndef XDP_ACTION_MAX
|
||||
#define XDP_ACTION_MAX (XDP_REDIRECT + 1)
|
||||
#endif
|
||||
|
||||
#define XDP_STATS_MAP_NAME xdp_stats_map
|
||||
|
||||
#endif /* __XDP_STATS_KERN_USER_H */
|
||||
@@ -0,0 +1,87 @@
|
||||
# Common Makefile parts for BPF-building with libbpf
|
||||
# --------------------------------------------------
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
#
|
||||
# This file should be included from your Makefile like:
|
||||
# LIB_DIR = ../lib/
|
||||
# include $(LIB_DIR)/common.mk
|
||||
#
|
||||
# It is expected that you define the variables:
|
||||
# BPF_TARGETS and USER_TARGETS
|
||||
# as a space-separated list
|
||||
#
|
||||
BPF_C = ${BPF_TARGETS:=.c}
|
||||
BPF_OBJ = ${BPF_C:.c=.o}
|
||||
USER_C := ${USER_TARGETS:=.c}
|
||||
USER_OBJ := ${USER_C:.c=.o}
|
||||
BPF_OBJ_INSTALL ?= $(BPF_OBJ)
|
||||
|
||||
# Expect this is defined by including Makefile, but define if not
|
||||
LIB_DIR ?= ../lib
|
||||
LDLIBS ?= $(USER_LIBS)
|
||||
|
||||
include $(LIB_DIR)/defines.mk
|
||||
|
||||
# Extend if including Makefile already added some
|
||||
LIB_OBJS += $(foreach obj,$(UTIL_OBJS),$(LIB_DIR)/util/$(obj))
|
||||
|
||||
EXTRA_DEPS +=
|
||||
EXTRA_USER_DEPS +=
|
||||
|
||||
# Detect submodule libbpf source file changes
|
||||
ifeq ($(SYSTEM_LIBBPF),n)
|
||||
LIBBPF_SOURCES := $(wildcard $(LIBBPF_DIR)/src/*.[ch])
|
||||
endif
|
||||
|
||||
# BPF-prog kern and userspace shares struct via header file:
|
||||
KERN_USER_H ?= $(wildcard common_kern_user.h)
|
||||
|
||||
CFLAGS += -I$(HEADER_DIR) -I$(LIB_DIR)/util
|
||||
BPF_CFLAGS += -I$(HEADER_DIR)
|
||||
|
||||
BPF_HEADERS := $(wildcard $(HEADER_DIR)/bpf/*.h) $(wildcard $(HEADER_DIR)/xdp/*.h)
|
||||
|
||||
all: $(USER_TARGETS) $(BPF_OBJ) $(EXTRA_TARGETS)
|
||||
|
||||
.PHONY: clean
|
||||
clean::
|
||||
$(Q)rm -f $(USER_TARGETS) $(BPF_OBJ) $(USER_OBJ) $(USER_GEN) *.ll
|
||||
|
||||
$(OBJECT_LIBBPF): $(LIBBPF_SOURCES)
|
||||
$(Q)$(MAKE) -C $(LIB_DIR) libbpf
|
||||
|
||||
$(CONFIGMK):
|
||||
$(Q)$(MAKE) -C $(LIB_DIR)/.. config.mk
|
||||
|
||||
# Create expansions for dependencies
|
||||
LIB_H := ${LIB_OBJS:.o=.h}
|
||||
|
||||
# Detect if any of common obj changed and create dependency on .h-files
|
||||
$(LIB_OBJS): %.o: %.c %.h $(LIB_H)
|
||||
$(Q)$(MAKE) -C $(dir $@) $(notdir $@)
|
||||
|
||||
$(USER_TARGETS): %: %.c $(OBJECT_LIBBPF) $(OBJECT_LIBXDP) $(LIBMK) $(LIB_OBJS) $(KERN_USER_H) $(EXTRA_DEPS) $(EXTRA_USER_DEPS)
|
||||
$(QUIET_CC)$(CC) -Wall $(CFLAGS) $(LDFLAGS) -o $@ $(LIB_OBJS) \
|
||||
$< $(LDLIBS)
|
||||
|
||||
$(BPF_OBJ): %.o: %.c $(KERN_USER_H) $(EXTRA_DEPS) $(BPF_HEADERS) $(LIBMK)
|
||||
$(QUIET_CLANG)$(CLANG) -S \
|
||||
-target bpf \
|
||||
-D __BPF_TRACING__ \
|
||||
$(BPF_CFLAGS) \
|
||||
-Wall \
|
||||
-Wno-unused-value \
|
||||
-Wno-pointer-sign \
|
||||
-Wno-compare-distinct-pointer-types \
|
||||
-O2 -emit-llvm -c -g -o ${@:.o=.ll} $<
|
||||
$(QUIET_LLC)$(LLC) -march=bpf -filetype=obj -o $@ ${@:.o=.ll}
|
||||
|
||||
|
||||
.PHONY: test
|
||||
ifeq ($(TEST_FILE),)
|
||||
test:
|
||||
@echo " No tests defined"
|
||||
else
|
||||
test: all
|
||||
$(Q)$(TEST_DIR)/test_runner.sh $(TEST_FILE)
|
||||
endif
|
||||
@@ -0,0 +1,33 @@
|
||||
CFLAGS ?= -O2 -g
|
||||
BPF_CFLAGS ?= -Wno-visibility
|
||||
|
||||
include $(LIB_DIR)/../config.mk
|
||||
|
||||
PREFIX?=/usr/local
|
||||
LIBDIR?=$(PREFIX)/lib
|
||||
SBINDIR?=$(PREFIX)/sbin
|
||||
HDRDIR?=$(PREFIX)/include/xdp
|
||||
DATADIR?=$(PREFIX)/share
|
||||
MANDIR?=$(DATADIR)/man
|
||||
BPF_DIR_MNT ?=/sys/fs/bpf
|
||||
BPF_OBJECT_DIR ?=$(LIBDIR)/bpf
|
||||
MAX_DISPATCHER_ACTIONS ?=10
|
||||
|
||||
HEADER_DIR = $(LIB_DIR)/../headers
|
||||
TEST_DIR = $(LIB_DIR)/testing
|
||||
LIBBPF_DIR := $(LIB_DIR)/libbpf
|
||||
|
||||
DEFINES := -DBPF_DIR_MNT=\"$(BPF_DIR_MNT)\" -DBPF_OBJECT_PATH=\"$(BPF_OBJECT_DIR)\"
|
||||
|
||||
ifneq ($(PRODUCTION),1)
|
||||
DEFINES += -DDEBUG
|
||||
endif
|
||||
|
||||
HAVE_FEATURES :=
|
||||
|
||||
CFLAGS += $(DEFINES)
|
||||
BPF_CFLAGS += $(DEFINES)
|
||||
|
||||
CONFIGMK := $(LIB_DIR)/../config.mk
|
||||
LIBMK := Makefile $(CONFIGMK) $(LIB_DIR)/defines.mk $(LIB_DIR)/common.mk
|
||||
|
||||
Submodule
+1
Submodule lib/libbpf added at bbe442da7a
@@ -0,0 +1,79 @@
|
||||
# -*- fill-column: 76; -*-
|
||||
#+TITLE: Test environment script
|
||||
#+OPTIONS: ^:nil
|
||||
|
||||
This directory contains a setup script that you can use to create test
|
||||
environments for testing your XDP programs. It works by creating virtual
|
||||
ethernet (veth) interface pairs and moving one end of each pair to another
|
||||
network namespace. You can load the XDP program in the other namespace and
|
||||
send traffic to it through the interface that is visible in the root
|
||||
namespace.
|
||||
|
||||
Run =./testenv.sh= with no parameter to get a list of available commands, or
|
||||
run =./testenv.sh --help= to get the full help listing with all options. The
|
||||
script can maintain several environments active at the same time, and you
|
||||
can switch between them using the =--name= option.
|
||||
|
||||
If you don't specify a name, the most recently used environment will be
|
||||
used. If you don't specify a name when setting up a new environment, a
|
||||
random name will be generated for you.
|
||||
|
||||
Examples:
|
||||
|
||||
Setup new environment named "test":
|
||||
=./testenv.sh setup --name=test=
|
||||
|
||||
Create a shell alias for easy use of script from anywhere:
|
||||
=eval $(./testenv.sh alias)=
|
||||
|
||||
See the currently active environment, and a list of all active environment
|
||||
names (with alias defined as above):
|
||||
=t status=
|
||||
|
||||
Enter the currently active environment:
|
||||
=t enter=
|
||||
|
||||
Execute a command inside the environment:
|
||||
=t exec -- ip a=
|
||||
|
||||
Teardown the environment:
|
||||
=t teardown=
|
||||
|
||||
* Understanding the network topology
|
||||
|
||||
When setting up a test environment, there will be a virtual link between the
|
||||
environment inside the new namespace, and the interface visible from the
|
||||
host system root namespace. The new namespace will be named after the
|
||||
environment name passed to the script, as will the interface visible in the
|
||||
outer namespace. The interface *inside* the namespace will always be named
|
||||
'veth0'.
|
||||
|
||||
To illustrate this, creating a test environment with the name 'test01' (with
|
||||
=t setup --name test01= will result in the following environment being set
|
||||
up:
|
||||
|
||||
#+begin_example
|
||||
+-----------------------------+ +-----------------------------+
|
||||
| Root namespace | | Testenv namespace 'test01' |
|
||||
| | From 'test01' | |
|
||||
| +--------+ TX-> RX-> +--------+ |
|
||||
| | test01 +--------------------------+ veth0 | |
|
||||
| +--------+ <-RX <-TX +--------+ |
|
||||
| | From 'veth0' | |
|
||||
+-----------------------------+ +-----------------------------+
|
||||
#+end_example
|
||||
|
||||
The 'test01' interface visible in the root namespace is the one we will be
|
||||
installing XDP programs on in the tutorial lessons. The XDP program will see
|
||||
packets being *received* on this interface; as you can see from the diagram,
|
||||
this means all packets being transmitted from inside the new namespace.
|
||||
|
||||
The setup is created this way to simulate the case where the host machine
|
||||
have physical interfaces; but instead of the traffic arriving from outside
|
||||
hosts on physical interfaces, they will arrive from inside the namespace on
|
||||
the virtual interface. This also means that when you generate traffic to
|
||||
test your XDP programs, you need to generate it from *inside* the test
|
||||
environment. The =t ping= command will start the ping inside the test
|
||||
environment by default, and you can run arbitrary programs inside the
|
||||
environment by using =t exec -- <command>=, or simply spawning a shell with
|
||||
=t enter=.
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# These are the config options for the testlab
|
||||
|
||||
|
||||
SETUP_SCRIPT="$(dirname "$0")/setup-env.sh"
|
||||
STATEDIR="${TMPDIR:-/tmp}/bpf-examples"
|
||||
IP6_SUBNET=fc00:dead:cafe # must have exactly three :-separated elements
|
||||
IP6_PREFIX_SIZE=64 # Size of assigned prefixes
|
||||
IP6_FULL_PREFIX_SIZE=48 # Size of IP6_SUBNET
|
||||
IP4_SUBNET=10.11
|
||||
IP4_PREFIX_SIZE=24 # Size of assigned prefixes
|
||||
IP4_FULL_PREFIX_SIZE=16 # Size of IP4_SUBNET
|
||||
VLAN_IDS=(1 2)
|
||||
GENERATED_NAME_PREFIX="bpfex"
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# Script to setup things inside a test environment, used by testenv.sh for
|
||||
# executing commands.
|
||||
#
|
||||
# Author: Toke Høiland-Jørgensen ([email protected])
|
||||
# Date: 7 March 2019
|
||||
# Copyright (c) 2019 Red Hat
|
||||
|
||||
|
||||
die()
|
||||
{
|
||||
echo "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ -n "$TESTENV_NAME" ] || die "TESTENV_NAME missing from environment"
|
||||
[ -n "$1" ] || die "Usage: $0 <command to execute>"
|
||||
|
||||
set -o nounset
|
||||
|
||||
mount -t bpf bpf /sys/fs/bpf/ || die "Unable to mount /sys/fs/bpf inside test environment"
|
||||
|
||||
exec "$@"
|
||||
Executable
+550
@@ -0,0 +1,550 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# Script to setup and manage test environment for the XDP tutorial.
|
||||
# See README.org for instructions on how to use.
|
||||
#
|
||||
# Author: Toke Høiland-Jørgensen ([email protected])
|
||||
# Date: 6 March 2019
|
||||
# Copyright (c) 2019 Red Hat
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
umask 077
|
||||
|
||||
source "$(dirname "$0")/config.sh"
|
||||
|
||||
NEEDED_TOOLS="ethtool ip tc ping"
|
||||
MAX_NAMELEN=15
|
||||
|
||||
# Global state variables that will be set by options etc below
|
||||
GENERATE_NEW=0
|
||||
CLEANUP_FUNC=
|
||||
STATEFILE=
|
||||
CMD=
|
||||
NS=
|
||||
LEGACY_IP=0
|
||||
USE_VLAN=0
|
||||
RUN_ON_INNER=0
|
||||
|
||||
# State variables that are written to and read from statefile
|
||||
STATEVARS=(IP6_PREFIX IP4_PREFIX
|
||||
INSIDE_IP6 INSIDE_IP4 INSIDE_MAC
|
||||
OUTSIDE_IP6 OUTSIDE_IP4 OUTSIDE_MAC
|
||||
ENABLE_IPV4 ENABLE_VLAN)
|
||||
IP6_PREFIX=
|
||||
IP4_PREFIX=
|
||||
INSIDE_IP6=
|
||||
INSIDE_IP4=
|
||||
INSIDE_MAC=
|
||||
OUTSIDE_IP6=
|
||||
OUTSIDE_IP4=
|
||||
OUTSIDE_MAC=
|
||||
ENABLE_IPV4=0
|
||||
ENABLE_VLAN=0
|
||||
|
||||
die()
|
||||
{
|
||||
echo "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
check_prereq()
|
||||
{
|
||||
local max_locked_mem=$(ulimit -l)
|
||||
|
||||
for t in $NEEDED_TOOLS; do
|
||||
which "$t" > /dev/null || die "Missing required tools: $t"
|
||||
done
|
||||
|
||||
if [ "$EUID" -ne "0" ]; then
|
||||
die "This script needs root permissions to run."
|
||||
fi
|
||||
|
||||
[ -d "$STATEDIR" ] || mkdir -p "$STATEDIR" || die "Unable to create state dir $STATEDIR"
|
||||
|
||||
if [ "$max_locked_mem" != "unlimited" ]; then
|
||||
ulimit -l unlimited || die "Unable to set ulimit"
|
||||
fi
|
||||
}
|
||||
|
||||
get_nsname()
|
||||
{
|
||||
local GENERATE=${1:-0}
|
||||
|
||||
if [ -z "$NS" ]; then
|
||||
[ -f "$STATEDIR/current" ] && NS=$(< "$STATEDIR/current")
|
||||
|
||||
if [ "$GENERATE" -eq "1" ] && [ -z "$NS" -o "$GENERATE_NEW" -eq "1" ]; then
|
||||
NS=$(printf "%s-%04x" "$GENERATED_NAME_PREFIX" $RANDOM)
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${#NS}" -gt "$MAX_NAMELEN" ]; then
|
||||
die "Environment name '$NS' is too long (max $MAX_NAMELEN)"
|
||||
fi
|
||||
|
||||
STATEFILE="$STATEDIR/${NS}.state"
|
||||
}
|
||||
|
||||
ensure_nsname()
|
||||
{
|
||||
[ -z "$NS" ] && die "No environment selected; use --name to select one or 'setup' to create one"
|
||||
[ -e "$STATEFILE" ] || die "Environment for $NS doesn't seem to exist"
|
||||
|
||||
echo "$NS" > "$STATEDIR/current"
|
||||
|
||||
read_statefile
|
||||
}
|
||||
|
||||
get_num()
|
||||
{
|
||||
local num=1
|
||||
if [ -f "$STATEDIR/highest_num" ]; then
|
||||
num=$(( 1 + $(< "$STATEDIR/highest_num" )))
|
||||
fi
|
||||
|
||||
echo $num > "$STATEDIR/highest_num"
|
||||
printf "%x" $num
|
||||
}
|
||||
|
||||
write_statefile()
|
||||
{
|
||||
[ -z "$STATEFILE" ] && return 1
|
||||
echo > "$STATEFILE"
|
||||
for var in "${STATEVARS[@]}"; do
|
||||
echo "${var}='$(eval echo '$'$var)'" >> "$STATEFILE"
|
||||
done
|
||||
}
|
||||
|
||||
read_statefile()
|
||||
{
|
||||
local value
|
||||
for var in "${STATEVARS[@]}"; do
|
||||
value=$(source "$STATEFILE"; eval echo '$'$var)
|
||||
eval "$var=\"$value\""
|
||||
done
|
||||
}
|
||||
|
||||
cleanup_setup()
|
||||
{
|
||||
echo "Error during setup, removing partially-configured environment '$NS'" >&2
|
||||
set +o errexit
|
||||
ip netns del "$NS" 2>/dev/null
|
||||
ip link del dev "$NS" 2>/dev/null
|
||||
rm -f "$STATEFILE"
|
||||
}
|
||||
|
||||
cleanup_teardown()
|
||||
{
|
||||
echo "Warning: Errors during teardown, partial environment may be left" >&2
|
||||
}
|
||||
|
||||
|
||||
cleanup()
|
||||
{
|
||||
[ -n "$CLEANUP_FUNC" ] && $CLEANUP_FUNC
|
||||
|
||||
[ -d "$STATEDIR" ] || return 0
|
||||
|
||||
local statefiles=("$STATEDIR"/*.state)
|
||||
|
||||
if [ "${#statefiles[*]}" -eq 1 ] && [ ! -e "${statefiles[0]}" ]; then
|
||||
rm -f "${STATEDIR}/highest_num" "${STATEDIR}/current"
|
||||
rmdir "$STATEDIR"
|
||||
fi
|
||||
}
|
||||
|
||||
iface_macaddr()
|
||||
{
|
||||
local iface="$1"
|
||||
local ns="${2:-}"
|
||||
local output
|
||||
|
||||
if [ -n "$ns" ]; then
|
||||
output=$(ip -br -n "$ns" link show dev "$iface")
|
||||
else
|
||||
output=$(ip -br link show dev "$iface")
|
||||
fi
|
||||
echo "$output" | awk '{print $3}'
|
||||
}
|
||||
|
||||
set_sysctls()
|
||||
{
|
||||
local iface="$1"
|
||||
local in_ns="${2:-}"
|
||||
local nscmd=
|
||||
|
||||
[ -n "$in_ns" ] && nscmd="ip netns exec $in_ns"
|
||||
local sysctls=(accept_dad
|
||||
accept_ra
|
||||
mldv1_unsolicited_report_interval
|
||||
mldv2_unsolicited_report_interval)
|
||||
|
||||
for s in ${sysctls[*]}; do
|
||||
$nscmd sysctl -w net.ipv6.conf.$iface.${s}=0 >/dev/null
|
||||
done
|
||||
}
|
||||
|
||||
wait_for_dev()
|
||||
{
|
||||
local iface="$1"
|
||||
local in_ns="${2:-}"
|
||||
local retries=5 # max retries
|
||||
local nscmd=
|
||||
|
||||
[ -n "$in_ns" ] && nscmd="ip netns exec $in_ns"
|
||||
while [ "$retries" -gt "0" ]; do
|
||||
if ! $nscmd ip addr show dev $iface | grep -q tentative; then return 0; fi
|
||||
sleep 0.5
|
||||
retries=$((retries -1))
|
||||
done
|
||||
}
|
||||
|
||||
get_vlan_prefix()
|
||||
{
|
||||
# Split the IPv6 prefix, and add the VLAN ID to the upper byte of the fourth
|
||||
# element in the prefix. This will break if the global prefix config doesn't
|
||||
# have exactly three elements in it.
|
||||
local prefix="$1"
|
||||
local vid="$2"
|
||||
(IFS=:; set -- $prefix; printf "%s:%s:%s:%x::" "$1" "$2" "$3" $(($4 + $vid * 4096)))
|
||||
}
|
||||
|
||||
setup()
|
||||
{
|
||||
get_nsname 1
|
||||
|
||||
echo "Setting up new environment '$NS'"
|
||||
|
||||
[ -e "$STATEFILE" ] && die "Environment for '$NS' already exists"
|
||||
|
||||
local NUM=$(get_num "$NS")
|
||||
local PEERNAME="testl-ve-$NUM"
|
||||
[ -z "$IP6_PREFIX" ] && IP6_PREFIX="${IP6_SUBNET}:${NUM}::"
|
||||
[ -z "$IP4_PREFIX" ] && IP4_PREFIX="${IP4_SUBNET}.$((0x$NUM))."
|
||||
|
||||
INSIDE_IP6="${IP6_PREFIX}2"
|
||||
INSIDE_IP4="${IP4_PREFIX}2"
|
||||
OUTSIDE_IP6="${IP6_PREFIX}1"
|
||||
OUTSIDE_IP4="${IP4_PREFIX}1"
|
||||
|
||||
CLEANUP_FUNC=cleanup_setup
|
||||
|
||||
if ! mount | grep -q /sys/fs/bpf; then
|
||||
mount -t bpf bpf /sys/fs/bpf/
|
||||
fi
|
||||
|
||||
ip netns add "$NS"
|
||||
ip link add dev "$NS" type veth peer name veth0 netns "$NS"
|
||||
|
||||
OUTSIDE_MAC=$(iface_macaddr "$NS")
|
||||
INSIDE_MAC=$(iface_macaddr veth0 "$NS")
|
||||
|
||||
set_sysctls $NS
|
||||
ip link set dev "$NS" up
|
||||
ip addr add dev "$NS" "${OUTSIDE_IP6}/${IP6_PREFIX_SIZE}"
|
||||
ethtool -K "$NS" rxvlan off txvlan off
|
||||
# Prevent neighbour queries on the link
|
||||
ip neigh add "$INSIDE_IP6" lladdr "$INSIDE_MAC" dev "$NS" nud permanent
|
||||
|
||||
set_sysctls veth0 "$NS"
|
||||
ip -n "$NS" link set dev lo up
|
||||
ip -n "$NS" link set dev veth0 up
|
||||
ip -n "$NS" addr add dev veth0 "${INSIDE_IP6}/${IP6_PREFIX_SIZE}"
|
||||
ip netns exec "$NS" ethtool -K veth0 rxvlan off txvlan off
|
||||
# Prevent neighbour queries on the link
|
||||
ip -n "$NS" neigh add "$OUTSIDE_IP6" lladdr "$OUTSIDE_MAC" dev veth0 nud permanent
|
||||
# Add route for whole test subnet, to make it easier to communicate between
|
||||
# namespaces
|
||||
ip -n "$NS" route add "${IP6_SUBNET}::/$IP6_FULL_PREFIX_SIZE" via "$OUTSIDE_IP6" dev veth0
|
||||
|
||||
if [ "$LEGACY_IP" -eq "1" ]; then
|
||||
ip addr add dev "$NS" "${OUTSIDE_IP4}/${IP4_PREFIX_SIZE}"
|
||||
ip -n "$NS" addr add dev veth0 "${INSIDE_IP4}/${IP4_PREFIX_SIZE}"
|
||||
ip neigh add "$INSIDE_IP4" lladdr "$INSIDE_MAC" dev "$NS" nud permanent
|
||||
ip -n "$NS" neigh add "$OUTSIDE_IP4" lladdr "$OUTSIDE_MAC" dev veth0 nud permanent
|
||||
ip -n "$NS" route add "${IP4_SUBNET}/${IP4_FULL_PREFIX_SIZE}" via "$OUTSIDE_IP4" dev veth0
|
||||
ENABLE_IPV4=1
|
||||
else
|
||||
ENABLE_IPV4=0
|
||||
fi
|
||||
|
||||
if [ "$USE_VLAN" -eq "1" ]; then
|
||||
ENABLE_VLAN=1
|
||||
for vid in "${VLAN_IDS[@]}"; do
|
||||
local vlpx="$(get_vlan_prefix "$IP6_PREFIX" "$vid")"
|
||||
local inside_ip="${vlpx}2"
|
||||
local outside_ip="${vlpx}1"
|
||||
ip link add dev "${NS}.$vid" link "$NS" type vlan id "$vid"
|
||||
ip link set dev "${NS}.$vid" up
|
||||
ip addr add dev "${NS}.$vid" "${outside_ip}/${IP6_PREFIX_SIZE}"
|
||||
ip neigh add "$inside_ip" lladdr "$INSIDE_MAC" dev "${NS}.$vid" nud permanent
|
||||
set_sysctls "${NS}/$vid"
|
||||
|
||||
ip -n "$NS" link add dev "veth0.$vid" link "veth0" type vlan id "$vid"
|
||||
ip -n "$NS" link set dev "veth0.$vid" up
|
||||
ip -n "$NS" addr add dev "veth0.$vid" "${inside_ip}/${IP6_PREFIX_SIZE}"
|
||||
ip -n "$NS" neigh add "$outside_ip" lladdr "$OUTSIDE_MAC" dev "veth0.$vid" nud permanent
|
||||
set_sysctls "veth0/$vid" "$NS"
|
||||
done
|
||||
else
|
||||
ENABLE_VLAN=0
|
||||
fi
|
||||
|
||||
write_statefile
|
||||
|
||||
CLEANUP_FUNC=
|
||||
|
||||
echo -n "Setup environment '$NS' with peer ip ${INSIDE_IP6}"
|
||||
[ "$ENABLE_IPV4" -eq "1" ] && echo " and ${INSIDE_IP4}." || echo "."
|
||||
echo "Waiting for interface configuration to settle..."
|
||||
echo ""
|
||||
wait_for_dev "$NS" && wait_for_dev veth0 "$NS"
|
||||
|
||||
LEGACY_IP=0 USE_VLAN=0 run_ping -c 1
|
||||
|
||||
echo "$NS" > "$STATEDIR/current"
|
||||
}
|
||||
|
||||
teardown()
|
||||
{
|
||||
get_nsname && ensure_nsname "$NS"
|
||||
|
||||
echo "Tearing down environment '$NS'"
|
||||
|
||||
CLEANUP_FUNC=cleanup_teardown
|
||||
|
||||
ip link del dev "$NS"
|
||||
ip netns del "$NS"
|
||||
rm -f "$STATEFILE"
|
||||
[ -d "/sys/fs/bpf/$NS" ] && rmdir "/sys/fs/bpf/$NS" || true
|
||||
|
||||
if [ -f "$STATEDIR/current" ]; then
|
||||
local CUR=$(< "$STATEDIR/current" )
|
||||
[[ "$CUR" == "$NS" ]] && rm -f "$STATEDIR/current"
|
||||
fi
|
||||
|
||||
CLEANUP_FUNC=
|
||||
}
|
||||
|
||||
reset()
|
||||
{
|
||||
teardown && setup
|
||||
}
|
||||
|
||||
ns_exec()
|
||||
{
|
||||
get_nsname && ensure_nsname "$NS"
|
||||
|
||||
ip netns exec "$NS" env TESTENV_NAME="$NS" "$SETUP_SCRIPT" "$@"
|
||||
}
|
||||
|
||||
enter()
|
||||
{
|
||||
ns_exec "${SHELL:-bash}"
|
||||
}
|
||||
|
||||
run_ping()
|
||||
{
|
||||
local PING
|
||||
local IP
|
||||
|
||||
get_nsname && ensure_nsname "$NS"
|
||||
|
||||
echo "Running ping from inside test environment:"
|
||||
echo ""
|
||||
|
||||
if [ "$LEGACY_IP" -eq "1" ]; then
|
||||
PING=$(which ping)
|
||||
IP="${OUTSIDE_IP4}"
|
||||
[ "$USE_VLAN" -eq "0" ] || die "Can't use --legacy-ip and --vlan at the same time."
|
||||
[ "$ENABLE_IPV4" -eq "1" ] || die "No legacy IP addresses configured in environment."
|
||||
else
|
||||
PING=$(which ping6 2>/dev/null || which ping)
|
||||
if [ "$USE_VLAN" -eq "0" ]; then
|
||||
IP="${OUTSIDE_IP6}"
|
||||
else
|
||||
[ "$ENABLE_VLAN" -eq "1" ] || die "No VLANs configured in environment."
|
||||
IP="$(get_vlan_prefix "$IP6_PREFIX" "${VLAN_IDS[0]}")1"
|
||||
fi
|
||||
fi
|
||||
|
||||
ns_exec "$PING" "$IP" "$@"
|
||||
}
|
||||
|
||||
run_tcpdump()
|
||||
{
|
||||
get_nsname && ensure_nsname "$NS"
|
||||
|
||||
if [ "$RUN_ON_INNER" -eq "1" ]; then
|
||||
ns_exec tcpdump -nei veth0 "$@"
|
||||
else
|
||||
tcpdump -nei "$NS" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
status()
|
||||
{
|
||||
get_nsname
|
||||
|
||||
echo "Currently selected environment: ${NS:-None}"
|
||||
if [ -n "$NS" ] && [ -e "$STATEFILE" ]; then
|
||||
read_statefile
|
||||
echo -n " Namespace: "; ip netns | grep "^$NS"
|
||||
echo " Prefix: ${IP6_PREFIX}/${IP6_PREFIX_SIZE}"
|
||||
[ "$ENABLE_IPV4" -eq "1" ] && echo " Legacy prefix: ${IP4_PREFIX}0/${IP4_PREFIX_SIZE}"
|
||||
echo -n " Iface: "; ip -br a show dev "$NS" | sed 's/\s\+/ /g'
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "All existing environments:"
|
||||
for f in "$STATEDIR"/*.state; do
|
||||
if [ ! -e "$f" ]; then
|
||||
echo " No environments exist"
|
||||
break
|
||||
fi
|
||||
NAME=$(basename "$f" .state)
|
||||
echo " $NAME"
|
||||
done
|
||||
}
|
||||
|
||||
print_alias()
|
||||
{
|
||||
local scriptname="$(readlink -e "$0")"
|
||||
local sudo=
|
||||
|
||||
[ -t 1 ] && echo "Eval this with \`eval \$($0 alias)\` to create shell alias" >&2
|
||||
|
||||
if [ "$EUID" -ne "0" ]; then
|
||||
sudo="sudo "
|
||||
echo "WARNING: Creating sudo alias; be careful, this script WILL execute arbitrary programs" >&2
|
||||
fi
|
||||
|
||||
echo "" >&2
|
||||
|
||||
|
||||
echo "alias t='$sudo$scriptname'"
|
||||
}
|
||||
|
||||
usage()
|
||||
{
|
||||
local FULL=${1:-}
|
||||
|
||||
echo "Usage: $0 [options] <command> [param]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo "setup Setup and initialise new environment"
|
||||
echo "teardown Tear down existing environment"
|
||||
echo "reset Reset environment to original state"
|
||||
echo "exec <command> Exec <command> inside test environment"
|
||||
echo "enter Execute shell inside test environment"
|
||||
echo "ping Run ping inside test environment"
|
||||
echo "status (or st) Show status of test environment"
|
||||
echo "load Load XDP program on outer interface"
|
||||
echo "unload Unload XDP program on outer interface"
|
||||
echo "tcpdump Run on outer interface (or inner with --inner)"
|
||||
echo ""
|
||||
|
||||
if [ -z "$FULL" ] ; then
|
||||
echo "Use --help to see the list of options."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Options:"
|
||||
echo "-h, --help Show this usage text"
|
||||
echo ""
|
||||
echo "-n, --name <name> Set name of test environment. If not set, the last used"
|
||||
echo " name will be used, or a new one generated."
|
||||
echo ""
|
||||
echo "-g, --gen-new Generate a new test environment name even though an existing"
|
||||
echo " environment is selected as the current one."
|
||||
echo ""
|
||||
echo " --legacy-ip Enable legacy IP (IPv4) support."
|
||||
echo " For setup and reset commands this enables configuration of legacy"
|
||||
echo " IP addresses on the interface, for the ping command it switches to"
|
||||
echo " legacy ping."
|
||||
echo ""
|
||||
echo " --vlan Enable VLAN support."
|
||||
echo " When used with the setup and reset commands, these VLAN IDs will"
|
||||
echo " be configured: ${VLAN_IDS[*]}. The VLAN interfaces are named as"
|
||||
echo " <ifname>.<vlid>."
|
||||
echo " When used with the ping command, the pings will be sent on the"
|
||||
echo " first VLAN ID (${VLAN_IDS[0]})."
|
||||
echo ""
|
||||
echo " --inner Use with tcpdump command to run on inner interface."
|
||||
echo ""
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
OPTS="hn:gl:s:"
|
||||
LONGOPTS="help,name:,gen-new,legacy-ip,vlan,inner"
|
||||
|
||||
OPTIONS=$(getopt -o "$OPTS" --long "$LONGOPTS" -- "$@")
|
||||
[ "$?" -ne "0" ] && usage >&2 || true
|
||||
|
||||
eval set -- "$OPTIONS"
|
||||
|
||||
|
||||
while true; do
|
||||
arg="$1"
|
||||
shift
|
||||
|
||||
case "$arg" in
|
||||
-h | --help)
|
||||
usage full >&2
|
||||
;;
|
||||
-n | --name)
|
||||
NS="$1"
|
||||
shift
|
||||
;;
|
||||
-g | --gen-new)
|
||||
GENERATE_NEW=1
|
||||
;;
|
||||
--legacy-ip)
|
||||
LEGACY_IP=1
|
||||
;;
|
||||
--vlan)
|
||||
USE_VLAN=1
|
||||
;;
|
||||
--inner)
|
||||
RUN_ON_INNER=1
|
||||
;;
|
||||
-- )
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ "$#" -eq 0 ] && usage >&2
|
||||
|
||||
case "$1" in
|
||||
st|sta|status)
|
||||
CMD=status
|
||||
;;
|
||||
setup|teardown|reset|enter)
|
||||
CMD="$1"
|
||||
;;
|
||||
"exec")
|
||||
CMD=ns_exec
|
||||
;;
|
||||
ping|tcpdump)
|
||||
CMD="run_$1"
|
||||
;;
|
||||
"alias")
|
||||
print_alias
|
||||
exit 0
|
||||
;;
|
||||
"help")
|
||||
usage full >&2
|
||||
;;
|
||||
*)
|
||||
usage >&2
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
trap cleanup EXIT
|
||||
check_prereq
|
||||
$CMD "$@"
|
||||
Reference in New Issue
Block a user