10ab5539fSJakub Sitnicki // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
20ab5539fSJakub Sitnicki // Copyright (c) 2020 Cloudflare
30ab5539fSJakub Sitnicki 
40ab5539fSJakub Sitnicki #include <errno.h>
50ab5539fSJakub Sitnicki #include <stdbool.h>
60ab5539fSJakub Sitnicki #include <stddef.h>
70ab5539fSJakub Sitnicki #include <linux/bpf.h>
80ab5539fSJakub Sitnicki #include <linux/in.h>
90ab5539fSJakub Sitnicki #include <sys/socket.h>
100ab5539fSJakub Sitnicki 
110ab5539fSJakub Sitnicki #include <bpf/bpf_endian.h>
120ab5539fSJakub Sitnicki #include <bpf/bpf_helpers.h>
130ab5539fSJakub Sitnicki 
140ab5539fSJakub Sitnicki #define IP4(a, b, c, d)					\
150ab5539fSJakub Sitnicki 	bpf_htonl((((__u32)(a) & 0xffU) << 24) |	\
160ab5539fSJakub Sitnicki 		  (((__u32)(b) & 0xffU) << 16) |	\
170ab5539fSJakub Sitnicki 		  (((__u32)(c) & 0xffU) <<  8) |	\
180ab5539fSJakub Sitnicki 		  (((__u32)(d) & 0xffU) <<  0))
190ab5539fSJakub Sitnicki #define IP6(aaaa, bbbb, cccc, dddd)			\
200ab5539fSJakub Sitnicki 	{ bpf_htonl(aaaa), bpf_htonl(bbbb), bpf_htonl(cccc), bpf_htonl(dddd) }
210ab5539fSJakub Sitnicki 
226458bde3SIlya Leoshkevich /* Macros for least-significant byte and word accesses. */
236458bde3SIlya Leoshkevich #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
246458bde3SIlya Leoshkevich #define LSE_INDEX(index, size) (index)
256458bde3SIlya Leoshkevich #else
266458bde3SIlya Leoshkevich #define LSE_INDEX(index, size) ((size) - (index) - 1)
276458bde3SIlya Leoshkevich #endif
286458bde3SIlya Leoshkevich #define LSB(value, index)				\
296458bde3SIlya Leoshkevich 	(((__u8 *)&(value))[LSE_INDEX((index), sizeof(value))])
306458bde3SIlya Leoshkevich #define LSW(value, index)				\
316458bde3SIlya Leoshkevich 	(((__u16 *)&(value))[LSE_INDEX((index), sizeof(value) / 2)])
326458bde3SIlya Leoshkevich 
330ab5539fSJakub Sitnicki #define MAX_SOCKS 32
340ab5539fSJakub Sitnicki 
350ab5539fSJakub Sitnicki struct {
360ab5539fSJakub Sitnicki 	__uint(type, BPF_MAP_TYPE_SOCKMAP);
370ab5539fSJakub Sitnicki 	__uint(max_entries, MAX_SOCKS);
380ab5539fSJakub Sitnicki 	__type(key, __u32);
390ab5539fSJakub Sitnicki 	__type(value, __u64);
400ab5539fSJakub Sitnicki } redir_map SEC(".maps");
410ab5539fSJakub Sitnicki 
420ab5539fSJakub Sitnicki struct {
430ab5539fSJakub Sitnicki 	__uint(type, BPF_MAP_TYPE_ARRAY);
440ab5539fSJakub Sitnicki 	__uint(max_entries, 2);
450ab5539fSJakub Sitnicki 	__type(key, int);
460ab5539fSJakub Sitnicki 	__type(value, int);
470ab5539fSJakub Sitnicki } run_map SEC(".maps");
480ab5539fSJakub Sitnicki 
490ab5539fSJakub Sitnicki enum {
500ab5539fSJakub Sitnicki 	PROG1 = 0,
510ab5539fSJakub Sitnicki 	PROG2,
520ab5539fSJakub Sitnicki };
530ab5539fSJakub Sitnicki 
540ab5539fSJakub Sitnicki enum {
550ab5539fSJakub Sitnicki 	SERVER_A = 0,
560ab5539fSJakub Sitnicki 	SERVER_B,
570ab5539fSJakub Sitnicki };
580ab5539fSJakub Sitnicki 
590ab5539fSJakub Sitnicki /* Addressable key/value constants for convenience */
600ab5539fSJakub Sitnicki static const int KEY_PROG1 = PROG1;
610ab5539fSJakub Sitnicki static const int KEY_PROG2 = PROG2;
620ab5539fSJakub Sitnicki static const int PROG_DONE = 1;
630ab5539fSJakub Sitnicki 
640ab5539fSJakub Sitnicki static const __u32 KEY_SERVER_A = SERVER_A;
650ab5539fSJakub Sitnicki static const __u32 KEY_SERVER_B = SERVER_B;
660ab5539fSJakub Sitnicki 
67509b2937SLorenz Bauer static const __u16 SRC_PORT = bpf_htons(8008);
68509b2937SLorenz Bauer static const __u32 SRC_IP4 = IP4(127, 0, 0, 2);
69509b2937SLorenz Bauer static const __u32 SRC_IP6[] = IP6(0xfd000000, 0x0, 0x0, 0x00000002);
70509b2937SLorenz Bauer 
710ab5539fSJakub Sitnicki static const __u16 DST_PORT = 7007; /* Host byte order */
720ab5539fSJakub Sitnicki static const __u32 DST_IP4 = IP4(127, 0, 0, 1);
730ab5539fSJakub Sitnicki static const __u32 DST_IP6[] = IP6(0xfd000000, 0x0, 0x0, 0x00000001);
740ab5539fSJakub Sitnicki 
757c80c87aSAndrii Nakryiko SEC("sk_lookup")
lookup_pass(struct bpf_sk_lookup * ctx)760ab5539fSJakub Sitnicki int lookup_pass(struct bpf_sk_lookup *ctx)
770ab5539fSJakub Sitnicki {
780ab5539fSJakub Sitnicki 	return SK_PASS;
790ab5539fSJakub Sitnicki }
800ab5539fSJakub Sitnicki 
817c80c87aSAndrii Nakryiko SEC("sk_lookup")
lookup_drop(struct bpf_sk_lookup * ctx)820ab5539fSJakub Sitnicki int lookup_drop(struct bpf_sk_lookup *ctx)
830ab5539fSJakub Sitnicki {
840ab5539fSJakub Sitnicki 	return SK_DROP;
850ab5539fSJakub Sitnicki }
860ab5539fSJakub Sitnicki 
878b4fd2bfSMark Pashmfouroush SEC("sk_lookup")
check_ifindex(struct bpf_sk_lookup * ctx)888b4fd2bfSMark Pashmfouroush int check_ifindex(struct bpf_sk_lookup *ctx)
898b4fd2bfSMark Pashmfouroush {
908b4fd2bfSMark Pashmfouroush 	if (ctx->ingress_ifindex == 1)
918b4fd2bfSMark Pashmfouroush 		return SK_DROP;
928b4fd2bfSMark Pashmfouroush 	return SK_PASS;
938b4fd2bfSMark Pashmfouroush }
948b4fd2bfSMark Pashmfouroush 
9515669e1dSAndrii Nakryiko SEC("sk_reuseport")
reuseport_pass(struct sk_reuseport_md * ctx)960ab5539fSJakub Sitnicki int reuseport_pass(struct sk_reuseport_md *ctx)
970ab5539fSJakub Sitnicki {
980ab5539fSJakub Sitnicki 	return SK_PASS;
990ab5539fSJakub Sitnicki }
1000ab5539fSJakub Sitnicki 
10115669e1dSAndrii Nakryiko SEC("sk_reuseport")
reuseport_drop(struct sk_reuseport_md * ctx)1020ab5539fSJakub Sitnicki int reuseport_drop(struct sk_reuseport_md *ctx)
1030ab5539fSJakub Sitnicki {
1040ab5539fSJakub Sitnicki 	return SK_DROP;
1050ab5539fSJakub Sitnicki }
1060ab5539fSJakub Sitnicki 
1070ab5539fSJakub Sitnicki /* Redirect packets destined for port DST_PORT to socket at redir_map[0]. */
1087c80c87aSAndrii Nakryiko SEC("sk_lookup")
redir_port(struct bpf_sk_lookup * ctx)1090ab5539fSJakub Sitnicki int redir_port(struct bpf_sk_lookup *ctx)
1100ab5539fSJakub Sitnicki {
1110ab5539fSJakub Sitnicki 	struct bpf_sock *sk;
1120ab5539fSJakub Sitnicki 	int err;
1130ab5539fSJakub Sitnicki 
1140ab5539fSJakub Sitnicki 	if (ctx->local_port != DST_PORT)
1150ab5539fSJakub Sitnicki 		return SK_PASS;
1160ab5539fSJakub Sitnicki 
1170ab5539fSJakub Sitnicki 	sk = bpf_map_lookup_elem(&redir_map, &KEY_SERVER_A);
1180ab5539fSJakub Sitnicki 	if (!sk)
1190ab5539fSJakub Sitnicki 		return SK_PASS;
1200ab5539fSJakub Sitnicki 
1210ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, sk, 0);
1220ab5539fSJakub Sitnicki 	bpf_sk_release(sk);
1230ab5539fSJakub Sitnicki 	return err ? SK_DROP : SK_PASS;
1240ab5539fSJakub Sitnicki }
1250ab5539fSJakub Sitnicki 
1260ab5539fSJakub Sitnicki /* Redirect packets destined for DST_IP4 address to socket at redir_map[0]. */
1277c80c87aSAndrii Nakryiko SEC("sk_lookup")
redir_ip4(struct bpf_sk_lookup * ctx)1280ab5539fSJakub Sitnicki int redir_ip4(struct bpf_sk_lookup *ctx)
1290ab5539fSJakub Sitnicki {
1300ab5539fSJakub Sitnicki 	struct bpf_sock *sk;
1310ab5539fSJakub Sitnicki 	int err;
1320ab5539fSJakub Sitnicki 
1330ab5539fSJakub Sitnicki 	if (ctx->family != AF_INET)
1340ab5539fSJakub Sitnicki 		return SK_PASS;
1350ab5539fSJakub Sitnicki 	if (ctx->local_port != DST_PORT)
1360ab5539fSJakub Sitnicki 		return SK_PASS;
1370ab5539fSJakub Sitnicki 	if (ctx->local_ip4 != DST_IP4)
1380ab5539fSJakub Sitnicki 		return SK_PASS;
1390ab5539fSJakub Sitnicki 
1400ab5539fSJakub Sitnicki 	sk = bpf_map_lookup_elem(&redir_map, &KEY_SERVER_A);
1410ab5539fSJakub Sitnicki 	if (!sk)
1420ab5539fSJakub Sitnicki 		return SK_PASS;
1430ab5539fSJakub Sitnicki 
1440ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, sk, 0);
1450ab5539fSJakub Sitnicki 	bpf_sk_release(sk);
1460ab5539fSJakub Sitnicki 	return err ? SK_DROP : SK_PASS;
1470ab5539fSJakub Sitnicki }
1480ab5539fSJakub Sitnicki 
1490ab5539fSJakub Sitnicki /* Redirect packets destined for DST_IP6 address to socket at redir_map[0]. */
1507c80c87aSAndrii Nakryiko SEC("sk_lookup")
redir_ip6(struct bpf_sk_lookup * ctx)1510ab5539fSJakub Sitnicki int redir_ip6(struct bpf_sk_lookup *ctx)
1520ab5539fSJakub Sitnicki {
1530ab5539fSJakub Sitnicki 	struct bpf_sock *sk;
1540ab5539fSJakub Sitnicki 	int err;
1550ab5539fSJakub Sitnicki 
1560ab5539fSJakub Sitnicki 	if (ctx->family != AF_INET6)
1570ab5539fSJakub Sitnicki 		return SK_PASS;
1580ab5539fSJakub Sitnicki 	if (ctx->local_port != DST_PORT)
1590ab5539fSJakub Sitnicki 		return SK_PASS;
1600ab5539fSJakub Sitnicki 	if (ctx->local_ip6[0] != DST_IP6[0] ||
1610ab5539fSJakub Sitnicki 	    ctx->local_ip6[1] != DST_IP6[1] ||
1620ab5539fSJakub Sitnicki 	    ctx->local_ip6[2] != DST_IP6[2] ||
1630ab5539fSJakub Sitnicki 	    ctx->local_ip6[3] != DST_IP6[3])
1640ab5539fSJakub Sitnicki 		return SK_PASS;
1650ab5539fSJakub Sitnicki 
1660ab5539fSJakub Sitnicki 	sk = bpf_map_lookup_elem(&redir_map, &KEY_SERVER_A);
1670ab5539fSJakub Sitnicki 	if (!sk)
1680ab5539fSJakub Sitnicki 		return SK_PASS;
1690ab5539fSJakub Sitnicki 
1700ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, sk, 0);
1710ab5539fSJakub Sitnicki 	bpf_sk_release(sk);
1720ab5539fSJakub Sitnicki 	return err ? SK_DROP : SK_PASS;
1730ab5539fSJakub Sitnicki }
1740ab5539fSJakub Sitnicki 
1757c80c87aSAndrii Nakryiko SEC("sk_lookup")
select_sock_a(struct bpf_sk_lookup * ctx)1760ab5539fSJakub Sitnicki int select_sock_a(struct bpf_sk_lookup *ctx)
1770ab5539fSJakub Sitnicki {
1780ab5539fSJakub Sitnicki 	struct bpf_sock *sk;
1790ab5539fSJakub Sitnicki 	int err;
1800ab5539fSJakub Sitnicki 
1810ab5539fSJakub Sitnicki 	sk = bpf_map_lookup_elem(&redir_map, &KEY_SERVER_A);
1820ab5539fSJakub Sitnicki 	if (!sk)
1830ab5539fSJakub Sitnicki 		return SK_PASS;
1840ab5539fSJakub Sitnicki 
1850ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, sk, 0);
1860ab5539fSJakub Sitnicki 	bpf_sk_release(sk);
1870ab5539fSJakub Sitnicki 	return err ? SK_DROP : SK_PASS;
1880ab5539fSJakub Sitnicki }
1890ab5539fSJakub Sitnicki 
1907c80c87aSAndrii Nakryiko SEC("sk_lookup")
select_sock_a_no_reuseport(struct bpf_sk_lookup * ctx)1910ab5539fSJakub Sitnicki int select_sock_a_no_reuseport(struct bpf_sk_lookup *ctx)
1920ab5539fSJakub Sitnicki {
1930ab5539fSJakub Sitnicki 	struct bpf_sock *sk;
1940ab5539fSJakub Sitnicki 	int err;
1950ab5539fSJakub Sitnicki 
1960ab5539fSJakub Sitnicki 	sk = bpf_map_lookup_elem(&redir_map, &KEY_SERVER_A);
1970ab5539fSJakub Sitnicki 	if (!sk)
1980ab5539fSJakub Sitnicki 		return SK_DROP;
1990ab5539fSJakub Sitnicki 
2000ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, sk, BPF_SK_LOOKUP_F_NO_REUSEPORT);
2010ab5539fSJakub Sitnicki 	bpf_sk_release(sk);
2020ab5539fSJakub Sitnicki 	return err ? SK_DROP : SK_PASS;
2030ab5539fSJakub Sitnicki }
2040ab5539fSJakub Sitnicki 
20515669e1dSAndrii Nakryiko SEC("sk_reuseport")
select_sock_b(struct sk_reuseport_md * ctx)2060ab5539fSJakub Sitnicki int select_sock_b(struct sk_reuseport_md *ctx)
2070ab5539fSJakub Sitnicki {
2080ab5539fSJakub Sitnicki 	__u32 key = KEY_SERVER_B;
2090ab5539fSJakub Sitnicki 	int err;
2100ab5539fSJakub Sitnicki 
2110ab5539fSJakub Sitnicki 	err = bpf_sk_select_reuseport(ctx, &redir_map, &key, 0);
2120ab5539fSJakub Sitnicki 	return err ? SK_DROP : SK_PASS;
2130ab5539fSJakub Sitnicki }
2140ab5539fSJakub Sitnicki 
2150ab5539fSJakub Sitnicki /* Check that bpf_sk_assign() returns -EEXIST if socket already selected. */
2167c80c87aSAndrii Nakryiko SEC("sk_lookup")
sk_assign_eexist(struct bpf_sk_lookup * ctx)2170ab5539fSJakub Sitnicki int sk_assign_eexist(struct bpf_sk_lookup *ctx)
2180ab5539fSJakub Sitnicki {
2190ab5539fSJakub Sitnicki 	struct bpf_sock *sk;
2200ab5539fSJakub Sitnicki 	int err, ret;
2210ab5539fSJakub Sitnicki 
2220ab5539fSJakub Sitnicki 	ret = SK_DROP;
2230ab5539fSJakub Sitnicki 	sk = bpf_map_lookup_elem(&redir_map, &KEY_SERVER_B);
2240ab5539fSJakub Sitnicki 	if (!sk)
2250ab5539fSJakub Sitnicki 		goto out;
2260ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, sk, 0);
2270ab5539fSJakub Sitnicki 	if (err)
2280ab5539fSJakub Sitnicki 		goto out;
2290ab5539fSJakub Sitnicki 	bpf_sk_release(sk);
2300ab5539fSJakub Sitnicki 
2310ab5539fSJakub Sitnicki 	sk = bpf_map_lookup_elem(&redir_map, &KEY_SERVER_A);
2320ab5539fSJakub Sitnicki 	if (!sk)
2330ab5539fSJakub Sitnicki 		goto out;
2340ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, sk, 0);
2350ab5539fSJakub Sitnicki 	if (err != -EEXIST) {
2360ab5539fSJakub Sitnicki 		bpf_printk("sk_assign returned %d, expected %d\n",
2370ab5539fSJakub Sitnicki 			   err, -EEXIST);
2380ab5539fSJakub Sitnicki 		goto out;
2390ab5539fSJakub Sitnicki 	}
2400ab5539fSJakub Sitnicki 
2410ab5539fSJakub Sitnicki 	ret = SK_PASS; /* Success, redirect to KEY_SERVER_B */
2420ab5539fSJakub Sitnicki out:
2430ab5539fSJakub Sitnicki 	if (sk)
2440ab5539fSJakub Sitnicki 		bpf_sk_release(sk);
2450ab5539fSJakub Sitnicki 	return ret;
2460ab5539fSJakub Sitnicki }
2470ab5539fSJakub Sitnicki 
2480ab5539fSJakub Sitnicki /* Check that bpf_sk_assign(BPF_SK_LOOKUP_F_REPLACE) can override selection. */
2497c80c87aSAndrii Nakryiko SEC("sk_lookup")
sk_assign_replace_flag(struct bpf_sk_lookup * ctx)2500ab5539fSJakub Sitnicki int sk_assign_replace_flag(struct bpf_sk_lookup *ctx)
2510ab5539fSJakub Sitnicki {
2520ab5539fSJakub Sitnicki 	struct bpf_sock *sk;
2530ab5539fSJakub Sitnicki 	int err, ret;
2540ab5539fSJakub Sitnicki 
2550ab5539fSJakub Sitnicki 	ret = SK_DROP;
2560ab5539fSJakub Sitnicki 	sk = bpf_map_lookup_elem(&redir_map, &KEY_SERVER_A);
2570ab5539fSJakub Sitnicki 	if (!sk)
2580ab5539fSJakub Sitnicki 		goto out;
2590ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, sk, 0);
2600ab5539fSJakub Sitnicki 	if (err)
2610ab5539fSJakub Sitnicki 		goto out;
2620ab5539fSJakub Sitnicki 	bpf_sk_release(sk);
2630ab5539fSJakub Sitnicki 
2640ab5539fSJakub Sitnicki 	sk = bpf_map_lookup_elem(&redir_map, &KEY_SERVER_B);
2650ab5539fSJakub Sitnicki 	if (!sk)
2660ab5539fSJakub Sitnicki 		goto out;
2670ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, sk, BPF_SK_LOOKUP_F_REPLACE);
2680ab5539fSJakub Sitnicki 	if (err) {
2690ab5539fSJakub Sitnicki 		bpf_printk("sk_assign returned %d, expected 0\n", err);
2700ab5539fSJakub Sitnicki 		goto out;
2710ab5539fSJakub Sitnicki 	}
2720ab5539fSJakub Sitnicki 
2730ab5539fSJakub Sitnicki 	ret = SK_PASS; /* Success, redirect to KEY_SERVER_B */
2740ab5539fSJakub Sitnicki out:
2750ab5539fSJakub Sitnicki 	if (sk)
2760ab5539fSJakub Sitnicki 		bpf_sk_release(sk);
2770ab5539fSJakub Sitnicki 	return ret;
2780ab5539fSJakub Sitnicki }
2790ab5539fSJakub Sitnicki 
2800ab5539fSJakub Sitnicki /* Check that bpf_sk_assign(sk=NULL) is accepted. */
2817c80c87aSAndrii Nakryiko SEC("sk_lookup")
sk_assign_null(struct bpf_sk_lookup * ctx)2820ab5539fSJakub Sitnicki int sk_assign_null(struct bpf_sk_lookup *ctx)
2830ab5539fSJakub Sitnicki {
2840ab5539fSJakub Sitnicki 	struct bpf_sock *sk = NULL;
2850ab5539fSJakub Sitnicki 	int err, ret;
2860ab5539fSJakub Sitnicki 
2870ab5539fSJakub Sitnicki 	ret = SK_DROP;
2880ab5539fSJakub Sitnicki 
2890ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, NULL, 0);
2900ab5539fSJakub Sitnicki 	if (err) {
2910ab5539fSJakub Sitnicki 		bpf_printk("sk_assign returned %d, expected 0\n", err);
2920ab5539fSJakub Sitnicki 		goto out;
2930ab5539fSJakub Sitnicki 	}
2940ab5539fSJakub Sitnicki 
2950ab5539fSJakub Sitnicki 	sk = bpf_map_lookup_elem(&redir_map, &KEY_SERVER_B);
2960ab5539fSJakub Sitnicki 	if (!sk)
2970ab5539fSJakub Sitnicki 		goto out;
2980ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, sk, BPF_SK_LOOKUP_F_REPLACE);
2990ab5539fSJakub Sitnicki 	if (err) {
3000ab5539fSJakub Sitnicki 		bpf_printk("sk_assign returned %d, expected 0\n", err);
3010ab5539fSJakub Sitnicki 		goto out;
3020ab5539fSJakub Sitnicki 	}
3030ab5539fSJakub Sitnicki 
3040ab5539fSJakub Sitnicki 	if (ctx->sk != sk)
3050ab5539fSJakub Sitnicki 		goto out;
3060ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, NULL, 0);
3070ab5539fSJakub Sitnicki 	if (err != -EEXIST)
3080ab5539fSJakub Sitnicki 		goto out;
3090ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, NULL, BPF_SK_LOOKUP_F_REPLACE);
3100ab5539fSJakub Sitnicki 	if (err)
3110ab5539fSJakub Sitnicki 		goto out;
3120ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, sk, BPF_SK_LOOKUP_F_REPLACE);
3130ab5539fSJakub Sitnicki 	if (err)
3140ab5539fSJakub Sitnicki 		goto out;
3150ab5539fSJakub Sitnicki 
3160ab5539fSJakub Sitnicki 	ret = SK_PASS; /* Success, redirect to KEY_SERVER_B */
3170ab5539fSJakub Sitnicki out:
3180ab5539fSJakub Sitnicki 	if (sk)
3190ab5539fSJakub Sitnicki 		bpf_sk_release(sk);
3200ab5539fSJakub Sitnicki 	return ret;
3210ab5539fSJakub Sitnicki }
3220ab5539fSJakub Sitnicki 
3230ab5539fSJakub Sitnicki /* Check that selected sk is accessible through context. */
3247c80c87aSAndrii Nakryiko SEC("sk_lookup")
access_ctx_sk(struct bpf_sk_lookup * ctx)3250ab5539fSJakub Sitnicki int access_ctx_sk(struct bpf_sk_lookup *ctx)
3260ab5539fSJakub Sitnicki {
3270ab5539fSJakub Sitnicki 	struct bpf_sock *sk1 = NULL, *sk2 = NULL;
3280ab5539fSJakub Sitnicki 	int err, ret;
3290ab5539fSJakub Sitnicki 
3300ab5539fSJakub Sitnicki 	ret = SK_DROP;
3310ab5539fSJakub Sitnicki 
3320ab5539fSJakub Sitnicki 	/* Try accessing unassigned (NULL) ctx->sk field */
3330ab5539fSJakub Sitnicki 	if (ctx->sk && ctx->sk->family != AF_INET)
3340ab5539fSJakub Sitnicki 		goto out;
3350ab5539fSJakub Sitnicki 
3360ab5539fSJakub Sitnicki 	/* Assign a value to ctx->sk */
3370ab5539fSJakub Sitnicki 	sk1 = bpf_map_lookup_elem(&redir_map, &KEY_SERVER_A);
3380ab5539fSJakub Sitnicki 	if (!sk1)
3390ab5539fSJakub Sitnicki 		goto out;
3400ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, sk1, 0);
3410ab5539fSJakub Sitnicki 	if (err)
3420ab5539fSJakub Sitnicki 		goto out;
3430ab5539fSJakub Sitnicki 	if (ctx->sk != sk1)
3440ab5539fSJakub Sitnicki 		goto out;
3450ab5539fSJakub Sitnicki 
3460ab5539fSJakub Sitnicki 	/* Access ctx->sk fields */
3470ab5539fSJakub Sitnicki 	if (ctx->sk->family != AF_INET ||
3480ab5539fSJakub Sitnicki 	    ctx->sk->type != SOCK_STREAM ||
3490ab5539fSJakub Sitnicki 	    ctx->sk->state != BPF_TCP_LISTEN)
3500ab5539fSJakub Sitnicki 		goto out;
3510ab5539fSJakub Sitnicki 
3520ab5539fSJakub Sitnicki 	/* Reset selection */
3530ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, NULL, BPF_SK_LOOKUP_F_REPLACE);
3540ab5539fSJakub Sitnicki 	if (err)
3550ab5539fSJakub Sitnicki 		goto out;
3560ab5539fSJakub Sitnicki 	if (ctx->sk)
3570ab5539fSJakub Sitnicki 		goto out;
3580ab5539fSJakub Sitnicki 
3590ab5539fSJakub Sitnicki 	/* Assign another socket */
3600ab5539fSJakub Sitnicki 	sk2 = bpf_map_lookup_elem(&redir_map, &KEY_SERVER_B);
3610ab5539fSJakub Sitnicki 	if (!sk2)
3620ab5539fSJakub Sitnicki 		goto out;
3630ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, sk2, BPF_SK_LOOKUP_F_REPLACE);
3640ab5539fSJakub Sitnicki 	if (err)
3650ab5539fSJakub Sitnicki 		goto out;
3660ab5539fSJakub Sitnicki 	if (ctx->sk != sk2)
3670ab5539fSJakub Sitnicki 		goto out;
3680ab5539fSJakub Sitnicki 
3690ab5539fSJakub Sitnicki 	/* Access reassigned ctx->sk fields */
3700ab5539fSJakub Sitnicki 	if (ctx->sk->family != AF_INET ||
3710ab5539fSJakub Sitnicki 	    ctx->sk->type != SOCK_STREAM ||
3720ab5539fSJakub Sitnicki 	    ctx->sk->state != BPF_TCP_LISTEN)
3730ab5539fSJakub Sitnicki 		goto out;
3740ab5539fSJakub Sitnicki 
3750ab5539fSJakub Sitnicki 	ret = SK_PASS; /* Success, redirect to KEY_SERVER_B */
3760ab5539fSJakub Sitnicki out:
3770ab5539fSJakub Sitnicki 	if (sk1)
3780ab5539fSJakub Sitnicki 		bpf_sk_release(sk1);
3790ab5539fSJakub Sitnicki 	if (sk2)
3800ab5539fSJakub Sitnicki 		bpf_sk_release(sk2);
3810ab5539fSJakub Sitnicki 	return ret;
3820ab5539fSJakub Sitnicki }
3830ab5539fSJakub Sitnicki 
3840ab5539fSJakub Sitnicki /* Check narrow loads from ctx fields that support them.
3850ab5539fSJakub Sitnicki  *
3860ab5539fSJakub Sitnicki  * Narrow loads of size >= target field size from a non-zero offset
3870ab5539fSJakub Sitnicki  * are not covered because they give bogus results, that is the
3880ab5539fSJakub Sitnicki  * verifier ignores the offset.
3890ab5539fSJakub Sitnicki  */
3907c80c87aSAndrii Nakryiko SEC("sk_lookup")
ctx_narrow_access(struct bpf_sk_lookup * ctx)3910ab5539fSJakub Sitnicki int ctx_narrow_access(struct bpf_sk_lookup *ctx)
3920ab5539fSJakub Sitnicki {
3930ab5539fSJakub Sitnicki 	struct bpf_sock *sk;
3942ed0dc59SJakub Sitnicki 	__u32 val_u32;
3950ab5539fSJakub Sitnicki 	bool v4;
3960ab5539fSJakub Sitnicki 
3970ab5539fSJakub Sitnicki 	v4 = (ctx->family == AF_INET);
3980ab5539fSJakub Sitnicki 
3990ab5539fSJakub Sitnicki 	/* Narrow loads from family field */
4006458bde3SIlya Leoshkevich 	if (LSB(ctx->family, 0) != (v4 ? AF_INET : AF_INET6) ||
4016458bde3SIlya Leoshkevich 	    LSB(ctx->family, 1) != 0 || LSB(ctx->family, 2) != 0 || LSB(ctx->family, 3) != 0)
4020ab5539fSJakub Sitnicki 		return SK_DROP;
4036458bde3SIlya Leoshkevich 	if (LSW(ctx->family, 0) != (v4 ? AF_INET : AF_INET6))
4040ab5539fSJakub Sitnicki 		return SK_DROP;
4050ab5539fSJakub Sitnicki 
4066458bde3SIlya Leoshkevich 	/* Narrow loads from protocol field */
4076458bde3SIlya Leoshkevich 	if (LSB(ctx->protocol, 0) != IPPROTO_TCP ||
4086458bde3SIlya Leoshkevich 	    LSB(ctx->protocol, 1) != 0 || LSB(ctx->protocol, 2) != 0 || LSB(ctx->protocol, 3) != 0)
4090ab5539fSJakub Sitnicki 		return SK_DROP;
4106458bde3SIlya Leoshkevich 	if (LSW(ctx->protocol, 0) != IPPROTO_TCP)
4110ab5539fSJakub Sitnicki 		return SK_DROP;
4120ab5539fSJakub Sitnicki 
413509b2937SLorenz Bauer 	/* Narrow loads from remote_port field. Expect SRC_PORT. */
414509b2937SLorenz Bauer 	if (LSB(ctx->remote_port, 0) != ((SRC_PORT >> 0) & 0xff) ||
4153c69611bSJakub Sitnicki 	    LSB(ctx->remote_port, 1) != ((SRC_PORT >> 8) & 0xff))
4160ab5539fSJakub Sitnicki 		return SK_DROP;
417509b2937SLorenz Bauer 	if (LSW(ctx->remote_port, 0) != SRC_PORT)
4180ab5539fSJakub Sitnicki 		return SK_DROP;
4190ab5539fSJakub Sitnicki 
420ce523680SJakub Sitnicki 	/*
421ce523680SJakub Sitnicki 	 * NOTE: 4-byte load from bpf_sk_lookup at remote_port offset
422ce523680SJakub Sitnicki 	 * is quirky. It gets rewritten by the access converter to a
423ce523680SJakub Sitnicki 	 * 2-byte load for backward compatibility. Treating the load
424ce523680SJakub Sitnicki 	 * result as a be16 value makes the code portable across
425ce523680SJakub Sitnicki 	 * little- and big-endian platforms.
426ce523680SJakub Sitnicki 	 */
4272ed0dc59SJakub Sitnicki 	val_u32 = *(__u32 *)&ctx->remote_port;
428ce523680SJakub Sitnicki 	if (val_u32 != SRC_PORT)
4292ed0dc59SJakub Sitnicki 		return SK_DROP;
4302ed0dc59SJakub Sitnicki 
4310ab5539fSJakub Sitnicki 	/* Narrow loads from local_port field. Expect DST_PORT. */
4326458bde3SIlya Leoshkevich 	if (LSB(ctx->local_port, 0) != ((DST_PORT >> 0) & 0xff) ||
4336458bde3SIlya Leoshkevich 	    LSB(ctx->local_port, 1) != ((DST_PORT >> 8) & 0xff) ||
4346458bde3SIlya Leoshkevich 	    LSB(ctx->local_port, 2) != 0 || LSB(ctx->local_port, 3) != 0)
4350ab5539fSJakub Sitnicki 		return SK_DROP;
4366458bde3SIlya Leoshkevich 	if (LSW(ctx->local_port, 0) != DST_PORT)
4370ab5539fSJakub Sitnicki 		return SK_DROP;
4380ab5539fSJakub Sitnicki 
4390ab5539fSJakub Sitnicki 	/* Narrow loads from IPv4 fields */
4400ab5539fSJakub Sitnicki 	if (v4) {
441509b2937SLorenz Bauer 		/* Expect SRC_IP4 in remote_ip4 */
442509b2937SLorenz Bauer 		if (LSB(ctx->remote_ip4, 0) != ((SRC_IP4 >> 0) & 0xff) ||
443509b2937SLorenz Bauer 		    LSB(ctx->remote_ip4, 1) != ((SRC_IP4 >> 8) & 0xff) ||
444509b2937SLorenz Bauer 		    LSB(ctx->remote_ip4, 2) != ((SRC_IP4 >> 16) & 0xff) ||
445509b2937SLorenz Bauer 		    LSB(ctx->remote_ip4, 3) != ((SRC_IP4 >> 24) & 0xff))
4460ab5539fSJakub Sitnicki 			return SK_DROP;
447509b2937SLorenz Bauer 		if (LSW(ctx->remote_ip4, 0) != ((SRC_IP4 >> 0) & 0xffff) ||
448509b2937SLorenz Bauer 		    LSW(ctx->remote_ip4, 1) != ((SRC_IP4 >> 16) & 0xffff))
4490ab5539fSJakub Sitnicki 			return SK_DROP;
4500ab5539fSJakub Sitnicki 
4510ab5539fSJakub Sitnicki 		/* Expect DST_IP4 in local_ip4 */
4526458bde3SIlya Leoshkevich 		if (LSB(ctx->local_ip4, 0) != ((DST_IP4 >> 0) & 0xff) ||
4536458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip4, 1) != ((DST_IP4 >> 8) & 0xff) ||
4546458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip4, 2) != ((DST_IP4 >> 16) & 0xff) ||
4556458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip4, 3) != ((DST_IP4 >> 24) & 0xff))
4560ab5539fSJakub Sitnicki 			return SK_DROP;
4576458bde3SIlya Leoshkevich 		if (LSW(ctx->local_ip4, 0) != ((DST_IP4 >> 0) & 0xffff) ||
4586458bde3SIlya Leoshkevich 		    LSW(ctx->local_ip4, 1) != ((DST_IP4 >> 16) & 0xffff))
4590ab5539fSJakub Sitnicki 			return SK_DROP;
4600ab5539fSJakub Sitnicki 	} else {
4610ab5539fSJakub Sitnicki 		/* Expect 0.0.0.0 IPs when family != AF_INET */
4626458bde3SIlya Leoshkevich 		if (LSB(ctx->remote_ip4, 0) != 0 || LSB(ctx->remote_ip4, 1) != 0 ||
4636458bde3SIlya Leoshkevich 		    LSB(ctx->remote_ip4, 2) != 0 || LSB(ctx->remote_ip4, 3) != 0)
4640ab5539fSJakub Sitnicki 			return SK_DROP;
4656458bde3SIlya Leoshkevich 		if (LSW(ctx->remote_ip4, 0) != 0 || LSW(ctx->remote_ip4, 1) != 0)
4660ab5539fSJakub Sitnicki 			return SK_DROP;
4670ab5539fSJakub Sitnicki 
4686458bde3SIlya Leoshkevich 		if (LSB(ctx->local_ip4, 0) != 0 || LSB(ctx->local_ip4, 1) != 0 ||
4696458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip4, 2) != 0 || LSB(ctx->local_ip4, 3) != 0)
4700ab5539fSJakub Sitnicki 			return SK_DROP;
4716458bde3SIlya Leoshkevich 		if (LSW(ctx->local_ip4, 0) != 0 || LSW(ctx->local_ip4, 1) != 0)
4720ab5539fSJakub Sitnicki 			return SK_DROP;
4730ab5539fSJakub Sitnicki 	}
4740ab5539fSJakub Sitnicki 
4750ab5539fSJakub Sitnicki 	/* Narrow loads from IPv6 fields */
4760ab5539fSJakub Sitnicki 	if (!v4) {
477509b2937SLorenz Bauer 		/* Expect SRC_IP6 in remote_ip6 */
478509b2937SLorenz Bauer 		if (LSB(ctx->remote_ip6[0], 0) != ((SRC_IP6[0] >> 0) & 0xff) ||
479509b2937SLorenz Bauer 		    LSB(ctx->remote_ip6[0], 1) != ((SRC_IP6[0] >> 8) & 0xff) ||
480509b2937SLorenz Bauer 		    LSB(ctx->remote_ip6[0], 2) != ((SRC_IP6[0] >> 16) & 0xff) ||
481509b2937SLorenz Bauer 		    LSB(ctx->remote_ip6[0], 3) != ((SRC_IP6[0] >> 24) & 0xff) ||
482509b2937SLorenz Bauer 		    LSB(ctx->remote_ip6[1], 0) != ((SRC_IP6[1] >> 0) & 0xff) ||
483509b2937SLorenz Bauer 		    LSB(ctx->remote_ip6[1], 1) != ((SRC_IP6[1] >> 8) & 0xff) ||
484509b2937SLorenz Bauer 		    LSB(ctx->remote_ip6[1], 2) != ((SRC_IP6[1] >> 16) & 0xff) ||
485509b2937SLorenz Bauer 		    LSB(ctx->remote_ip6[1], 3) != ((SRC_IP6[1] >> 24) & 0xff) ||
486509b2937SLorenz Bauer 		    LSB(ctx->remote_ip6[2], 0) != ((SRC_IP6[2] >> 0) & 0xff) ||
487509b2937SLorenz Bauer 		    LSB(ctx->remote_ip6[2], 1) != ((SRC_IP6[2] >> 8) & 0xff) ||
488509b2937SLorenz Bauer 		    LSB(ctx->remote_ip6[2], 2) != ((SRC_IP6[2] >> 16) & 0xff) ||
489509b2937SLorenz Bauer 		    LSB(ctx->remote_ip6[2], 3) != ((SRC_IP6[2] >> 24) & 0xff) ||
490509b2937SLorenz Bauer 		    LSB(ctx->remote_ip6[3], 0) != ((SRC_IP6[3] >> 0) & 0xff) ||
491509b2937SLorenz Bauer 		    LSB(ctx->remote_ip6[3], 1) != ((SRC_IP6[3] >> 8) & 0xff) ||
492509b2937SLorenz Bauer 		    LSB(ctx->remote_ip6[3], 2) != ((SRC_IP6[3] >> 16) & 0xff) ||
493509b2937SLorenz Bauer 		    LSB(ctx->remote_ip6[3], 3) != ((SRC_IP6[3] >> 24) & 0xff))
4940ab5539fSJakub Sitnicki 			return SK_DROP;
495509b2937SLorenz Bauer 		if (LSW(ctx->remote_ip6[0], 0) != ((SRC_IP6[0] >> 0) & 0xffff) ||
496509b2937SLorenz Bauer 		    LSW(ctx->remote_ip6[0], 1) != ((SRC_IP6[0] >> 16) & 0xffff) ||
497509b2937SLorenz Bauer 		    LSW(ctx->remote_ip6[1], 0) != ((SRC_IP6[1] >> 0) & 0xffff) ||
498509b2937SLorenz Bauer 		    LSW(ctx->remote_ip6[1], 1) != ((SRC_IP6[1] >> 16) & 0xffff) ||
499509b2937SLorenz Bauer 		    LSW(ctx->remote_ip6[2], 0) != ((SRC_IP6[2] >> 0) & 0xffff) ||
500509b2937SLorenz Bauer 		    LSW(ctx->remote_ip6[2], 1) != ((SRC_IP6[2] >> 16) & 0xffff) ||
501509b2937SLorenz Bauer 		    LSW(ctx->remote_ip6[3], 0) != ((SRC_IP6[3] >> 0) & 0xffff) ||
502509b2937SLorenz Bauer 		    LSW(ctx->remote_ip6[3], 1) != ((SRC_IP6[3] >> 16) & 0xffff))
5030ab5539fSJakub Sitnicki 			return SK_DROP;
5040ab5539fSJakub Sitnicki 		/* Expect DST_IP6 in local_ip6 */
5056458bde3SIlya Leoshkevich 		if (LSB(ctx->local_ip6[0], 0) != ((DST_IP6[0] >> 0) & 0xff) ||
5066458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[0], 1) != ((DST_IP6[0] >> 8) & 0xff) ||
5076458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[0], 2) != ((DST_IP6[0] >> 16) & 0xff) ||
5086458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[0], 3) != ((DST_IP6[0] >> 24) & 0xff) ||
5096458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[1], 0) != ((DST_IP6[1] >> 0) & 0xff) ||
5106458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[1], 1) != ((DST_IP6[1] >> 8) & 0xff) ||
5116458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[1], 2) != ((DST_IP6[1] >> 16) & 0xff) ||
5126458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[1], 3) != ((DST_IP6[1] >> 24) & 0xff) ||
5136458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[2], 0) != ((DST_IP6[2] >> 0) & 0xff) ||
5146458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[2], 1) != ((DST_IP6[2] >> 8) & 0xff) ||
5156458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[2], 2) != ((DST_IP6[2] >> 16) & 0xff) ||
5166458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[2], 3) != ((DST_IP6[2] >> 24) & 0xff) ||
5176458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[3], 0) != ((DST_IP6[3] >> 0) & 0xff) ||
5186458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[3], 1) != ((DST_IP6[3] >> 8) & 0xff) ||
5196458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[3], 2) != ((DST_IP6[3] >> 16) & 0xff) ||
5206458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[3], 3) != ((DST_IP6[3] >> 24) & 0xff))
5210ab5539fSJakub Sitnicki 			return SK_DROP;
5226458bde3SIlya Leoshkevich 		if (LSW(ctx->local_ip6[0], 0) != ((DST_IP6[0] >> 0) & 0xffff) ||
5236458bde3SIlya Leoshkevich 		    LSW(ctx->local_ip6[0], 1) != ((DST_IP6[0] >> 16) & 0xffff) ||
5246458bde3SIlya Leoshkevich 		    LSW(ctx->local_ip6[1], 0) != ((DST_IP6[1] >> 0) & 0xffff) ||
5256458bde3SIlya Leoshkevich 		    LSW(ctx->local_ip6[1], 1) != ((DST_IP6[1] >> 16) & 0xffff) ||
5266458bde3SIlya Leoshkevich 		    LSW(ctx->local_ip6[2], 0) != ((DST_IP6[2] >> 0) & 0xffff) ||
5276458bde3SIlya Leoshkevich 		    LSW(ctx->local_ip6[2], 1) != ((DST_IP6[2] >> 16) & 0xffff) ||
5286458bde3SIlya Leoshkevich 		    LSW(ctx->local_ip6[3], 0) != ((DST_IP6[3] >> 0) & 0xffff) ||
5296458bde3SIlya Leoshkevich 		    LSW(ctx->local_ip6[3], 1) != ((DST_IP6[3] >> 16) & 0xffff))
5300ab5539fSJakub Sitnicki 			return SK_DROP;
5310ab5539fSJakub Sitnicki 	} else {
5320ab5539fSJakub Sitnicki 		/* Expect :: IPs when family != AF_INET6 */
5336458bde3SIlya Leoshkevich 		if (LSB(ctx->remote_ip6[0], 0) != 0 || LSB(ctx->remote_ip6[0], 1) != 0 ||
5346458bde3SIlya Leoshkevich 		    LSB(ctx->remote_ip6[0], 2) != 0 || LSB(ctx->remote_ip6[0], 3) != 0 ||
5356458bde3SIlya Leoshkevich 		    LSB(ctx->remote_ip6[1], 0) != 0 || LSB(ctx->remote_ip6[1], 1) != 0 ||
5366458bde3SIlya Leoshkevich 		    LSB(ctx->remote_ip6[1], 2) != 0 || LSB(ctx->remote_ip6[1], 3) != 0 ||
5376458bde3SIlya Leoshkevich 		    LSB(ctx->remote_ip6[2], 0) != 0 || LSB(ctx->remote_ip6[2], 1) != 0 ||
5386458bde3SIlya Leoshkevich 		    LSB(ctx->remote_ip6[2], 2) != 0 || LSB(ctx->remote_ip6[2], 3) != 0 ||
5396458bde3SIlya Leoshkevich 		    LSB(ctx->remote_ip6[3], 0) != 0 || LSB(ctx->remote_ip6[3], 1) != 0 ||
5406458bde3SIlya Leoshkevich 		    LSB(ctx->remote_ip6[3], 2) != 0 || LSB(ctx->remote_ip6[3], 3) != 0)
5410ab5539fSJakub Sitnicki 			return SK_DROP;
5426458bde3SIlya Leoshkevich 		if (LSW(ctx->remote_ip6[0], 0) != 0 || LSW(ctx->remote_ip6[0], 1) != 0 ||
5436458bde3SIlya Leoshkevich 		    LSW(ctx->remote_ip6[1], 0) != 0 || LSW(ctx->remote_ip6[1], 1) != 0 ||
5446458bde3SIlya Leoshkevich 		    LSW(ctx->remote_ip6[2], 0) != 0 || LSW(ctx->remote_ip6[2], 1) != 0 ||
5456458bde3SIlya Leoshkevich 		    LSW(ctx->remote_ip6[3], 0) != 0 || LSW(ctx->remote_ip6[3], 1) != 0)
5460ab5539fSJakub Sitnicki 			return SK_DROP;
5470ab5539fSJakub Sitnicki 
5486458bde3SIlya Leoshkevich 		if (LSB(ctx->local_ip6[0], 0) != 0 || LSB(ctx->local_ip6[0], 1) != 0 ||
5496458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[0], 2) != 0 || LSB(ctx->local_ip6[0], 3) != 0 ||
5506458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[1], 0) != 0 || LSB(ctx->local_ip6[1], 1) != 0 ||
5516458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[1], 2) != 0 || LSB(ctx->local_ip6[1], 3) != 0 ||
5526458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[2], 0) != 0 || LSB(ctx->local_ip6[2], 1) != 0 ||
5536458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[2], 2) != 0 || LSB(ctx->local_ip6[2], 3) != 0 ||
5546458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[3], 0) != 0 || LSB(ctx->local_ip6[3], 1) != 0 ||
5556458bde3SIlya Leoshkevich 		    LSB(ctx->local_ip6[3], 2) != 0 || LSB(ctx->local_ip6[3], 3) != 0)
5560ab5539fSJakub Sitnicki 			return SK_DROP;
5576458bde3SIlya Leoshkevich 		if (LSW(ctx->remote_ip6[0], 0) != 0 || LSW(ctx->remote_ip6[0], 1) != 0 ||
5586458bde3SIlya Leoshkevich 		    LSW(ctx->remote_ip6[1], 0) != 0 || LSW(ctx->remote_ip6[1], 1) != 0 ||
5596458bde3SIlya Leoshkevich 		    LSW(ctx->remote_ip6[2], 0) != 0 || LSW(ctx->remote_ip6[2], 1) != 0 ||
5606458bde3SIlya Leoshkevich 		    LSW(ctx->remote_ip6[3], 0) != 0 || LSW(ctx->remote_ip6[3], 1) != 0)
5610ab5539fSJakub Sitnicki 			return SK_DROP;
5620ab5539fSJakub Sitnicki 	}
5630ab5539fSJakub Sitnicki 
5640ab5539fSJakub Sitnicki 	/* Success, redirect to KEY_SERVER_B */
5650ab5539fSJakub Sitnicki 	sk = bpf_map_lookup_elem(&redir_map, &KEY_SERVER_B);
5660ab5539fSJakub Sitnicki 	if (sk) {
5670ab5539fSJakub Sitnicki 		bpf_sk_assign(ctx, sk, 0);
5680ab5539fSJakub Sitnicki 		bpf_sk_release(sk);
5690ab5539fSJakub Sitnicki 	}
5700ab5539fSJakub Sitnicki 	return SK_PASS;
5710ab5539fSJakub Sitnicki }
5720ab5539fSJakub Sitnicki 
5730ab5539fSJakub Sitnicki /* Check that sk_assign rejects SERVER_A socket with -ESOCKNOSUPPORT */
5747c80c87aSAndrii Nakryiko SEC("sk_lookup")
sk_assign_esocknosupport(struct bpf_sk_lookup * ctx)5750ab5539fSJakub Sitnicki int sk_assign_esocknosupport(struct bpf_sk_lookup *ctx)
5760ab5539fSJakub Sitnicki {
5770ab5539fSJakub Sitnicki 	struct bpf_sock *sk;
5780ab5539fSJakub Sitnicki 	int err, ret;
5790ab5539fSJakub Sitnicki 
5800ab5539fSJakub Sitnicki 	ret = SK_DROP;
5810ab5539fSJakub Sitnicki 	sk = bpf_map_lookup_elem(&redir_map, &KEY_SERVER_A);
5820ab5539fSJakub Sitnicki 	if (!sk)
5830ab5539fSJakub Sitnicki 		goto out;
5840ab5539fSJakub Sitnicki 
5850ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, sk, 0);
5860ab5539fSJakub Sitnicki 	if (err != -ESOCKTNOSUPPORT) {
5870ab5539fSJakub Sitnicki 		bpf_printk("sk_assign returned %d, expected %d\n",
5880ab5539fSJakub Sitnicki 			   err, -ESOCKTNOSUPPORT);
5890ab5539fSJakub Sitnicki 		goto out;
5900ab5539fSJakub Sitnicki 	}
5910ab5539fSJakub Sitnicki 
5920ab5539fSJakub Sitnicki 	ret = SK_PASS; /* Success, pass to regular lookup */
5930ab5539fSJakub Sitnicki out:
5940ab5539fSJakub Sitnicki 	if (sk)
5950ab5539fSJakub Sitnicki 		bpf_sk_release(sk);
5960ab5539fSJakub Sitnicki 	return ret;
5970ab5539fSJakub Sitnicki }
5980ab5539fSJakub Sitnicki 
5997c80c87aSAndrii Nakryiko SEC("sk_lookup")
multi_prog_pass1(struct bpf_sk_lookup * ctx)6000ab5539fSJakub Sitnicki int multi_prog_pass1(struct bpf_sk_lookup *ctx)
6010ab5539fSJakub Sitnicki {
6020ab5539fSJakub Sitnicki 	bpf_map_update_elem(&run_map, &KEY_PROG1, &PROG_DONE, BPF_ANY);
6030ab5539fSJakub Sitnicki 	return SK_PASS;
6040ab5539fSJakub Sitnicki }
6050ab5539fSJakub Sitnicki 
6067c80c87aSAndrii Nakryiko SEC("sk_lookup")
multi_prog_pass2(struct bpf_sk_lookup * ctx)6070ab5539fSJakub Sitnicki int multi_prog_pass2(struct bpf_sk_lookup *ctx)
6080ab5539fSJakub Sitnicki {
6090ab5539fSJakub Sitnicki 	bpf_map_update_elem(&run_map, &KEY_PROG2, &PROG_DONE, BPF_ANY);
6100ab5539fSJakub Sitnicki 	return SK_PASS;
6110ab5539fSJakub Sitnicki }
6120ab5539fSJakub Sitnicki 
6137c80c87aSAndrii Nakryiko SEC("sk_lookup")
multi_prog_drop1(struct bpf_sk_lookup * ctx)6140ab5539fSJakub Sitnicki int multi_prog_drop1(struct bpf_sk_lookup *ctx)
6150ab5539fSJakub Sitnicki {
6160ab5539fSJakub Sitnicki 	bpf_map_update_elem(&run_map, &KEY_PROG1, &PROG_DONE, BPF_ANY);
6170ab5539fSJakub Sitnicki 	return SK_DROP;
6180ab5539fSJakub Sitnicki }
6190ab5539fSJakub Sitnicki 
6207c80c87aSAndrii Nakryiko SEC("sk_lookup")
multi_prog_drop2(struct bpf_sk_lookup * ctx)6210ab5539fSJakub Sitnicki int multi_prog_drop2(struct bpf_sk_lookup *ctx)
6220ab5539fSJakub Sitnicki {
6230ab5539fSJakub Sitnicki 	bpf_map_update_elem(&run_map, &KEY_PROG2, &PROG_DONE, BPF_ANY);
6240ab5539fSJakub Sitnicki 	return SK_DROP;
6250ab5539fSJakub Sitnicki }
6260ab5539fSJakub Sitnicki 
select_server_a(struct bpf_sk_lookup * ctx)6270ab5539fSJakub Sitnicki static __always_inline int select_server_a(struct bpf_sk_lookup *ctx)
6280ab5539fSJakub Sitnicki {
6290ab5539fSJakub Sitnicki 	struct bpf_sock *sk;
6300ab5539fSJakub Sitnicki 	int err;
6310ab5539fSJakub Sitnicki 
6320ab5539fSJakub Sitnicki 	sk = bpf_map_lookup_elem(&redir_map, &KEY_SERVER_A);
6330ab5539fSJakub Sitnicki 	if (!sk)
6340ab5539fSJakub Sitnicki 		return SK_DROP;
6350ab5539fSJakub Sitnicki 
6360ab5539fSJakub Sitnicki 	err = bpf_sk_assign(ctx, sk, 0);
6370ab5539fSJakub Sitnicki 	bpf_sk_release(sk);
6380ab5539fSJakub Sitnicki 	if (err)
6390ab5539fSJakub Sitnicki 		return SK_DROP;
6400ab5539fSJakub Sitnicki 
6410ab5539fSJakub Sitnicki 	return SK_PASS;
6420ab5539fSJakub Sitnicki }
6430ab5539fSJakub Sitnicki 
6447c80c87aSAndrii Nakryiko SEC("sk_lookup")
multi_prog_redir1(struct bpf_sk_lookup * ctx)6450ab5539fSJakub Sitnicki int multi_prog_redir1(struct bpf_sk_lookup *ctx)
6460ab5539fSJakub Sitnicki {
647*c8ed6685SAndrii Nakryiko 	(void)select_server_a(ctx);
6480ab5539fSJakub Sitnicki 	bpf_map_update_elem(&run_map, &KEY_PROG1, &PROG_DONE, BPF_ANY);
6490ab5539fSJakub Sitnicki 	return SK_PASS;
6500ab5539fSJakub Sitnicki }
6510ab5539fSJakub Sitnicki 
6527c80c87aSAndrii Nakryiko SEC("sk_lookup")
multi_prog_redir2(struct bpf_sk_lookup * ctx)6530ab5539fSJakub Sitnicki int multi_prog_redir2(struct bpf_sk_lookup *ctx)
6540ab5539fSJakub Sitnicki {
655*c8ed6685SAndrii Nakryiko 	(void)select_server_a(ctx);
6560ab5539fSJakub Sitnicki 	bpf_map_update_elem(&run_map, &KEY_PROG2, &PROG_DONE, BPF_ANY);
6570ab5539fSJakub Sitnicki 	return SK_PASS;
6580ab5539fSJakub Sitnicki }
6590ab5539fSJakub Sitnicki 
6600ab5539fSJakub Sitnicki char _license[] SEC("license") = "Dual BSD/GPL";
661