1a999696cSStanislav Fomichev // SPDX-License-Identifier: GPL-2.0
2a999696cSStanislav Fomichev
3a999696cSStanislav Fomichev #include <string.h>
4a999696cSStanislav Fomichev
5a999696cSStanislav Fomichev #include <linux/stddef.h>
6a999696cSStanislav Fomichev #include <linux/bpf.h>
7a999696cSStanislav Fomichev #include <linux/in.h>
8a999696cSStanislav Fomichev #include <linux/in6.h>
9a999696cSStanislav Fomichev #include <linux/if.h>
10a999696cSStanislav Fomichev #include <errno.h>
11a999696cSStanislav Fomichev
12a999696cSStanislav Fomichev #include <bpf/bpf_helpers.h>
13a999696cSStanislav Fomichev #include <bpf/bpf_endian.h>
14a999696cSStanislav Fomichev
15a999696cSStanislav Fomichev #define SERV4_IP 0xc0a801feU /* 192.168.1.254 */
16a999696cSStanislav Fomichev #define SERV4_PORT 4040
17a999696cSStanislav Fomichev #define SERV4_REWRITE_IP 0x7f000001U /* 127.0.0.1 */
18a999696cSStanislav Fomichev #define SERV4_REWRITE_PORT 4444
19a999696cSStanislav Fomichev
20a540c81aSStanislav Fomichev #ifndef IFNAMSIZ
21a540c81aSStanislav Fomichev #define IFNAMSIZ 16
22a540c81aSStanislav Fomichev #endif
23a540c81aSStanislav Fomichev
bind_to_device(struct bpf_sock_addr * ctx)24a540c81aSStanislav Fomichev static __inline int bind_to_device(struct bpf_sock_addr *ctx)
25a540c81aSStanislav Fomichev {
26a540c81aSStanislav Fomichev char veth1[IFNAMSIZ] = "test_sock_addr1";
27a540c81aSStanislav Fomichev char veth2[IFNAMSIZ] = "test_sock_addr2";
28a540c81aSStanislav Fomichev char missing[IFNAMSIZ] = "nonexistent_dev";
29a540c81aSStanislav Fomichev char del_bind[IFNAMSIZ] = "";
303218231dSDaniel Borkmann int veth1_idx, veth2_idx;
31a540c81aSStanislav Fomichev
32a540c81aSStanislav Fomichev if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
33a540c81aSStanislav Fomichev &veth1, sizeof(veth1)))
34a540c81aSStanislav Fomichev return 1;
353218231dSDaniel Borkmann if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
363218231dSDaniel Borkmann &veth1_idx, sizeof(veth1_idx)) || !veth1_idx)
373218231dSDaniel Borkmann return 1;
38a540c81aSStanislav Fomichev if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
39a540c81aSStanislav Fomichev &veth2, sizeof(veth2)))
40a540c81aSStanislav Fomichev return 1;
413218231dSDaniel Borkmann if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
423218231dSDaniel Borkmann &veth2_idx, sizeof(veth2_idx)) || !veth2_idx ||
433218231dSDaniel Borkmann veth1_idx == veth2_idx)
443218231dSDaniel Borkmann return 1;
45a540c81aSStanislav Fomichev if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
46a540c81aSStanislav Fomichev &missing, sizeof(missing)) != -ENODEV)
47a540c81aSStanislav Fomichev return 1;
483218231dSDaniel Borkmann if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
493218231dSDaniel Borkmann &veth1_idx, sizeof(veth1_idx)))
503218231dSDaniel Borkmann return 1;
51a540c81aSStanislav Fomichev if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
52a540c81aSStanislav Fomichev &del_bind, sizeof(del_bind)))
53a540c81aSStanislav Fomichev return 1;
54a540c81aSStanislav Fomichev
55a540c81aSStanislav Fomichev return 0;
56a540c81aSStanislav Fomichev }
57a540c81aSStanislav Fomichev
bind_reuseport(struct bpf_sock_addr * ctx)58*6503b9f2SManu Bretelle static __inline int bind_reuseport(struct bpf_sock_addr *ctx)
59*6503b9f2SManu Bretelle {
60*6503b9f2SManu Bretelle int val = 1;
61*6503b9f2SManu Bretelle
62*6503b9f2SManu Bretelle if (bpf_setsockopt(ctx, SOL_SOCKET, SO_REUSEPORT,
63*6503b9f2SManu Bretelle &val, sizeof(val)))
64*6503b9f2SManu Bretelle return 1;
65*6503b9f2SManu Bretelle if (bpf_getsockopt(ctx, SOL_SOCKET, SO_REUSEPORT,
66*6503b9f2SManu Bretelle &val, sizeof(val)) || !val)
67*6503b9f2SManu Bretelle return 1;
68*6503b9f2SManu Bretelle val = 0;
69*6503b9f2SManu Bretelle if (bpf_setsockopt(ctx, SOL_SOCKET, SO_REUSEPORT,
70*6503b9f2SManu Bretelle &val, sizeof(val)))
71*6503b9f2SManu Bretelle return 1;
72*6503b9f2SManu Bretelle if (bpf_getsockopt(ctx, SOL_SOCKET, SO_REUSEPORT,
73*6503b9f2SManu Bretelle &val, sizeof(val)) || val)
74*6503b9f2SManu Bretelle return 1;
75*6503b9f2SManu Bretelle
76*6503b9f2SManu Bretelle return 0;
77*6503b9f2SManu Bretelle }
78*6503b9f2SManu Bretelle
misc_opts(struct bpf_sock_addr * ctx,int opt)793218231dSDaniel Borkmann static __inline int misc_opts(struct bpf_sock_addr *ctx, int opt)
803218231dSDaniel Borkmann {
813218231dSDaniel Borkmann int old, tmp, new = 0xeb9f;
823218231dSDaniel Borkmann
833218231dSDaniel Borkmann /* Socket in test case has guarantee that old never equals to new. */
843218231dSDaniel Borkmann if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)) ||
853218231dSDaniel Borkmann old == new)
863218231dSDaniel Borkmann return 1;
873218231dSDaniel Borkmann if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &new, sizeof(new)))
883218231dSDaniel Borkmann return 1;
893218231dSDaniel Borkmann if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &tmp, sizeof(tmp)) ||
903218231dSDaniel Borkmann tmp != new)
913218231dSDaniel Borkmann return 1;
923218231dSDaniel Borkmann if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)))
933218231dSDaniel Borkmann return 1;
943218231dSDaniel Borkmann
953218231dSDaniel Borkmann return 0;
963218231dSDaniel Borkmann }
973218231dSDaniel Borkmann
98a999696cSStanislav Fomichev SEC("cgroup/bind4")
bind_v4_prog(struct bpf_sock_addr * ctx)99a999696cSStanislav Fomichev int bind_v4_prog(struct bpf_sock_addr *ctx)
100a999696cSStanislav Fomichev {
101a999696cSStanislav Fomichev struct bpf_sock *sk;
102a999696cSStanislav Fomichev __u32 user_ip4;
103a999696cSStanislav Fomichev __u16 user_port;
104a999696cSStanislav Fomichev
105a999696cSStanislav Fomichev sk = ctx->sk;
106a999696cSStanislav Fomichev if (!sk)
107a999696cSStanislav Fomichev return 0;
108a999696cSStanislav Fomichev
109a999696cSStanislav Fomichev if (sk->family != AF_INET)
110a999696cSStanislav Fomichev return 0;
111a999696cSStanislav Fomichev
112a999696cSStanislav Fomichev if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM)
113a999696cSStanislav Fomichev return 0;
114a999696cSStanislav Fomichev
115a999696cSStanislav Fomichev if (ctx->user_ip4 != bpf_htonl(SERV4_IP) ||
116a999696cSStanislav Fomichev ctx->user_port != bpf_htons(SERV4_PORT))
117a999696cSStanislav Fomichev return 0;
118a999696cSStanislav Fomichev
119a999696cSStanislav Fomichev // u8 narrow loads:
120a999696cSStanislav Fomichev user_ip4 = 0;
121a999696cSStanislav Fomichev user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[0] << 0;
122a999696cSStanislav Fomichev user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[1] << 8;
123a999696cSStanislav Fomichev user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[2] << 16;
124a999696cSStanislav Fomichev user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[3] << 24;
125a999696cSStanislav Fomichev if (ctx->user_ip4 != user_ip4)
126a999696cSStanislav Fomichev return 0;
127a999696cSStanislav Fomichev
128a999696cSStanislav Fomichev user_port = 0;
129a999696cSStanislav Fomichev user_port |= ((volatile __u8 *)&ctx->user_port)[0] << 0;
130a999696cSStanislav Fomichev user_port |= ((volatile __u8 *)&ctx->user_port)[1] << 8;
131a999696cSStanislav Fomichev if (ctx->user_port != user_port)
132a999696cSStanislav Fomichev return 0;
133a999696cSStanislav Fomichev
134a999696cSStanislav Fomichev // u16 narrow loads:
135a999696cSStanislav Fomichev user_ip4 = 0;
136a999696cSStanislav Fomichev user_ip4 |= ((volatile __u16 *)&ctx->user_ip4)[0] << 0;
137a999696cSStanislav Fomichev user_ip4 |= ((volatile __u16 *)&ctx->user_ip4)[1] << 16;
138a999696cSStanislav Fomichev if (ctx->user_ip4 != user_ip4)
139a999696cSStanislav Fomichev return 0;
140a999696cSStanislav Fomichev
141a540c81aSStanislav Fomichev /* Bind to device and unbind it. */
142a540c81aSStanislav Fomichev if (bind_to_device(ctx))
143a540c81aSStanislav Fomichev return 0;
144a540c81aSStanislav Fomichev
1453218231dSDaniel Borkmann /* Test for misc socket options. */
1463218231dSDaniel Borkmann if (misc_opts(ctx, SO_MARK) || misc_opts(ctx, SO_PRIORITY))
1473218231dSDaniel Borkmann return 0;
1483218231dSDaniel Borkmann
149*6503b9f2SManu Bretelle /* Set reuseport and unset */
150*6503b9f2SManu Bretelle if (bind_reuseport(ctx))
151*6503b9f2SManu Bretelle return 0;
152*6503b9f2SManu Bretelle
153a999696cSStanislav Fomichev ctx->user_ip4 = bpf_htonl(SERV4_REWRITE_IP);
154a999696cSStanislav Fomichev ctx->user_port = bpf_htons(SERV4_REWRITE_PORT);
155a999696cSStanislav Fomichev
156a999696cSStanislav Fomichev return 1;
157a999696cSStanislav Fomichev }
158a999696cSStanislav Fomichev
159a999696cSStanislav Fomichev char _license[] SEC("license") = "GPL";
160