Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bafc0ba8 authored by Lorenz Bauer's avatar Lorenz Bauer Committed by Alexei Starovoitov
Browse files

selftests/bpf: add tests for bpf_tcp_check_syncookie and bpf_skc_lookup_tcp



Add tests which verify that the new helpers work for both IPv4 and
IPv6, by forcing SYN cookies to always on. Use a new network namespace
to avoid clobbering the global SYN cookie settings.

Signed-off-by: default avatarLorenz Bauer <lmb@cloudflare.com>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent 5792d52d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -30,4 +30,5 @@ test_netcnt
test_section_names
test_tcpnotify_user
test_libbpf
test_tcp_check_syncookie_user
alu32
+3 −2
Original line number Diff line number Diff line
@@ -51,7 +51,8 @@ TEST_PROGS := test_kmod.sh \
	test_skb_cgroup_id.sh \
	test_flow_dissector.sh \
	test_xdp_vlan.sh \
	test_lwt_ip_encap.sh
	test_lwt_ip_encap.sh \
	test_tcp_check_syncookie.sh

TEST_PROGS_EXTENDED := with_addr.sh \
	with_tunnels.sh \
@@ -60,7 +61,7 @@ TEST_PROGS_EXTENDED := with_addr.sh \

# Compile but not part of 'make run_tests'
TEST_GEN_PROGS_EXTENDED = test_libbpf_open test_sock_addr test_skb_cgroup_id_user \
	flow_dissector_load test_flow_dissector
	flow_dissector_load test_flow_dissector test_tcp_check_syncookie_user

include ../lib.mk

+8 −0
Original line number Diff line number Diff line
@@ -159,6 +159,11 @@ static struct bpf_sock *(*bpf_sk_lookup_tcp)(void *ctx,
					     int size, unsigned long long netns_id,
					     unsigned long long flags) =
	(void *) BPF_FUNC_sk_lookup_tcp;
static struct bpf_sock *(*bpf_skc_lookup_tcp)(void *ctx,
					     struct bpf_sock_tuple *tuple,
					     int size, unsigned long long netns_id,
					     unsigned long long flags) =
	(void *) BPF_FUNC_skc_lookup_tcp;
static struct bpf_sock *(*bpf_sk_lookup_udp)(void *ctx,
					     struct bpf_sock_tuple *tuple,
					     int size, unsigned long long netns_id,
@@ -184,6 +189,9 @@ static struct bpf_sock *(*bpf_get_listener_sock)(struct bpf_sock *sk) =
	(void *) BPF_FUNC_get_listener_sock;
static int (*bpf_skb_ecn_set_ce)(void *ctx) =
	(void *) BPF_FUNC_skb_ecn_set_ce;
static int (*bpf_tcp_check_syncookie)(struct bpf_sock *sk,
	    void *ip, int ip_len, void *tcp, int tcp_len) =
	(void *) BPF_FUNC_tcp_check_syncookie;

/* llvm builtin functions that eBPF C program may use to
 * emit BPF_LD_ABS and BPF_LD_IND instructions
+129 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018 Facebook
// Copyright (c) 2019 Cloudflare

#include <string.h>

#include <linux/bpf.h>
#include <linux/pkt_cls.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <sys/socket.h>
#include <linux/tcp.h>

#include "bpf_helpers.h"
#include "bpf_endian.h"

struct bpf_map_def SEC("maps") results = {
	.type = BPF_MAP_TYPE_ARRAY,
	.key_size = sizeof(__u32),
	.value_size = sizeof(__u64),
	.max_entries = 1,
};

static __always_inline void check_syncookie(void *ctx, void *data,
					    void *data_end)
{
	struct bpf_sock_tuple tup;
	struct bpf_sock *sk;
	struct ethhdr *ethh;
	struct iphdr *ipv4h;
	struct ipv6hdr *ipv6h;
	struct tcphdr *tcph;
	int ret;
	__u32 key = 0;
	__u64 value = 1;

	ethh = data;
	if (ethh + 1 > data_end)
		return;

	switch (bpf_ntohs(ethh->h_proto)) {
	case ETH_P_IP:
		ipv4h = data + sizeof(struct ethhdr);
		if (ipv4h + 1 > data_end)
			return;

		if (ipv4h->ihl != 5)
			return;

		tcph = data + sizeof(struct ethhdr) + sizeof(struct iphdr);
		if (tcph + 1 > data_end)
			return;

		tup.ipv4.saddr = ipv4h->saddr;
		tup.ipv4.daddr = ipv4h->daddr;
		tup.ipv4.sport = tcph->source;
		tup.ipv4.dport = tcph->dest;

		sk = bpf_skc_lookup_tcp(ctx, &tup, sizeof(tup.ipv4),
					BPF_F_CURRENT_NETNS, 0);
		if (!sk)
			return;

		if (sk->state != BPF_TCP_LISTEN)
			goto release;

		ret = bpf_tcp_check_syncookie(sk, ipv4h, sizeof(*ipv4h),
					      tcph, sizeof(*tcph));
		break;

	case ETH_P_IPV6:
		ipv6h = data + sizeof(struct ethhdr);
		if (ipv6h + 1 > data_end)
			return;

		if (ipv6h->nexthdr != IPPROTO_TCP)
			return;

		tcph = data + sizeof(struct ethhdr) + sizeof(struct ipv6hdr);
		if (tcph + 1 > data_end)
			return;

		memcpy(tup.ipv6.saddr, &ipv6h->saddr, sizeof(tup.ipv6.saddr));
		memcpy(tup.ipv6.daddr, &ipv6h->daddr, sizeof(tup.ipv6.daddr));
		tup.ipv6.sport = tcph->source;
		tup.ipv6.dport = tcph->dest;

		sk = bpf_skc_lookup_tcp(ctx, &tup, sizeof(tup.ipv6),
					BPF_F_CURRENT_NETNS, 0);
		if (!sk)
			return;

		if (sk->state != BPF_TCP_LISTEN)
			goto release;

		ret = bpf_tcp_check_syncookie(sk, ipv6h, sizeof(*ipv6h),
					      tcph, sizeof(*tcph));
		break;

	default:
		return;
	}

	if (ret == 0)
		bpf_map_update_elem(&results, &key, &value, 0);

release:
	bpf_sk_release(sk);
}

SEC("clsact/check_syncookie")
int check_syncookie_clsact(struct __sk_buff *skb)
{
	check_syncookie(skb, (void *)(long)skb->data,
			(void *)(long)skb->data_end);
	return TC_ACT_OK;
}

SEC("xdp/check_syncookie")
int check_syncookie_xdp(struct xdp_md *ctx)
{
	check_syncookie(ctx, (void *)(long)ctx->data,
			(void *)(long)ctx->data_end);
	return XDP_PASS;
}

char _license[] SEC("license") = "GPL";
+81 −0
Original line number Diff line number Diff line
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2018 Facebook
# Copyright (c) 2019 Cloudflare

set -eu

wait_for_ip()
{
	local _i
	printf "Wait for IP %s to become available " "$1"
	for _i in $(seq ${MAX_PING_TRIES}); do
		printf "."
		if ns1_exec ping -c 1 -W 1 "$1" >/dev/null 2>&1; then
			echo " OK"
			return
		fi
		sleep 1
	done
	echo 1>&2 "ERROR: Timeout waiting for test IP to become available."
	exit 1
}

get_prog_id()
{
	awk '/ id / {sub(/.* id /, "", $0); print($1)}'
}

ns1_exec()
{
	ip netns exec ns1 "$@"
}

setup()
{
	ip netns add ns1
	ns1_exec ip link set lo up

	ns1_exec sysctl -w net.ipv4.tcp_syncookies=2

	wait_for_ip 127.0.0.1
	wait_for_ip ::1
}

cleanup()
{
	ip netns del ns1 2>/dev/null || :
}

main()
{
	trap cleanup EXIT 2 3 6 15
	setup

	printf "Testing clsact..."
	ns1_exec tc qdisc add dev "${TEST_IF}" clsact
	ns1_exec tc filter add dev "${TEST_IF}" ingress \
		bpf obj "${BPF_PROG_OBJ}" sec "${CLSACT_SECTION}" da

	BPF_PROG_ID=$(ns1_exec tc filter show dev "${TEST_IF}" ingress | \
		      get_prog_id)
	ns1_exec "${PROG}" "${BPF_PROG_ID}"
	ns1_exec tc qdisc del dev "${TEST_IF}" clsact

	printf "Testing XDP..."
	ns1_exec ip link set "${TEST_IF}" xdp \
		object "${BPF_PROG_OBJ}" section "${XDP_SECTION}"
	BPF_PROG_ID=$(ns1_exec ip link show "${TEST_IF}" | get_prog_id)
	ns1_exec "${PROG}" "${BPF_PROG_ID}"
}

DIR=$(dirname $0)
TEST_IF=lo
MAX_PING_TRIES=5
BPF_PROG_OBJ="${DIR}/test_tcp_check_syncookie_kern.o"
CLSACT_SECTION="clsact/check_syncookie"
XDP_SECTION="xdp/check_syncookie"
BPF_PROG_ID=0
PROG="${DIR}/test_tcp_check_syncookie_user"

main
Loading