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