// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* End-to-end eBPF tunnel test suite
* The file tests BPF network tunnel implementation.
*
* Topology:
* ---------
* root namespace | at_ns0 namespace
* |
* ----------- | -----------
* | tnl dev | | | tnl dev | (overlay network)
* ----------- | -----------
* metadata-mode | metadata-mode
* with bpf | with bpf
* |
* ---------- | ----------
* | veth1 | --------- | veth0 | (underlay network)
* ---------- peer ----------
*
*
* Device Configuration
* --------------------
* root namespace with metadata-mode tunnel + BPF
* Device names and addresses:
* veth1 IP 1: 172.16.1.200, IPv6: 00::22 (underlay)
* IP 2: 172.16.1.20, IPv6: 00::bb (underlay)
* tunnel dev <type>11, ex: gre11, IPv4: 10.1.1.200, IPv6: 1::22 (overlay)
*
* Namespace at_ns0 with native tunnel
* Device names and addresses:
* veth0 IPv4: 172.16.1.100, IPv6: 00::11 (underlay)
* tunnel dev <type>00, ex: gre00, IPv4: 10.1.1.100, IPv6: 1::11 (overlay)
*
*
* End-to-end ping packet flow
* ---------------------------
* Most of the tests start by namespace creation, device configuration,
* then ping the underlay and overlay network. When doing 'ping 10.1.1.100'
* from root namespace, the following operations happen:
* 1) Route lookup shows 10.1.1.100/24 belongs to tnl dev, fwd to tnl dev.
* 2) Tnl device's egress BPF program is triggered and set the tunnel metadata,
* with local_ip=172.16.1.200, remote_ip=172.16.1.100. BPF program choose
* the primary or secondary ip of veth1 as the local ip of tunnel. The
* choice is made based on the value of bpf map local_ip_map.
* 3) Outer tunnel header is prepended and route the packet to veth1's egress.
* 4) veth0's ingress queue receive the tunneled packet at namespace at_ns0.
* 5) Tunnel protocol handler, ex: vxlan_rcv, decap the packet.
* 6) Forward the packet to the overlay tnl dev.
*/
#include <arpa/inet.h>
#include <linux/if_link.h>
#include <linux/if_tun.h>
#include <linux/limits.h>
#include <linux/sysctl.h>
#include <linux/time_types.h>
#include <linux/net_tstamp.h>
#include <net/if.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include "test_progs.h"
#include "network_helpers.h"
#include "test_tunnel_kern.skel.h"
#define IP4_ADDR_VETH0 "172.16.1.100"
#define IP4_ADDR1_VETH1 "172.16.1.200"
#define IP4_ADDR2_VETH1 "172.16.1.20"
#define IP4_ADDR_TUNL_DEV0 "10.1.1.100"
#define IP4_ADDR_TUNL_DEV1 "10.1.1.200"
#define IP6_ADDR_VETH0 "::11"
#define IP6_ADDR1_VETH1 "::22"
#define IP6_ADDR2_VETH1 "::bb"
#define IP4_ADDR1_HEX_VETH1 0xac1001c8
#define IP4_ADDR2_HEX_VETH1 0xac100114
#define IP6_ADDR1_HEX_VETH1 0x22
#define IP6_ADDR2_HEX_VETH1 0xbb
#define MAC_TUNL_DEV0 "52:54:00:d9:01:00"
#define MAC_TUNL_DEV1 "52:54:00:d9:02:00"
#define MAC_VETH1 "52:54:00:d9:03:00"
#define VXLAN_TUNL_DEV0 "vxlan00"
#define VXLAN_TUNL_DEV1 "vxlan11"
#define IP6VXLAN_TUNL_DEV0 "ip6vxlan00"
#define IP6VXLAN_TUNL_DEV1 "ip6vxlan11"
#define IPIP_TUNL_DEV0 "ipip00"
#define IPIP_TUNL_DEV1 "ipip11"
#define XFRM_AUTH "0x1111111111111111111111111111111111111111"
#define XFRM_ENC "0x22222222222222222222222222222222"
#define XFRM_SPI_IN_TO_OUT 0x1
#define XFRM_SPI_OUT_TO_IN 0x2
#define PING_ARGS "-i 0.01 -c 3 -w 10 -q"
static int config_device(void)
{
SYS(fail, "ip netns add at_ns0");
SYS(fail, "ip link add veth0 address " MAC_VETH1 " type veth peer name veth1");
SYS(fail, "ip link set veth0 netns at_ns0");
SYS(fail, "ip addr add " IP4_ADDR1_VETH1 "/24 dev veth1");
SYS(fail, "ip link set dev veth1 up mtu 1500");
SYS(fail, "ip netns exec at_ns0 ip addr add " IP4_ADDR_VETH0 "/24 dev veth0");
SYS(fail, "ip netns exec at_ns0 ip link set dev veth0 up mtu 1500");
return 0;
fail:
return -1;
}
static void cleanup(void)
{
SYS_NOFAIL("test -f /var/run/netns/at_ns0 && ip netns delete at_ns0");
SYS_NOFAIL("ip link del veth1");
SYS_NOFAIL("ip link del %s", VXLAN_TUNL_DEV1);
SYS_NOFAIL("ip link del %s", IP6VXLAN_TUNL_DEV1);
}
static int add_vxlan_tunnel(void)
{
/* at_ns0 namespace */
SYS(fail, "ip netns exec at_ns0 ip link add dev %s type vxlan external gbp dstport 4789",
VXLAN_TUNL_DEV0);
SYS(fail, "ip netns exec at_ns0 ip link set dev %s address %s up",
VXLAN_TUNL_DEV0, MAC_TUNL_DEV0);
SYS(fail, "ip netns exec at_ns0 ip addr add dev %s %s/24",
VXLAN_TUNL_DEV0, IP4_ADDR_TUNL_DEV0);
SYS(fail, "ip netns exec at_ns0 ip neigh add %s lladdr %s dev %s",
IP4_ADDR_TUNL_DEV1, MAC_TUNL_DEV1, VXLAN_TUNL_DEV0);
SYS(fail, "ip netns exec at_ns0 ip neigh add %s lladdr %s dev veth0",
IP4_ADDR2_VETH1, MAC_VETH1);
/* root namespace */
SYS(fail, "ip link add dev %s type vxlan external gbp dstport 4789",
VXLAN_TUNL_DEV1);
SYS(fail, "ip link set dev %s address %s up", VXLAN_TUNL_DEV1, MAC_TUNL_DEV1);
SYS(fail, "ip addr add dev %s %s/24", VXLAN_TUNL_DEV1, IP4_ADDR_TUNL_DEV1);
SYS(fail, "ip neigh add %s lladdr %s dev %s",
IP4_ADDR_TUNL_DEV0, MAC_TUNL_DEV0, VXLAN_TUNL_DEV1);
return 0;
fail:
return -1;
}
static void delete_vxlan_tunnel(void)
{
SYS_NOFAIL("ip netns exec at_ns0 ip link delete dev %s",
VXLAN_TUNL_DEV0);
SYS_NOFAIL("ip link delete dev %s", VXLAN_TUNL_DEV1);
}
static int add_ip6vxlan_tunnel(void)
{
SYS(fail, "ip netns exec at_ns0 ip -6 addr add %s/96 dev veth0",
IP6_ADDR_VETH0);
SYS(fail, "ip netns exec at_ns0 ip link set dev veth0 up");
SYS(fail, "ip -6 addr add %s/96 dev veth1", IP6_ADDR1_VETH1);
SYS(fail, "ip -6 addr add %s/96 dev veth1", IP6_ADDR2_VETH1);
SYS(fail, "ip link set dev veth1 up");
/* at_ns0 namespace */
SYS(fail, "ip netns exec at_ns0 ip link add dev %s type vxlan external dstport 4789",
IP6VXLAN_TUNL_DEV0);
SYS(fail, "ip netns exec at_ns0 ip addr add dev %s %s/24",
IP6VXLAN_TUNL_DEV0, IP4_ADDR_TUNL_DEV0);
SYS(fail, "ip netns exec at_ns0 ip link set dev %s address %s up",
IP6VXLAN_TUNL_DEV0, MAC_TUNL_DEV0);
/* root namespace */
SYS(fail, "ip link add dev %s type vxlan external dstport 4789",
IP6VXLAN_TUNL_DEV1);
SYS(fail, "ip addr add dev %s %s/24", IP6VXLAN_TUNL_DEV1, IP4_ADDR_TUNL_DEV1);
SYS(fail, "ip link set dev %s address %s up",
IP6VXLAN_TUNL_DEV1, MAC_TUNL_DEV1);
return 0;
fail:
return -1;
}
static void delete_ip6vxlan_tunnel(void)
{
SYS_NOFAIL("ip netns exec at_ns0 ip -6 addr delete %s/96 dev veth0",
IP6_ADDR_VETH0);
SYS_NOFAIL("ip -6 addr delete %s/96 dev veth1", IP6_ADDR1_VETH1);
SYS_NOFAIL("ip -6 addr delete %s/96 dev veth1", IP6_ADDR2_VETH1);
SYS_NOFAIL("ip netns exec at_ns0 ip link delete dev %s",
IP6VXLAN_TUNL_DEV0);
SYS_NOFAIL("ip link delete dev %s", IP6VXLAN_TUNL_DEV1);
}
enum ipip_encap {
NONE = 0,
FOU = 1,
GUE = 2,
};
static int set_ipip_encap(const char *ipproto, const char *type)
{
SYS(fail, "ip -n at_ns0 fou add port 5555 %s", ipproto);
SYS(fail, "ip -n at_ns0 link set dev %s type ipip encap %s",
IPIP_TUNL_DEV0, type);
SYS(fail, "ip -n at_ns0 link set dev %s type ipip encap-dport 5555",
IPIP_TUNL_DEV0);
return 0;
fail:
return -1;
}
static int add_ipip_tunnel(enum ipip_encap encap)
{
int err;
const char *ipproto, *type;
switch (encap) {
case FOU:
ipproto = "ipproto 4";
type = "fou";
break;
case GUE:
ipproto = "gue";
type = ipproto;
break;
default:
ipproto = NULL;
type = ipproto;
}
/* at_ns0 namespace */
SYS(fail, "ip -n at_ns0 link add dev %s type ipip local %s remote %s",
IPIP_TUNL_DEV0, IP4_ADDR_VETH0, IP4_ADDR1_VETH1);
if (type && ipproto) {
err = set_ipip_encap(ipproto, type);
if (!ASSERT_OK(err, "set_ipip_encap"))
goto fail;
}
SYS(fail, "ip -n at_ns0 link set dev %s up", IPIP_TUNL_DEV0);
SYS(fail, "ip -n at_ns0 addr add dev %s %s/24",
IPIP_TUNL_DEV0, IP4_ADDR_TUNL_DEV0);
/* root namespace */
if (type && ipproto)
SYS(fail, "ip fou add port 5555 %s", ipproto);
SYS(fail, "ip link add dev %s type ipip external", IPIP_TUNL_DEV1);
SYS(fail, "ip link set dev %s up", IPIP_TUNL_DEV1);
SYS(fail, "ip addr add dev %s %s/24", IPIP_TUNL_DEV1,
IP4_ADDR_TUNL_DEV1);
return 0;
fail:
return -1;
}
static void delete_ipip_tunnel(void)
{
SYS_NOFAIL("ip -n at_ns0 link delete dev %s", IPIP_TUNL_DEV0);
SYS_NOFAIL("ip -n at_ns0 fou del port 5555");
SYS_NOFAIL("ip link delete dev %s", IPIP_TUNL_DEV1);
SYS_NOFAIL("ip fou del port 5555");
}
static int add_xfrm_tunnel(void)
{
/* at_ns0 namespace
* at_ns0 -> root
*/
SYS(fail,
"ip netns exec at_ns0 "
"ip xfrm state add src %s dst %s proto esp "
"spi %d reqid 1 mode tunnel replay-window 42 "
"auth-trunc 'hmac(sha1)' %s 96 enc 'cbc(aes)' %s",
IP4_ADDR_VETH0, IP4_ADDR1_VETH1, XFRM_SPI_IN_TO_OUT, XFRM_AUTH, XFRM_ENC);
SYS(fail,
"ip netns exec at_ns0 "
"ip xfrm policy add src %s/32 dst %s/32 dir out "
"tmpl src %s dst %s proto esp reqid 1 "
"mode tunnel",
IP4_ADDR_TUNL_DEV0, IP4_ADDR_TUNL_DEV1, IP4_ADDR_VETH0, IP4_ADDR1_VETH1);
/* root -> at_ns0 */
SYS(fail,
"ip netns exec at_ns0 "
"ip xfrm state add src %s dst %s proto esp "
"spi %d reqid 2 mode tunnel "
"auth-trunc 'hmac(sha1)' %s 96 enc 'cbc(aes)' %s",
IP4_ADDR1_VETH1, IP4_ADDR_VETH0, XFRM_SPI_OUT_TO_IN, XFRM_AUTH, XFRM_ENC);
SYS(fail,
"ip netns exec at_ns0 "
"ip xfrm policy add src %s/32 dst %s/32 dir in "
"tmpl src %s dst %s proto esp reqid 2 "
"mode tunnel",
IP4_ADDR_TUNL_DEV1, IP4_ADDR_TUNL_DEV0, IP4_ADDR1_VETH1, IP4_ADDR_VETH0);
/* address & route */
SYS(fail, "ip netns exec at_ns0 ip addr add dev veth0 %s/32",
IP4_ADDR_TUNL_DEV0);
SYS(fail, "ip netns exec at_ns0 ip route add %s dev veth0 via %s src %s",
IP4_ADDR_TUNL_DEV1, IP4_ADDR1_VETH1, IP4_ADDR_TUNL_DEV0);
/* root namespace
* at_ns0 -> root
*/
SYS(fail,
"ip xfrm state add src %s dst %s proto esp "
"spi %d reqid 1 mode tunnel replay-window 42 "
"auth-trunc 'hmac(sha1)' %s 96 enc 'cbc(aes)' %s",
IP4_ADDR_VETH0, IP4_ADDR1_VETH1, XFRM_SPI_IN_TO_OUT, XFRM_AUTH, XFRM_ENC);
SYS(fail,
"ip xfrm policy add src %s/32 dst %s/32 dir in "
"tmpl src %s dst %s proto esp reqid 1 "
"mode tunnel",
IP4_ADDR_TUNL_DEV0, IP4_ADDR_TUNL_DEV1, IP4_ADDR_VETH0, IP4_ADDR1_VETH1);
/* root -> at_ns0 */
SYS(fail,
"ip xfrm state add src %s dst %s proto esp "
"spi %d reqid 2 mode tunnel "
"auth-trunc 'hmac(sha1)' %s 96 enc 'cbc(aes)' %s",
IP4_ADDR1_VETH1, IP4_ADDR_VETH0, XFRM_SPI_OUT_TO_IN, XFRM_AUTH, XFRM_ENC);
SYS(fail,
"ip xfrm policy add src %s/32 dst %s/32 dir out "
"tmpl src %s dst %s proto esp reqid 2 "
"mode tunnel",
IP4_ADDR_TUNL_DEV1, IP4_ADDR_TUNL_DEV0, IP4_ADDR1_VETH1, IP4_ADDR_VETH0);
/* address & route */
SYS(fail, "ip addr add dev veth1 %s/32", IP4_ADDR_TUNL_DEV1);
SYS(fail, "ip route add %s dev veth1 via %s src %s",
IP4_ADDR_TUNL_DEV0, IP4_ADDR_VETH0, IP4_ADDR_TUNL_DEV1);
return 0;
fail:
return -1;
}
static void delete_xfrm_tunnel(void)
{
SYS_NOFAIL("ip xfrm policy delete dir out src %s/32 dst %s/32",
IP4_ADDR_TUNL_DEV1, IP4_ADDR_TUNL_DEV0);
SYS_NOFAIL("ip xfrm policy delete dir in src %s/32 dst %s/32",
IP4_ADDR_TUNL_DEV0, IP4_ADDR_TUNL_DEV1);
SYS_NOFAIL("ip xfrm state delete src %s dst %s proto esp spi %d",
IP4_ADDR_VETH0, IP4_ADDR1_VETH1, XFRM_SPI_IN_TO_OUT);
SYS_NOFAIL("ip xfrm state delete src %s dst %s proto esp spi %d",
IP4_ADDR1_VETH1, IP4_ADDR_VETH0, XFRM_SPI_OUT_TO_IN);
}
static int test_ping(int family, const char *addr)
{
SYS(fail, "%s %s %s > /dev/null", ping_command(family), PING_ARGS, addr);
return 0;
fail:
return -1;
}
static int attach_tc_prog(struct bpf_tc_hook *hook, int igr_fd, int egr_fd)
{
DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts1, .handle = 1,
.priority = 1, .prog_fd = igr_fd);
DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts2, .handle = 1,
.priority = 1, .prog_fd = egr_fd);
int ret;
ret = bpf_tc_hook_create(hook);
if (!ASSERT_OK(ret, "create tc hook"))
return ret;
if (igr_fd >= 0) {
hook->attach_point = BPF_TC_INGRESS;
ret = bpf_tc_attach(hook, &opts1);
if (!ASSERT_OK(ret, "bpf_tc_attach")) {
bpf_tc_hook_destroy(hook);
return ret;
}
}
if (egr_fd >= 0) {
hook->attach_point = BPF_TC_EGRESS;
ret = bpf_tc_attach(hook, &opts2);
if (!ASSERT_OK(ret, "bpf_tc_attach")) {
bpf_tc_hook_destroy(hook);
return ret;
}
}
return 0;
}
static void test_vxlan_tunnel(void)
{
struct test_tunnel_kern *skel = NULL;
struct nstoken *nstoken;
int local_ip_map_fd = -1;
int set_src_prog_fd, get_src_prog_fd;
int set_dst_prog_fd;
int key = 0, ifindex = -1;
uint local_ip;
int err;
DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook,
.attach_point = BPF_TC_INGRESS);
/* add vxlan tunnel */
err = add_vxlan_tunnel();
if (!ASSERT_OK(err, "add vxlan tunnel"))
goto done;
/* load and attach bpf prog to tunnel dev tc hook point */
skel = test_tunnel_kern__open_and_load();
if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
goto done;
ifindex = if_nametoindex(VXLAN_TUNL_DEV1);
if (!ASSERT_NEQ(ifindex, 0, "vxlan11 ifindex"))
goto done;
tc_hook.ifindex = ifindex;
get_src_prog_fd = bpf_program__fd(skel->progs.vxlan_get_tunnel_src);
set_src_prog_fd = bpf_program__fd(skel->progs.vxlan_set_tunnel_src);
if (!ASSERT_GE(get_src_prog_fd, 0, "bpf_program__fd"))
goto done;
if (!ASSERT_GE(set_src_prog_fd, 0, "bpf_program__fd"))
goto done;
if (attach_tc_prog(&tc_hook, get_src_prog_fd, set_src_prog_fd))
goto done;
/* load and attach bpf prog to veth dev tc hook point */
ifindex = if_nametoindex("veth1");
if (!ASSERT_NEQ(ifindex, 0, "veth1 ifindex"))
goto done;
tc_hook.ifindex = ifindex;
set_dst_prog_fd = bpf_program__fd(skel->progs.veth_set_outer_dst);
if (!ASSERT_GE(set_dst_prog_fd, 0, "bpf_program__fd"))
goto done;
if (attach_tc_prog(&tc_hook, set_dst_prog_fd, -1))
goto done;
/* load and attach prog set_md to tunnel dev tc hook point at_ns0 */
nstoken = open_netns("at_ns0");
if (!ASSERT_OK_PTR(nstoken, "setns src"))
goto done;
ifindex = if_nametoindex(VXLAN_TUNL_DEV0);
if (!ASSERT_NEQ(ifindex, 0, "vxlan00 ifindex"))
goto done;
tc_hook.ifindex = ifindex;
set_dst_prog_fd = bpf_program__fd(skel->progs.vxlan_set_tunnel_dst);
if (!ASSERT_GE(set_dst_prog_fd, 0, "bpf_program__fd"))
goto done;
if (attach_tc_prog(&tc_hook, -1, set_dst_prog_fd))
goto done;
close_netns(nstoken);
/* use veth1 ip 2 as tunnel source ip */
local_ip_map_fd = bpf_map__fd(skel->maps.local_ip_map);
if (!ASSERT_GE(local_ip_map_fd, 0, "bpf_map__fd"))
goto done;
local_ip = IP4_ADDR2_HEX_VETH1;
err = bpf_map_update_elem(local_ip_map_fd, &key, &local_ip, BPF_ANY);
if (!ASSERT_OK(err, "update bpf local_ip_map"))
goto done;
/* ping test */
err = test_ping(AF_INET, IP4_ADDR_TUNL_DEV0);
if (!ASSERT_OK(err, "test_ping"))
goto done;
done:
/* delete vxlan tunnel */
delete_vxlan_tunnel();
if (local_ip_map_fd >= 0)
close(local_ip_map_fd);
if (skel)
test_tunnel_kern__destroy(skel);
}
static void test_ip6vxlan_tunnel(void)
{
struct test_tunnel_kern *skel = NULL;
struct nstoken *nstoken;
int local_ip_map_fd = -1;
int set_src_prog_fd, get_src_prog_fd;
int set_dst_prog_fd;
int key = 0, ifindex = -1;
uint local_ip;
int err;
DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook,
.attach_point = BPF_TC_INGRESS);
/* add vxlan tunnel */
err = add_ip6vxlan_tunnel();
if (!ASSERT_OK(err, "add_ip6vxlan_tunnel"))
goto done;
/* load and attach bpf prog to tunnel dev tc hook point */
skel = test_tunnel_kern__open_and_load();
if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
goto done;
ifindex = if_nametoindex(IP6VXLAN_TUNL_DEV1);
if (!ASSERT_NEQ(ifindex, 0, "ip6vxlan11 ifindex"))
goto done;
tc_hook.ifindex = ifindex;
get_src_prog_fd = bpf_program__fd(skel->progs.ip6vxlan_get_tunnel_src);
set_src_prog_fd = bpf_program__fd(skel->progs.ip6vxlan_set_tunnel_src);
if (!ASSERT_GE(set_src_prog_fd, 0, "bpf_program__fd"))
goto done;
if (!ASSERT_GE(get_src_prog_fd, 0, "bpf_program__fd"))
goto done;
if (attach_tc_prog(&tc_hook, get_src_prog_fd, set_src_prog_fd))
goto done;
/* load and attach prog set_md to tunnel dev tc hook point at_ns0 */
nstoken = open_netns("at_ns0");
if (!ASSERT_OK_PTR(nstoken, "setns src"))
goto done;
ifindex = if_nametoindex(IP6VXLAN_TUNL_DEV0);
if (!ASSERT_NEQ(ifindex, 0, "ip6vxlan00 ifindex"))
goto done;
tc_hook.ifindex = ifindex;
set_dst_prog_fd = bpf_program__fd(skel->progs.ip6vxlan_set_tunnel_dst);
if (!ASSERT_GE(set_dst_prog_fd, 0, "bpf_program__fd"))
goto done;
if (attach_tc_prog(&tc_hook, -1, set_dst_prog_fd))
goto done;
close_netns(nstoken);
/* use veth1 ip 2 as tunnel source ip */
local_ip_map_fd = bpf_map__fd(skel->maps.local_ip_map);
if (!ASSERT_GE(local_ip_map_fd, 0, "get local_ip_map fd"))
goto done;
local_ip = IP6_ADDR2_HEX_VETH1;
err = bpf_map_update_elem(local_ip_map_fd, &key, &local_ip, BPF_ANY);
if (!ASSERT_OK(err, "update bpf local_ip_map"))
goto done;
/* ping test */
err = test_ping(AF_INET, IP4_ADDR_TUNL_DEV0);
if (!ASSERT_OK(err, "test_ping"))
goto done;
done:
/* delete ipv6 vxlan tunnel */
delete_ip6vxlan_tunnel();
if (local_ip_map_fd >= 0)
close(local_ip_map_fd);
if (skel)
test_tunnel_kern__destroy(skel);
}
static void test_ipip_tunnel(enum ipip_encap encap)
{
struct test_tunnel_kern *skel = NULL;
struct nstoken *nstoken;
int set_src_prog_fd, get_src_prog_fd;
int ifindex = -1;
int err;
DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook,
.attach_point = BPF_TC_INGRESS);
/* add ipip tunnel */
err = add_ipip_tunnel(encap);
if (!ASSERT_OK(err, "add_ipip_tunnel"))
goto done;
/* load and attach bpf prog to tunnel dev tc hook point */
skel = test_tunnel_kern__open_and_load();
if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
goto done;
ifindex = if_nametoindex(IPIP_TUNL_DEV1);
if (!ASSERT_NEQ(ifindex, 0, "ipip11 ifindex"))
goto done;
tc_hook.ifindex = ifindex;
switch (encap) {
case FOU:
get_src_prog_fd = bpf_program__fd(
skel->progs.ipip_encap_get_tunnel);
set_src_prog_fd = bpf_program__fd(
skel->progs.ipip_fou_set_tunnel);
break;
case GUE:
get_src_prog_fd = bpf_program__fd(
skel->progs.ipip_encap_get_tunnel);
set_src_prog_fd = bpf_program__fd(
skel->progs.ipip_gue_set_tunnel);
break;
default:
get_src_prog_fd = bpf_program__fd(
skel->progs.ipip_get_tunnel);
set_src_prog_fd = bpf_program__fd(
skel->progs.ipip_set_tunnel);
}
if (!ASSERT_GE(set_src_prog_fd, 0, "bpf_program__fd"))
goto done;
if (!ASSERT_GE(get_src_prog_fd, 0, "bpf_program__fd"))
goto done;
if (attach_tc_prog(&tc_hook, get_src_prog_fd, set_src_prog_fd))
goto done;
/* ping from root namespace test */
err = test_ping(AF_INET, IP4_ADDR_TUNL_DEV0);
if (!ASSERT_OK(err, "test_ping"))
goto done;
/* ping from at_ns0 namespace test */
nstoken = open_netns("at_ns0");
if (!ASSERT_OK_PTR(nstoken, "setns"))
goto done;
err = test_ping(AF_INET, IP4_ADDR_TUNL_DEV1);
if (!ASSERT_OK(err, "test_ping"))
goto done;
close_netns(nstoken);
done:
/* delete ipip tunnel */
delete_ipip_tunnel();
if (skel)
test_tunnel_kern__destroy(skel);
}
static void test_xfrm_tunnel(void)
{
DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook,
.attach_point = BPF_TC_INGRESS);
LIBBPF_OPTS(bpf_xdp_attach_opts, opts);
struct test_tunnel_kern *skel = NULL;
struct nstoken *nstoken;
int xdp_prog_fd;
int tc_prog_fd;
int ifindex;
int err;
err = add_xfrm_tunnel();
if (!ASSERT_OK(err, "add_xfrm_tunnel"))
return;
skel = test_tunnel_kern__open_and_load();
if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
goto done;
ifindex = if_nametoindex("veth1");
if (!ASSERT_NEQ(ifindex, 0, "veth1 ifindex"))
goto done;
/* attach tc prog to tunnel dev */
tc_hook.ifindex = ifindex;
tc_prog_fd = bpf_program__fd(skel->progs.xfrm_get_state);
if (!ASSERT_GE(tc_prog_fd, 0, "bpf_program__fd"))
goto done;
if (attach_tc_prog(&tc_hook, tc_prog_fd, -1))
goto done;
/* attach xdp prog to tunnel dev */
xdp_prog_fd = bpf_program__fd(skel->progs.xfrm_get_state_xdp);
if (!ASSERT_GE(xdp_prog_fd, 0, "bpf_program__fd"))
goto done;
err = bpf_xdp_attach(ifindex, xdp_prog_fd, XDP_FLAGS_REPLACE, &opts);
if (!ASSERT_OK(err, "bpf_xdp_attach"))
goto done;
/* ping from at_ns0 namespace test */
nstoken = open_netns("at_ns0");
if (!ASSERT_OK_PTR(nstoken, "setns"))
goto done;
err = test_ping(AF_INET, IP4_ADDR_TUNL_DEV1);
close_netns(nstoken);
if (!ASSERT_OK(err, "test_ping"))
goto done;
if (!ASSERT_EQ(skel->bss->xfrm_reqid, 1, "req_id"))
goto done;
if (!ASSERT_EQ(skel->bss->xfrm_spi, XFRM_SPI_IN_TO_OUT, "spi"))
goto done;
if (!ASSERT_EQ(skel->bss->xfrm_remote_ip, 0xac100164, "remote_ip"))
goto done;
if (!ASSERT_EQ(skel->bss->xfrm_replay_window, 42, "replay_window"))
goto done;
done:
delete_xfrm_tunnel();
if (skel)
test_tunnel_kern__destroy(skel);
}
#define RUN_TEST(name, ...) \
({ \
if (test__start_subtest(#name)) { \
config_device(); \
test_ ## name(__VA_ARGS__); \
cleanup(); \
} \
})
static void *test_tunnel_run_tests(void *arg)
{
RUN_TEST(vxlan_tunnel);
RUN_TEST(ip6vxlan_tunnel);
RUN_TEST(ipip_tunnel, NONE);
RUN_TEST(ipip_tunnel, FOU);
RUN_TEST(ipip_tunnel, GUE);
RUN_TEST(xfrm_tunnel);
return NULL;
}
void test_tunnel(void)
{
pthread_t test_thread;
int err;
/* Run the tests in their own thread to isolate the namespace changes
* so they do not affect the environment of other tests.
* (specifically needed because of unshare(CLONE_NEWNS) in open_netns())
*/
err = pthread_create(&test_thread, NULL, &test_tunnel_run_tests, NULL);
if (ASSERT_OK(err, "pthread_create"))
ASSERT_OK(pthread_join(test_thread, NULL), "pthread_join");
}