powerpc/tm: Fix restoring FP/VMX facility incorrectly on interrupts
[sfrench/cifs-2.6.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_rlimit.h"
17 #include "bpf_util.h"
18
19 #define CG_PATH         "/foo"
20 #define MAX_INSNS       512
21
22 char bpf_log_buf[BPF_LOG_BUF_SIZE];
23 static bool verbose = false;
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, 0x01000000, 4),
236                         BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
237                                     offsetof(struct bpf_sock, src_port)),
238                         BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),
239
240                         /* return DENY; */
241                         BPF_MOV64_IMM(BPF_REG_0, 0),
242                         BPF_JMP_A(1),
243
244                         /* else return ALLOW; */
245                         BPF_MOV64_IMM(BPF_REG_0, 1),
246                         BPF_EXIT_INSN(),
247                 },
248                 BPF_CGROUP_INET6_POST_BIND,
249                 BPF_CGROUP_INET6_POST_BIND,
250                 AF_INET6,
251                 SOCK_STREAM,
252                 "::1",
253                 8193,
254                 BIND_REJECT,
255         },
256         {
257                 "bind4 allow specific IP & port",
258                 .insns = {
259                         BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
260
261                         /* if (ip == expected && port == expected) */
262                         BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
263                                     offsetof(struct bpf_sock, src_ip4)),
264                         BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x0100007F, 4),
265                         BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
266                                     offsetof(struct bpf_sock, src_port)),
267                         BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
268
269                         /* return ALLOW; */
270                         BPF_MOV64_IMM(BPF_REG_0, 1),
271                         BPF_JMP_A(1),
272
273                         /* else return DENY; */
274                         BPF_MOV64_IMM(BPF_REG_0, 0),
275                         BPF_EXIT_INSN(),
276                 },
277                 BPF_CGROUP_INET4_POST_BIND,
278                 BPF_CGROUP_INET4_POST_BIND,
279                 AF_INET,
280                 SOCK_STREAM,
281                 "127.0.0.1",
282                 4098,
283                 SUCCESS,
284         },
285         {
286                 "bind4 allow all",
287                 .insns = {
288                         BPF_MOV64_IMM(BPF_REG_0, 1),
289                         BPF_EXIT_INSN(),
290                 },
291                 BPF_CGROUP_INET4_POST_BIND,
292                 BPF_CGROUP_INET4_POST_BIND,
293                 AF_INET,
294                 SOCK_STREAM,
295                 "0.0.0.0",
296                 0,
297                 SUCCESS,
298         },
299         {
300                 "bind6 allow all",
301                 .insns = {
302                         BPF_MOV64_IMM(BPF_REG_0, 1),
303                         BPF_EXIT_INSN(),
304                 },
305                 BPF_CGROUP_INET6_POST_BIND,
306                 BPF_CGROUP_INET6_POST_BIND,
307                 AF_INET6,
308                 SOCK_STREAM,
309                 "::",
310                 0,
311                 SUCCESS,
312         },
313 };
314
315 static size_t probe_prog_length(const struct bpf_insn *fp)
316 {
317         size_t len;
318
319         for (len = MAX_INSNS - 1; len > 0; --len)
320                 if (fp[len].code != 0 || fp[len].imm != 0)
321                         break;
322         return len + 1;
323 }
324
325 static int load_sock_prog(const struct bpf_insn *prog,
326                           enum bpf_attach_type attach_type)
327 {
328         struct bpf_load_program_attr attr;
329         int ret;
330
331         memset(&attr, 0, sizeof(struct bpf_load_program_attr));
332         attr.prog_type = BPF_PROG_TYPE_CGROUP_SOCK;
333         attr.expected_attach_type = attach_type;
334         attr.insns = prog;
335         attr.insns_cnt = probe_prog_length(attr.insns);
336         attr.license = "GPL";
337         attr.log_level = 2;
338
339         ret = bpf_load_program_xattr(&attr, bpf_log_buf, BPF_LOG_BUF_SIZE);
340         if (verbose && ret < 0)
341                 fprintf(stderr, "%s\n", bpf_log_buf);
342
343         return ret;
344 }
345
346 static int attach_sock_prog(int cgfd, int progfd,
347                             enum bpf_attach_type attach_type)
348 {
349         return bpf_prog_attach(progfd, cgfd, attach_type, BPF_F_ALLOW_OVERRIDE);
350 }
351
352 static int bind_sock(int domain, int type, const char *ip, unsigned short port)
353 {
354         struct sockaddr_storage addr;
355         struct sockaddr_in6 *addr6;
356         struct sockaddr_in *addr4;
357         int sockfd = -1;
358         socklen_t len;
359         int err = 0;
360
361         sockfd = socket(domain, type, 0);
362         if (sockfd < 0)
363                 goto err;
364
365         memset(&addr, 0, sizeof(addr));
366
367         if (domain == AF_INET) {
368                 len = sizeof(struct sockaddr_in);
369                 addr4 = (struct sockaddr_in *)&addr;
370                 addr4->sin_family = domain;
371                 addr4->sin_port = htons(port);
372                 if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1)
373                         goto err;
374         } else if (domain == AF_INET6) {
375                 len = sizeof(struct sockaddr_in6);
376                 addr6 = (struct sockaddr_in6 *)&addr;
377                 addr6->sin6_family = domain;
378                 addr6->sin6_port = htons(port);
379                 if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1)
380                         goto err;
381         } else {
382                 goto err;
383         }
384
385         if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1)
386                 goto err;
387
388         goto out;
389 err:
390         err = -1;
391 out:
392         close(sockfd);
393         return err;
394 }
395
396 static int run_test_case(int cgfd, const struct sock_test *test)
397 {
398         int progfd = -1;
399         int err = 0;
400
401         printf("Test case: %s .. ", test->descr);
402         progfd = load_sock_prog(test->insns, test->expected_attach_type);
403         if (progfd < 0) {
404                 if (test->result == LOAD_REJECT)
405                         goto out;
406                 else
407                         goto err;
408         }
409
410         if (attach_sock_prog(cgfd, progfd, test->attach_type) == -1) {
411                 if (test->result == ATTACH_REJECT)
412                         goto out;
413                 else
414                         goto err;
415         }
416
417         if (bind_sock(test->domain, test->type, test->ip, test->port) == -1) {
418                 /* sys_bind() may fail for different reasons, errno has to be
419                  * checked to confirm that BPF program rejected it.
420                  */
421                 if (test->result == BIND_REJECT && errno == EPERM)
422                         goto out;
423                 else
424                         goto err;
425         }
426
427
428         if (test->result != SUCCESS)
429                 goto err;
430
431         goto out;
432 err:
433         err = -1;
434 out:
435         /* Detaching w/o checking return code: best effort attempt. */
436         if (progfd != -1)
437                 bpf_prog_detach(cgfd, test->attach_type);
438         close(progfd);
439         printf("[%s]\n", err ? "FAIL" : "PASS");
440         return err;
441 }
442
443 static int run_tests(int cgfd)
444 {
445         int passes = 0;
446         int fails = 0;
447         int i;
448
449         for (i = 0; i < ARRAY_SIZE(tests); ++i) {
450                 if (run_test_case(cgfd, &tests[i]))
451                         ++fails;
452                 else
453                         ++passes;
454         }
455         printf("Summary: %d PASSED, %d FAILED\n", passes, fails);
456         return fails ? -1 : 0;
457 }
458
459 int main(int argc, char **argv)
460 {
461         int cgfd = -1;
462         int err = 0;
463
464         if (setup_cgroup_environment())
465                 goto err;
466
467         cgfd = create_and_get_cgroup(CG_PATH);
468         if (cgfd < 0)
469                 goto err;
470
471         if (join_cgroup(CG_PATH))
472                 goto err;
473
474         if (run_tests(cgfd))
475                 goto err;
476
477         goto out;
478 err:
479         err = -1;
480 out:
481         close(cgfd);
482         cleanup_cgroup_environment();
483         return err;
484 }