File Index
Files and directories
Sign in
FILE

test_tunnel.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/bpf/prog_tests/test_tunnel.c C
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2
   3/*
   4 * End-to-end eBPF tunnel test suite
   5 *   The file tests BPF network tunnel implementation.
   6 *
   7 * Topology:
   8 * ---------
   9 *     root namespace   |     at_ns0 namespace
  10 *                       |
  11 *       -----------     |     -----------
  12 *       | tnl dev |     |     | tnl dev |  (overlay network)
  13 *       -----------     |     -----------
  14 *       metadata-mode   |     metadata-mode
  15 *        with bpf       |       with bpf
  16 *                       |
  17 *       ----------      |     ----------
  18 *       |  veth1  | --------- |  veth0  |  (underlay network)
  19 *       ----------    peer    ----------
  20 *
  21 *
  22 *  Device Configuration
  23 *  --------------------
  24 *  root namespace with metadata-mode tunnel + BPF
  25 *  Device names and addresses:
  26 *	veth1 IP 1: 172.16.1.200, IPv6: 00::22 (underlay)
  27 *		IP 2: 172.16.1.20, IPv6: 00::bb (underlay)
  28 *	tunnel dev <type>11, ex: gre11, IPv4: 10.1.1.200, IPv6: 1::22 (overlay)
  29 *
  30 *  Namespace at_ns0 with native tunnel
  31 *  Device names and addresses:
  32 *	veth0 IPv4: 172.16.1.100, IPv6: 00::11 (underlay)
  33 *	tunnel dev <type>00, ex: gre00, IPv4: 10.1.1.100, IPv6: 1::11 (overlay)
  34 *
  35 *
  36 * End-to-end ping packet flow
  37 *  ---------------------------
  38 *  Most of the tests start by namespace creation, device configuration,
  39 *  then ping the underlay and overlay network.  When doing 'ping 10.1.1.100'
  40 *  from root namespace, the following operations happen:
  41 *  1) Route lookup shows 10.1.1.100/24 belongs to tnl dev, fwd to tnl dev.
  42 *  2) Tnl device's egress BPF program is triggered and set the tunnel metadata,
  43 *     with local_ip=172.16.1.200, remote_ip=172.16.1.100. BPF program choose
  44 *     the primary or secondary ip of veth1 as the local ip of tunnel. The
  45 *     choice is made based on the value of bpf map local_ip_map.
  46 *  3) Outer tunnel header is prepended and route the packet to veth1's egress.
  47 *  4) veth0's ingress queue receive the tunneled packet at namespace at_ns0.
  48 *  5) Tunnel protocol handler, ex: vxlan_rcv, decap the packet.
  49 *  6) Forward the packet to the overlay tnl dev.
  50 */
  51
  52#include <arpa/inet.h>
  53#include <linux/if_link.h>
  54#include <linux/if_tun.h>
  55#include <linux/limits.h>
  56#include <linux/sysctl.h>
  57#include <linux/time_types.h>
  58#include <linux/net_tstamp.h>
  59#include <net/if.h>
  60#include <stdbool.h>
  61#include <stdio.h>
  62#include <sys/stat.h>
  63#include <unistd.h>
  64
  65#include "test_progs.h"
  66#include "network_helpers.h"
  67#include "test_tunnel_kern.skel.h"
  68
  69#define IP4_ADDR_VETH0 "172.16.1.100"
  70#define IP4_ADDR1_VETH1 "172.16.1.200"
  71#define IP4_ADDR2_VETH1 "172.16.1.20"
  72#define IP4_ADDR_TUNL_DEV0 "10.1.1.100"
  73#define IP4_ADDR_TUNL_DEV1 "10.1.1.200"
  74#define IP6_ADDR_TUNL_DEV0 "fc80::100"
  75#define IP6_ADDR_TUNL_DEV1 "fc80::200"
  76
  77#define IP6_ADDR_VETH0 "::11"
  78#define IP6_ADDR1_VETH1 "::22"
  79#define IP6_ADDR2_VETH1 "::bb"
  80
  81#define IP4_ADDR1_HEX_VETH1 0xac1001c8
  82#define IP4_ADDR2_HEX_VETH1 0xac100114
  83#define IP6_ADDR1_HEX_VETH1 0x22
  84#define IP6_ADDR2_HEX_VETH1 0xbb
  85
  86#define MAC_TUNL_DEV0 "52:54:00:d9:01:00"
  87#define MAC_TUNL_DEV1 "52:54:00:d9:02:00"
  88#define MAC_VETH1 "52:54:00:d9:03:00"
  89
  90#define VXLAN_TUNL_DEV0 "vxlan00"
  91#define VXLAN_TUNL_DEV1 "vxlan11"
  92#define IP6VXLAN_TUNL_DEV0 "ip6vxlan00"
  93#define IP6VXLAN_TUNL_DEV1 "ip6vxlan11"
  94
  95#define IPIP_TUNL_DEV0 "ipip00"
  96#define IPIP_TUNL_DEV1 "ipip11"
  97
  98#define XFRM_AUTH "0x1111111111111111111111111111111111111111"
  99#define XFRM_ENC "0x22222222222222222222222222222222"
 100#define XFRM_SPI_IN_TO_OUT 0x1
 101#define XFRM_SPI_OUT_TO_IN 0x2
 102
 103#define GRE_TUNL_DEV0 "gre00"
 104#define GRE_TUNL_DEV1 "gre11"
 105
 106#define IP6GRE_TUNL_DEV0 "ip6gre00"
 107#define IP6GRE_TUNL_DEV1 "ip6gre11"
 108
 109#define ERSPAN_TUNL_DEV0 "erspan00"
 110#define ERSPAN_TUNL_DEV1 "erspan11"
 111
 112#define IP6ERSPAN_TUNL_DEV0 "ip6erspan00"
 113#define IP6ERSPAN_TUNL_DEV1 "ip6erspan11"
 114
 115#define GENEVE_TUNL_DEV0 "geneve00"
 116#define GENEVE_TUNL_DEV1 "geneve11"
 117
 118#define IP6GENEVE_TUNL_DEV0 "ip6geneve00"
 119#define IP6GENEVE_TUNL_DEV1 "ip6geneve11"
 120
 121#define IP6TNL_TUNL_DEV0 "ip6tnl00"
 122#define IP6TNL_TUNL_DEV1 "ip6tnl11"
 123
 124#define PING_ARGS "-i 0.01 -c 3 -w 10 -q"
 125
 126static int config_device(void)
 127{
 128	SYS(fail, "ip netns add at_ns0");
 129	SYS(fail, "ip link add veth0 address " MAC_VETH1 " type veth peer name veth1");
 130	SYS(fail, "ip link set veth0 netns at_ns0");
 131	SYS(fail, "ip addr add " IP4_ADDR1_VETH1 "/24 dev veth1");
 132	SYS(fail, "ip link set dev veth1 up mtu 1500");
 133	SYS(fail, "ip netns exec at_ns0 ip addr add " IP4_ADDR_VETH0 "/24 dev veth0");
 134	SYS(fail, "ip netns exec at_ns0 ip link set dev veth0 up mtu 1500");
 135
 136	return 0;
 137fail:
 138	return -1;
 139}
 140
 141static void cleanup(void)
 142{
 143	SYS_NOFAIL("test -f /var/run/netns/at_ns0 && ip netns delete at_ns0");
 144	SYS_NOFAIL("ip link del veth1");
 145	SYS_NOFAIL("ip link del %s", VXLAN_TUNL_DEV1);
 146	SYS_NOFAIL("ip link del %s", IP6VXLAN_TUNL_DEV1);
 147}
 148
 149static int add_vxlan_tunnel(void)
 150{
 151	/* at_ns0 namespace */
 152	SYS(fail, "ip netns exec at_ns0 ip link add dev %s type vxlan external gbp dstport 4789",
 153	    VXLAN_TUNL_DEV0);
 154	SYS(fail, "ip netns exec at_ns0 ip link set dev %s address %s up",
 155	    VXLAN_TUNL_DEV0, MAC_TUNL_DEV0);
 156	SYS(fail, "ip netns exec at_ns0 ip addr add dev %s %s/24",
 157	    VXLAN_TUNL_DEV0, IP4_ADDR_TUNL_DEV0);
 158	SYS(fail, "ip netns exec at_ns0 ip neigh add %s lladdr %s dev %s",
 159	    IP4_ADDR_TUNL_DEV1, MAC_TUNL_DEV1, VXLAN_TUNL_DEV0);
 160	SYS(fail, "ip netns exec at_ns0 ip neigh add %s lladdr %s dev veth0",
 161	    IP4_ADDR2_VETH1, MAC_VETH1);
 162
 163	/* root namespace */
 164	SYS(fail, "ip link add dev %s type vxlan external gbp dstport 4789",
 165	    VXLAN_TUNL_DEV1);
 166	SYS(fail, "ip link set dev %s address %s up", VXLAN_TUNL_DEV1, MAC_TUNL_DEV1);
 167	SYS(fail, "ip addr add dev %s %s/24", VXLAN_TUNL_DEV1, IP4_ADDR_TUNL_DEV1);
 168	SYS(fail, "ip neigh add %s lladdr %s dev %s",
 169	    IP4_ADDR_TUNL_DEV0, MAC_TUNL_DEV0, VXLAN_TUNL_DEV1);
 170
 171	return 0;
 172fail:
 173	return -1;
 174}
 175
 176static void delete_vxlan_tunnel(void)
 177{
 178	SYS_NOFAIL("ip netns exec at_ns0 ip link delete dev %s",
 179		   VXLAN_TUNL_DEV0);
 180	SYS_NOFAIL("ip link delete dev %s", VXLAN_TUNL_DEV1);
 181}
 182
 183static int add_ip6vxlan_tunnel(void)
 184{
 185	SYS(fail, "ip netns exec at_ns0 ip -6 addr add %s/96 dev veth0",
 186	    IP6_ADDR_VETH0);
 187	SYS(fail, "ip netns exec at_ns0 ip link set dev veth0 up");
 188	SYS(fail, "ip -6 addr add %s/96 dev veth1", IP6_ADDR1_VETH1);
 189	SYS(fail, "ip -6 addr add %s/96 dev veth1", IP6_ADDR2_VETH1);
 190	SYS(fail, "ip link set dev veth1 up");
 191
 192	/* at_ns0 namespace */
 193	SYS(fail, "ip netns exec at_ns0 ip link add dev %s type vxlan external dstport 4789",
 194	    IP6VXLAN_TUNL_DEV0);
 195	SYS(fail, "ip netns exec at_ns0 ip addr add dev %s %s/24",
 196	    IP6VXLAN_TUNL_DEV0, IP4_ADDR_TUNL_DEV0);
 197	SYS(fail, "ip netns exec at_ns0 ip link set dev %s address %s up",
 198	    IP6VXLAN_TUNL_DEV0, MAC_TUNL_DEV0);
 199
 200	/* root namespace */
 201	SYS(fail, "ip link add dev %s type vxlan external dstport 4789",
 202	    IP6VXLAN_TUNL_DEV1);
 203	SYS(fail, "ip addr add dev %s %s/24", IP6VXLAN_TUNL_DEV1, IP4_ADDR_TUNL_DEV1);
 204	SYS(fail, "ip link set dev %s address %s up",
 205	    IP6VXLAN_TUNL_DEV1, MAC_TUNL_DEV1);
 206
 207	return 0;
 208fail:
 209	return -1;
 210}
 211
 212static void delete_ip6vxlan_tunnel(void)
 213{
 214	SYS_NOFAIL("ip netns exec at_ns0 ip -6 addr delete %s/96 dev veth0",
 215		   IP6_ADDR_VETH0);
 216	SYS_NOFAIL("ip -6 addr delete %s/96 dev veth1", IP6_ADDR1_VETH1);
 217	SYS_NOFAIL("ip -6 addr delete %s/96 dev veth1", IP6_ADDR2_VETH1);
 218	SYS_NOFAIL("ip netns exec at_ns0 ip link delete dev %s",
 219		   IP6VXLAN_TUNL_DEV0);
 220	SYS_NOFAIL("ip link delete dev %s", IP6VXLAN_TUNL_DEV1);
 221}
 222
 223enum ipip_encap {
 224	NONE	= 0,
 225	FOU	= 1,
 226	GUE	= 2,
 227};
 228
 229static int set_ipip_encap(const char *ipproto, const char *type)
 230{
 231	SYS(fail, "ip -n at_ns0 fou add port 5555 %s", ipproto);
 232	SYS(fail, "ip -n at_ns0 link set dev %s type ipip encap %s",
 233	    IPIP_TUNL_DEV0, type);
 234	SYS(fail, "ip -n at_ns0 link set dev %s type ipip encap-dport 5555",
 235	    IPIP_TUNL_DEV0);
 236
 237	return 0;
 238fail:
 239	return -1;
 240}
 241
 242static int set_ipv4_addr(const char *dev0, const char *dev1)
 243{
 244	SYS(fail, "ip -n at_ns0 link set dev %s up", dev0);
 245	SYS(fail, "ip -n at_ns0 addr add dev %s %s/24", dev0, IP4_ADDR_TUNL_DEV0);
 246	SYS(fail, "ip link set dev %s up", dev1);
 247	SYS(fail, "ip addr add dev %s %s/24", dev1, IP4_ADDR_TUNL_DEV1);
 248
 249	return 0;
 250fail:
 251	return 1;
 252}
 253
 254static int add_ipip_tunnel(enum ipip_encap encap)
 255{
 256	int err;
 257	const char *ipproto, *type;
 258
 259	switch (encap) {
 260	case FOU:
 261		ipproto = "ipproto 4";
 262		type = "fou";
 263		break;
 264	case GUE:
 265		ipproto = "gue";
 266		type = ipproto;
 267		break;
 268	default:
 269		ipproto = NULL;
 270		type = ipproto;
 271	}
 272
 273	/* at_ns0 namespace */
 274	SYS(fail, "ip -n at_ns0 link add dev %s type ipip local %s remote %s",
 275	    IPIP_TUNL_DEV0, IP4_ADDR_VETH0, IP4_ADDR1_VETH1);
 276
 277	if (type && ipproto) {
 278		err = set_ipip_encap(ipproto, type);
 279		if (!ASSERT_OK(err, "set_ipip_encap"))
 280			goto fail;
 281	}
 282
 283	SYS(fail, "ip -n at_ns0 link set dev %s up", IPIP_TUNL_DEV0);
 284	SYS(fail, "ip -n at_ns0 addr add dev %s %s/24",
 285	    IPIP_TUNL_DEV0, IP4_ADDR_TUNL_DEV0);
 286
 287	/* root namespace */
 288	if (type && ipproto)
 289		SYS(fail, "ip fou add port 5555 %s", ipproto);
 290	SYS(fail, "ip link add dev %s type ipip external", IPIP_TUNL_DEV1);
 291	SYS(fail, "ip link set dev %s up", IPIP_TUNL_DEV1);
 292	SYS(fail, "ip addr add dev %s %s/24", IPIP_TUNL_DEV1,
 293	    IP4_ADDR_TUNL_DEV1);
 294
 295	return 0;
 296fail:
 297	return -1;
 298}
 299
 300static void delete_ipip_tunnel(void)
 301{
 302	SYS_NOFAIL("ip -n at_ns0 link delete dev %s", IPIP_TUNL_DEV0);
 303	SYS_NOFAIL("ip -n at_ns0 fou del port 5555");
 304	SYS_NOFAIL("ip link delete dev %s", IPIP_TUNL_DEV1);
 305	SYS_NOFAIL("ip fou del port 5555");
 306}
 307
 308static int add_xfrm_tunnel(void)
 309{
 310	/* at_ns0 namespace
 311	 * at_ns0 -> root
 312	 */
 313	SYS(fail,
 314	    "ip netns exec at_ns0 "
 315		"ip xfrm state add src %s dst %s proto esp "
 316			"spi %d reqid 1 mode tunnel replay-window 42 "
 317			"auth-trunc 'hmac(sha1)' %s 96 enc 'cbc(aes)' %s",
 318	    IP4_ADDR_VETH0, IP4_ADDR1_VETH1, XFRM_SPI_IN_TO_OUT, XFRM_AUTH, XFRM_ENC);
 319	SYS(fail,
 320	    "ip netns exec at_ns0 "
 321		"ip xfrm policy add src %s/32 dst %s/32 dir out "
 322			"tmpl src %s dst %s proto esp reqid 1 "
 323			"mode tunnel",
 324	    IP4_ADDR_TUNL_DEV0, IP4_ADDR_TUNL_DEV1, IP4_ADDR_VETH0, IP4_ADDR1_VETH1);
 325
 326	/* root -> at_ns0 */
 327	SYS(fail,
 328	    "ip netns exec at_ns0 "
 329		"ip xfrm state add src %s dst %s proto esp "
 330			"spi %d reqid 2 mode tunnel "
 331			"auth-trunc 'hmac(sha1)' %s 96 enc 'cbc(aes)' %s",
 332	    IP4_ADDR1_VETH1, IP4_ADDR_VETH0, XFRM_SPI_OUT_TO_IN, XFRM_AUTH, XFRM_ENC);
 333	SYS(fail,
 334	    "ip netns exec at_ns0 "
 335		"ip xfrm policy add src %s/32 dst %s/32 dir in "
 336			"tmpl src %s dst %s proto esp reqid 2 "
 337			"mode tunnel",
 338	    IP4_ADDR_TUNL_DEV1, IP4_ADDR_TUNL_DEV0, IP4_ADDR1_VETH1, IP4_ADDR_VETH0);
 339
 340	/* address & route */
 341	SYS(fail, "ip netns exec at_ns0 ip addr add dev veth0 %s/32",
 342	    IP4_ADDR_TUNL_DEV0);
 343	SYS(fail, "ip netns exec at_ns0 ip route add %s dev veth0 via %s src %s",
 344	    IP4_ADDR_TUNL_DEV1, IP4_ADDR1_VETH1, IP4_ADDR_TUNL_DEV0);
 345
 346	/* root namespace
 347	 * at_ns0 -> root
 348	 */
 349	SYS(fail,
 350	    "ip xfrm state add src %s dst %s proto esp "
 351		    "spi %d reqid 1 mode tunnel replay-window 42 "
 352		    "auth-trunc 'hmac(sha1)' %s 96  enc 'cbc(aes)' %s",
 353	    IP4_ADDR_VETH0, IP4_ADDR1_VETH1, XFRM_SPI_IN_TO_OUT, XFRM_AUTH, XFRM_ENC);
 354	SYS(fail,
 355	    "ip xfrm policy add src %s/32 dst %s/32 dir in "
 356		    "tmpl src %s dst %s proto esp reqid 1 "
 357		    "mode tunnel",
 358	    IP4_ADDR_TUNL_DEV0, IP4_ADDR_TUNL_DEV1, IP4_ADDR_VETH0, IP4_ADDR1_VETH1);
 359
 360	/* root -> at_ns0 */
 361	SYS(fail,
 362	    "ip xfrm state add src %s dst %s proto esp "
 363		    "spi %d reqid 2 mode tunnel "
 364		    "auth-trunc 'hmac(sha1)' %s 96  enc 'cbc(aes)' %s",
 365	    IP4_ADDR1_VETH1, IP4_ADDR_VETH0, XFRM_SPI_OUT_TO_IN, XFRM_AUTH, XFRM_ENC);
 366	SYS(fail,
 367	    "ip xfrm policy add src %s/32 dst %s/32 dir out "
 368		    "tmpl src %s dst %s proto esp reqid 2 "
 369		    "mode tunnel",
 370	    IP4_ADDR_TUNL_DEV1, IP4_ADDR_TUNL_DEV0, IP4_ADDR1_VETH1, IP4_ADDR_VETH0);
 371
 372	/* address & route */
 373	SYS(fail, "ip addr add dev veth1 %s/32", IP4_ADDR_TUNL_DEV1);
 374	SYS(fail, "ip route add %s dev veth1 via %s src %s",
 375	    IP4_ADDR_TUNL_DEV0, IP4_ADDR_VETH0, IP4_ADDR_TUNL_DEV1);
 376
 377	return 0;
 378fail:
 379	return -1;
 380}
 381
 382static void delete_xfrm_tunnel(void)
 383{
 384	SYS_NOFAIL("ip xfrm policy delete dir out src %s/32 dst %s/32",
 385		   IP4_ADDR_TUNL_DEV1, IP4_ADDR_TUNL_DEV0);
 386	SYS_NOFAIL("ip xfrm policy delete dir in src %s/32 dst %s/32",
 387		   IP4_ADDR_TUNL_DEV0, IP4_ADDR_TUNL_DEV1);
 388	SYS_NOFAIL("ip xfrm state delete src %s dst %s proto esp spi %d",
 389		   IP4_ADDR_VETH0, IP4_ADDR1_VETH1, XFRM_SPI_IN_TO_OUT);
 390	SYS_NOFAIL("ip xfrm state delete src %s dst %s proto esp spi %d",
 391		   IP4_ADDR1_VETH1, IP4_ADDR_VETH0, XFRM_SPI_OUT_TO_IN);
 392}
 393
 394static int add_ipv4_tunnel(const char *dev0, const char *dev1,
 395			   const char *type, const char *opt)
 396{
 397	if (!type || !opt || !dev0 || !dev1)
 398		return -1;
 399
 400	SYS(fail, "ip -n at_ns0 link add dev %s type %s %s local %s remote %s",
 401	    dev0, type, opt, IP4_ADDR_VETH0, IP4_ADDR1_VETH1);
 402
 403	SYS(fail, "ip link add dev %s type %s external", dev1, type);
 404
 405	return set_ipv4_addr(dev0, dev1);
 406fail:
 407	return -1;
 408}
 409
 410static void delete_tunnel(const char *dev0, const char *dev1)
 411{
 412	if (!dev0 || !dev1)
 413		return;
 414
 415	SYS_NOFAIL("ip netns exec at_ns0 ip link delete dev %s", dev0);
 416	SYS_NOFAIL("ip link delete dev %s", dev1);
 417}
 418
 419static int set_ipv6_addr(const char *dev0, const char *dev1)
 420{
 421	/* disable IPv6 DAD because it might take too long and fail tests */
 422	SYS(fail, "ip -n at_ns0 addr add %s/96 dev veth0 nodad", IP6_ADDR_VETH0);
 423	SYS(fail, "ip -n at_ns0 link set dev veth0 up");
 424	SYS(fail, "ip addr add %s/96 dev veth1 nodad", IP6_ADDR1_VETH1);
 425	SYS(fail, "ip link set dev veth1 up");
 426
 427	SYS(fail, "ip -n at_ns0 addr add dev %s %s/24", dev0, IP4_ADDR_TUNL_DEV0);
 428	SYS(fail, "ip -n at_ns0 addr add dev %s %s/96 nodad", dev0, IP6_ADDR_TUNL_DEV0);
 429	SYS(fail, "ip -n at_ns0 link set dev %s up", dev0);
 430
 431	SYS(fail, "ip addr add dev %s %s/24", dev1, IP4_ADDR_TUNL_DEV1);
 432	SYS(fail, "ip addr add dev %s %s/96 nodad", dev1, IP6_ADDR_TUNL_DEV1);
 433	SYS(fail, "ip link set dev %s up", dev1);
 434	return 0;
 435fail:
 436	return 1;
 437}
 438
 439static int add_ipv6_tunnel(const char *dev0, const char *dev1,
 440			   const char *type, const char *opt)
 441{
 442	if (!type || !opt || !dev0 || !dev1)
 443		return -1;
 444
 445	SYS(fail, "ip -n at_ns0 link add dev %s type %s %s local %s remote %s",
 446	    dev0, type, opt, IP6_ADDR_VETH0, IP6_ADDR1_VETH1);
 447
 448	SYS(fail, "ip link add dev %s type %s external", dev1, type);
 449
 450	return set_ipv6_addr(dev0, dev1);
 451fail:
 452	return -1;
 453}
 454
 455static int add_geneve_tunnel(const char *dev0, const char *dev1,
 456			     const char *type, const char *opt)
 457{
 458	if (!type || !opt || !dev0 || !dev1)
 459		return -1;
 460
 461	SYS(fail, "ip -n at_ns0 link add dev %s type %s id 2 %s remote %s",
 462	    dev0, type, opt, IP4_ADDR1_VETH1);
 463
 464	SYS(fail, "ip link add dev %s type %s %s external", dev1, type, opt);
 465
 466	return set_ipv4_addr(dev0, dev1);
 467fail:
 468	return -1;
 469}
 470
 471static int add_ip6geneve_tunnel(const char *dev0, const char *dev1,
 472			     const char *type, const char *opt)
 473{
 474	if (!type || !opt || !dev0 || !dev1)
 475		return -1;
 476
 477	SYS(fail, "ip -n at_ns0 link add dev %s type %s id 22 %s remote %s",
 478	    dev0, type, opt, IP6_ADDR1_VETH1);
 479
 480	SYS(fail, "ip link add dev %s type %s %s external", dev1, type, opt);
 481
 482	return set_ipv6_addr(dev0, dev1);
 483fail:
 484	return -1;
 485}
 486
 487static int test_ping(int family, const char *addr)
 488{
 489	SYS(fail, "%s %s %s > /dev/null", ping_command(family), PING_ARGS, addr);
 490	return 0;
 491fail:
 492	return -1;
 493}
 494
 495static void ping_dev0(void)
 496{
 497	/* ping from root namespace test */
 498	test_ping(AF_INET, IP4_ADDR_TUNL_DEV0);
 499}
 500
 501static void ping_dev1(void)
 502{
 503	struct nstoken *nstoken;
 504
 505	/* ping from at_ns0 namespace test */
 506	nstoken = open_netns("at_ns0");
 507	if (!ASSERT_OK_PTR(nstoken, "setns"))
 508		return;
 509
 510	test_ping(AF_INET, IP4_ADDR_TUNL_DEV1);
 511	close_netns(nstoken);
 512}
 513
 514static void ping6_veth0(void)
 515{
 516	test_ping(AF_INET6, IP6_ADDR_VETH0);
 517}
 518
 519static void ping6_dev0(void)
 520{
 521	test_ping(AF_INET6, IP6_ADDR_TUNL_DEV0);
 522}
 523
 524static void ping6_dev1(void)
 525{
 526	struct nstoken *nstoken;
 527
 528	/* ping from at_ns0 namespace test */
 529	nstoken = open_netns("at_ns0");
 530	if (!ASSERT_OK_PTR(nstoken, "setns"))
 531		return;
 532
 533	test_ping(AF_INET, IP6_ADDR_TUNL_DEV1);
 534	close_netns(nstoken);
 535}
 536
 537static void test_vxlan_tunnel(void)
 538{
 539	struct test_tunnel_kern *skel = NULL;
 540	struct nstoken *nstoken;
 541	int local_ip_map_fd = -1;
 542	int set_src_prog_fd, get_src_prog_fd;
 543	int set_dst_prog_fd;
 544	int key = 0;
 545	uint local_ip;
 546	int err;
 547
 548	/* add vxlan tunnel */
 549	err = add_vxlan_tunnel();
 550	if (!ASSERT_OK(err, "add vxlan tunnel"))
 551		goto done;
 552
 553	/* load and attach bpf prog to tunnel dev tc hook point */
 554	skel = test_tunnel_kern__open_and_load();
 555	if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
 556		goto done;
 557	get_src_prog_fd = bpf_program__fd(skel->progs.vxlan_get_tunnel_src);
 558	set_src_prog_fd = bpf_program__fd(skel->progs.vxlan_set_tunnel_src);
 559	if (tc_prog_attach(VXLAN_TUNL_DEV1, get_src_prog_fd, set_src_prog_fd))
 560		goto done;
 561
 562	/* load and attach bpf prog to veth dev tc hook point */
 563	set_dst_prog_fd = bpf_program__fd(skel->progs.veth_set_outer_dst);
 564	if (tc_prog_attach("veth1", set_dst_prog_fd, -1))
 565		goto done;
 566
 567	/* load and attach prog set_md to tunnel dev tc hook point at_ns0 */
 568	nstoken = open_netns("at_ns0");
 569	if (!ASSERT_OK_PTR(nstoken, "setns src"))
 570		goto done;
 571	set_dst_prog_fd = bpf_program__fd(skel->progs.vxlan_set_tunnel_dst);
 572	if (tc_prog_attach(VXLAN_TUNL_DEV0, -1, set_dst_prog_fd))
 573		goto done;
 574	close_netns(nstoken);
 575
 576	/* use veth1 ip 2 as tunnel source ip */
 577	local_ip_map_fd = bpf_map__fd(skel->maps.local_ip_map);
 578	if (!ASSERT_GE(local_ip_map_fd, 0, "bpf_map__fd"))
 579		goto done;
 580	local_ip = IP4_ADDR2_HEX_VETH1;
 581	err = bpf_map_update_elem(local_ip_map_fd, &key, &local_ip, BPF_ANY);
 582	if (!ASSERT_OK(err, "update bpf local_ip_map"))
 583		goto done;
 584
 585	/* ping test */
 586	ping_dev0();
 587
 588done:
 589	/* delete vxlan tunnel */
 590	delete_vxlan_tunnel();
 591	if (local_ip_map_fd >= 0)
 592		close(local_ip_map_fd);
 593	if (skel)
 594		test_tunnel_kern__destroy(skel);
 595}
 596
 597static void test_ip6vxlan_tunnel(void)
 598{
 599	struct test_tunnel_kern *skel = NULL;
 600	struct nstoken *nstoken;
 601	int local_ip_map_fd = -1;
 602	int set_src_prog_fd, get_src_prog_fd;
 603	int set_dst_prog_fd;
 604	int key = 0;
 605	uint local_ip;
 606	int err;
 607
 608	/* add vxlan tunnel */
 609	err = add_ip6vxlan_tunnel();
 610	if (!ASSERT_OK(err, "add_ip6vxlan_tunnel"))
 611		goto done;
 612
 613	/* load and attach bpf prog to tunnel dev tc hook point */
 614	skel = test_tunnel_kern__open_and_load();
 615	if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
 616		goto done;
 617	get_src_prog_fd = bpf_program__fd(skel->progs.ip6vxlan_get_tunnel_src);
 618	set_src_prog_fd = bpf_program__fd(skel->progs.ip6vxlan_set_tunnel_src);
 619	if (tc_prog_attach(IP6VXLAN_TUNL_DEV1, get_src_prog_fd, set_src_prog_fd))
 620		goto done;
 621
 622	/* load and attach prog set_md to tunnel dev tc hook point at_ns0 */
 623	nstoken = open_netns("at_ns0");
 624	if (!ASSERT_OK_PTR(nstoken, "setns src"))
 625		goto done;
 626	set_dst_prog_fd = bpf_program__fd(skel->progs.ip6vxlan_set_tunnel_dst);
 627	if (tc_prog_attach(IP6VXLAN_TUNL_DEV0, -1, set_dst_prog_fd))
 628		goto done;
 629	close_netns(nstoken);
 630
 631	/* use veth1 ip 2 as tunnel source ip */
 632	local_ip_map_fd = bpf_map__fd(skel->maps.local_ip_map);
 633	if (!ASSERT_GE(local_ip_map_fd, 0, "get local_ip_map fd"))
 634		goto done;
 635	local_ip = IP6_ADDR2_HEX_VETH1;
 636	err = bpf_map_update_elem(local_ip_map_fd, &key, &local_ip, BPF_ANY);
 637	if (!ASSERT_OK(err, "update bpf local_ip_map"))
 638		goto done;
 639
 640	/* ping test */
 641	ping_dev0();
 642
 643done:
 644	/* delete ipv6 vxlan tunnel */
 645	delete_ip6vxlan_tunnel();
 646	if (local_ip_map_fd >= 0)
 647		close(local_ip_map_fd);
 648	if (skel)
 649		test_tunnel_kern__destroy(skel);
 650}
 651
 652static void test_ipip_tunnel(enum ipip_encap encap)
 653{
 654	struct test_tunnel_kern *skel = NULL;
 655	int set_src_prog_fd, get_src_prog_fd;
 656	int err;
 657
 658	/* add ipip tunnel */
 659	err = add_ipip_tunnel(encap);
 660	if (!ASSERT_OK(err, "add_ipip_tunnel"))
 661		goto done;
 662
 663	/* load and attach bpf prog to tunnel dev tc hook point */
 664	skel = test_tunnel_kern__open_and_load();
 665	if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
 666		goto done;
 667
 668	switch (encap) {
 669	case FOU:
 670		get_src_prog_fd = bpf_program__fd(
 671			skel->progs.ipip_encap_get_tunnel);
 672		set_src_prog_fd = bpf_program__fd(
 673			skel->progs.ipip_fou_set_tunnel);
 674		break;
 675	case GUE:
 676		get_src_prog_fd = bpf_program__fd(
 677			skel->progs.ipip_encap_get_tunnel);
 678		set_src_prog_fd = bpf_program__fd(
 679			skel->progs.ipip_gue_set_tunnel);
 680		break;
 681	default:
 682		get_src_prog_fd = bpf_program__fd(
 683			skel->progs.ipip_get_tunnel);
 684		set_src_prog_fd = bpf_program__fd(
 685			skel->progs.ipip_set_tunnel);
 686	}
 687
 688	if (tc_prog_attach(IPIP_TUNL_DEV1, get_src_prog_fd, set_src_prog_fd))
 689		goto done;
 690
 691	ping_dev0();
 692	ping_dev1();
 693
 694done:
 695	/* delete ipip tunnel */
 696	delete_ipip_tunnel();
 697	if (skel)
 698		test_tunnel_kern__destroy(skel);
 699}
 700
 701static void test_xfrm_tunnel(void)
 702{
 703	LIBBPF_OPTS(bpf_xdp_attach_opts, opts);
 704	struct test_tunnel_kern *skel = NULL;
 705	int xdp_prog_fd;
 706	int tc_prog_fd;
 707	int ifindex;
 708	int err;
 709
 710	err = add_xfrm_tunnel();
 711	if (!ASSERT_OK(err, "add_xfrm_tunnel"))
 712		return;
 713
 714	skel = test_tunnel_kern__open_and_load();
 715	if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
 716		goto done;
 717
 718
 719	/* attach tc prog to tunnel dev */
 720	tc_prog_fd = bpf_program__fd(skel->progs.xfrm_get_state);
 721	if (tc_prog_attach("veth1", tc_prog_fd, -1))
 722		goto done;
 723
 724	/* attach xdp prog to tunnel dev */
 725	ifindex = if_nametoindex("veth1");
 726	if (!ASSERT_NEQ(ifindex, 0, "veth1 ifindex"))
 727		goto done;
 728	xdp_prog_fd = bpf_program__fd(skel->progs.xfrm_get_state_xdp);
 729	if (!ASSERT_GE(xdp_prog_fd, 0, "bpf_program__fd"))
 730		goto done;
 731	err = bpf_xdp_attach(ifindex, xdp_prog_fd, XDP_FLAGS_REPLACE, &opts);
 732	if (!ASSERT_OK(err, "bpf_xdp_attach"))
 733		goto done;
 734
 735	ping_dev1();
 736
 737	if (!ASSERT_EQ(skel->bss->xfrm_reqid, 1, "req_id"))
 738		goto done;
 739	if (!ASSERT_EQ(skel->bss->xfrm_spi, XFRM_SPI_IN_TO_OUT, "spi"))
 740		goto done;
 741	if (!ASSERT_EQ(skel->bss->xfrm_remote_ip, 0xac100164, "remote_ip"))
 742		goto done;
 743	if (!ASSERT_EQ(skel->bss->xfrm_replay_window, 42, "replay_window"))
 744		goto done;
 745
 746done:
 747	delete_xfrm_tunnel();
 748	if (skel)
 749		test_tunnel_kern__destroy(skel);
 750}
 751
 752enum gre_test {
 753	GRE,
 754	GRE_NOKEY,
 755	GRETAP,
 756	GRETAP_NOKEY,
 757};
 758
 759static void test_gre_tunnel(enum gre_test test)
 760{
 761	struct test_tunnel_kern *skel;
 762	int set_fd, get_fd;
 763	int err;
 764
 765	skel = test_tunnel_kern__open_and_load();
 766	if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
 767		return;
 768
 769	switch (test) {
 770	case GRE:
 771		err = add_ipv4_tunnel(GRE_TUNL_DEV0, GRE_TUNL_DEV1, "gre", "seq");
 772		set_fd = bpf_program__fd(skel->progs.gre_set_tunnel_no_key);
 773		get_fd = bpf_program__fd(skel->progs.gre_get_tunnel);
 774		break;
 775	case GRE_NOKEY:
 776		err = add_ipv4_tunnel(GRE_TUNL_DEV0, GRE_TUNL_DEV1, "gre", "seq key 2");
 777		set_fd = bpf_program__fd(skel->progs.gre_set_tunnel);
 778		get_fd = bpf_program__fd(skel->progs.gre_get_tunnel);
 779		break;
 780	case GRETAP:
 781		err = add_ipv4_tunnel(GRE_TUNL_DEV0, GRE_TUNL_DEV1, "gretap", "seq");
 782		set_fd = bpf_program__fd(skel->progs.gre_set_tunnel_no_key);
 783		get_fd = bpf_program__fd(skel->progs.gre_get_tunnel);
 784		break;
 785	case GRETAP_NOKEY:
 786		err = add_ipv4_tunnel(GRE_TUNL_DEV0, GRE_TUNL_DEV1, "gretap", "seq key 2");
 787		set_fd = bpf_program__fd(skel->progs.gre_set_tunnel);
 788		get_fd = bpf_program__fd(skel->progs.gre_get_tunnel);
 789		break;
 790	}
 791	if (!ASSERT_OK(err, "add tunnel"))
 792		goto done;
 793
 794	if (tc_prog_attach(GRE_TUNL_DEV1, get_fd, set_fd))
 795		goto done;
 796
 797	ping_dev0();
 798	ping_dev1();
 799
 800done:
 801	delete_tunnel(GRE_TUNL_DEV0, GRE_TUNL_DEV1);
 802	test_tunnel_kern__destroy(skel);
 803}
 804
 805enum ip6gre_test {
 806	IP6GRE,
 807	IP6GRETAP
 808};
 809
 810static void test_ip6gre_tunnel(enum ip6gre_test test)
 811{
 812	struct test_tunnel_kern *skel;
 813	int set_fd, get_fd;
 814	int err;
 815
 816	skel = test_tunnel_kern__open_and_load();
 817	if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
 818		return;
 819
 820	switch (test) {
 821	case IP6GRE:
 822		err = add_ipv6_tunnel(IP6GRE_TUNL_DEV0, IP6GRE_TUNL_DEV1,
 823				      "ip6gre", "flowlabel 0xbcdef key 2");
 824		break;
 825	case IP6GRETAP:
 826		err = add_ipv6_tunnel(IP6GRE_TUNL_DEV0, IP6GRE_TUNL_DEV1,
 827				      "ip6gretap", "flowlabel 0xbcdef key 2");
 828		break;
 829	}
 830	if (!ASSERT_OK(err, "add tunnel"))
 831		goto done;
 832
 833	set_fd = bpf_program__fd(skel->progs.ip6gretap_set_tunnel);
 834	get_fd = bpf_program__fd(skel->progs.ip6gretap_get_tunnel);
 835	if (tc_prog_attach(IP6GRE_TUNL_DEV1, get_fd, set_fd))
 836		goto done;
 837
 838	ping6_veth0();
 839	ping6_dev1();
 840	ping_dev0();
 841	ping_dev1();
 842done:
 843	delete_tunnel(IP6GRE_TUNL_DEV0, IP6GRE_TUNL_DEV1);
 844	test_tunnel_kern__destroy(skel);
 845}
 846
 847enum erspan_test {
 848	V1,
 849	V2
 850};
 851
 852static void test_erspan_tunnel(enum erspan_test test)
 853{
 854	struct test_tunnel_kern *skel;
 855	int set_fd, get_fd;
 856	int err;
 857
 858	skel = test_tunnel_kern__open_and_load();
 859	if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
 860		return;
 861
 862	switch (test) {
 863	case V1:
 864		err = add_ipv4_tunnel(ERSPAN_TUNL_DEV0, ERSPAN_TUNL_DEV1,
 865				      "erspan", "seq key 2 erspan_ver 1 erspan 123");
 866		break;
 867	case V2:
 868		err = add_ipv4_tunnel(ERSPAN_TUNL_DEV0, ERSPAN_TUNL_DEV1,
 869				      "erspan",
 870				      "seq key 2 erspan_ver 2 erspan_dir egress erspan_hwid 3");
 871		break;
 872	}
 873	if (!ASSERT_OK(err, "add tunnel"))
 874		goto done;
 875
 876	set_fd = bpf_program__fd(skel->progs.erspan_set_tunnel);
 877	get_fd = bpf_program__fd(skel->progs.erspan_get_tunnel);
 878	if (tc_prog_attach(ERSPAN_TUNL_DEV1, get_fd, set_fd))
 879		goto done;
 880
 881	ping_dev0();
 882	ping_dev1();
 883done:
 884	delete_tunnel(ERSPAN_TUNL_DEV0, ERSPAN_TUNL_DEV1);
 885	test_tunnel_kern__destroy(skel);
 886}
 887
 888static void test_ip6erspan_tunnel(enum erspan_test test)
 889{
 890	struct test_tunnel_kern *skel;
 891	int set_fd, get_fd;
 892	int err;
 893
 894	skel = test_tunnel_kern__open_and_load();
 895	if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
 896		return;
 897
 898	switch (test) {
 899	case V1:
 900		err = add_ipv6_tunnel(IP6ERSPAN_TUNL_DEV0, IP6ERSPAN_TUNL_DEV1,
 901				      "ip6erspan", "seq key 2 erspan_ver 1 erspan 123");
 902		break;
 903	case V2:
 904		err = add_ipv6_tunnel(IP6ERSPAN_TUNL_DEV0, IP6ERSPAN_TUNL_DEV1,
 905				      "ip6erspan",
 906				      "seq key 2 erspan_ver 2 erspan_dir egress erspan_hwid 7");
 907		break;
 908	}
 909	if (!ASSERT_OK(err, "add tunnel"))
 910		goto done;
 911
 912	set_fd = bpf_program__fd(skel->progs.ip4ip6erspan_set_tunnel);
 913	get_fd = bpf_program__fd(skel->progs.ip4ip6erspan_get_tunnel);
 914	if (tc_prog_attach(IP6ERSPAN_TUNL_DEV1, get_fd, set_fd))
 915		goto done;
 916
 917	ping6_veth0();
 918	ping_dev1();
 919done:
 920	delete_tunnel(IP6ERSPAN_TUNL_DEV0, IP6ERSPAN_TUNL_DEV1);
 921	test_tunnel_kern__destroy(skel);
 922}
 923
 924static void test_geneve_tunnel(void)
 925{
 926	struct test_tunnel_kern *skel;
 927	int set_fd, get_fd;
 928	int err;
 929
 930	skel = test_tunnel_kern__open_and_load();
 931	if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
 932		return;
 933
 934	err = add_geneve_tunnel(GENEVE_TUNL_DEV0, GENEVE_TUNL_DEV1,
 935				"geneve", "dstport 6081");
 936	if (!ASSERT_OK(err, "add tunnel"))
 937		goto done;
 938
 939	set_fd = bpf_program__fd(skel->progs.geneve_set_tunnel);
 940	get_fd = bpf_program__fd(skel->progs.geneve_get_tunnel);
 941	if (tc_prog_attach(GENEVE_TUNL_DEV1, get_fd, set_fd))
 942		goto done;
 943
 944	ping_dev0();
 945	ping_dev1();
 946done:
 947	delete_tunnel(GENEVE_TUNL_DEV0, GENEVE_TUNL_DEV1);
 948	test_tunnel_kern__destroy(skel);
 949}
 950
 951static void test_ip6geneve_tunnel(void)
 952{
 953	struct test_tunnel_kern *skel;
 954	int set_fd, get_fd;
 955	int err;
 956
 957	skel = test_tunnel_kern__open_and_load();
 958	if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
 959		return;
 960
 961	err = add_ip6geneve_tunnel(IP6GENEVE_TUNL_DEV0, IP6GENEVE_TUNL_DEV1,
 962				   "geneve", "");
 963	if (!ASSERT_OK(err, "add tunnel"))
 964		goto done;
 965
 966	set_fd = bpf_program__fd(skel->progs.ip6geneve_set_tunnel);
 967	get_fd = bpf_program__fd(skel->progs.ip6geneve_get_tunnel);
 968	if (tc_prog_attach(IP6GENEVE_TUNL_DEV1, get_fd, set_fd))
 969		goto done;
 970
 971	ping_dev0();
 972	ping_dev1();
 973done:
 974	delete_tunnel(IP6GENEVE_TUNL_DEV0, IP6GENEVE_TUNL_DEV1);
 975	test_tunnel_kern__destroy(skel);
 976}
 977
 978enum ip6tnl_test {
 979	IPIP6,
 980	IP6IP6
 981};
 982
 983static void test_ip6tnl_tunnel(enum ip6tnl_test test)
 984{
 985	struct test_tunnel_kern *skel;
 986	int set_fd, get_fd;
 987	int err;
 988
 989	skel = test_tunnel_kern__open_and_load();
 990	if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
 991		return;
 992
 993	err = add_ipv6_tunnel(IP6TNL_TUNL_DEV0, IP6TNL_TUNL_DEV1, "ip6tnl", "");
 994	if (!ASSERT_OK(err, "add tunnel"))
 995		goto done;
 996
 997	switch (test) {
 998	case IPIP6:
 999		set_fd = bpf_program__fd(skel->progs.ipip6_set_tunnel);
1000		get_fd = bpf_program__fd(skel->progs.ipip6_get_tunnel);
1001		break;
1002	case IP6IP6:
1003		set_fd = bpf_program__fd(skel->progs.ip6ip6_set_tunnel);
1004		get_fd = bpf_program__fd(skel->progs.ip6ip6_get_tunnel);
1005		break;
1006	}
1007	if (tc_prog_attach(IP6TNL_TUNL_DEV1, get_fd, set_fd))
1008		goto done;
1009
1010	ping6_veth0();
1011	switch (test) {
1012	case IPIP6:
1013		ping_dev0();
1014		ping_dev1();
1015		break;
1016	case IP6IP6:
1017		ping6_dev0();
1018		ping6_dev1();
1019		break;
1020	}
1021
1022done:
1023	delete_tunnel(IP6TNL_TUNL_DEV0, IP6TNL_TUNL_DEV1);
1024	test_tunnel_kern__destroy(skel);
1025}
1026
1027#define RUN_TEST(name, ...)						\
1028	({								\
1029		if (test__start_subtest(#name)) {			\
1030			config_device();				\
1031			test_ ## name(__VA_ARGS__);			\
1032			cleanup();					\
1033		}							\
1034	})
1035
1036static void *test_tunnel_run_tests(void *arg)
1037{
1038	RUN_TEST(vxlan_tunnel);
1039	RUN_TEST(ip6vxlan_tunnel);
1040	RUN_TEST(ipip_tunnel, NONE);
1041	RUN_TEST(ipip_tunnel, FOU);
1042	RUN_TEST(ipip_tunnel, GUE);
1043	RUN_TEST(xfrm_tunnel);
1044	RUN_TEST(gre_tunnel, GRE);
1045	RUN_TEST(gre_tunnel, GRE_NOKEY);
1046	RUN_TEST(gre_tunnel, GRETAP);
1047	RUN_TEST(gre_tunnel, GRETAP_NOKEY);
1048	RUN_TEST(ip6gre_tunnel, IP6GRE);
1049	RUN_TEST(ip6gre_tunnel, IP6GRETAP);
1050	RUN_TEST(erspan_tunnel, V1);
1051	RUN_TEST(erspan_tunnel, V2);
1052	RUN_TEST(ip6erspan_tunnel, V1);
1053	RUN_TEST(ip6erspan_tunnel, V2);
1054	RUN_TEST(geneve_tunnel);
1055	RUN_TEST(ip6geneve_tunnel);
1056	RUN_TEST(ip6tnl_tunnel, IPIP6);
1057	RUN_TEST(ip6tnl_tunnel, IP6IP6);
1058
1059	return NULL;
1060}
1061
1062void test_tunnel(void)
1063{
1064	pthread_t test_thread;
1065	int err;
1066
1067	/* Run the tests in their own thread to isolate the namespace changes
1068	 * so they do not affect the environment of other tests.
1069	 * (specifically needed because of unshare(CLONE_NEWNS) in open_netns())
1070	 */
1071	err = pthread_create(&test_thread, NULL, &test_tunnel_run_tests, NULL);
1072	if (ASSERT_OK(err, "pthread_create"))
1073		ASSERT_OK(pthread_join(test_thread, NULL), "pthread_join");
1074}
~/ Read-only file preview.