xref: /linux/tools/testing/selftests/bpf/prog_tests/sock_post_bind.c (revision 79d2e1919a2728ef49d938eb20ebd5903c14dfb0)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/bpf.h>
3 #include <test_progs.h>
4 #include "cgroup_helpers.h"
5 
6 #define TEST_NS "sock_post_bind"
7 
8 static char bpf_log_buf[4096];
9 
10 static struct sock_post_bind_test {
11 	const char			*descr;
12 	/* BPF prog properties */
13 	const struct bpf_insn		insns[64];
14 	enum bpf_attach_type		attach_type;
15 	enum bpf_attach_type		expected_attach_type;
16 	/* Socket properties */
17 	int				domain;
18 	int				type;
19 	/* Endpoint to bind() to */
20 	const char *ip;
21 	unsigned short port;
22 	unsigned short port_retry;
23 
24 	/* Expected test result */
25 	enum {
26 		ATTACH_REJECT,
27 		BIND_REJECT,
28 		SUCCESS,
29 		RETRY_SUCCESS,
30 		RETRY_REJECT
31 	} result;
32 } tests[] = {
33 	{
34 		.descr = "attach type mismatch bind4 vs bind6",
35 		.insns = {
36 			BPF_MOV64_IMM(BPF_REG_0, 1),
37 			BPF_EXIT_INSN(),
38 		},
39 		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
40 		.attach_type = BPF_CGROUP_INET6_POST_BIND,
41 		.result = ATTACH_REJECT,
42 	},
43 	{
44 		.descr = "attach type mismatch bind6 vs bind4",
45 		.insns = {
46 			BPF_MOV64_IMM(BPF_REG_0, 1),
47 			BPF_EXIT_INSN(),
48 		},
49 		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
50 		.attach_type = BPF_CGROUP_INET4_POST_BIND,
51 		.result = ATTACH_REJECT,
52 	},
53 	{
54 		.descr = "attach type mismatch default vs bind4",
55 		.insns = {
56 			BPF_MOV64_IMM(BPF_REG_0, 1),
57 			BPF_EXIT_INSN(),
58 		},
59 		.expected_attach_type = 0,
60 		.attach_type = BPF_CGROUP_INET4_POST_BIND,
61 		.result = ATTACH_REJECT,
62 	},
63 	{
64 		.descr = "attach type mismatch bind6 vs sock_create",
65 		.insns = {
66 			BPF_MOV64_IMM(BPF_REG_0, 1),
67 			BPF_EXIT_INSN(),
68 		},
69 		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
70 		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
71 		.result = ATTACH_REJECT,
72 	},
73 	{
74 		.descr = "bind4 reject all",
75 		.insns = {
76 			BPF_MOV64_IMM(BPF_REG_0, 0),
77 			BPF_EXIT_INSN(),
78 		},
79 		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
80 		.attach_type = BPF_CGROUP_INET4_POST_BIND,
81 		.domain = AF_INET,
82 		.type = SOCK_STREAM,
83 		.ip = "0.0.0.0",
84 		.result = BIND_REJECT,
85 	},
86 	{
87 		.descr = "bind6 reject all",
88 		.insns = {
89 			BPF_MOV64_IMM(BPF_REG_0, 0),
90 			BPF_EXIT_INSN(),
91 		},
92 		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
93 		.attach_type = BPF_CGROUP_INET6_POST_BIND,
94 		.domain = AF_INET6,
95 		.type = SOCK_STREAM,
96 		.ip = "::",
97 		.result = BIND_REJECT,
98 	},
99 	{
100 		.descr = "bind6 deny specific IP & port",
101 		.insns = {
102 			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
103 
104 			/* if (ip == expected && port == expected) */
105 			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
106 				    offsetof(struct bpf_sock, src_ip6[3])),
107 			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
108 				    __bpf_constant_ntohl(0x00000001), 4),
109 			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
110 				    offsetof(struct bpf_sock, src_port)),
111 			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),
112 
113 			/* return DENY; */
114 			BPF_MOV64_IMM(BPF_REG_0, 0),
115 			BPF_JMP_A(1),
116 
117 			/* else return ALLOW; */
118 			BPF_MOV64_IMM(BPF_REG_0, 1),
119 			BPF_EXIT_INSN(),
120 		},
121 		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
122 		.attach_type = BPF_CGROUP_INET6_POST_BIND,
123 		.domain = AF_INET6,
124 		.type = SOCK_STREAM,
125 		.ip = "::1",
126 		.port = 8193,
127 		.result = BIND_REJECT,
128 	},
129 	{
130 		.descr = "bind4 allow specific IP & port",
131 		.insns = {
132 			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
133 
134 			/* if (ip == expected && port == expected) */
135 			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
136 				    offsetof(struct bpf_sock, src_ip4)),
137 			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
138 				    __bpf_constant_ntohl(0x7F000001), 4),
139 			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
140 				    offsetof(struct bpf_sock, src_port)),
141 			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
142 
143 			/* return ALLOW; */
144 			BPF_MOV64_IMM(BPF_REG_0, 1),
145 			BPF_JMP_A(1),
146 
147 			/* else return DENY; */
148 			BPF_MOV64_IMM(BPF_REG_0, 0),
149 			BPF_EXIT_INSN(),
150 		},
151 		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
152 		.attach_type = BPF_CGROUP_INET4_POST_BIND,
153 		.domain = AF_INET,
154 		.type = SOCK_STREAM,
155 		.ip = "127.0.0.1",
156 		.port = 4098,
157 		.result = SUCCESS,
158 	},
159 	{
160 		.descr = "bind4 deny specific IP & port of TCP, and retry",
161 		.insns = {
162 			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
163 
164 			/* if (ip == expected && port == expected) */
165 			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
166 				    offsetof(struct bpf_sock, src_ip4)),
167 			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
168 				    __bpf_constant_ntohl(0x7F000001), 4),
169 			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
170 				    offsetof(struct bpf_sock, src_port)),
171 			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
172 
173 			/* return DENY; */
174 			BPF_MOV64_IMM(BPF_REG_0, 0),
175 			BPF_JMP_A(1),
176 
177 			/* else return ALLOW; */
178 			BPF_MOV64_IMM(BPF_REG_0, 1),
179 			BPF_EXIT_INSN(),
180 		},
181 		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
182 		.attach_type = BPF_CGROUP_INET4_POST_BIND,
183 		.domain = AF_INET,
184 		.type = SOCK_STREAM,
185 		.ip = "127.0.0.1",
186 		.port = 4098,
187 		.port_retry = 5000,
188 		.result = RETRY_SUCCESS,
189 	},
190 	{
191 		.descr = "bind4 deny specific IP & port of UDP, and retry",
192 		.insns = {
193 			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
194 
195 			/* if (ip == expected && port == expected) */
196 			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
197 				    offsetof(struct bpf_sock, src_ip4)),
198 			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
199 				    __bpf_constant_ntohl(0x7F000001), 4),
200 			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
201 				    offsetof(struct bpf_sock, src_port)),
202 			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
203 
204 			/* return DENY; */
205 			BPF_MOV64_IMM(BPF_REG_0, 0),
206 			BPF_JMP_A(1),
207 
208 			/* else return ALLOW; */
209 			BPF_MOV64_IMM(BPF_REG_0, 1),
210 			BPF_EXIT_INSN(),
211 		},
212 		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
213 		.attach_type = BPF_CGROUP_INET4_POST_BIND,
214 		.domain = AF_INET,
215 		.type = SOCK_DGRAM,
216 		.ip = "127.0.0.1",
217 		.port = 4098,
218 		.port_retry = 5000,
219 		.result = RETRY_SUCCESS,
220 	},
221 	{
222 		.descr = "bind6 deny specific IP & port, and retry",
223 		.insns = {
224 			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
225 
226 			/* if (ip == expected && port == expected) */
227 			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
228 				    offsetof(struct bpf_sock, src_ip6[3])),
229 			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
230 				    __bpf_constant_ntohl(0x00000001), 4),
231 			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
232 				    offsetof(struct bpf_sock, src_port)),
233 			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),
234 
235 			/* return DENY; */
236 			BPF_MOV64_IMM(BPF_REG_0, 0),
237 			BPF_JMP_A(1),
238 
239 			/* else return ALLOW; */
240 			BPF_MOV64_IMM(BPF_REG_0, 1),
241 			BPF_EXIT_INSN(),
242 		},
243 		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
244 		.attach_type = BPF_CGROUP_INET6_POST_BIND,
245 		.domain = AF_INET6,
246 		.type = SOCK_STREAM,
247 		.ip = "::1",
248 		.port = 8193,
249 		.port_retry = 9000,
250 		.result = RETRY_SUCCESS,
251 	},
252 	{
253 		.descr = "bind4 allow all",
254 		.insns = {
255 			BPF_MOV64_IMM(BPF_REG_0, 1),
256 			BPF_EXIT_INSN(),
257 		},
258 		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
259 		.attach_type = BPF_CGROUP_INET4_POST_BIND,
260 		.domain = AF_INET,
261 		.type = SOCK_STREAM,
262 		.ip = "0.0.0.0",
263 		.result = SUCCESS,
264 	},
265 	{
266 		.descr = "bind6 allow all",
267 		.insns = {
268 			BPF_MOV64_IMM(BPF_REG_0, 1),
269 			BPF_EXIT_INSN(),
270 		},
271 		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
272 		.attach_type = BPF_CGROUP_INET6_POST_BIND,
273 		.domain = AF_INET6,
274 		.type = SOCK_STREAM,
275 		.ip = "::",
276 		.result = SUCCESS,
277 	},
278 };
279 
280 static int load_prog(const struct bpf_insn *insns,
281 		     enum bpf_attach_type expected_attach_type)
282 {
283 	LIBBPF_OPTS(bpf_prog_load_opts, opts,
284 		    .expected_attach_type = expected_attach_type,
285 		    .log_level = 2,
286 		    .log_buf = bpf_log_buf,
287 		    .log_size = sizeof(bpf_log_buf),
288 	);
289 	int fd, insns_cnt = 0;
290 
291 	for (;
292 	     insns[insns_cnt].code != (BPF_JMP | BPF_EXIT);
293 	     insns_cnt++) {
294 	}
295 	insns_cnt++;
296 
297 	fd = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL", insns,
298 			   insns_cnt, &opts);
299 	if (fd < 0)
300 		fprintf(stderr, "%s\n", bpf_log_buf);
301 
302 	return fd;
303 }
304 
305 static int bind_sock(int domain, int type, const char *ip,
306 		     unsigned short port, unsigned short port_retry)
307 {
308 	struct sockaddr_storage addr;
309 	struct sockaddr_in6 *addr6;
310 	struct sockaddr_in *addr4;
311 	int sockfd = -1;
312 	socklen_t len;
313 	int res = SUCCESS;
314 
315 	sockfd = socket(domain, type, 0);
316 	if (sockfd < 0)
317 		goto err;
318 
319 	memset(&addr, 0, sizeof(addr));
320 
321 	if (domain == AF_INET) {
322 		len = sizeof(struct sockaddr_in);
323 		addr4 = (struct sockaddr_in *)&addr;
324 		addr4->sin_family = domain;
325 		addr4->sin_port = htons(port);
326 		if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1)
327 			goto err;
328 	} else if (domain == AF_INET6) {
329 		len = sizeof(struct sockaddr_in6);
330 		addr6 = (struct sockaddr_in6 *)&addr;
331 		addr6->sin6_family = domain;
332 		addr6->sin6_port = htons(port);
333 		if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1)
334 			goto err;
335 	} else {
336 		goto err;
337 	}
338 
339 	if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) {
340 		/* sys_bind() may fail for different reasons, errno has to be
341 		 * checked to confirm that BPF program rejected it.
342 		 */
343 		if (errno != EPERM)
344 			goto err;
345 		if (port_retry)
346 			goto retry;
347 		res = BIND_REJECT;
348 		goto out;
349 	}
350 
351 	goto out;
352 retry:
353 	if (domain == AF_INET)
354 		addr4->sin_port = htons(port_retry);
355 	else
356 		addr6->sin6_port = htons(port_retry);
357 	if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) {
358 		if (errno != EPERM)
359 			goto err;
360 		res = RETRY_REJECT;
361 	} else {
362 		res = RETRY_SUCCESS;
363 	}
364 	goto out;
365 err:
366 	res = -1;
367 out:
368 	close(sockfd);
369 	return res;
370 }
371 
372 static int run_test(int cgroup_fd, struct sock_post_bind_test *test)
373 {
374 	int err, prog_fd, res, ret = 0;
375 
376 	prog_fd = load_prog(test->insns, test->expected_attach_type);
377 	if (prog_fd < 0)
378 		goto err;
379 
380 	err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0);
381 	if (err < 0) {
382 		if (test->result == ATTACH_REJECT)
383 			goto out;
384 		else
385 			goto err;
386 	}
387 
388 	res = bind_sock(test->domain, test->type, test->ip, test->port,
389 			test->port_retry);
390 	if (res > 0 && test->result == res)
391 		goto out;
392 err:
393 	ret = -1;
394 out:
395 	/* Detaching w/o checking return code: best effort attempt. */
396 	if (prog_fd != -1)
397 		bpf_prog_detach(cgroup_fd, test->attach_type);
398 	close(prog_fd);
399 	return ret;
400 }
401 
402 void test_sock_post_bind(void)
403 {
404 	struct netns_obj *ns;
405 	int cgroup_fd;
406 	int i;
407 
408 	cgroup_fd = test__join_cgroup("/post_bind");
409 	if (!ASSERT_OK_FD(cgroup_fd, "join_cgroup"))
410 		return;
411 
412 	ns = netns_new(TEST_NS, true);
413 	if (!ASSERT_OK_PTR(ns, "netns_new"))
414 		goto cleanup;
415 
416 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
417 		if (!test__start_subtest(tests[i].descr))
418 			continue;
419 
420 		ASSERT_OK(run_test(cgroup_fd, &tests[i]), tests[i].descr);
421 	}
422 
423 cleanup:
424 	netns_free(ns);
425 	close(cgroup_fd);
426 }
427