Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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
  8static char bpf_log_buf[4096];
  9
 10static 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
280static 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
305static 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;
352retry:
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;
365err:
366	res = -1;
367out:
368	close(sockfd);
369	return res;
370}
371
372static 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;
392err:
393	ret = -1;
394out:
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
402void 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
423cleanup:
424	netns_free(ns);
425	close(cgroup_fd);
426}