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 SERV6_IP_0		0xfaceb00c /* face:b00c:1234:5678::abcd */
16a999696cSStanislav Fomichev #define SERV6_IP_1		0x12345678
17a999696cSStanislav Fomichev #define SERV6_IP_2		0x00000000
18a999696cSStanislav Fomichev #define SERV6_IP_3		0x0000abcd
19a999696cSStanislav Fomichev #define SERV6_PORT		6060
20a999696cSStanislav Fomichev #define SERV6_REWRITE_IP_0	0x00000000
21a999696cSStanislav Fomichev #define SERV6_REWRITE_IP_1	0x00000000
22a999696cSStanislav Fomichev #define SERV6_REWRITE_IP_2	0x00000000
23a999696cSStanislav Fomichev #define SERV6_REWRITE_IP_3	0x00000001
24a999696cSStanislav Fomichev #define SERV6_REWRITE_PORT	6666
25a999696cSStanislav Fomichev 
26a540c81aSStanislav Fomichev #ifndef IFNAMSIZ
27a540c81aSStanislav Fomichev #define IFNAMSIZ 16
28a540c81aSStanislav Fomichev #endif
29a540c81aSStanislav Fomichev 
bind_to_device(struct bpf_sock_addr * ctx)30a540c81aSStanislav Fomichev static __inline int bind_to_device(struct bpf_sock_addr *ctx)
31a540c81aSStanislav Fomichev {
32a540c81aSStanislav Fomichev 	char veth1[IFNAMSIZ] = "test_sock_addr1";
33a540c81aSStanislav Fomichev 	char veth2[IFNAMSIZ] = "test_sock_addr2";
34a540c81aSStanislav Fomichev 	char missing[IFNAMSIZ] = "nonexistent_dev";
35a540c81aSStanislav Fomichev 	char del_bind[IFNAMSIZ] = "";
363218231dSDaniel Borkmann 	int veth1_idx, veth2_idx;
37a540c81aSStanislav Fomichev 
38a540c81aSStanislav Fomichev 	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
39a540c81aSStanislav Fomichev 			   &veth1, sizeof(veth1)))
40a540c81aSStanislav Fomichev 		return 1;
413218231dSDaniel Borkmann 	if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
423218231dSDaniel Borkmann 			   &veth1_idx, sizeof(veth1_idx)) || !veth1_idx)
433218231dSDaniel Borkmann 		return 1;
44a540c81aSStanislav Fomichev 	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
45a540c81aSStanislav Fomichev 			   &veth2, sizeof(veth2)))
46a540c81aSStanislav Fomichev 		return 1;
473218231dSDaniel Borkmann 	if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
483218231dSDaniel Borkmann 			   &veth2_idx, sizeof(veth2_idx)) || !veth2_idx ||
493218231dSDaniel Borkmann 	    veth1_idx == veth2_idx)
503218231dSDaniel Borkmann 		return 1;
51a540c81aSStanislav Fomichev 	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
52a540c81aSStanislav Fomichev 			   &missing, sizeof(missing)) != -ENODEV)
53a540c81aSStanislav Fomichev 		return 1;
543218231dSDaniel Borkmann 	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
553218231dSDaniel Borkmann 			   &veth1_idx, sizeof(veth1_idx)))
563218231dSDaniel Borkmann 		return 1;
57a540c81aSStanislav Fomichev 	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
58a540c81aSStanislav Fomichev 			   &del_bind, sizeof(del_bind)))
59a540c81aSStanislav Fomichev 		return 1;
60a540c81aSStanislav Fomichev 
61a540c81aSStanislav Fomichev 	return 0;
62a540c81aSStanislav Fomichev }
63a540c81aSStanislav Fomichev 
bind_reuseport(struct bpf_sock_addr * ctx)64*6503b9f2SManu Bretelle static __inline int bind_reuseport(struct bpf_sock_addr *ctx)
65*6503b9f2SManu Bretelle {
66*6503b9f2SManu Bretelle 	int val = 1;
67*6503b9f2SManu Bretelle 
68*6503b9f2SManu Bretelle 	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_REUSEPORT,
69*6503b9f2SManu Bretelle 			   &val, sizeof(val)))
70*6503b9f2SManu Bretelle 		return 1;
71*6503b9f2SManu Bretelle 	if (bpf_getsockopt(ctx, SOL_SOCKET, SO_REUSEPORT,
72*6503b9f2SManu Bretelle 			   &val, sizeof(val)) || !val)
73*6503b9f2SManu Bretelle 		return 1;
74*6503b9f2SManu Bretelle 	val = 0;
75*6503b9f2SManu Bretelle 	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_REUSEPORT,
76*6503b9f2SManu Bretelle 			   &val, sizeof(val)))
77*6503b9f2SManu Bretelle 		return 1;
78*6503b9f2SManu Bretelle 	if (bpf_getsockopt(ctx, SOL_SOCKET, SO_REUSEPORT,
79*6503b9f2SManu Bretelle 			   &val, sizeof(val)) || val)
80*6503b9f2SManu Bretelle 		return 1;
81*6503b9f2SManu Bretelle 
82*6503b9f2SManu Bretelle 	return 0;
83*6503b9f2SManu Bretelle }
84*6503b9f2SManu Bretelle 
misc_opts(struct bpf_sock_addr * ctx,int opt)853218231dSDaniel Borkmann static __inline int misc_opts(struct bpf_sock_addr *ctx, int opt)
863218231dSDaniel Borkmann {
873218231dSDaniel Borkmann 	int old, tmp, new = 0xeb9f;
883218231dSDaniel Borkmann 
893218231dSDaniel Borkmann 	/* Socket in test case has guarantee that old never equals to new. */
903218231dSDaniel Borkmann 	if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)) ||
913218231dSDaniel Borkmann 	    old == new)
923218231dSDaniel Borkmann 		return 1;
933218231dSDaniel Borkmann 	if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &new, sizeof(new)))
943218231dSDaniel Borkmann 		return 1;
953218231dSDaniel Borkmann 	if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &tmp, sizeof(tmp)) ||
963218231dSDaniel Borkmann 	    tmp != new)
973218231dSDaniel Borkmann 		return 1;
983218231dSDaniel Borkmann 	if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)))
993218231dSDaniel Borkmann 		return 1;
1003218231dSDaniel Borkmann 
1013218231dSDaniel Borkmann 	return 0;
1023218231dSDaniel Borkmann }
1033218231dSDaniel Borkmann 
104a999696cSStanislav Fomichev SEC("cgroup/bind6")
bind_v6_prog(struct bpf_sock_addr * ctx)105a999696cSStanislav Fomichev int bind_v6_prog(struct bpf_sock_addr *ctx)
106a999696cSStanislav Fomichev {
107a999696cSStanislav Fomichev 	struct bpf_sock *sk;
108a999696cSStanislav Fomichev 	__u32 user_ip6;
109a999696cSStanislav Fomichev 	__u16 user_port;
110a999696cSStanislav Fomichev 	int i;
111a999696cSStanislav Fomichev 
112a999696cSStanislav Fomichev 	sk = ctx->sk;
113a999696cSStanislav Fomichev 	if (!sk)
114a999696cSStanislav Fomichev 		return 0;
115a999696cSStanislav Fomichev 
116a999696cSStanislav Fomichev 	if (sk->family != AF_INET6)
117a999696cSStanislav Fomichev 		return 0;
118a999696cSStanislav Fomichev 
119a999696cSStanislav Fomichev 	if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM)
120a999696cSStanislav Fomichev 		return 0;
121a999696cSStanislav Fomichev 
122a999696cSStanislav Fomichev 	if (ctx->user_ip6[0] != bpf_htonl(SERV6_IP_0) ||
123a999696cSStanislav Fomichev 	    ctx->user_ip6[1] != bpf_htonl(SERV6_IP_1) ||
124a999696cSStanislav Fomichev 	    ctx->user_ip6[2] != bpf_htonl(SERV6_IP_2) ||
125a999696cSStanislav Fomichev 	    ctx->user_ip6[3] != bpf_htonl(SERV6_IP_3) ||
126a999696cSStanislav Fomichev 	    ctx->user_port != bpf_htons(SERV6_PORT))
127a999696cSStanislav Fomichev 		return 0;
128a999696cSStanislav Fomichev 
129a999696cSStanislav Fomichev 	// u8 narrow loads:
130a999696cSStanislav Fomichev 	for (i = 0; i < 4; i++) {
131a999696cSStanislav Fomichev 		user_ip6 = 0;
132a999696cSStanislav Fomichev 		user_ip6 |= ((volatile __u8 *)&ctx->user_ip6[i])[0] << 0;
133a999696cSStanislav Fomichev 		user_ip6 |= ((volatile __u8 *)&ctx->user_ip6[i])[1] << 8;
134a999696cSStanislav Fomichev 		user_ip6 |= ((volatile __u8 *)&ctx->user_ip6[i])[2] << 16;
135a999696cSStanislav Fomichev 		user_ip6 |= ((volatile __u8 *)&ctx->user_ip6[i])[3] << 24;
136a999696cSStanislav Fomichev 		if (ctx->user_ip6[i] != user_ip6)
137a999696cSStanislav Fomichev 			return 0;
138a999696cSStanislav Fomichev 	}
139a999696cSStanislav Fomichev 
140a999696cSStanislav Fomichev 	user_port = 0;
141a999696cSStanislav Fomichev 	user_port |= ((volatile __u8 *)&ctx->user_port)[0] << 0;
142a999696cSStanislav Fomichev 	user_port |= ((volatile __u8 *)&ctx->user_port)[1] << 8;
143a999696cSStanislav Fomichev 	if (ctx->user_port != user_port)
144a999696cSStanislav Fomichev 		return 0;
145a999696cSStanislav Fomichev 
146a999696cSStanislav Fomichev 	// u16 narrow loads:
147a999696cSStanislav Fomichev 	for (i = 0; i < 4; i++) {
148a999696cSStanislav Fomichev 		user_ip6 = 0;
149a999696cSStanislav Fomichev 		user_ip6 |= ((volatile __u16 *)&ctx->user_ip6[i])[0] << 0;
150a999696cSStanislav Fomichev 		user_ip6 |= ((volatile __u16 *)&ctx->user_ip6[i])[1] << 16;
151a999696cSStanislav Fomichev 		if (ctx->user_ip6[i] != user_ip6)
152a999696cSStanislav Fomichev 			return 0;
153a999696cSStanislav Fomichev 	}
154a999696cSStanislav Fomichev 
155a540c81aSStanislav Fomichev 	/* Bind to device and unbind it. */
156a540c81aSStanislav Fomichev 	if (bind_to_device(ctx))
157a540c81aSStanislav Fomichev 		return 0;
158a540c81aSStanislav Fomichev 
1593218231dSDaniel Borkmann 	/* Test for misc socket options. */
1603218231dSDaniel Borkmann 	if (misc_opts(ctx, SO_MARK) || misc_opts(ctx, SO_PRIORITY))
1613218231dSDaniel Borkmann 		return 0;
1623218231dSDaniel Borkmann 
163*6503b9f2SManu Bretelle 	/* Set reuseport and unset */
164*6503b9f2SManu Bretelle 	if (bind_reuseport(ctx))
165*6503b9f2SManu Bretelle 		return 0;
166*6503b9f2SManu Bretelle 
167a999696cSStanislav Fomichev 	ctx->user_ip6[0] = bpf_htonl(SERV6_REWRITE_IP_0);
168a999696cSStanislav Fomichev 	ctx->user_ip6[1] = bpf_htonl(SERV6_REWRITE_IP_1);
169a999696cSStanislav Fomichev 	ctx->user_ip6[2] = bpf_htonl(SERV6_REWRITE_IP_2);
170a999696cSStanislav Fomichev 	ctx->user_ip6[3] = bpf_htonl(SERV6_REWRITE_IP_3);
171a999696cSStanislav Fomichev 	ctx->user_port = bpf_htons(SERV6_REWRITE_PORT);
172a999696cSStanislav Fomichev 
173a999696cSStanislav Fomichev 	return 1;
174a999696cSStanislav Fomichev }
175a999696cSStanislav Fomichev 
176a999696cSStanislav Fomichev char _license[] SEC("license") = "GPL";
177