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