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