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] = "";
32*3218231dSDaniel 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;
37*3218231dSDaniel Borkmann 	if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
38*3218231dSDaniel Borkmann 			   &veth1_idx, sizeof(veth1_idx)) || !veth1_idx)
39*3218231dSDaniel Borkmann 		return 1;
40a540c81aSStanislav Fomichev 	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
41a540c81aSStanislav Fomichev 			   &veth2, sizeof(veth2)))
42a540c81aSStanislav Fomichev 		return 1;
43*3218231dSDaniel Borkmann 	if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
44*3218231dSDaniel Borkmann 			   &veth2_idx, sizeof(veth2_idx)) || !veth2_idx ||
45*3218231dSDaniel Borkmann 	    veth1_idx == veth2_idx)
46*3218231dSDaniel Borkmann 		return 1;
47a540c81aSStanislav Fomichev 	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
48a540c81aSStanislav Fomichev 			   &missing, sizeof(missing)) != -ENODEV)
49a540c81aSStanislav Fomichev 		return 1;
50*3218231dSDaniel Borkmann 	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
51*3218231dSDaniel Borkmann 			   &veth1_idx, sizeof(veth1_idx)))
52*3218231dSDaniel 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*3218231dSDaniel Borkmann static __inline int misc_opts(struct bpf_sock_addr *ctx, int opt)
61*3218231dSDaniel Borkmann {
62*3218231dSDaniel Borkmann 	int old, tmp, new = 0xeb9f;
63*3218231dSDaniel Borkmann 
64*3218231dSDaniel Borkmann 	/* Socket in test case has guarantee that old never equals to new. */
65*3218231dSDaniel Borkmann 	if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)) ||
66*3218231dSDaniel Borkmann 	    old == new)
67*3218231dSDaniel Borkmann 		return 1;
68*3218231dSDaniel Borkmann 	if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &new, sizeof(new)))
69*3218231dSDaniel Borkmann 		return 1;
70*3218231dSDaniel Borkmann 	if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &tmp, sizeof(tmp)) ||
71*3218231dSDaniel Borkmann 	    tmp != new)
72*3218231dSDaniel Borkmann 		return 1;
73*3218231dSDaniel Borkmann 	if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)))
74*3218231dSDaniel Borkmann 		return 1;
75*3218231dSDaniel Borkmann 
76*3218231dSDaniel Borkmann 	return 0;
77*3218231dSDaniel Borkmann }
78*3218231dSDaniel Borkmann 
79a999696cSStanislav Fomichev SEC("cgroup/bind4")
80a999696cSStanislav Fomichev int bind_v4_prog(struct bpf_sock_addr *ctx)
81a999696cSStanislav Fomichev {
82a999696cSStanislav Fomichev 	struct bpf_sock *sk;
83a999696cSStanislav Fomichev 	__u32 user_ip4;
84a999696cSStanislav Fomichev 	__u16 user_port;
85a999696cSStanislav Fomichev 
86a999696cSStanislav Fomichev 	sk = ctx->sk;
87a999696cSStanislav Fomichev 	if (!sk)
88a999696cSStanislav Fomichev 		return 0;
89a999696cSStanislav Fomichev 
90a999696cSStanislav Fomichev 	if (sk->family != AF_INET)
91a999696cSStanislav Fomichev 		return 0;
92a999696cSStanislav Fomichev 
93a999696cSStanislav Fomichev 	if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM)
94a999696cSStanislav Fomichev 		return 0;
95a999696cSStanislav Fomichev 
96a999696cSStanislav Fomichev 	if (ctx->user_ip4 != bpf_htonl(SERV4_IP) ||
97a999696cSStanislav Fomichev 	    ctx->user_port != bpf_htons(SERV4_PORT))
98a999696cSStanislav Fomichev 		return 0;
99a999696cSStanislav Fomichev 
100a999696cSStanislav Fomichev 	// u8 narrow loads:
101a999696cSStanislav Fomichev 	user_ip4 = 0;
102a999696cSStanislav Fomichev 	user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[0] << 0;
103a999696cSStanislav Fomichev 	user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[1] << 8;
104a999696cSStanislav Fomichev 	user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[2] << 16;
105a999696cSStanislav Fomichev 	user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[3] << 24;
106a999696cSStanislav Fomichev 	if (ctx->user_ip4 != user_ip4)
107a999696cSStanislav Fomichev 		return 0;
108a999696cSStanislav Fomichev 
109a999696cSStanislav Fomichev 	user_port = 0;
110a999696cSStanislav Fomichev 	user_port |= ((volatile __u8 *)&ctx->user_port)[0] << 0;
111a999696cSStanislav Fomichev 	user_port |= ((volatile __u8 *)&ctx->user_port)[1] << 8;
112a999696cSStanislav Fomichev 	if (ctx->user_port != user_port)
113a999696cSStanislav Fomichev 		return 0;
114a999696cSStanislav Fomichev 
115a999696cSStanislav Fomichev 	// u16 narrow loads:
116a999696cSStanislav Fomichev 	user_ip4 = 0;
117a999696cSStanislav Fomichev 	user_ip4 |= ((volatile __u16 *)&ctx->user_ip4)[0] << 0;
118a999696cSStanislav Fomichev 	user_ip4 |= ((volatile __u16 *)&ctx->user_ip4)[1] << 16;
119a999696cSStanislav Fomichev 	if (ctx->user_ip4 != user_ip4)
120a999696cSStanislav Fomichev 		return 0;
121a999696cSStanislav Fomichev 
122a540c81aSStanislav Fomichev 	/* Bind to device and unbind it. */
123a540c81aSStanislav Fomichev 	if (bind_to_device(ctx))
124a540c81aSStanislav Fomichev 		return 0;
125a540c81aSStanislav Fomichev 
126*3218231dSDaniel Borkmann 	/* Test for misc socket options. */
127*3218231dSDaniel Borkmann 	if (misc_opts(ctx, SO_MARK) || misc_opts(ctx, SO_PRIORITY))
128*3218231dSDaniel Borkmann 		return 0;
129*3218231dSDaniel Borkmann 
130a999696cSStanislav Fomichev 	ctx->user_ip4 = bpf_htonl(SERV4_REWRITE_IP);
131a999696cSStanislav Fomichev 	ctx->user_port = bpf_htons(SERV4_REWRITE_PORT);
132a999696cSStanislav Fomichev 
133a999696cSStanislav Fomichev 	return 1;
134a999696cSStanislav Fomichev }
135a999696cSStanislav Fomichev 
136a999696cSStanislav Fomichev char _license[] SEC("license") = "GPL";
137