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