1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /* Copyright (C) 2017-2018 Netronome Systems, Inc. */
3
4 #define _GNU_SOURCE
5 #include <ctype.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <ftw.h>
9 #include <libgen.h>
10 #include <mntent.h>
11 #include <stdbool.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <linux/limits.h>
17 #include <linux/magic.h>
18 #include <net/if.h>
19 #include <sys/mount.h>
20 #include <sys/resource.h>
21 #include <sys/stat.h>
22 #include <sys/vfs.h>
23
24 #include <bpf/bpf.h>
25 #include <bpf/hashmap.h>
26 #include <bpf/libbpf.h> /* libbpf_num_possible_cpus */
27
28 #include "main.h"
29
30 #ifndef BPF_FS_MAGIC
31 #define BPF_FS_MAGIC 0xcafe4a11
32 #endif
33
34 const char * const attach_type_name[__MAX_BPF_ATTACH_TYPE] = {
35 [BPF_CGROUP_INET_INGRESS] = "ingress",
36 [BPF_CGROUP_INET_EGRESS] = "egress",
37 [BPF_CGROUP_INET_SOCK_CREATE] = "sock_create",
38 [BPF_CGROUP_INET_SOCK_RELEASE] = "sock_release",
39 [BPF_CGROUP_SOCK_OPS] = "sock_ops",
40 [BPF_CGROUP_DEVICE] = "device",
41 [BPF_CGROUP_INET4_BIND] = "bind4",
42 [BPF_CGROUP_INET6_BIND] = "bind6",
43 [BPF_CGROUP_INET4_CONNECT] = "connect4",
44 [BPF_CGROUP_INET6_CONNECT] = "connect6",
45 [BPF_CGROUP_INET4_POST_BIND] = "post_bind4",
46 [BPF_CGROUP_INET6_POST_BIND] = "post_bind6",
47 [BPF_CGROUP_INET4_GETPEERNAME] = "getpeername4",
48 [BPF_CGROUP_INET6_GETPEERNAME] = "getpeername6",
49 [BPF_CGROUP_INET4_GETSOCKNAME] = "getsockname4",
50 [BPF_CGROUP_INET6_GETSOCKNAME] = "getsockname6",
51 [BPF_CGROUP_UDP4_SENDMSG] = "sendmsg4",
52 [BPF_CGROUP_UDP6_SENDMSG] = "sendmsg6",
53 [BPF_CGROUP_SYSCTL] = "sysctl",
54 [BPF_CGROUP_UDP4_RECVMSG] = "recvmsg4",
55 [BPF_CGROUP_UDP6_RECVMSG] = "recvmsg6",
56 [BPF_CGROUP_GETSOCKOPT] = "getsockopt",
57 [BPF_CGROUP_SETSOCKOPT] = "setsockopt",
58
59 [BPF_SK_SKB_STREAM_PARSER] = "sk_skb_stream_parser",
60 [BPF_SK_SKB_STREAM_VERDICT] = "sk_skb_stream_verdict",
61 [BPF_SK_SKB_VERDICT] = "sk_skb_verdict",
62 [BPF_SK_MSG_VERDICT] = "sk_msg_verdict",
63 [BPF_LIRC_MODE2] = "lirc_mode2",
64 [BPF_FLOW_DISSECTOR] = "flow_dissector",
65 [BPF_TRACE_RAW_TP] = "raw_tp",
66 [BPF_TRACE_FENTRY] = "fentry",
67 [BPF_TRACE_FEXIT] = "fexit",
68 [BPF_MODIFY_RETURN] = "mod_ret",
69 [BPF_LSM_MAC] = "lsm_mac",
70 [BPF_SK_LOOKUP] = "sk_lookup",
71 [BPF_TRACE_ITER] = "trace_iter",
72 [BPF_XDP_DEVMAP] = "xdp_devmap",
73 [BPF_XDP_CPUMAP] = "xdp_cpumap",
74 [BPF_XDP] = "xdp",
75 [BPF_SK_REUSEPORT_SELECT] = "sk_skb_reuseport_select",
76 [BPF_SK_REUSEPORT_SELECT_OR_MIGRATE] = "sk_skb_reuseport_select_or_migrate",
77 };
78
p_err(const char * fmt,...)79 void p_err(const char *fmt, ...)
80 {
81 va_list ap;
82
83 va_start(ap, fmt);
84 if (json_output) {
85 jsonw_start_object(json_wtr);
86 jsonw_name(json_wtr, "error");
87 jsonw_vprintf_enquote(json_wtr, fmt, ap);
88 jsonw_end_object(json_wtr);
89 } else {
90 fprintf(stderr, "Error: ");
91 vfprintf(stderr, fmt, ap);
92 fprintf(stderr, "\n");
93 }
94 va_end(ap);
95 }
96
p_info(const char * fmt,...)97 void p_info(const char *fmt, ...)
98 {
99 va_list ap;
100
101 if (json_output)
102 return;
103
104 va_start(ap, fmt);
105 vfprintf(stderr, fmt, ap);
106 fprintf(stderr, "\n");
107 va_end(ap);
108 }
109
is_bpffs(char * path)110 static bool is_bpffs(char *path)
111 {
112 struct statfs st_fs;
113
114 if (statfs(path, &st_fs) < 0)
115 return false;
116
117 return (unsigned long)st_fs.f_type == BPF_FS_MAGIC;
118 }
119
set_max_rlimit(void)120 void set_max_rlimit(void)
121 {
122 struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
123
124 setrlimit(RLIMIT_MEMLOCK, &rinf);
125 }
126
127 static int
mnt_fs(const char * target,const char * type,char * buff,size_t bufflen)128 mnt_fs(const char *target, const char *type, char *buff, size_t bufflen)
129 {
130 bool bind_done = false;
131
132 while (mount("", target, "none", MS_PRIVATE | MS_REC, NULL)) {
133 if (errno != EINVAL || bind_done) {
134 snprintf(buff, bufflen,
135 "mount --make-private %s failed: %s",
136 target, strerror(errno));
137 return -1;
138 }
139
140 if (mount(target, target, "none", MS_BIND, NULL)) {
141 snprintf(buff, bufflen,
142 "mount --bind %s %s failed: %s",
143 target, target, strerror(errno));
144 return -1;
145 }
146
147 bind_done = true;
148 }
149
150 if (mount(type, target, type, 0, "mode=0700")) {
151 snprintf(buff, bufflen, "mount -t %s %s %s failed: %s",
152 type, type, target, strerror(errno));
153 return -1;
154 }
155
156 return 0;
157 }
158
mount_tracefs(const char * target)159 int mount_tracefs(const char *target)
160 {
161 char err_str[ERR_MAX_LEN];
162 int err;
163
164 err = mnt_fs(target, "tracefs", err_str, ERR_MAX_LEN);
165 if (err) {
166 err_str[ERR_MAX_LEN - 1] = '\0';
167 p_err("can't mount tracefs: %s", err_str);
168 }
169
170 return err;
171 }
172
open_obj_pinned(const char * path,bool quiet)173 int open_obj_pinned(const char *path, bool quiet)
174 {
175 char *pname;
176 int fd = -1;
177
178 pname = strdup(path);
179 if (!pname) {
180 if (!quiet)
181 p_err("mem alloc failed");
182 goto out_ret;
183 }
184
185 fd = bpf_obj_get(pname);
186 if (fd < 0) {
187 if (!quiet)
188 p_err("bpf obj get (%s): %s", pname,
189 errno == EACCES && !is_bpffs(dirname(pname)) ?
190 "directory not in bpf file system (bpffs)" :
191 strerror(errno));
192 goto out_free;
193 }
194
195 out_free:
196 free(pname);
197 out_ret:
198 return fd;
199 }
200
open_obj_pinned_any(const char * path,enum bpf_obj_type exp_type)201 int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type)
202 {
203 enum bpf_obj_type type;
204 int fd;
205
206 fd = open_obj_pinned(path, false);
207 if (fd < 0)
208 return -1;
209
210 type = get_fd_type(fd);
211 if (type < 0) {
212 close(fd);
213 return type;
214 }
215 if (type != exp_type) {
216 p_err("incorrect object type: %s", get_fd_type_name(type));
217 close(fd);
218 return -1;
219 }
220
221 return fd;
222 }
223
mount_bpffs_for_pin(const char * name)224 int mount_bpffs_for_pin(const char *name)
225 {
226 char err_str[ERR_MAX_LEN];
227 char *file;
228 char *dir;
229 int err = 0;
230
231 file = malloc(strlen(name) + 1);
232 if (!file) {
233 p_err("mem alloc failed");
234 return -1;
235 }
236
237 strcpy(file, name);
238 dir = dirname(file);
239
240 if (is_bpffs(dir))
241 /* nothing to do if already mounted */
242 goto out_free;
243
244 if (block_mount) {
245 p_err("no BPF file system found, not mounting it due to --nomount option");
246 err = -1;
247 goto out_free;
248 }
249
250 err = mnt_fs(dir, "bpf", err_str, ERR_MAX_LEN);
251 if (err) {
252 err_str[ERR_MAX_LEN - 1] = '\0';
253 p_err("can't mount BPF file system to pin the object (%s): %s",
254 name, err_str);
255 }
256
257 out_free:
258 free(file);
259 return err;
260 }
261
do_pin_fd(int fd,const char * name)262 int do_pin_fd(int fd, const char *name)
263 {
264 int err;
265
266 err = mount_bpffs_for_pin(name);
267 if (err)
268 return err;
269
270 err = bpf_obj_pin(fd, name);
271 if (err)
272 p_err("can't pin the object (%s): %s", name, strerror(errno));
273
274 return err;
275 }
276
do_pin_any(int argc,char ** argv,int (* get_fd)(int *,char ***))277 int do_pin_any(int argc, char **argv, int (*get_fd)(int *, char ***))
278 {
279 int err;
280 int fd;
281
282 fd = get_fd(&argc, &argv);
283 if (fd < 0)
284 return fd;
285
286 err = do_pin_fd(fd, *argv);
287
288 close(fd);
289 return err;
290 }
291
get_fd_type_name(enum bpf_obj_type type)292 const char *get_fd_type_name(enum bpf_obj_type type)
293 {
294 static const char * const names[] = {
295 [BPF_OBJ_UNKNOWN] = "unknown",
296 [BPF_OBJ_PROG] = "prog",
297 [BPF_OBJ_MAP] = "map",
298 };
299
300 if (type < 0 || type >= ARRAY_SIZE(names) || !names[type])
301 return names[BPF_OBJ_UNKNOWN];
302
303 return names[type];
304 }
305
get_fd_type(int fd)306 int get_fd_type(int fd)
307 {
308 char path[PATH_MAX];
309 char buf[512];
310 ssize_t n;
311
312 snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
313
314 n = readlink(path, buf, sizeof(buf));
315 if (n < 0) {
316 p_err("can't read link type: %s", strerror(errno));
317 return -1;
318 }
319 if (n == sizeof(path)) {
320 p_err("can't read link type: path too long!");
321 return -1;
322 }
323
324 if (strstr(buf, "bpf-map"))
325 return BPF_OBJ_MAP;
326 else if (strstr(buf, "bpf-prog"))
327 return BPF_OBJ_PROG;
328 else if (strstr(buf, "bpf-link"))
329 return BPF_OBJ_LINK;
330
331 return BPF_OBJ_UNKNOWN;
332 }
333
get_fdinfo(int fd,const char * key)334 char *get_fdinfo(int fd, const char *key)
335 {
336 char path[PATH_MAX];
337 char *line = NULL;
338 size_t line_n = 0;
339 ssize_t n;
340 FILE *fdi;
341
342 snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", fd);
343
344 fdi = fopen(path, "r");
345 if (!fdi)
346 return NULL;
347
348 while ((n = getline(&line, &line_n, fdi)) > 0) {
349 char *value;
350 int len;
351
352 if (!strstr(line, key))
353 continue;
354
355 fclose(fdi);
356
357 value = strchr(line, '\t');
358 if (!value || !value[1]) {
359 free(line);
360 return NULL;
361 }
362 value++;
363
364 len = strlen(value);
365 memmove(line, value, len);
366 line[len - 1] = '\0';
367
368 return line;
369 }
370
371 free(line);
372 fclose(fdi);
373 return NULL;
374 }
375
print_data_json(uint8_t * data,size_t len)376 void print_data_json(uint8_t *data, size_t len)
377 {
378 unsigned int i;
379
380 jsonw_start_array(json_wtr);
381 for (i = 0; i < len; i++)
382 jsonw_printf(json_wtr, "%d", data[i]);
383 jsonw_end_array(json_wtr);
384 }
385
print_hex_data_json(uint8_t * data,size_t len)386 void print_hex_data_json(uint8_t *data, size_t len)
387 {
388 unsigned int i;
389
390 jsonw_start_array(json_wtr);
391 for (i = 0; i < len; i++)
392 jsonw_printf(json_wtr, "\"0x%02hhx\"", data[i]);
393 jsonw_end_array(json_wtr);
394 }
395
396 /* extra params for nftw cb */
397 static struct hashmap *build_fn_table;
398 static enum bpf_obj_type build_fn_type;
399
do_build_table_cb(const char * fpath,const struct stat * sb,int typeflag,struct FTW * ftwbuf)400 static int do_build_table_cb(const char *fpath, const struct stat *sb,
401 int typeflag, struct FTW *ftwbuf)
402 {
403 struct bpf_prog_info pinned_info;
404 __u32 len = sizeof(pinned_info);
405 enum bpf_obj_type objtype;
406 int fd, err = 0;
407 char *path;
408
409 if (typeflag != FTW_F)
410 goto out_ret;
411
412 fd = open_obj_pinned(fpath, true);
413 if (fd < 0)
414 goto out_ret;
415
416 objtype = get_fd_type(fd);
417 if (objtype != build_fn_type)
418 goto out_close;
419
420 memset(&pinned_info, 0, sizeof(pinned_info));
421 if (bpf_obj_get_info_by_fd(fd, &pinned_info, &len))
422 goto out_close;
423
424 path = strdup(fpath);
425 if (!path) {
426 err = -1;
427 goto out_close;
428 }
429
430 err = hashmap__append(build_fn_table, u32_as_hash_field(pinned_info.id), path);
431 if (err) {
432 p_err("failed to append entry to hashmap for ID %u, path '%s': %s",
433 pinned_info.id, path, strerror(errno));
434 goto out_close;
435 }
436
437 out_close:
438 close(fd);
439 out_ret:
440 return err;
441 }
442
build_pinned_obj_table(struct hashmap * tab,enum bpf_obj_type type)443 int build_pinned_obj_table(struct hashmap *tab,
444 enum bpf_obj_type type)
445 {
446 struct mntent *mntent = NULL;
447 FILE *mntfile = NULL;
448 int flags = FTW_PHYS;
449 int nopenfd = 16;
450 int err = 0;
451
452 mntfile = setmntent("/proc/mounts", "r");
453 if (!mntfile)
454 return -1;
455
456 build_fn_table = tab;
457 build_fn_type = type;
458
459 while ((mntent = getmntent(mntfile))) {
460 char *path = mntent->mnt_dir;
461
462 if (strncmp(mntent->mnt_type, "bpf", 3) != 0)
463 continue;
464 err = nftw(path, do_build_table_cb, nopenfd, flags);
465 if (err)
466 break;
467 }
468 fclose(mntfile);
469 return err;
470 }
471
delete_pinned_obj_table(struct hashmap * map)472 void delete_pinned_obj_table(struct hashmap *map)
473 {
474 struct hashmap_entry *entry;
475 size_t bkt;
476
477 if (!map)
478 return;
479
480 hashmap__for_each_entry(map, entry, bkt)
481 free(entry->value);
482
483 hashmap__free(map);
484 }
485
get_page_size(void)486 unsigned int get_page_size(void)
487 {
488 static int result;
489
490 if (!result)
491 result = getpagesize();
492 return result;
493 }
494
get_possible_cpus(void)495 unsigned int get_possible_cpus(void)
496 {
497 int cpus = libbpf_num_possible_cpus();
498
499 if (cpus < 0) {
500 p_err("Can't get # of possible cpus: %s", strerror(-cpus));
501 exit(-1);
502 }
503 return cpus;
504 }
505
506 static char *
ifindex_to_name_ns(__u32 ifindex,__u32 ns_dev,__u32 ns_ino,char * buf)507 ifindex_to_name_ns(__u32 ifindex, __u32 ns_dev, __u32 ns_ino, char *buf)
508 {
509 struct stat st;
510 int err;
511
512 err = stat("/proc/self/ns/net", &st);
513 if (err) {
514 p_err("Can't stat /proc/self: %s", strerror(errno));
515 return NULL;
516 }
517
518 if (st.st_dev != ns_dev || st.st_ino != ns_ino)
519 return NULL;
520
521 return if_indextoname(ifindex, buf);
522 }
523
read_sysfs_hex_int(char * path)524 static int read_sysfs_hex_int(char *path)
525 {
526 char vendor_id_buf[8];
527 int len;
528 int fd;
529
530 fd = open(path, O_RDONLY);
531 if (fd < 0) {
532 p_err("Can't open %s: %s", path, strerror(errno));
533 return -1;
534 }
535
536 len = read(fd, vendor_id_buf, sizeof(vendor_id_buf));
537 close(fd);
538 if (len < 0) {
539 p_err("Can't read %s: %s", path, strerror(errno));
540 return -1;
541 }
542 if (len >= (int)sizeof(vendor_id_buf)) {
543 p_err("Value in %s too long", path);
544 return -1;
545 }
546
547 vendor_id_buf[len] = 0;
548
549 return strtol(vendor_id_buf, NULL, 0);
550 }
551
read_sysfs_netdev_hex_int(char * devname,const char * entry_name)552 static int read_sysfs_netdev_hex_int(char *devname, const char *entry_name)
553 {
554 char full_path[64];
555
556 snprintf(full_path, sizeof(full_path), "/sys/class/net/%s/device/%s",
557 devname, entry_name);
558
559 return read_sysfs_hex_int(full_path);
560 }
561
562 const char *
ifindex_to_bfd_params(__u32 ifindex,__u64 ns_dev,__u64 ns_ino,const char ** opt)563 ifindex_to_bfd_params(__u32 ifindex, __u64 ns_dev, __u64 ns_ino,
564 const char **opt)
565 {
566 char devname[IF_NAMESIZE];
567 int vendor_id;
568 int device_id;
569
570 if (!ifindex_to_name_ns(ifindex, ns_dev, ns_ino, devname)) {
571 p_err("Can't get net device name for ifindex %d: %s", ifindex,
572 strerror(errno));
573 return NULL;
574 }
575
576 vendor_id = read_sysfs_netdev_hex_int(devname, "vendor");
577 if (vendor_id < 0) {
578 p_err("Can't get device vendor id for %s", devname);
579 return NULL;
580 }
581
582 switch (vendor_id) {
583 case 0x19ee:
584 device_id = read_sysfs_netdev_hex_int(devname, "device");
585 if (device_id != 0x4000 &&
586 device_id != 0x6000 &&
587 device_id != 0x6003)
588 p_info("Unknown NFP device ID, assuming it is NFP-6xxx arch");
589 *opt = "ctx4";
590 return "NFP-6xxx";
591 default:
592 p_err("Can't get bfd arch name for device vendor id 0x%04x",
593 vendor_id);
594 return NULL;
595 }
596 }
597
print_dev_plain(__u32 ifindex,__u64 ns_dev,__u64 ns_inode)598 void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
599 {
600 char name[IF_NAMESIZE];
601
602 if (!ifindex)
603 return;
604
605 printf(" offloaded_to ");
606 if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
607 printf("%s", name);
608 else
609 printf("ifindex %u ns_dev %llu ns_ino %llu",
610 ifindex, ns_dev, ns_inode);
611 }
612
print_dev_json(__u32 ifindex,__u64 ns_dev,__u64 ns_inode)613 void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
614 {
615 char name[IF_NAMESIZE];
616
617 if (!ifindex)
618 return;
619
620 jsonw_name(json_wtr, "dev");
621 jsonw_start_object(json_wtr);
622 jsonw_uint_field(json_wtr, "ifindex", ifindex);
623 jsonw_uint_field(json_wtr, "ns_dev", ns_dev);
624 jsonw_uint_field(json_wtr, "ns_inode", ns_inode);
625 if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
626 jsonw_string_field(json_wtr, "ifname", name);
627 jsonw_end_object(json_wtr);
628 }
629
parse_u32_arg(int * argc,char *** argv,__u32 * val,const char * what)630 int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what)
631 {
632 char *endptr;
633
634 NEXT_ARGP();
635
636 if (*val) {
637 p_err("%s already specified", what);
638 return -1;
639 }
640
641 *val = strtoul(**argv, &endptr, 0);
642 if (*endptr) {
643 p_err("can't parse %s as %s", **argv, what);
644 return -1;
645 }
646 NEXT_ARGP();
647
648 return 0;
649 }
650
651 int __printf(2, 0)
print_all_levels(__maybe_unused enum libbpf_print_level level,const char * format,va_list args)652 print_all_levels(__maybe_unused enum libbpf_print_level level,
653 const char *format, va_list args)
654 {
655 return vfprintf(stderr, format, args);
656 }
657
prog_fd_by_nametag(void * nametag,int ** fds,bool tag)658 static int prog_fd_by_nametag(void *nametag, int **fds, bool tag)
659 {
660 unsigned int id = 0;
661 int fd, nb_fds = 0;
662 void *tmp;
663 int err;
664
665 while (true) {
666 struct bpf_prog_info info = {};
667 __u32 len = sizeof(info);
668
669 err = bpf_prog_get_next_id(id, &id);
670 if (err) {
671 if (errno != ENOENT) {
672 p_err("%s", strerror(errno));
673 goto err_close_fds;
674 }
675 return nb_fds;
676 }
677
678 fd = bpf_prog_get_fd_by_id(id);
679 if (fd < 0) {
680 p_err("can't get prog by id (%u): %s",
681 id, strerror(errno));
682 goto err_close_fds;
683 }
684
685 err = bpf_obj_get_info_by_fd(fd, &info, &len);
686 if (err) {
687 p_err("can't get prog info (%u): %s",
688 id, strerror(errno));
689 goto err_close_fd;
690 }
691
692 if ((tag && memcmp(nametag, info.tag, BPF_TAG_SIZE)) ||
693 (!tag && strncmp(nametag, info.name, BPF_OBJ_NAME_LEN))) {
694 close(fd);
695 continue;
696 }
697
698 if (nb_fds > 0) {
699 tmp = realloc(*fds, (nb_fds + 1) * sizeof(int));
700 if (!tmp) {
701 p_err("failed to realloc");
702 goto err_close_fd;
703 }
704 *fds = tmp;
705 }
706 (*fds)[nb_fds++] = fd;
707 }
708
709 err_close_fd:
710 close(fd);
711 err_close_fds:
712 while (--nb_fds >= 0)
713 close((*fds)[nb_fds]);
714 return -1;
715 }
716
prog_parse_fds(int * argc,char *** argv,int ** fds)717 int prog_parse_fds(int *argc, char ***argv, int **fds)
718 {
719 if (is_prefix(**argv, "id")) {
720 unsigned int id;
721 char *endptr;
722
723 NEXT_ARGP();
724
725 id = strtoul(**argv, &endptr, 0);
726 if (*endptr) {
727 p_err("can't parse %s as ID", **argv);
728 return -1;
729 }
730 NEXT_ARGP();
731
732 (*fds)[0] = bpf_prog_get_fd_by_id(id);
733 if ((*fds)[0] < 0) {
734 p_err("get by id (%u): %s", id, strerror(errno));
735 return -1;
736 }
737 return 1;
738 } else if (is_prefix(**argv, "tag")) {
739 unsigned char tag[BPF_TAG_SIZE];
740
741 NEXT_ARGP();
742
743 if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2,
744 tag + 3, tag + 4, tag + 5, tag + 6, tag + 7)
745 != BPF_TAG_SIZE) {
746 p_err("can't parse tag");
747 return -1;
748 }
749 NEXT_ARGP();
750
751 return prog_fd_by_nametag(tag, fds, true);
752 } else if (is_prefix(**argv, "name")) {
753 char *name;
754
755 NEXT_ARGP();
756
757 name = **argv;
758 if (strlen(name) > BPF_OBJ_NAME_LEN - 1) {
759 p_err("can't parse name");
760 return -1;
761 }
762 NEXT_ARGP();
763
764 return prog_fd_by_nametag(name, fds, false);
765 } else if (is_prefix(**argv, "pinned")) {
766 char *path;
767
768 NEXT_ARGP();
769
770 path = **argv;
771 NEXT_ARGP();
772
773 (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_PROG);
774 if ((*fds)[0] < 0)
775 return -1;
776 return 1;
777 }
778
779 p_err("expected 'id', 'tag', 'name' or 'pinned', got: '%s'?", **argv);
780 return -1;
781 }
782
prog_parse_fd(int * argc,char *** argv)783 int prog_parse_fd(int *argc, char ***argv)
784 {
785 int *fds = NULL;
786 int nb_fds, fd;
787
788 fds = malloc(sizeof(int));
789 if (!fds) {
790 p_err("mem alloc failed");
791 return -1;
792 }
793 nb_fds = prog_parse_fds(argc, argv, &fds);
794 if (nb_fds != 1) {
795 if (nb_fds > 1) {
796 p_err("several programs match this handle");
797 while (nb_fds--)
798 close(fds[nb_fds]);
799 }
800 fd = -1;
801 goto exit_free;
802 }
803
804 fd = fds[0];
805 exit_free:
806 free(fds);
807 return fd;
808 }
809
map_fd_by_name(char * name,int ** fds)810 static int map_fd_by_name(char *name, int **fds)
811 {
812 unsigned int id = 0;
813 int fd, nb_fds = 0;
814 void *tmp;
815 int err;
816
817 while (true) {
818 struct bpf_map_info info = {};
819 __u32 len = sizeof(info);
820
821 err = bpf_map_get_next_id(id, &id);
822 if (err) {
823 if (errno != ENOENT) {
824 p_err("%s", strerror(errno));
825 goto err_close_fds;
826 }
827 return nb_fds;
828 }
829
830 fd = bpf_map_get_fd_by_id(id);
831 if (fd < 0) {
832 p_err("can't get map by id (%u): %s",
833 id, strerror(errno));
834 goto err_close_fds;
835 }
836
837 err = bpf_obj_get_info_by_fd(fd, &info, &len);
838 if (err) {
839 p_err("can't get map info (%u): %s",
840 id, strerror(errno));
841 goto err_close_fd;
842 }
843
844 if (strncmp(name, info.name, BPF_OBJ_NAME_LEN)) {
845 close(fd);
846 continue;
847 }
848
849 if (nb_fds > 0) {
850 tmp = realloc(*fds, (nb_fds + 1) * sizeof(int));
851 if (!tmp) {
852 p_err("failed to realloc");
853 goto err_close_fd;
854 }
855 *fds = tmp;
856 }
857 (*fds)[nb_fds++] = fd;
858 }
859
860 err_close_fd:
861 close(fd);
862 err_close_fds:
863 while (--nb_fds >= 0)
864 close((*fds)[nb_fds]);
865 return -1;
866 }
867
map_parse_fds(int * argc,char *** argv,int ** fds)868 int map_parse_fds(int *argc, char ***argv, int **fds)
869 {
870 if (is_prefix(**argv, "id")) {
871 unsigned int id;
872 char *endptr;
873
874 NEXT_ARGP();
875
876 id = strtoul(**argv, &endptr, 0);
877 if (*endptr) {
878 p_err("can't parse %s as ID", **argv);
879 return -1;
880 }
881 NEXT_ARGP();
882
883 (*fds)[0] = bpf_map_get_fd_by_id(id);
884 if ((*fds)[0] < 0) {
885 p_err("get map by id (%u): %s", id, strerror(errno));
886 return -1;
887 }
888 return 1;
889 } else if (is_prefix(**argv, "name")) {
890 char *name;
891
892 NEXT_ARGP();
893
894 name = **argv;
895 if (strlen(name) > BPF_OBJ_NAME_LEN - 1) {
896 p_err("can't parse name");
897 return -1;
898 }
899 NEXT_ARGP();
900
901 return map_fd_by_name(name, fds);
902 } else if (is_prefix(**argv, "pinned")) {
903 char *path;
904
905 NEXT_ARGP();
906
907 path = **argv;
908 NEXT_ARGP();
909
910 (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_MAP);
911 if ((*fds)[0] < 0)
912 return -1;
913 return 1;
914 }
915
916 p_err("expected 'id', 'name' or 'pinned', got: '%s'?", **argv);
917 return -1;
918 }
919
map_parse_fd(int * argc,char *** argv)920 int map_parse_fd(int *argc, char ***argv)
921 {
922 int *fds = NULL;
923 int nb_fds, fd;
924
925 fds = malloc(sizeof(int));
926 if (!fds) {
927 p_err("mem alloc failed");
928 return -1;
929 }
930 nb_fds = map_parse_fds(argc, argv, &fds);
931 if (nb_fds != 1) {
932 if (nb_fds > 1) {
933 p_err("several maps match this handle");
934 while (nb_fds--)
935 close(fds[nb_fds]);
936 }
937 fd = -1;
938 goto exit_free;
939 }
940
941 fd = fds[0];
942 exit_free:
943 free(fds);
944 return fd;
945 }
946
map_parse_fd_and_info(int * argc,char *** argv,void * info,__u32 * info_len)947 int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len)
948 {
949 int err;
950 int fd;
951
952 fd = map_parse_fd(argc, argv);
953 if (fd < 0)
954 return -1;
955
956 err = bpf_obj_get_info_by_fd(fd, info, info_len);
957 if (err) {
958 p_err("can't get map info: %s", strerror(errno));
959 close(fd);
960 return err;
961 }
962
963 return fd;
964 }
965
hash_fn_for_key_as_id(const void * key,void * ctx)966 size_t hash_fn_for_key_as_id(const void *key, void *ctx)
967 {
968 return (size_t)key;
969 }
970
equal_fn_for_key_as_id(const void * k1,const void * k2,void * ctx)971 bool equal_fn_for_key_as_id(const void *k1, const void *k2, void *ctx)
972 {
973 return k1 == k2;
974 }
975