1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) |
2 | /* Copyright (C) 2019 Netronome Systems, Inc. */ |
3 | /* Copyright (C) 2020 Facebook, Inc. */ |
4 | #include <ctype.h> |
5 | #include <stdlib.h> |
6 | #include <string.h> |
7 | #include <errno.h> |
8 | #include <bpf/bpf.h> |
9 | #include <bpf/libbpf.h> |
10 | #include "test_progs.h" |
11 | #include "testing_helpers.h" |
12 | #include <linux/membarrier.h> |
13 | |
14 | int parse_num_list(const char *s, bool **num_set, int *num_set_len) |
15 | { |
16 | int i, set_len = 0, new_len, num, start = 0, end = -1; |
17 | bool *set = NULL, *tmp, parsing_end = false; |
18 | char *next; |
19 | |
20 | while (s[0]) { |
21 | errno = 0; |
22 | num = strtol(s, &next, 10); |
23 | if (errno) |
24 | return -errno; |
25 | |
26 | if (parsing_end) |
27 | end = num; |
28 | else |
29 | start = num; |
30 | |
31 | if (!parsing_end && *next == '-') { |
32 | s = next + 1; |
33 | parsing_end = true; |
34 | continue; |
35 | } else if (*next == ',') { |
36 | parsing_end = false; |
37 | s = next + 1; |
38 | end = num; |
39 | } else if (*next == '\0') { |
40 | parsing_end = false; |
41 | s = next; |
42 | end = num; |
43 | } else { |
44 | return -EINVAL; |
45 | } |
46 | |
47 | if (start > end) |
48 | return -EINVAL; |
49 | |
50 | if (end + 1 > set_len) { |
51 | new_len = end + 1; |
52 | tmp = realloc(set, new_len); |
53 | if (!tmp) { |
54 | free(set); |
55 | return -ENOMEM; |
56 | } |
57 | for (i = set_len; i < start; i++) |
58 | tmp[i] = false; |
59 | set = tmp; |
60 | set_len = new_len; |
61 | } |
62 | for (i = start; i <= end; i++) |
63 | set[i] = true; |
64 | } |
65 | |
66 | if (!set || parsing_end) |
67 | return -EINVAL; |
68 | |
69 | *num_set = set; |
70 | *num_set_len = set_len; |
71 | |
72 | return 0; |
73 | } |
74 | |
75 | static int do_insert_test(struct test_filter_set *set, |
76 | char *test_str, |
77 | char *subtest_str) |
78 | { |
79 | struct test_filter *tmp, *test; |
80 | char **ctmp; |
81 | int i; |
82 | |
83 | for (i = 0; i < set->cnt; i++) { |
84 | test = &set->tests[i]; |
85 | |
86 | if (strcmp(test_str, test->name) == 0) { |
87 | free(test_str); |
88 | goto subtest; |
89 | } |
90 | } |
91 | |
92 | tmp = realloc(set->tests, sizeof(*test) * (set->cnt + 1)); |
93 | if (!tmp) |
94 | return -ENOMEM; |
95 | |
96 | set->tests = tmp; |
97 | test = &set->tests[set->cnt]; |
98 | |
99 | test->name = test_str; |
100 | test->subtests = NULL; |
101 | test->subtest_cnt = 0; |
102 | |
103 | set->cnt++; |
104 | |
105 | subtest: |
106 | if (!subtest_str) |
107 | return 0; |
108 | |
109 | for (i = 0; i < test->subtest_cnt; i++) { |
110 | if (strcmp(subtest_str, test->subtests[i]) == 0) { |
111 | free(subtest_str); |
112 | return 0; |
113 | } |
114 | } |
115 | |
116 | ctmp = realloc(test->subtests, |
117 | sizeof(*test->subtests) * (test->subtest_cnt + 1)); |
118 | if (!ctmp) |
119 | return -ENOMEM; |
120 | |
121 | test->subtests = ctmp; |
122 | test->subtests[test->subtest_cnt] = subtest_str; |
123 | |
124 | test->subtest_cnt++; |
125 | |
126 | return 0; |
127 | } |
128 | |
129 | static int insert_test(struct test_filter_set *set, |
130 | char *test_spec, |
131 | bool is_glob_pattern) |
132 | { |
133 | char *pattern, *subtest_str, *ext_test_str, *ext_subtest_str = NULL; |
134 | int glob_chars = 0; |
135 | |
136 | if (is_glob_pattern) { |
137 | pattern = "%s" ; |
138 | } else { |
139 | pattern = "*%s*" ; |
140 | glob_chars = 2; |
141 | } |
142 | |
143 | subtest_str = strchr(test_spec, '/'); |
144 | if (subtest_str) { |
145 | *subtest_str = '\0'; |
146 | subtest_str += 1; |
147 | } |
148 | |
149 | ext_test_str = malloc(strlen(test_spec) + glob_chars + 1); |
150 | if (!ext_test_str) |
151 | goto err; |
152 | |
153 | sprintf(buf: ext_test_str, fmt: pattern, test_spec); |
154 | |
155 | if (subtest_str) { |
156 | ext_subtest_str = malloc(strlen(subtest_str) + glob_chars + 1); |
157 | if (!ext_subtest_str) |
158 | goto err; |
159 | |
160 | sprintf(buf: ext_subtest_str, fmt: pattern, subtest_str); |
161 | } |
162 | |
163 | return do_insert_test(set, test_str: ext_test_str, subtest_str: ext_subtest_str); |
164 | |
165 | err: |
166 | free(ext_test_str); |
167 | free(ext_subtest_str); |
168 | |
169 | return -ENOMEM; |
170 | } |
171 | |
172 | int parse_test_list_file(const char *path, |
173 | struct test_filter_set *set, |
174 | bool is_glob_pattern) |
175 | { |
176 | char *buf = NULL, *capture_start, *capture_end, *scan_end; |
177 | size_t buflen = 0; |
178 | int err = 0; |
179 | FILE *f; |
180 | |
181 | f = fopen(path, "r" ); |
182 | if (!f) { |
183 | err = -errno; |
184 | fprintf(stderr, "Failed to open '%s': %d\n" , path, err); |
185 | return err; |
186 | } |
187 | |
188 | while (getline(&buf, &buflen, f) != -1) { |
189 | capture_start = buf; |
190 | |
191 | while (isspace(*capture_start)) |
192 | ++capture_start; |
193 | |
194 | capture_end = capture_start; |
195 | scan_end = capture_start; |
196 | |
197 | while (*scan_end && *scan_end != '#') { |
198 | if (!isspace(*scan_end)) |
199 | capture_end = scan_end; |
200 | |
201 | ++scan_end; |
202 | } |
203 | |
204 | if (capture_end == capture_start) |
205 | continue; |
206 | |
207 | *(++capture_end) = '\0'; |
208 | |
209 | err = insert_test(set, test_spec: capture_start, is_glob_pattern); |
210 | if (err) |
211 | break; |
212 | } |
213 | |
214 | fclose(f); |
215 | return err; |
216 | } |
217 | |
218 | int parse_test_list(const char *s, |
219 | struct test_filter_set *set, |
220 | bool is_glob_pattern) |
221 | { |
222 | char *input, *state = NULL, *test_spec; |
223 | int err = 0; |
224 | |
225 | input = strdup(s); |
226 | if (!input) |
227 | return -ENOMEM; |
228 | |
229 | while ((test_spec = strtok_r(state ? NULL : input, "," , &state))) { |
230 | err = insert_test(set, test_spec, is_glob_pattern); |
231 | if (err) |
232 | break; |
233 | } |
234 | |
235 | free(input); |
236 | return err; |
237 | } |
238 | |
239 | __u32 link_info_prog_id(const struct bpf_link *link, struct bpf_link_info *info) |
240 | { |
241 | __u32 info_len = sizeof(*info); |
242 | int err; |
243 | |
244 | memset(info, 0, sizeof(*info)); |
245 | err = bpf_link_get_info_by_fd(bpf_link__fd(link), info, &info_len); |
246 | if (err) { |
247 | printf("failed to get link info: %d\n" , -errno); |
248 | return 0; |
249 | } |
250 | return info->prog_id; |
251 | } |
252 | |
253 | int = 0; |
254 | |
255 | int testing_prog_flags(void) |
256 | { |
257 | static int cached_flags = -1; |
258 | static int prog_flags[] = { BPF_F_TEST_RND_HI32, BPF_F_TEST_REG_INVARIANTS }; |
259 | static struct bpf_insn insns[] = { |
260 | BPF_MOV64_IMM(BPF_REG_0, 0), |
261 | BPF_EXIT_INSN(), |
262 | }; |
263 | int insn_cnt = ARRAY_SIZE(insns), i, fd, flags = 0; |
264 | LIBBPF_OPTS(bpf_prog_load_opts, opts); |
265 | |
266 | if (cached_flags >= 0) |
267 | return cached_flags; |
268 | |
269 | for (i = 0; i < ARRAY_SIZE(prog_flags); i++) { |
270 | opts.prog_flags = prog_flags[i]; |
271 | fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, "flag-test" , "GPL" , |
272 | insns, insn_cnt, &opts); |
273 | if (fd >= 0) { |
274 | flags |= prog_flags[i]; |
275 | close(fd); |
276 | } |
277 | } |
278 | |
279 | cached_flags = flags; |
280 | return cached_flags; |
281 | } |
282 | |
283 | int bpf_prog_test_load(const char *file, enum bpf_prog_type type, |
284 | struct bpf_object **pobj, int *prog_fd) |
285 | { |
286 | LIBBPF_OPTS(bpf_object_open_opts, opts, |
287 | .kernel_log_level = extra_prog_load_log_flags, |
288 | ); |
289 | struct bpf_object *obj; |
290 | struct bpf_program *prog; |
291 | __u32 flags; |
292 | int err; |
293 | |
294 | obj = bpf_object__open_file(file, &opts); |
295 | if (!obj) |
296 | return -errno; |
297 | |
298 | prog = bpf_object__next_program(obj, NULL); |
299 | if (!prog) { |
300 | err = -ENOENT; |
301 | goto err_out; |
302 | } |
303 | |
304 | if (type != BPF_PROG_TYPE_UNSPEC && bpf_program__type(prog) != type) |
305 | bpf_program__set_type(prog, type); |
306 | |
307 | flags = bpf_program__flags(prog) | testing_prog_flags(); |
308 | bpf_program__set_flags(prog, flags); |
309 | |
310 | err = bpf_object__load(obj); |
311 | if (err) |
312 | goto err_out; |
313 | |
314 | *pobj = obj; |
315 | *prog_fd = bpf_program__fd(prog); |
316 | |
317 | return 0; |
318 | err_out: |
319 | bpf_object__close(obj); |
320 | return err; |
321 | } |
322 | |
323 | int bpf_test_load_program(enum bpf_prog_type type, const struct bpf_insn *insns, |
324 | size_t insns_cnt, const char *license, |
325 | __u32 kern_version, char *log_buf, |
326 | size_t log_buf_sz) |
327 | { |
328 | LIBBPF_OPTS(bpf_prog_load_opts, opts, |
329 | .kern_version = kern_version, |
330 | .prog_flags = testing_prog_flags(), |
331 | .log_level = extra_prog_load_log_flags, |
332 | .log_buf = log_buf, |
333 | .log_size = log_buf_sz, |
334 | ); |
335 | |
336 | return bpf_prog_load(type, NULL, license, insns, insns_cnt, &opts); |
337 | } |
338 | |
339 | __u64 read_perf_max_sample_freq(void) |
340 | { |
341 | __u64 sample_freq = 5000; /* fallback to 5000 on error */ |
342 | FILE *f; |
343 | |
344 | f = fopen("/proc/sys/kernel/perf_event_max_sample_rate" , "r" ); |
345 | if (f == NULL) { |
346 | printf("Failed to open /proc/sys/kernel/perf_event_max_sample_rate: err %d\n" |
347 | "return default value: 5000\n" , -errno); |
348 | return sample_freq; |
349 | } |
350 | if (fscanf(f, "%llu" , &sample_freq) != 1) { |
351 | printf("Failed to parse /proc/sys/kernel/perf_event_max_sample_rate: err %d\n" |
352 | "return default value: 5000\n" , -errno); |
353 | } |
354 | |
355 | fclose(f); |
356 | return sample_freq; |
357 | } |
358 | |
359 | int finit_module(int fd, const char *param_values, int flags) |
360 | { |
361 | return syscall(__NR_finit_module, fd, param_values, flags); |
362 | } |
363 | |
364 | int delete_module(const char *name, int flags) |
365 | { |
366 | return syscall(__NR_delete_module, name, flags); |
367 | } |
368 | |
369 | int unload_bpf_testmod(bool verbose) |
370 | { |
371 | if (kern_sync_rcu()) |
372 | fprintf(stdout, "Failed to trigger kernel-side RCU sync!\n" ); |
373 | if (delete_module(name: "bpf_testmod" , flags: 0)) { |
374 | if (errno == ENOENT) { |
375 | if (verbose) |
376 | fprintf(stdout, "bpf_testmod.ko is already unloaded.\n" ); |
377 | return -1; |
378 | } |
379 | fprintf(stdout, "Failed to unload bpf_testmod.ko from kernel: %d\n" , -errno); |
380 | return -1; |
381 | } |
382 | if (verbose) |
383 | fprintf(stdout, "Successfully unloaded bpf_testmod.ko.\n" ); |
384 | return 0; |
385 | } |
386 | |
387 | int load_bpf_testmod(bool verbose) |
388 | { |
389 | int fd; |
390 | |
391 | if (verbose) |
392 | fprintf(stdout, "Loading bpf_testmod.ko...\n" ); |
393 | |
394 | fd = open("bpf_testmod.ko" , O_RDONLY); |
395 | if (fd < 0) { |
396 | fprintf(stdout, "Can't find bpf_testmod.ko kernel module: %d\n" , -errno); |
397 | return -ENOENT; |
398 | } |
399 | if (finit_module(fd, param_values: "" , flags: 0)) { |
400 | fprintf(stdout, "Failed to load bpf_testmod.ko into the kernel: %d\n" , -errno); |
401 | close(fd); |
402 | return -EINVAL; |
403 | } |
404 | close(fd); |
405 | |
406 | if (verbose) |
407 | fprintf(stdout, "Successfully loaded bpf_testmod.ko.\n" ); |
408 | return 0; |
409 | } |
410 | |
411 | /* |
412 | * Trigger synchronize_rcu() in kernel. |
413 | */ |
414 | int kern_sync_rcu(void) |
415 | { |
416 | return syscall(__NR_membarrier, MEMBARRIER_CMD_SHARED, 0, 0); |
417 | } |
418 | |
419 | int get_xlated_program(int fd_prog, struct bpf_insn **buf, __u32 *cnt) |
420 | { |
421 | __u32 buf_element_size = sizeof(struct bpf_insn); |
422 | struct bpf_prog_info info = {}; |
423 | __u32 info_len = sizeof(info); |
424 | __u32 xlated_prog_len; |
425 | |
426 | if (bpf_prog_get_info_by_fd(fd_prog, &info, &info_len)) { |
427 | perror("bpf_prog_get_info_by_fd failed" ); |
428 | return -1; |
429 | } |
430 | |
431 | xlated_prog_len = info.xlated_prog_len; |
432 | if (xlated_prog_len % buf_element_size) { |
433 | printf("Program length %u is not multiple of %u\n" , |
434 | xlated_prog_len, buf_element_size); |
435 | return -1; |
436 | } |
437 | |
438 | *cnt = xlated_prog_len / buf_element_size; |
439 | *buf = calloc(*cnt, buf_element_size); |
440 | if (!buf) { |
441 | perror("can't allocate xlated program buffer" ); |
442 | return -ENOMEM; |
443 | } |
444 | |
445 | bzero(&info, sizeof(info)); |
446 | info.xlated_prog_len = xlated_prog_len; |
447 | info.xlated_prog_insns = (__u64)(unsigned long)*buf; |
448 | if (bpf_prog_get_info_by_fd(fd_prog, &info, &info_len)) { |
449 | perror("second bpf_prog_get_info_by_fd failed" ); |
450 | goto out_free_buf; |
451 | } |
452 | |
453 | return 0; |
454 | |
455 | out_free_buf: |
456 | free(*buf); |
457 | *buf = NULL; |
458 | return -1; |
459 | } |
460 | |
461 | bool is_jit_enabled(void) |
462 | { |
463 | const char *jit_sysctl = "/proc/sys/net/core/bpf_jit_enable" ; |
464 | bool enabled = false; |
465 | int sysctl_fd; |
466 | |
467 | sysctl_fd = open(jit_sysctl, O_RDONLY); |
468 | if (sysctl_fd != -1) { |
469 | char tmpc; |
470 | |
471 | if (read(sysctl_fd, &tmpc, sizeof(tmpc)) == 1) |
472 | enabled = (tmpc != '0'); |
473 | close(sysctl_fd); |
474 | } |
475 | |
476 | return enabled; |
477 | } |
478 | |