1// SPDX-License-Identifier: GPL-2.0
2
3#include <string.h>
4
5#include <linux/stddef.h>
6#include <linux/bpf.h>
7#include <linux/in.h>
8#include <linux/in6.h>
9#include <linux/if.h>
10#include <errno.h>
11
12#include <bpf/bpf_helpers.h>
13#include <bpf/bpf_endian.h>
14
15#define SERV4_IP		0xc0a801feU /* 192.168.1.254 */
16#define SERV4_PORT		4040
17#define SERV4_REWRITE_IP	0x7f000001U /* 127.0.0.1 */
18#define SERV4_REWRITE_PORT	4444
19
20#ifndef IFNAMSIZ
21#define IFNAMSIZ 16
22#endif
23
24static __inline int bind_to_device(struct bpf_sock_addr *ctx)
25{
26	char veth1[IFNAMSIZ] = "test_sock_addr1";
27	char veth2[IFNAMSIZ] = "test_sock_addr2";
28	char missing[IFNAMSIZ] = "nonexistent_dev";
29	char del_bind[IFNAMSIZ] = "";
30	int veth1_idx, veth2_idx;
31
32	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
33			   &veth1, sizeof(veth1)))
34		return 1;
35	if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
36			   &veth1_idx, sizeof(veth1_idx)) || !veth1_idx)
37		return 1;
38	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
39			   &veth2, sizeof(veth2)))
40		return 1;
41	if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
42			   &veth2_idx, sizeof(veth2_idx)) || !veth2_idx ||
43	    veth1_idx == veth2_idx)
44		return 1;
45	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
46			   &missing, sizeof(missing)) != -ENODEV)
47		return 1;
48	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
49			   &veth1_idx, sizeof(veth1_idx)))
50		return 1;
51	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
52			   &del_bind, sizeof(del_bind)))
53		return 1;
54
55	return 0;
56}
57
58static __inline int bind_reuseport(struct bpf_sock_addr *ctx)
59{
60	int val = 1;
61
62	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_REUSEPORT,
63			   &val, sizeof(val)))
64		return 1;
65	if (bpf_getsockopt(ctx, SOL_SOCKET, SO_REUSEPORT,
66			   &val, sizeof(val)) || !val)
67		return 1;
68	val = 0;
69	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_REUSEPORT,
70			   &val, sizeof(val)))
71		return 1;
72	if (bpf_getsockopt(ctx, SOL_SOCKET, SO_REUSEPORT,
73			   &val, sizeof(val)) || val)
74		return 1;
75
76	return 0;
77}
78
79static __inline int misc_opts(struct bpf_sock_addr *ctx, int opt)
80{
81	int old, tmp, new = 0xeb9f;
82
83	/* Socket in test case has guarantee that old never equals to new. */
84	if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)) ||
85	    old == new)
86		return 1;
87	if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &new, sizeof(new)))
88		return 1;
89	if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &tmp, sizeof(tmp)) ||
90	    tmp != new)
91		return 1;
92	if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)))
93		return 1;
94
95	return 0;
96}
97
98SEC("cgroup/bind4")
99int bind_v4_prog(struct bpf_sock_addr *ctx)
100{
101	struct bpf_sock *sk;
102	__u32 user_ip4;
103	__u16 user_port;
104
105	sk = ctx->sk;
106	if (!sk)
107		return 0;
108
109	if (sk->family != AF_INET)
110		return 0;
111
112	if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM)
113		return 0;
114
115	if (ctx->user_ip4 != bpf_htonl(SERV4_IP) ||
116	    ctx->user_port != bpf_htons(SERV4_PORT))
117		return 0;
118
119	// u8 narrow loads:
120	user_ip4 = 0;
121	user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[0] << 0;
122	user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[1] << 8;
123	user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[2] << 16;
124	user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[3] << 24;
125	if (ctx->user_ip4 != user_ip4)
126		return 0;
127
128	user_port = 0;
129	user_port |= ((volatile __u8 *)&ctx->user_port)[0] << 0;
130	user_port |= ((volatile __u8 *)&ctx->user_port)[1] << 8;
131	if (ctx->user_port != user_port)
132		return 0;
133
134	// u16 narrow loads:
135	user_ip4 = 0;
136	user_ip4 |= ((volatile __u16 *)&ctx->user_ip4)[0] << 0;
137	user_ip4 |= ((volatile __u16 *)&ctx->user_ip4)[1] << 16;
138	if (ctx->user_ip4 != user_ip4)
139		return 0;
140
141	/* Bind to device and unbind it. */
142	if (bind_to_device(ctx))
143		return 0;
144
145	/* Test for misc socket options. */
146	if (misc_opts(ctx, SO_MARK) || misc_opts(ctx, SO_PRIORITY))
147		return 0;
148
149	/* Set reuseport and unset */
150	if (bind_reuseport(ctx))
151		return 0;
152
153	ctx->user_ip4 = bpf_htonl(SERV4_REWRITE_IP);
154	ctx->user_port = bpf_htons(SERV4_REWRITE_PORT);
155
156	return 1;
157}
158
159char _license[] SEC("license") = "GPL";
160