]>
Commit | Line | Data |
---|---|---|
71bb428f | 1 | /* |
71e07ddc | 2 | * Copyright (C) 2017-2018 Netronome Systems, Inc. |
71bb428f JK |
3 | * |
4 | * This software is dual licensed under the GNU General License Version 2, | |
5 | * June 1991 as shown in the file COPYING in the top-level directory of this | |
6 | * source tree or the BSD 2-Clause License provided below. You have the | |
7 | * option to license this software under the complete terms of either license. | |
8 | * | |
9 | * The BSD 2-Clause License: | |
10 | * | |
11 | * Redistribution and use in source and binary forms, with or | |
12 | * without modification, are permitted provided that the following | |
13 | * conditions are met: | |
14 | * | |
15 | * 1. Redistributions of source code must retain the above | |
16 | * copyright notice, this list of conditions and the following | |
17 | * disclaimer. | |
18 | * | |
19 | * 2. Redistributions in binary form must reproduce the above | |
20 | * copyright notice, this list of conditions and the following | |
21 | * disclaimer in the documentation and/or other materials | |
22 | * provided with the distribution. | |
23 | * | |
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
31 | * SOFTWARE. | |
32 | */ | |
33 | ||
3ff5a4dc | 34 | #define _GNU_SOURCE |
71bb428f JK |
35 | #include <errno.h> |
36 | #include <fcntl.h> | |
c9c35995 | 37 | #include <stdarg.h> |
71bb428f JK |
38 | #include <stdio.h> |
39 | #include <stdlib.h> | |
40 | #include <string.h> | |
41 | #include <time.h> | |
42 | #include <unistd.h> | |
ba6dd679 | 43 | #include <net/if.h> |
71bb428f JK |
44 | #include <sys/types.h> |
45 | #include <sys/stat.h> | |
46 | ||
c8406848 JK |
47 | #include <linux/err.h> |
48 | ||
71bb428f | 49 | #include <bpf.h> |
49a086c2 | 50 | #include <libbpf.h> |
71bb428f | 51 | |
b6c1cedb | 52 | #include "cfg.h" |
71bb428f | 53 | #include "main.h" |
73bb5b4f | 54 | #include "xlated_dumper.h" |
71bb428f JK |
55 | |
56 | static const char * const prog_type_name[] = { | |
57 | [BPF_PROG_TYPE_UNSPEC] = "unspec", | |
58 | [BPF_PROG_TYPE_SOCKET_FILTER] = "socket_filter", | |
59 | [BPF_PROG_TYPE_KPROBE] = "kprobe", | |
60 | [BPF_PROG_TYPE_SCHED_CLS] = "sched_cls", | |
61 | [BPF_PROG_TYPE_SCHED_ACT] = "sched_act", | |
62 | [BPF_PROG_TYPE_TRACEPOINT] = "tracepoint", | |
63 | [BPF_PROG_TYPE_XDP] = "xdp", | |
64 | [BPF_PROG_TYPE_PERF_EVENT] = "perf_event", | |
65 | [BPF_PROG_TYPE_CGROUP_SKB] = "cgroup_skb", | |
66 | [BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup_sock", | |
67 | [BPF_PROG_TYPE_LWT_IN] = "lwt_in", | |
68 | [BPF_PROG_TYPE_LWT_OUT] = "lwt_out", | |
69 | [BPF_PROG_TYPE_LWT_XMIT] = "lwt_xmit", | |
70 | [BPF_PROG_TYPE_SOCK_OPS] = "sock_ops", | |
71 | [BPF_PROG_TYPE_SK_SKB] = "sk_skb", | |
45e5e121 | 72 | [BPF_PROG_TYPE_CGROUP_DEVICE] = "cgroup_device", |
393de512 AI |
73 | [BPF_PROG_TYPE_SK_MSG] = "sk_msg", |
74 | [BPF_PROG_TYPE_RAW_TRACEPOINT] = "raw_tracepoint", | |
75 | [BPF_PROG_TYPE_CGROUP_SOCK_ADDR] = "cgroup_sock_addr", | |
6bdd533c | 76 | [BPF_PROG_TYPE_LIRC_MODE2] = "lirc_mode2", |
c22fbae7 | 77 | [BPF_PROG_TYPE_FLOW_DISSECTOR] = "flow_dissector", |
71bb428f JK |
78 | }; |
79 | ||
b7d3826c JF |
80 | static const char * const attach_type_strings[] = { |
81 | [BPF_SK_SKB_STREAM_PARSER] = "stream_parser", | |
82 | [BPF_SK_SKB_STREAM_VERDICT] = "stream_verdict", | |
83 | [BPF_SK_MSG_VERDICT] = "msg_verdict", | |
84 | [__MAX_BPF_ATTACH_TYPE] = NULL, | |
85 | }; | |
86 | ||
87 | enum bpf_attach_type parse_attach_type(const char *str) | |
88 | { | |
89 | enum bpf_attach_type type; | |
90 | ||
91 | for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) { | |
92 | if (attach_type_strings[type] && | |
93 | is_prefix(str, attach_type_strings[type])) | |
94 | return type; | |
95 | } | |
96 | ||
97 | return __MAX_BPF_ATTACH_TYPE; | |
98 | } | |
99 | ||
71bb428f JK |
100 | static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) |
101 | { | |
102 | struct timespec real_time_ts, boot_time_ts; | |
103 | time_t wallclock_secs; | |
104 | struct tm load_tm; | |
105 | ||
106 | buf[--size] = '\0'; | |
107 | ||
108 | if (clock_gettime(CLOCK_REALTIME, &real_time_ts) || | |
109 | clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) { | |
110 | perror("Can't read clocks"); | |
111 | snprintf(buf, size, "%llu", nsecs / 1000000000); | |
112 | return; | |
113 | } | |
114 | ||
115 | wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) + | |
07480cbc JK |
116 | (real_time_ts.tv_nsec - boot_time_ts.tv_nsec + nsecs) / |
117 | 1000000000; | |
118 | ||
71bb428f JK |
119 | |
120 | if (!localtime_r(&wallclock_secs, &load_tm)) { | |
121 | snprintf(buf, size, "%llu", nsecs / 1000000000); | |
122 | return; | |
123 | } | |
124 | ||
a3fe1f6f QM |
125 | if (json_output) |
126 | strftime(buf, size, "%s", &load_tm); | |
127 | else | |
128 | strftime(buf, size, "%FT%T%z", &load_tm); | |
71bb428f JK |
129 | } |
130 | ||
131 | static int prog_fd_by_tag(unsigned char *tag) | |
132 | { | |
133 | struct bpf_prog_info info = {}; | |
134 | __u32 len = sizeof(info); | |
135 | unsigned int id = 0; | |
136 | int err; | |
137 | int fd; | |
138 | ||
139 | while (true) { | |
140 | err = bpf_prog_get_next_id(id, &id); | |
141 | if (err) { | |
9a5ab8bf | 142 | p_err("%s", strerror(errno)); |
71bb428f JK |
143 | return -1; |
144 | } | |
145 | ||
146 | fd = bpf_prog_get_fd_by_id(id); | |
147 | if (fd < 0) { | |
9a5ab8bf QM |
148 | p_err("can't get prog by id (%u): %s", |
149 | id, strerror(errno)); | |
71bb428f JK |
150 | return -1; |
151 | } | |
152 | ||
153 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | |
154 | if (err) { | |
9a5ab8bf QM |
155 | p_err("can't get prog info (%u): %s", |
156 | id, strerror(errno)); | |
71bb428f JK |
157 | close(fd); |
158 | return -1; | |
159 | } | |
160 | ||
161 | if (!memcmp(tag, info.tag, BPF_TAG_SIZE)) | |
162 | return fd; | |
163 | ||
164 | close(fd); | |
165 | } | |
166 | } | |
167 | ||
168 | int prog_parse_fd(int *argc, char ***argv) | |
169 | { | |
170 | int fd; | |
171 | ||
172 | if (is_prefix(**argv, "id")) { | |
173 | unsigned int id; | |
174 | char *endptr; | |
175 | ||
176 | NEXT_ARGP(); | |
177 | ||
178 | id = strtoul(**argv, &endptr, 0); | |
179 | if (*endptr) { | |
9a5ab8bf | 180 | p_err("can't parse %s as ID", **argv); |
71bb428f JK |
181 | return -1; |
182 | } | |
183 | NEXT_ARGP(); | |
184 | ||
185 | fd = bpf_prog_get_fd_by_id(id); | |
186 | if (fd < 0) | |
9a5ab8bf | 187 | p_err("get by id (%u): %s", id, strerror(errno)); |
71bb428f JK |
188 | return fd; |
189 | } else if (is_prefix(**argv, "tag")) { | |
190 | unsigned char tag[BPF_TAG_SIZE]; | |
191 | ||
192 | NEXT_ARGP(); | |
193 | ||
194 | if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2, | |
195 | tag + 3, tag + 4, tag + 5, tag + 6, tag + 7) | |
196 | != BPF_TAG_SIZE) { | |
9a5ab8bf | 197 | p_err("can't parse tag"); |
71bb428f JK |
198 | return -1; |
199 | } | |
200 | NEXT_ARGP(); | |
201 | ||
202 | return prog_fd_by_tag(tag); | |
203 | } else if (is_prefix(**argv, "pinned")) { | |
204 | char *path; | |
205 | ||
206 | NEXT_ARGP(); | |
207 | ||
208 | path = **argv; | |
209 | NEXT_ARGP(); | |
210 | ||
211 | return open_obj_pinned_any(path, BPF_OBJ_PROG); | |
212 | } | |
213 | ||
9a5ab8bf | 214 | p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv); |
71bb428f JK |
215 | return -1; |
216 | } | |
217 | ||
218 | static void show_prog_maps(int fd, u32 num_maps) | |
219 | { | |
220 | struct bpf_prog_info info = {}; | |
221 | __u32 len = sizeof(info); | |
222 | __u32 map_ids[num_maps]; | |
223 | unsigned int i; | |
224 | int err; | |
225 | ||
226 | info.nr_map_ids = num_maps; | |
227 | info.map_ids = ptr_to_u64(map_ids); | |
228 | ||
229 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | |
230 | if (err || !info.nr_map_ids) | |
231 | return; | |
232 | ||
743cc665 QM |
233 | if (json_output) { |
234 | jsonw_name(json_wtr, "map_ids"); | |
235 | jsonw_start_array(json_wtr); | |
236 | for (i = 0; i < info.nr_map_ids; i++) | |
237 | jsonw_uint(json_wtr, map_ids[i]); | |
238 | jsonw_end_array(json_wtr); | |
239 | } else { | |
240 | printf(" map_ids "); | |
241 | for (i = 0; i < info.nr_map_ids; i++) | |
242 | printf("%u%s", map_ids[i], | |
243 | i == info.nr_map_ids - 1 ? "" : ","); | |
244 | } | |
71bb428f JK |
245 | } |
246 | ||
743cc665 | 247 | static void print_prog_json(struct bpf_prog_info *info, int fd) |
71bb428f | 248 | { |
71bb428f | 249 | char *memlock; |
71bb428f | 250 | |
743cc665 QM |
251 | jsonw_start_object(json_wtr); |
252 | jsonw_uint_field(json_wtr, "id", info->id); | |
253 | if (info->type < ARRAY_SIZE(prog_type_name)) | |
254 | jsonw_string_field(json_wtr, "type", | |
255 | prog_type_name[info->type]); | |
256 | else | |
257 | jsonw_uint_field(json_wtr, "type", info->type); | |
258 | ||
259 | if (*info->name) | |
260 | jsonw_string_field(json_wtr, "name", info->name); | |
261 | ||
262 | jsonw_name(json_wtr, "tag"); | |
263 | jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"", | |
264 | info->tag[0], info->tag[1], info->tag[2], info->tag[3], | |
265 | info->tag[4], info->tag[5], info->tag[6], info->tag[7]); | |
266 | ||
9b984a20 JO |
267 | jsonw_bool_field(json_wtr, "gpl_compatible", info->gpl_compatible); |
268 | ||
52262210 JK |
269 | print_dev_json(info->ifindex, info->netns_dev, info->netns_ino); |
270 | ||
743cc665 QM |
271 | if (info->load_time) { |
272 | char buf[32]; | |
273 | ||
274 | print_boot_time(info->load_time, buf, sizeof(buf)); | |
275 | ||
276 | /* Piggy back on load_time, since 0 uid is a valid one */ | |
a3fe1f6f QM |
277 | jsonw_name(json_wtr, "loaded_at"); |
278 | jsonw_printf(json_wtr, "%s", buf); | |
743cc665 QM |
279 | jsonw_uint_field(json_wtr, "uid", info->created_by_uid); |
280 | } | |
281 | ||
282 | jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len); | |
283 | ||
284 | if (info->jited_prog_len) { | |
285 | jsonw_bool_field(json_wtr, "jited", true); | |
286 | jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len); | |
287 | } else { | |
288 | jsonw_bool_field(json_wtr, "jited", false); | |
71bb428f JK |
289 | } |
290 | ||
743cc665 QM |
291 | memlock = get_fdinfo(fd, "memlock"); |
292 | if (memlock) | |
293 | jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); | |
294 | free(memlock); | |
295 | ||
296 | if (info->nr_map_ids) | |
297 | show_prog_maps(fd, info->nr_map_ids); | |
298 | ||
4990f1f4 PB |
299 | if (!hash_empty(prog_table.table)) { |
300 | struct pinned_obj *obj; | |
301 | ||
302 | jsonw_name(json_wtr, "pinned"); | |
303 | jsonw_start_array(json_wtr); | |
304 | hash_for_each_possible(prog_table.table, obj, hash, info->id) { | |
305 | if (obj->id == info->id) | |
306 | jsonw_string(json_wtr, obj->path); | |
307 | } | |
308 | jsonw_end_array(json_wtr); | |
309 | } | |
310 | ||
743cc665 QM |
311 | jsonw_end_object(json_wtr); |
312 | } | |
313 | ||
314 | static void print_prog_plain(struct bpf_prog_info *info, int fd) | |
315 | { | |
316 | char *memlock; | |
317 | ||
318 | printf("%u: ", info->id); | |
319 | if (info->type < ARRAY_SIZE(prog_type_name)) | |
320 | printf("%s ", prog_type_name[info->type]); | |
71bb428f | 321 | else |
743cc665 | 322 | printf("type %u ", info->type); |
71bb428f | 323 | |
743cc665 QM |
324 | if (*info->name) |
325 | printf("name %s ", info->name); | |
71bb428f JK |
326 | |
327 | printf("tag "); | |
743cc665 | 328 | fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); |
52262210 | 329 | print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino); |
9b984a20 | 330 | printf("%s", info->gpl_compatible ? " gpl" : ""); |
71bb428f JK |
331 | printf("\n"); |
332 | ||
743cc665 | 333 | if (info->load_time) { |
71bb428f JK |
334 | char buf[32]; |
335 | ||
743cc665 | 336 | print_boot_time(info->load_time, buf, sizeof(buf)); |
71bb428f JK |
337 | |
338 | /* Piggy back on load_time, since 0 uid is a valid one */ | |
743cc665 | 339 | printf("\tloaded_at %s uid %u\n", buf, info->created_by_uid); |
71bb428f JK |
340 | } |
341 | ||
743cc665 | 342 | printf("\txlated %uB", info->xlated_prog_len); |
71bb428f | 343 | |
743cc665 QM |
344 | if (info->jited_prog_len) |
345 | printf(" jited %uB", info->jited_prog_len); | |
71bb428f JK |
346 | else |
347 | printf(" not jited"); | |
348 | ||
349 | memlock = get_fdinfo(fd, "memlock"); | |
350 | if (memlock) | |
351 | printf(" memlock %sB", memlock); | |
352 | free(memlock); | |
353 | ||
743cc665 QM |
354 | if (info->nr_map_ids) |
355 | show_prog_maps(fd, info->nr_map_ids); | |
71bb428f | 356 | |
4990f1f4 PB |
357 | if (!hash_empty(prog_table.table)) { |
358 | struct pinned_obj *obj; | |
359 | ||
4990f1f4 PB |
360 | hash_for_each_possible(prog_table.table, obj, hash, info->id) { |
361 | if (obj->id == info->id) | |
a8bfd2bc | 362 | printf("\n\tpinned %s", obj->path); |
4990f1f4 PB |
363 | } |
364 | } | |
365 | ||
71bb428f | 366 | printf("\n"); |
743cc665 QM |
367 | } |
368 | ||
369 | static int show_prog(int fd) | |
370 | { | |
371 | struct bpf_prog_info info = {}; | |
372 | __u32 len = sizeof(info); | |
373 | int err; | |
374 | ||
375 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | |
376 | if (err) { | |
9a5ab8bf | 377 | p_err("can't get prog info: %s", strerror(errno)); |
743cc665 QM |
378 | return -1; |
379 | } | |
380 | ||
381 | if (json_output) | |
382 | print_prog_json(&info, fd); | |
383 | else | |
384 | print_prog_plain(&info, fd); | |
71bb428f JK |
385 | |
386 | return 0; | |
387 | } | |
388 | ||
389 | static int do_show(int argc, char **argv) | |
743cc665 QM |
390 | { |
391 | __u32 id = 0; | |
71bb428f JK |
392 | int err; |
393 | int fd; | |
394 | ||
c541b734 PB |
395 | if (show_pinned) |
396 | build_pinned_obj_table(&prog_table, BPF_OBJ_PROG); | |
4990f1f4 | 397 | |
71bb428f JK |
398 | if (argc == 2) { |
399 | fd = prog_parse_fd(&argc, &argv); | |
400 | if (fd < 0) | |
401 | return -1; | |
402 | ||
403 | return show_prog(fd); | |
404 | } | |
405 | ||
406 | if (argc) | |
407 | return BAD_ARG(); | |
408 | ||
743cc665 QM |
409 | if (json_output) |
410 | jsonw_start_array(json_wtr); | |
71bb428f JK |
411 | while (true) { |
412 | err = bpf_prog_get_next_id(id, &id); | |
413 | if (err) { | |
1739c26d QM |
414 | if (errno == ENOENT) { |
415 | err = 0; | |
71bb428f | 416 | break; |
1739c26d | 417 | } |
9a5ab8bf QM |
418 | p_err("can't get next program: %s%s", strerror(errno), |
419 | errno == EINVAL ? " -- kernel too old?" : ""); | |
743cc665 QM |
420 | err = -1; |
421 | break; | |
71bb428f JK |
422 | } |
423 | ||
424 | fd = bpf_prog_get_fd_by_id(id); | |
425 | if (fd < 0) { | |
8207c6dd JK |
426 | if (errno == ENOENT) |
427 | continue; | |
9a5ab8bf QM |
428 | p_err("can't get prog by id (%u): %s", |
429 | id, strerror(errno)); | |
743cc665 QM |
430 | err = -1; |
431 | break; | |
71bb428f JK |
432 | } |
433 | ||
434 | err = show_prog(fd); | |
435 | close(fd); | |
436 | if (err) | |
743cc665 | 437 | break; |
71bb428f JK |
438 | } |
439 | ||
743cc665 QM |
440 | if (json_output) |
441 | jsonw_end_array(json_wtr); | |
442 | ||
443 | return err; | |
71bb428f JK |
444 | } |
445 | ||
446 | static int do_dump(int argc, char **argv) | |
447 | { | |
f84192ee | 448 | unsigned long *func_ksyms = NULL; |
71bb428f | 449 | struct bpf_prog_info info = {}; |
f7f62c71 | 450 | unsigned int *func_lens = NULL; |
3ddeac67 | 451 | const char *disasm_opt = NULL; |
f84192ee | 452 | unsigned int nr_func_ksyms; |
f7f62c71 | 453 | unsigned int nr_func_lens; |
7105e828 | 454 | struct dump_data dd = {}; |
71bb428f | 455 | __u32 len = sizeof(info); |
71bb428f JK |
456 | unsigned int buf_size; |
457 | char *filepath = NULL; | |
458 | bool opcodes = false; | |
b6c1cedb | 459 | bool visual = false; |
71bb428f JK |
460 | unsigned char *buf; |
461 | __u32 *member_len; | |
462 | __u64 *member_ptr; | |
463 | ssize_t n; | |
464 | int err; | |
465 | int fd; | |
466 | ||
467 | if (is_prefix(*argv, "jited")) { | |
468 | member_len = &info.jited_prog_len; | |
469 | member_ptr = &info.jited_prog_insns; | |
71bb428f JK |
470 | } else if (is_prefix(*argv, "xlated")) { |
471 | member_len = &info.xlated_prog_len; | |
472 | member_ptr = &info.xlated_prog_insns; | |
473 | } else { | |
9a5ab8bf | 474 | p_err("expected 'xlated' or 'jited', got: %s", *argv); |
71bb428f JK |
475 | return -1; |
476 | } | |
477 | NEXT_ARG(); | |
478 | ||
479 | if (argc < 2) | |
480 | usage(); | |
481 | ||
482 | fd = prog_parse_fd(&argc, &argv); | |
483 | if (fd < 0) | |
484 | return -1; | |
485 | ||
486 | if (is_prefix(*argv, "file")) { | |
487 | NEXT_ARG(); | |
488 | if (!argc) { | |
9a5ab8bf | 489 | p_err("expected file path"); |
71bb428f JK |
490 | return -1; |
491 | } | |
492 | ||
493 | filepath = *argv; | |
494 | NEXT_ARG(); | |
495 | } else if (is_prefix(*argv, "opcodes")) { | |
496 | opcodes = true; | |
497 | NEXT_ARG(); | |
b6c1cedb JW |
498 | } else if (is_prefix(*argv, "visual")) { |
499 | visual = true; | |
500 | NEXT_ARG(); | |
71bb428f JK |
501 | } |
502 | ||
71bb428f JK |
503 | if (argc) { |
504 | usage(); | |
505 | return -1; | |
506 | } | |
507 | ||
508 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | |
509 | if (err) { | |
9a5ab8bf | 510 | p_err("can't get prog info: %s", strerror(errno)); |
71bb428f JK |
511 | return -1; |
512 | } | |
513 | ||
514 | if (!*member_len) { | |
9a5ab8bf | 515 | p_info("no instructions returned"); |
71bb428f JK |
516 | close(fd); |
517 | return 0; | |
518 | } | |
519 | ||
520 | buf_size = *member_len; | |
521 | ||
522 | buf = malloc(buf_size); | |
523 | if (!buf) { | |
9a5ab8bf | 524 | p_err("mem alloc failed"); |
71bb428f JK |
525 | close(fd); |
526 | return -1; | |
527 | } | |
528 | ||
f84192ee SD |
529 | nr_func_ksyms = info.nr_jited_ksyms; |
530 | if (nr_func_ksyms) { | |
531 | func_ksyms = malloc(nr_func_ksyms * sizeof(__u64)); | |
532 | if (!func_ksyms) { | |
533 | p_err("mem alloc failed"); | |
534 | close(fd); | |
535 | goto err_free; | |
536 | } | |
537 | } | |
538 | ||
f7f62c71 SD |
539 | nr_func_lens = info.nr_jited_func_lens; |
540 | if (nr_func_lens) { | |
541 | func_lens = malloc(nr_func_lens * sizeof(__u32)); | |
542 | if (!func_lens) { | |
543 | p_err("mem alloc failed"); | |
544 | close(fd); | |
545 | goto err_free; | |
546 | } | |
547 | } | |
548 | ||
71bb428f JK |
549 | memset(&info, 0, sizeof(info)); |
550 | ||
551 | *member_ptr = ptr_to_u64(buf); | |
552 | *member_len = buf_size; | |
f84192ee SD |
553 | info.jited_ksyms = ptr_to_u64(func_ksyms); |
554 | info.nr_jited_ksyms = nr_func_ksyms; | |
f7f62c71 SD |
555 | info.jited_func_lens = ptr_to_u64(func_lens); |
556 | info.nr_jited_func_lens = nr_func_lens; | |
71bb428f JK |
557 | |
558 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | |
559 | close(fd); | |
560 | if (err) { | |
9a5ab8bf | 561 | p_err("can't get prog info: %s", strerror(errno)); |
71bb428f JK |
562 | goto err_free; |
563 | } | |
564 | ||
565 | if (*member_len > buf_size) { | |
9a5ab8bf | 566 | p_err("too many instructions returned"); |
71bb428f JK |
567 | goto err_free; |
568 | } | |
569 | ||
f84192ee SD |
570 | if (info.nr_jited_ksyms > nr_func_ksyms) { |
571 | p_err("too many addresses returned"); | |
572 | goto err_free; | |
573 | } | |
574 | ||
f7f62c71 SD |
575 | if (info.nr_jited_func_lens > nr_func_lens) { |
576 | p_err("too many values returned"); | |
577 | goto err_free; | |
578 | } | |
579 | ||
7105e828 DB |
580 | if ((member_len == &info.jited_prog_len && |
581 | info.jited_prog_insns == 0) || | |
582 | (member_len == &info.xlated_prog_len && | |
583 | info.xlated_prog_insns == 0)) { | |
584 | p_err("error retrieving insn dump: kernel.kptr_restrict set?"); | |
585 | goto err_free; | |
586 | } | |
587 | ||
71bb428f JK |
588 | if (filepath) { |
589 | fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); | |
590 | if (fd < 0) { | |
9a5ab8bf QM |
591 | p_err("can't open file %s: %s", filepath, |
592 | strerror(errno)); | |
71bb428f JK |
593 | goto err_free; |
594 | } | |
595 | ||
596 | n = write(fd, buf, *member_len); | |
597 | close(fd); | |
598 | if (n != *member_len) { | |
9a5ab8bf QM |
599 | p_err("error writing output file: %s", |
600 | n < 0 ? strerror(errno) : "short write"); | |
71bb428f JK |
601 | goto err_free; |
602 | } | |
52c84d36 QM |
603 | |
604 | if (json_output) | |
605 | jsonw_null(json_wtr); | |
3197239d JW |
606 | } else if (member_len == &info.jited_prog_len) { |
607 | const char *name = NULL; | |
608 | ||
609 | if (info.ifindex) { | |
3ddeac67 JK |
610 | name = ifindex_to_bfd_params(info.ifindex, |
611 | info.netns_dev, | |
612 | info.netns_ino, | |
613 | &disasm_opt); | |
3197239d JW |
614 | if (!name) |
615 | goto err_free; | |
7105e828 | 616 | } |
3197239d | 617 | |
f7f62c71 SD |
618 | if (info.nr_jited_func_lens && info.jited_func_lens) { |
619 | struct kernel_sym *sym = NULL; | |
620 | char sym_name[SYM_MAX_NAME]; | |
621 | unsigned char *img = buf; | |
622 | __u64 *ksyms = NULL; | |
623 | __u32 *lens; | |
624 | __u32 i; | |
625 | ||
626 | if (info.nr_jited_ksyms) { | |
627 | kernel_syms_load(&dd); | |
628 | ksyms = (__u64 *) info.jited_ksyms; | |
629 | } | |
630 | ||
631 | if (json_output) | |
632 | jsonw_start_array(json_wtr); | |
633 | ||
634 | lens = (__u32 *) info.jited_func_lens; | |
635 | for (i = 0; i < info.nr_jited_func_lens; i++) { | |
636 | if (ksyms) { | |
637 | sym = kernel_syms_search(&dd, ksyms[i]); | |
638 | if (sym) | |
639 | sprintf(sym_name, "%s", sym->name); | |
640 | else | |
641 | sprintf(sym_name, "0x%016llx", ksyms[i]); | |
642 | } else { | |
643 | strcpy(sym_name, "unknown"); | |
644 | } | |
645 | ||
646 | if (json_output) { | |
647 | jsonw_start_object(json_wtr); | |
648 | jsonw_name(json_wtr, "name"); | |
649 | jsonw_string(json_wtr, sym_name); | |
650 | jsonw_name(json_wtr, "insns"); | |
651 | } else { | |
652 | printf("%s:\n", sym_name); | |
653 | } | |
654 | ||
3ddeac67 JK |
655 | disasm_print_insn(img, lens[i], opcodes, name, |
656 | disasm_opt); | |
f7f62c71 SD |
657 | img += lens[i]; |
658 | ||
659 | if (json_output) | |
660 | jsonw_end_object(json_wtr); | |
661 | else | |
662 | printf("\n"); | |
663 | } | |
664 | ||
665 | if (json_output) | |
666 | jsonw_end_array(json_wtr); | |
667 | } else { | |
3ddeac67 JK |
668 | disasm_print_insn(buf, *member_len, opcodes, name, |
669 | disasm_opt); | |
f7f62c71 | 670 | } |
b6c1cedb JW |
671 | } else if (visual) { |
672 | if (json_output) | |
673 | jsonw_null(json_wtr); | |
674 | else | |
675 | dump_xlated_cfg(buf, *member_len); | |
3197239d JW |
676 | } else { |
677 | kernel_syms_load(&dd); | |
f84192ee SD |
678 | dd.nr_jited_ksyms = info.nr_jited_ksyms; |
679 | dd.jited_ksyms = (__u64 *) info.jited_ksyms; | |
680 | ||
3197239d JW |
681 | if (json_output) |
682 | dump_xlated_json(&dd, buf, *member_len, opcodes); | |
683 | else | |
684 | dump_xlated_plain(&dd, buf, *member_len, opcodes); | |
685 | kernel_syms_destroy(&dd); | |
71bb428f JK |
686 | } |
687 | ||
688 | free(buf); | |
f84192ee | 689 | free(func_ksyms); |
f7f62c71 | 690 | free(func_lens); |
71bb428f JK |
691 | return 0; |
692 | ||
693 | err_free: | |
694 | free(buf); | |
f84192ee | 695 | free(func_ksyms); |
f7f62c71 | 696 | free(func_lens); |
71bb428f JK |
697 | return -1; |
698 | } | |
699 | ||
700 | static int do_pin(int argc, char **argv) | |
701 | { | |
004b45c0 QM |
702 | int err; |
703 | ||
704 | err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id); | |
705 | if (!err && json_output) | |
706 | jsonw_null(json_wtr); | |
707 | return err; | |
71bb428f JK |
708 | } |
709 | ||
3ff5a4dc JK |
710 | struct map_replace { |
711 | int idx; | |
712 | int fd; | |
713 | char *name; | |
714 | }; | |
715 | ||
716 | int map_replace_compar(const void *p1, const void *p2) | |
717 | { | |
718 | const struct map_replace *a = p1, *b = p2; | |
719 | ||
720 | return a->idx - b->idx; | |
721 | } | |
722 | ||
b7d3826c JF |
723 | static int do_attach(int argc, char **argv) |
724 | { | |
725 | enum bpf_attach_type attach_type; | |
726 | int err, mapfd, progfd; | |
727 | ||
728 | if (!REQ_ARGS(5)) { | |
729 | p_err("too few parameters for map attach"); | |
730 | return -EINVAL; | |
731 | } | |
732 | ||
733 | progfd = prog_parse_fd(&argc, &argv); | |
734 | if (progfd < 0) | |
735 | return progfd; | |
736 | ||
737 | attach_type = parse_attach_type(*argv); | |
738 | if (attach_type == __MAX_BPF_ATTACH_TYPE) { | |
739 | p_err("invalid attach type"); | |
740 | return -EINVAL; | |
741 | } | |
742 | NEXT_ARG(); | |
743 | ||
744 | mapfd = map_parse_fd(&argc, &argv); | |
745 | if (mapfd < 0) | |
746 | return mapfd; | |
747 | ||
748 | err = bpf_prog_attach(progfd, mapfd, attach_type, 0); | |
749 | if (err) { | |
750 | p_err("failed prog attach to map"); | |
751 | return -EINVAL; | |
752 | } | |
753 | ||
754 | if (json_output) | |
755 | jsonw_null(json_wtr); | |
756 | return 0; | |
757 | } | |
758 | ||
759 | static int do_detach(int argc, char **argv) | |
760 | { | |
761 | enum bpf_attach_type attach_type; | |
762 | int err, mapfd, progfd; | |
763 | ||
764 | if (!REQ_ARGS(5)) { | |
765 | p_err("too few parameters for map detach"); | |
766 | return -EINVAL; | |
767 | } | |
768 | ||
769 | progfd = prog_parse_fd(&argc, &argv); | |
770 | if (progfd < 0) | |
771 | return progfd; | |
772 | ||
773 | attach_type = parse_attach_type(*argv); | |
774 | if (attach_type == __MAX_BPF_ATTACH_TYPE) { | |
775 | p_err("invalid attach type"); | |
776 | return -EINVAL; | |
777 | } | |
778 | NEXT_ARG(); | |
779 | ||
780 | mapfd = map_parse_fd(&argc, &argv); | |
781 | if (mapfd < 0) | |
782 | return mapfd; | |
783 | ||
784 | err = bpf_prog_detach2(progfd, mapfd, attach_type); | |
785 | if (err) { | |
786 | p_err("failed prog detach from map"); | |
787 | return -EINVAL; | |
788 | } | |
789 | ||
790 | if (json_output) | |
791 | jsonw_null(json_wtr); | |
792 | return 0; | |
793 | } | |
49a086c2 RG |
794 | static int do_load(int argc, char **argv) |
795 | { | |
c8406848 JK |
796 | enum bpf_attach_type expected_attach_type; |
797 | struct bpf_object_open_attr attr = { | |
ba6dd679 JK |
798 | .prog_type = BPF_PROG_TYPE_UNSPEC, |
799 | }; | |
3ff5a4dc JK |
800 | struct map_replace *map_replace = NULL; |
801 | unsigned int old_map_fds = 0; | |
c8406848 | 802 | struct bpf_program *prog; |
49a086c2 | 803 | struct bpf_object *obj; |
c8406848 JK |
804 | struct bpf_map *map; |
805 | const char *pinfile; | |
3ff5a4dc | 806 | unsigned int i, j; |
c8406848 | 807 | __u32 ifindex = 0; |
3ff5a4dc | 808 | int idx, err; |
49a086c2 | 809 | |
8d1fc3de JK |
810 | if (!REQ_ARGS(2)) |
811 | return -1; | |
c8406848 | 812 | attr.file = GET_ARG(); |
8d1fc3de | 813 | pinfile = GET_ARG(); |
49a086c2 | 814 | |
ba6dd679 | 815 | while (argc) { |
49f2cba3 JK |
816 | if (is_prefix(*argv, "type")) { |
817 | char *type; | |
818 | ||
819 | NEXT_ARG(); | |
820 | ||
821 | if (attr.prog_type != BPF_PROG_TYPE_UNSPEC) { | |
822 | p_err("program type already specified"); | |
3ff5a4dc | 823 | goto err_free_reuse_maps; |
49f2cba3 JK |
824 | } |
825 | if (!REQ_ARGS(1)) | |
3ff5a4dc | 826 | goto err_free_reuse_maps; |
49f2cba3 JK |
827 | |
828 | /* Put a '/' at the end of type to appease libbpf */ | |
829 | type = malloc(strlen(*argv) + 2); | |
830 | if (!type) { | |
831 | p_err("mem alloc failed"); | |
3ff5a4dc | 832 | goto err_free_reuse_maps; |
49f2cba3 JK |
833 | } |
834 | *type = 0; | |
835 | strcat(type, *argv); | |
836 | strcat(type, "/"); | |
837 | ||
838 | err = libbpf_prog_type_by_name(type, &attr.prog_type, | |
c8406848 | 839 | &expected_attach_type); |
49f2cba3 JK |
840 | free(type); |
841 | if (err < 0) { | |
842 | p_err("unknown program type '%s'", *argv); | |
3ff5a4dc | 843 | goto err_free_reuse_maps; |
49f2cba3 JK |
844 | } |
845 | NEXT_ARG(); | |
3ff5a4dc | 846 | } else if (is_prefix(*argv, "map")) { |
dde7011a | 847 | void *new_map_replace; |
3ff5a4dc JK |
848 | char *endptr, *name; |
849 | int fd; | |
850 | ||
851 | NEXT_ARG(); | |
852 | ||
853 | if (!REQ_ARGS(4)) | |
854 | goto err_free_reuse_maps; | |
855 | ||
856 | if (is_prefix(*argv, "idx")) { | |
857 | NEXT_ARG(); | |
858 | ||
859 | idx = strtoul(*argv, &endptr, 0); | |
860 | if (*endptr) { | |
861 | p_err("can't parse %s as IDX", *argv); | |
862 | goto err_free_reuse_maps; | |
863 | } | |
864 | name = NULL; | |
865 | } else if (is_prefix(*argv, "name")) { | |
866 | NEXT_ARG(); | |
867 | ||
868 | name = *argv; | |
869 | idx = -1; | |
870 | } else { | |
871 | p_err("expected 'idx' or 'name', got: '%s'?", | |
872 | *argv); | |
873 | goto err_free_reuse_maps; | |
874 | } | |
875 | NEXT_ARG(); | |
876 | ||
877 | fd = map_parse_fd(&argc, &argv); | |
878 | if (fd < 0) | |
879 | goto err_free_reuse_maps; | |
880 | ||
dde7011a JK |
881 | new_map_replace = reallocarray(map_replace, |
882 | old_map_fds + 1, | |
883 | sizeof(*map_replace)); | |
884 | if (!new_map_replace) { | |
3ff5a4dc JK |
885 | p_err("mem alloc failed"); |
886 | goto err_free_reuse_maps; | |
887 | } | |
dde7011a JK |
888 | map_replace = new_map_replace; |
889 | ||
3ff5a4dc JK |
890 | map_replace[old_map_fds].idx = idx; |
891 | map_replace[old_map_fds].name = name; | |
892 | map_replace[old_map_fds].fd = fd; | |
893 | old_map_fds++; | |
49f2cba3 | 894 | } else if (is_prefix(*argv, "dev")) { |
ba6dd679 JK |
895 | NEXT_ARG(); |
896 | ||
c8406848 | 897 | if (ifindex) { |
ba6dd679 | 898 | p_err("offload device already specified"); |
3ff5a4dc | 899 | goto err_free_reuse_maps; |
ba6dd679 JK |
900 | } |
901 | if (!REQ_ARGS(1)) | |
3ff5a4dc | 902 | goto err_free_reuse_maps; |
ba6dd679 | 903 | |
c8406848 JK |
904 | ifindex = if_nametoindex(*argv); |
905 | if (!ifindex) { | |
ba6dd679 JK |
906 | p_err("unrecognized netdevice '%s': %s", |
907 | *argv, strerror(errno)); | |
3ff5a4dc | 908 | goto err_free_reuse_maps; |
ba6dd679 JK |
909 | } |
910 | NEXT_ARG(); | |
911 | } else { | |
3ff5a4dc | 912 | p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?", |
ba6dd679 | 913 | *argv); |
3ff5a4dc | 914 | goto err_free_reuse_maps; |
ba6dd679 JK |
915 | } |
916 | } | |
917 | ||
c034a177 | 918 | obj = __bpf_object__open_xattr(&attr, bpf_flags); |
c8406848 JK |
919 | if (IS_ERR_OR_NULL(obj)) { |
920 | p_err("failed to open object file"); | |
3ff5a4dc | 921 | goto err_free_reuse_maps; |
49a086c2 RG |
922 | } |
923 | ||
c8406848 JK |
924 | prog = bpf_program__next(NULL, obj); |
925 | if (!prog) { | |
926 | p_err("object file doesn't contain any bpf program"); | |
927 | goto err_close_obj; | |
928 | } | |
929 | ||
930 | bpf_program__set_ifindex(prog, ifindex); | |
931 | if (attr.prog_type == BPF_PROG_TYPE_UNSPEC) { | |
932 | const char *sec_name = bpf_program__title(prog, false); | |
933 | ||
934 | err = libbpf_prog_type_by_name(sec_name, &attr.prog_type, | |
935 | &expected_attach_type); | |
936 | if (err < 0) { | |
937 | p_err("failed to guess program type based on section name %s\n", | |
938 | sec_name); | |
939 | goto err_close_obj; | |
940 | } | |
941 | } | |
942 | bpf_program__set_type(prog, attr.prog_type); | |
943 | bpf_program__set_expected_attach_type(prog, expected_attach_type); | |
944 | ||
3ff5a4dc JK |
945 | qsort(map_replace, old_map_fds, sizeof(*map_replace), |
946 | map_replace_compar); | |
947 | ||
948 | /* After the sort maps by name will be first on the list, because they | |
949 | * have idx == -1. Resolve them. | |
950 | */ | |
951 | j = 0; | |
952 | while (j < old_map_fds && map_replace[j].name) { | |
953 | i = 0; | |
954 | bpf_map__for_each(map, obj) { | |
955 | if (!strcmp(bpf_map__name(map), map_replace[j].name)) { | |
956 | map_replace[j].idx = i; | |
957 | break; | |
958 | } | |
959 | i++; | |
960 | } | |
961 | if (map_replace[j].idx == -1) { | |
962 | p_err("unable to find map '%s'", map_replace[j].name); | |
963 | goto err_close_obj; | |
964 | } | |
965 | j++; | |
966 | } | |
967 | /* Resort if any names were resolved */ | |
968 | if (j) | |
969 | qsort(map_replace, old_map_fds, sizeof(*map_replace), | |
970 | map_replace_compar); | |
971 | ||
972 | /* Set ifindex and name reuse */ | |
973 | j = 0; | |
974 | idx = 0; | |
975 | bpf_map__for_each(map, obj) { | |
c8406848 JK |
976 | if (!bpf_map__is_offload_neutral(map)) |
977 | bpf_map__set_ifindex(map, ifindex); | |
978 | ||
3ff5a4dc JK |
979 | if (j < old_map_fds && idx == map_replace[j].idx) { |
980 | err = bpf_map__reuse_fd(map, map_replace[j++].fd); | |
981 | if (err) { | |
982 | p_err("unable to set up map reuse: %d", err); | |
983 | goto err_close_obj; | |
984 | } | |
985 | ||
986 | /* Next reuse wants to apply to the same map */ | |
987 | if (j < old_map_fds && map_replace[j].idx == idx) { | |
988 | p_err("replacement for map idx %d specified more than once", | |
989 | idx); | |
990 | goto err_close_obj; | |
991 | } | |
992 | } | |
993 | ||
994 | idx++; | |
995 | } | |
996 | if (j < old_map_fds) { | |
997 | p_err("map idx '%d' not used", map_replace[j].idx); | |
998 | goto err_close_obj; | |
999 | } | |
1000 | ||
c8406848 JK |
1001 | err = bpf_object__load(obj); |
1002 | if (err) { | |
1003 | p_err("failed to load object file"); | |
1004 | goto err_close_obj; | |
1005 | } | |
1006 | ||
1007 | if (do_pin_fd(bpf_program__fd(prog), pinfile)) | |
bfee71fb | 1008 | goto err_close_obj; |
49a086c2 RG |
1009 | |
1010 | if (json_output) | |
1011 | jsonw_null(json_wtr); | |
1012 | ||
bfee71fb | 1013 | bpf_object__close(obj); |
3ff5a4dc JK |
1014 | for (i = 0; i < old_map_fds; i++) |
1015 | close(map_replace[i].fd); | |
1016 | free(map_replace); | |
bfee71fb | 1017 | |
49a086c2 | 1018 | return 0; |
bfee71fb JK |
1019 | |
1020 | err_close_obj: | |
1021 | bpf_object__close(obj); | |
3ff5a4dc JK |
1022 | err_free_reuse_maps: |
1023 | for (i = 0; i < old_map_fds; i++) | |
1024 | close(map_replace[i].fd); | |
1025 | free(map_replace); | |
bfee71fb | 1026 | return -1; |
49a086c2 RG |
1027 | } |
1028 | ||
71bb428f JK |
1029 | static int do_help(int argc, char **argv) |
1030 | { | |
004b45c0 QM |
1031 | if (json_output) { |
1032 | jsonw_null(json_wtr); | |
1033 | return 0; | |
1034 | } | |
1035 | ||
71bb428f | 1036 | fprintf(stderr, |
6ebe6dbd | 1037 | "Usage: %s %s { show | list } [PROG]\n" |
b6c1cedb | 1038 | " %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n" |
8dfbc6d1 | 1039 | " %s %s dump jited PROG [{ file FILE | opcodes }]\n" |
71bb428f | 1040 | " %s %s pin PROG FILE\n" |
3ff5a4dc JK |
1041 | " %s %s load OBJ FILE [type TYPE] [dev NAME] \\\n" |
1042 | " [map { idx IDX | name NAME } MAP]\n" | |
b7d3826c JF |
1043 | " %s %s attach PROG ATTACH_TYPE MAP\n" |
1044 | " %s %s detach PROG ATTACH_TYPE MAP\n" | |
71bb428f JK |
1045 | " %s %s help\n" |
1046 | "\n" | |
3ff5a4dc | 1047 | " " HELP_SPEC_MAP "\n" |
71bb428f | 1048 | " " HELP_SPEC_PROGRAM "\n" |
49f2cba3 JK |
1049 | " TYPE := { socket | kprobe | kretprobe | classifier | action |\n" |
1050 | " tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n" | |
1051 | " cgroup/sock | cgroup/dev | lwt_in | lwt_out | lwt_xmit |\n" | |
1052 | " lwt_seg6local | sockops | sk_skb | sk_msg | lirc_mode2 |\n" | |
1053 | " cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n" | |
1054 | " cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n" | |
1055 | " cgroup/sendmsg4 | cgroup/sendmsg6 }\n" | |
b7d3826c | 1056 | " ATTACH_TYPE := { msg_verdict | skb_verdict | skb_parse }\n" |
0641c3c8 | 1057 | " " HELP_SPEC_OPTIONS "\n" |
71bb428f JK |
1058 | "", |
1059 | bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], | |
b7d3826c JF |
1060 | bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], |
1061 | bin_name, argv[-2], bin_name, argv[-2]); | |
71bb428f JK |
1062 | |
1063 | return 0; | |
1064 | } | |
1065 | ||
1066 | static const struct cmd cmds[] = { | |
1067 | { "show", do_show }, | |
6ebe6dbd | 1068 | { "list", do_show }, |
9f606179 | 1069 | { "help", do_help }, |
71bb428f JK |
1070 | { "dump", do_dump }, |
1071 | { "pin", do_pin }, | |
49a086c2 | 1072 | { "load", do_load }, |
b7d3826c JF |
1073 | { "attach", do_attach }, |
1074 | { "detach", do_detach }, | |
71bb428f JK |
1075 | { 0 } |
1076 | }; | |
1077 | ||
1078 | int do_prog(int argc, char **argv) | |
1079 | { | |
1080 | return cmd_select(cmds, argc, argv, do_help); | |
1081 | } |