GNU Linux-libre 4.19.264-gnu1
[releases.git] / tools / testing / selftests / bpf / test_sock.c
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2018 Facebook
3
4 #include <stdio.h>
5 #include <unistd.h>
6
7 #include <arpa/inet.h>
8 #include <sys/types.h>
9 #include <sys/socket.h>
10
11 #include <linux/filter.h>
12
13 #include <bpf/bpf.h>
14
15 #include "cgroup_helpers.h"
16 #include "bpf_endian.h"
17 #include "bpf_rlimit.h"
18 #include "bpf_util.h"
19
20 #define CG_PATH         "/foo"
21 #define MAX_INSNS       512
22
23 char bpf_log_buf[BPF_LOG_BUF_SIZE];
24
25 struct sock_test {
26         const char *descr;
27         /* BPF prog properties */
28         struct bpf_insn insns[MAX_INSNS];
29         enum bpf_attach_type expected_attach_type;
30         enum bpf_attach_type attach_type;
31         /* Socket properties */
32         int domain;
33         int type;
34         /* Endpoint to bind() to */
35         const char *ip;
36         unsigned short port;
37         /* Expected test result */
38         enum {
39                 LOAD_REJECT,
40                 ATTACH_REJECT,
41                 BIND_REJECT,
42                 SUCCESS,
43         } result;
44 };
45
46 static struct sock_test tests[] = {
47         {
48                 "bind4 load with invalid access: src_ip6",
49                 .insns = {
50                         BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
51                         BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
52                                     offsetof(struct bpf_sock, src_ip6[0])),
53                         BPF_MOV64_IMM(BPF_REG_0, 1),
54                         BPF_EXIT_INSN(),
55                 },
56                 BPF_CGROUP_INET4_POST_BIND,
57                 BPF_CGROUP_INET4_POST_BIND,
58                 0,
59                 0,
60                 NULL,
61                 0,
62                 LOAD_REJECT,
63         },
64         {
65                 "bind4 load with invalid access: mark",
66                 .insns = {
67                         BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
68                         BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
69                                     offsetof(struct bpf_sock, mark)),
70                         BPF_MOV64_IMM(BPF_REG_0, 1),
71                         BPF_EXIT_INSN(),
72                 },
73                 BPF_CGROUP_INET4_POST_BIND,
74                 BPF_CGROUP_INET4_POST_BIND,
75                 0,
76                 0,
77                 NULL,
78                 0,
79                 LOAD_REJECT,
80         },
81         {
82                 "bind6 load with invalid access: src_ip4",
83                 .insns = {
84                         BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
85                         BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
86                                     offsetof(struct bpf_sock, src_ip4)),
87                         BPF_MOV64_IMM(BPF_REG_0, 1),
88                         BPF_EXIT_INSN(),
89                 },
90                 BPF_CGROUP_INET6_POST_BIND,
91                 BPF_CGROUP_INET6_POST_BIND,
92                 0,
93                 0,
94                 NULL,
95                 0,
96                 LOAD_REJECT,
97         },
98         {
99                 "sock_create load with invalid access: src_port",
100                 .insns = {
101                         BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
102                         BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
103                                     offsetof(struct bpf_sock, src_port)),
104                         BPF_MOV64_IMM(BPF_REG_0, 1),
105                         BPF_EXIT_INSN(),
106                 },
107                 BPF_CGROUP_INET_SOCK_CREATE,
108                 BPF_CGROUP_INET_SOCK_CREATE,
109                 0,
110                 0,
111                 NULL,
112                 0,
113                 LOAD_REJECT,
114         },
115         {
116                 "sock_create load w/o expected_attach_type (compat mode)",
117                 .insns = {
118                         BPF_MOV64_IMM(BPF_REG_0, 1),
119                         BPF_EXIT_INSN(),
120                 },
121                 0,
122                 BPF_CGROUP_INET_SOCK_CREATE,
123                 AF_INET,
124                 SOCK_STREAM,
125                 "127.0.0.1",
126                 8097,
127                 SUCCESS,
128         },
129         {
130                 "sock_create load w/ expected_attach_type",
131                 .insns = {
132                         BPF_MOV64_IMM(BPF_REG_0, 1),
133                         BPF_EXIT_INSN(),
134                 },
135                 BPF_CGROUP_INET_SOCK_CREATE,
136                 BPF_CGROUP_INET_SOCK_CREATE,
137                 AF_INET,
138                 SOCK_STREAM,
139                 "127.0.0.1",
140                 8097,
141                 SUCCESS,
142         },
143         {
144                 "attach type mismatch bind4 vs bind6",
145                 .insns = {
146                         BPF_MOV64_IMM(BPF_REG_0, 1),
147                         BPF_EXIT_INSN(),
148                 },
149                 BPF_CGROUP_INET4_POST_BIND,
150                 BPF_CGROUP_INET6_POST_BIND,
151                 0,
152                 0,
153                 NULL,
154                 0,
155                 ATTACH_REJECT,
156         },
157         {
158                 "attach type mismatch bind6 vs bind4",
159                 .insns = {
160                         BPF_MOV64_IMM(BPF_REG_0, 1),
161                         BPF_EXIT_INSN(),
162                 },
163                 BPF_CGROUP_INET6_POST_BIND,
164                 BPF_CGROUP_INET4_POST_BIND,
165                 0,
166                 0,
167                 NULL,
168                 0,
169                 ATTACH_REJECT,
170         },
171         {
172                 "attach type mismatch default vs bind4",
173                 .insns = {
174                         BPF_MOV64_IMM(BPF_REG_0, 1),
175                         BPF_EXIT_INSN(),
176                 },
177                 0,
178                 BPF_CGROUP_INET4_POST_BIND,
179                 0,
180                 0,
181                 NULL,
182                 0,
183                 ATTACH_REJECT,
184         },
185         {
186                 "attach type mismatch bind6 vs sock_create",
187                 .insns = {
188                         BPF_MOV64_IMM(BPF_REG_0, 1),
189                         BPF_EXIT_INSN(),
190                 },
191                 BPF_CGROUP_INET6_POST_BIND,
192                 BPF_CGROUP_INET_SOCK_CREATE,
193                 0,
194                 0,
195                 NULL,
196                 0,
197                 ATTACH_REJECT,
198         },
199         {
200                 "bind4 reject all",
201                 .insns = {
202                         BPF_MOV64_IMM(BPF_REG_0, 0),
203                         BPF_EXIT_INSN(),
204                 },
205                 BPF_CGROUP_INET4_POST_BIND,
206                 BPF_CGROUP_INET4_POST_BIND,
207                 AF_INET,
208                 SOCK_STREAM,
209                 "0.0.0.0",
210                 0,
211                 BIND_REJECT,
212         },
213         {
214                 "bind6 reject all",
215                 .insns = {
216                         BPF_MOV64_IMM(BPF_REG_0, 0),
217                         BPF_EXIT_INSN(),
218                 },
219                 BPF_CGROUP_INET6_POST_BIND,
220                 BPF_CGROUP_INET6_POST_BIND,
221                 AF_INET6,
222                 SOCK_STREAM,
223                 "::",
224                 0,
225                 BIND_REJECT,
226         },
227         {
228                 "bind6 deny specific IP & port",
229                 .insns = {
230                         BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
231
232                         /* if (ip == expected && port == expected) */
233                         BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
234                                     offsetof(struct bpf_sock, src_ip6[3])),
235                         BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
236                                     __bpf_constant_ntohl(0x00000001), 4),
237                         BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
238                                     offsetof(struct bpf_sock, src_port)),
239                         BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),
240
241                         /* return DENY; */
242                         BPF_MOV64_IMM(BPF_REG_0, 0),
243                         BPF_JMP_A(1),
244
245                         /* else return ALLOW; */
246                         BPF_MOV64_IMM(BPF_REG_0, 1),
247                         BPF_EXIT_INSN(),
248                 },
249                 BPF_CGROUP_INET6_POST_BIND,
250                 BPF_CGROUP_INET6_POST_BIND,
251                 AF_INET6,
252                 SOCK_STREAM,
253                 "::1",
254                 8193,
255                 BIND_REJECT,
256         },
257         {
258                 "bind4 allow specific IP & port",
259                 .insns = {
260                         BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
261
262                         /* if (ip == expected && port == expected) */
263                         BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
264                                     offsetof(struct bpf_sock, src_ip4)),
265                         BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
266                                     __bpf_constant_ntohl(0x7F000001), 4),
267                         BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
268                                     offsetof(struct bpf_sock, src_port)),
269                         BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
270
271                         /* return ALLOW; */
272                         BPF_MOV64_IMM(BPF_REG_0, 1),
273                         BPF_JMP_A(1),
274
275                         /* else return DENY; */
276                         BPF_MOV64_IMM(BPF_REG_0, 0),
277                         BPF_EXIT_INSN(),
278                 },
279                 BPF_CGROUP_INET4_POST_BIND,
280                 BPF_CGROUP_INET4_POST_BIND,
281                 AF_INET,
282                 SOCK_STREAM,
283                 "127.0.0.1",
284                 4098,
285                 SUCCESS,
286         },
287         {
288                 "bind4 allow all",
289                 .insns = {
290                         BPF_MOV64_IMM(BPF_REG_0, 1),
291                         BPF_EXIT_INSN(),
292                 },
293                 BPF_CGROUP_INET4_POST_BIND,
294                 BPF_CGROUP_INET4_POST_BIND,
295                 AF_INET,
296                 SOCK_STREAM,
297                 "0.0.0.0",
298                 0,
299                 SUCCESS,
300         },
301         {
302                 "bind6 allow all",
303                 .insns = {
304                         BPF_MOV64_IMM(BPF_REG_0, 1),
305                         BPF_EXIT_INSN(),
306                 },
307                 BPF_CGROUP_INET6_POST_BIND,
308                 BPF_CGROUP_INET6_POST_BIND,
309                 AF_INET6,
310                 SOCK_STREAM,
311                 "::",
312                 0,
313                 SUCCESS,
314         },
315 };
316
317 static size_t probe_prog_length(const struct bpf_insn *fp)
318 {
319         size_t len;
320
321         for (len = MAX_INSNS - 1; len > 0; --len)
322                 if (fp[len].code != 0 || fp[len].imm != 0)
323                         break;
324         return len + 1;
325 }
326
327 static int load_sock_prog(const struct bpf_insn *prog,
328                           enum bpf_attach_type attach_type)
329 {
330         struct bpf_load_program_attr attr;
331
332         memset(&attr, 0, sizeof(struct bpf_load_program_attr));
333         attr.prog_type = BPF_PROG_TYPE_CGROUP_SOCK;
334         attr.expected_attach_type = attach_type;
335         attr.insns = prog;
336         attr.insns_cnt = probe_prog_length(attr.insns);
337         attr.license = "GPL";
338
339         return bpf_load_program_xattr(&attr, bpf_log_buf, BPF_LOG_BUF_SIZE);
340 }
341
342 static int attach_sock_prog(int cgfd, int progfd,
343                             enum bpf_attach_type attach_type)
344 {
345         return bpf_prog_attach(progfd, cgfd, attach_type, BPF_F_ALLOW_OVERRIDE);
346 }
347
348 static int bind_sock(int domain, int type, const char *ip, unsigned short port)
349 {
350         struct sockaddr_storage addr;
351         struct sockaddr_in6 *addr6;
352         struct sockaddr_in *addr4;
353         int sockfd = -1;
354         socklen_t len;
355         int err = 0;
356
357         sockfd = socket(domain, type, 0);
358         if (sockfd < 0)
359                 goto err;
360
361         memset(&addr, 0, sizeof(addr));
362
363         if (domain == AF_INET) {
364                 len = sizeof(struct sockaddr_in);
365                 addr4 = (struct sockaddr_in *)&addr;
366                 addr4->sin_family = domain;
367                 addr4->sin_port = htons(port);
368                 if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1)
369                         goto err;
370         } else if (domain == AF_INET6) {
371                 len = sizeof(struct sockaddr_in6);
372                 addr6 = (struct sockaddr_in6 *)&addr;
373                 addr6->sin6_family = domain;
374                 addr6->sin6_port = htons(port);
375                 if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1)
376                         goto err;
377         } else {
378                 goto err;
379         }
380
381         if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1)
382                 goto err;
383
384         goto out;
385 err:
386         err = -1;
387 out:
388         close(sockfd);
389         return err;
390 }
391
392 static int run_test_case(int cgfd, const struct sock_test *test)
393 {
394         int progfd = -1;
395         int err = 0;
396
397         printf("Test case: %s .. ", test->descr);
398         progfd = load_sock_prog(test->insns, test->expected_attach_type);
399         if (progfd < 0) {
400                 if (test->result == LOAD_REJECT)
401                         goto out;
402                 else
403                         goto err;
404         }
405
406         if (attach_sock_prog(cgfd, progfd, test->attach_type) == -1) {
407                 if (test->result == ATTACH_REJECT)
408                         goto out;
409                 else
410                         goto err;
411         }
412
413         if (bind_sock(test->domain, test->type, test->ip, test->port) == -1) {
414                 /* sys_bind() may fail for different reasons, errno has to be
415                  * checked to confirm that BPF program rejected it.
416                  */
417                 if (test->result == BIND_REJECT && errno == EPERM)
418                         goto out;
419                 else
420                         goto err;
421         }
422
423
424         if (test->result != SUCCESS)
425                 goto err;
426
427         goto out;
428 err:
429         err = -1;
430 out:
431         /* Detaching w/o checking return code: best effort attempt. */
432         if (progfd != -1)
433                 bpf_prog_detach(cgfd, test->attach_type);
434         close(progfd);
435         printf("[%s]\n", err ? "FAIL" : "PASS");
436         return err;
437 }
438
439 static int run_tests(int cgfd)
440 {
441         int passes = 0;
442         int fails = 0;
443         int i;
444
445         for (i = 0; i < ARRAY_SIZE(tests); ++i) {
446                 if (run_test_case(cgfd, &tests[i]))
447                         ++fails;
448                 else
449                         ++passes;
450         }
451         printf("Summary: %d PASSED, %d FAILED\n", passes, fails);
452         return fails ? -1 : 0;
453 }
454
455 int main(int argc, char **argv)
456 {
457         int cgfd = -1;
458         int err = 0;
459
460         if (setup_cgroup_environment())
461                 goto err;
462
463         cgfd = create_and_get_cgroup(CG_PATH);
464         if (!cgfd)
465                 goto err;
466
467         if (join_cgroup(CG_PATH))
468                 goto err;
469
470         if (run_tests(cgfd))
471                 goto err;
472
473         goto out;
474 err:
475         err = -1;
476 out:
477         close(cgfd);
478         cleanup_cgroup_environment();
479         return err;
480 }