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