xref: /linux/tools/testing/selftests/bpf/prog_tests/sock_create.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/bpf.h>
3 #include <test_progs.h>
4 #include "cgroup_helpers.h"
5 
6 static char bpf_log_buf[4096];
7 static bool verbose;
8 
9 enum sock_create_test_error {
10 	OK = 0,
11 	DENY_CREATE,
12 };
13 
14 static struct sock_create_test {
15 	const char			*descr;
16 	const struct bpf_insn		insns[64];
17 	enum bpf_attach_type		attach_type;
18 	enum bpf_attach_type		expected_attach_type;
19 
20 	int				domain;
21 	int				type;
22 	int				protocol;
23 
24 	int				optname;
25 	int				optval;
26 	enum sock_create_test_error	error;
27 } tests[] = {
28 	{
29 		.descr = "AF_INET set priority",
30 		.insns = {
31 			/* r3 = 123 (priority) */
32 			BPF_MOV64_IMM(BPF_REG_3, 123),
33 			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3,
34 				    offsetof(struct bpf_sock, priority)),
35 
36 			/* return 1 */
37 			BPF_MOV64_IMM(BPF_REG_0, 1),
38 			BPF_EXIT_INSN(),
39 		},
40 		.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
41 		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
42 
43 		.domain = AF_INET,
44 		.type = SOCK_DGRAM,
45 
46 		.optname = SO_PRIORITY,
47 		.optval = 123,
48 	},
49 	{
50 		.descr = "AF_INET6 set priority",
51 		.insns = {
52 			/* r3 = 123 (priority) */
53 			BPF_MOV64_IMM(BPF_REG_3, 123),
54 			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3,
55 				    offsetof(struct bpf_sock, priority)),
56 
57 			/* return 1 */
58 			BPF_MOV64_IMM(BPF_REG_0, 1),
59 			BPF_EXIT_INSN(),
60 		},
61 		.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
62 		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
63 
64 		.domain = AF_INET6,
65 		.type = SOCK_DGRAM,
66 
67 		.optname = SO_PRIORITY,
68 		.optval = 123,
69 	},
70 	{
71 		.descr = "AF_INET set mark",
72 		.insns = {
73 			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
74 
75 			/* get uid of process */
76 			BPF_EMIT_CALL(BPF_FUNC_get_current_uid_gid),
77 			BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffffffff),
78 
79 			/* if uid is 0, use given mark(666), else use uid as the mark */
80 			BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
81 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
82 			BPF_MOV64_IMM(BPF_REG_3, 666),
83 
84 			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
85 			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3,
86 				    offsetof(struct bpf_sock, mark)),
87 
88 			/* return 1 */
89 			BPF_MOV64_IMM(BPF_REG_0, 1),
90 			BPF_EXIT_INSN(),
91 		},
92 		.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
93 		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
94 
95 		.domain = AF_INET,
96 		.type = SOCK_DGRAM,
97 
98 		.optname = SO_MARK,
99 		.optval = 666,
100 	},
101 	{
102 		.descr = "AF_INET6 set mark",
103 		.insns = {
104 			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
105 
106 			/* get uid of process */
107 			BPF_EMIT_CALL(BPF_FUNC_get_current_uid_gid),
108 			BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffffffff),
109 
110 			/* if uid is 0, use given mark(666), else use uid as the mark */
111 			BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
112 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
113 			BPF_MOV64_IMM(BPF_REG_3, 666),
114 
115 			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
116 			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3,
117 				    offsetof(struct bpf_sock, mark)),
118 
119 			/* return 1 */
120 			BPF_MOV64_IMM(BPF_REG_0, 1),
121 			BPF_EXIT_INSN(),
122 		},
123 		.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
124 		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
125 
126 		.domain = AF_INET6,
127 		.type = SOCK_DGRAM,
128 
129 		.optname = SO_MARK,
130 		.optval = 666,
131 	},
132 	{
133 		.descr = "AF_INET bound to iface",
134 		.insns = {
135 			/* r3 = 1 (lo interface) */
136 			BPF_MOV64_IMM(BPF_REG_3, 1),
137 			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3,
138 				    offsetof(struct bpf_sock, bound_dev_if)),
139 
140 			/* return 1 */
141 			BPF_MOV64_IMM(BPF_REG_0, 1),
142 			BPF_EXIT_INSN(),
143 		},
144 		.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
145 		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
146 
147 		.domain = AF_INET,
148 		.type = SOCK_DGRAM,
149 
150 		.optname = SO_BINDTOIFINDEX,
151 		.optval = 1,
152 	},
153 	{
154 		.descr = "AF_INET6 bound to iface",
155 		.insns = {
156 			/* r3 = 1 (lo interface) */
157 			BPF_MOV64_IMM(BPF_REG_3, 1),
158 			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3,
159 				    offsetof(struct bpf_sock, bound_dev_if)),
160 
161 			/* return 1 */
162 			BPF_MOV64_IMM(BPF_REG_0, 1),
163 			BPF_EXIT_INSN(),
164 		},
165 		.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
166 		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
167 
168 		.domain = AF_INET6,
169 		.type = SOCK_DGRAM,
170 
171 		.optname = SO_BINDTOIFINDEX,
172 		.optval = 1,
173 	},
174 	{
175 		.descr = "block AF_INET, SOCK_DGRAM, IPPROTO_ICMP socket",
176 		.insns = {
177 			BPF_MOV64_IMM(BPF_REG_0, 1),	/* r0 = verdict */
178 
179 			/* sock->family == AF_INET */
180 			BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1,
181 				    offsetof(struct bpf_sock, family)),
182 			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, AF_INET, 5),
183 
184 			/* sock->type == SOCK_DGRAM */
185 			BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1,
186 				    offsetof(struct bpf_sock, type)),
187 			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, SOCK_DGRAM, 3),
188 
189 			/* sock->protocol == IPPROTO_ICMP */
190 			BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1,
191 				    offsetof(struct bpf_sock, protocol)),
192 			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, IPPROTO_ICMP, 1),
193 
194 			/* return 0 (block) */
195 			BPF_MOV64_IMM(BPF_REG_0, 0),
196 			BPF_EXIT_INSN(),
197 		},
198 		.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
199 		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
200 
201 		.domain = AF_INET,
202 		.type = SOCK_DGRAM,
203 		.protocol = IPPROTO_ICMP,
204 
205 		.error = DENY_CREATE,
206 	},
207 	{
208 		.descr = "block AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6 socket",
209 		.insns = {
210 			BPF_MOV64_IMM(BPF_REG_0, 1),	/* r0 = verdict */
211 
212 			/* sock->family == AF_INET6 */
213 			BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1,
214 				    offsetof(struct bpf_sock, family)),
215 			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, AF_INET6, 5),
216 
217 			/* sock->type == SOCK_DGRAM */
218 			BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1,
219 				    offsetof(struct bpf_sock, type)),
220 			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, SOCK_DGRAM, 3),
221 
222 			/* sock->protocol == IPPROTO_ICMPV6 */
223 			BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1,
224 				    offsetof(struct bpf_sock, protocol)),
225 			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, IPPROTO_ICMPV6, 1),
226 
227 			/* return 0 (block) */
228 			BPF_MOV64_IMM(BPF_REG_0, 0),
229 			BPF_EXIT_INSN(),
230 		},
231 		.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
232 		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
233 
234 		.domain = AF_INET,
235 		.type = SOCK_DGRAM,
236 		.protocol = IPPROTO_ICMPV6,
237 
238 		.error = DENY_CREATE,
239 	},
240 	{
241 		.descr = "load w/o expected_attach_type (compat mode)",
242 		.insns = {
243 			/* return 1 */
244 			BPF_MOV64_IMM(BPF_REG_0, 1),
245 			BPF_EXIT_INSN(),
246 		},
247 		.expected_attach_type = 0,
248 		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
249 
250 		.domain = AF_INET,
251 		.type = SOCK_STREAM,
252 	},
253 };
254 
255 static int load_prog(const struct bpf_insn *insns,
256 		     enum bpf_attach_type expected_attach_type)
257 {
258 	LIBBPF_OPTS(bpf_prog_load_opts, opts,
259 		    .expected_attach_type = expected_attach_type,
260 		    .log_level = 2,
261 		    .log_buf = bpf_log_buf,
262 		    .log_size = sizeof(bpf_log_buf),
263 	);
264 	int fd, insns_cnt = 0;
265 
266 	for (;
267 	     insns[insns_cnt].code != (BPF_JMP | BPF_EXIT);
268 	     insns_cnt++) {
269 	}
270 	insns_cnt++;
271 
272 	fd = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL", insns,
273 			   insns_cnt, &opts);
274 	if (verbose && fd < 0)
275 		fprintf(stderr, "%s\n", bpf_log_buf);
276 
277 	return fd;
278 }
279 
280 static int run_test(int cgroup_fd, struct sock_create_test *test)
281 {
282 	int sock_fd, err, prog_fd, optval, ret = -1;
283 	socklen_t optlen = sizeof(optval);
284 
285 	prog_fd = load_prog(test->insns, test->expected_attach_type);
286 	if (prog_fd < 0) {
287 		log_err("Failed to load BPF program");
288 		return -1;
289 	}
290 
291 	err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0);
292 	if (err < 0) {
293 		log_err("Failed to attach BPF program");
294 		goto close_prog_fd;
295 	}
296 
297 	sock_fd = socket(test->domain, test->type, test->protocol);
298 	if (sock_fd < 0) {
299 		if (test->error == DENY_CREATE)
300 			ret = 0;
301 		else
302 			log_err("Failed to create socket");
303 
304 		goto detach_prog;
305 	}
306 
307 	if (test->optname) {
308 		err = getsockopt(sock_fd, SOL_SOCKET, test->optname, &optval, &optlen);
309 		if (err) {
310 			log_err("Failed to call getsockopt");
311 			goto cleanup;
312 		}
313 
314 		if (optval != test->optval) {
315 			errno = 0;
316 			log_err("getsockopt returned unexpected optval");
317 			goto cleanup;
318 		}
319 	}
320 
321 	ret = test->error != OK;
322 
323 cleanup:
324 	close(sock_fd);
325 detach_prog:
326 	bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type);
327 close_prog_fd:
328 	close(prog_fd);
329 	return ret;
330 }
331 
332 void test_sock_create(void)
333 {
334 	int cgroup_fd, i;
335 
336 	cgroup_fd = test__join_cgroup("/sock_create");
337 	if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup"))
338 		return;
339 
340 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
341 		if (!test__start_subtest(tests[i].descr))
342 			continue;
343 
344 		ASSERT_OK(run_test(cgroup_fd, &tests[i]), tests[i].descr);
345 	}
346 
347 	close(cgroup_fd);
348 }
349