File Index
Files and directories
Sign in
FILE

common.c

File preview with syntax highlighting.

linux-6.19.14/tools/bpf/bpftool/common.c C
   1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
   2/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
   3
   4#ifndef _GNU_SOURCE
   5#define _GNU_SOURCE
   6#endif
   7#include <assert.h>
   8#include <ctype.h>
   9#include <errno.h>
  10#include <fcntl.h>
  11#include <ftw.h>
  12#include <libgen.h>
  13#include <mntent.h>
  14#include <stdbool.h>
  15#include <stdio.h>
  16#include <stdlib.h>
  17#include <string.h>
  18#include <unistd.h>
  19#include <net/if.h>
  20#include <sys/mount.h>
  21#include <sys/resource.h>
  22#include <sys/stat.h>
  23#include <sys/vfs.h>
  24#include <sys/utsname.h>
  25
  26#include <linux/filter.h>
  27#include <linux/limits.h>
  28#include <linux/magic.h>
  29#include <linux/unistd.h>
  30
  31#include <bpf/bpf.h>
  32#include <bpf/hashmap.h>
  33#include <bpf/libbpf.h> /* libbpf_num_possible_cpus */
  34#include <bpf/btf.h>
  35#include <zlib.h>
  36
  37#include "main.h"
  38
  39#ifndef BPF_FS_MAGIC
  40#define BPF_FS_MAGIC		0xcafe4a11
  41#endif
  42
  43void p_err(const char *fmt, ...)
  44{
  45	va_list ap;
  46
  47	va_start(ap, fmt);
  48	if (json_output) {
  49		jsonw_start_object(json_wtr);
  50		jsonw_name(json_wtr, "error");
  51		jsonw_vprintf_enquote(json_wtr, fmt, ap);
  52		jsonw_end_object(json_wtr);
  53	} else {
  54		fprintf(stderr, "Error: ");
  55		vfprintf(stderr, fmt, ap);
  56		fprintf(stderr, "\n");
  57	}
  58	va_end(ap);
  59}
  60
  61void p_info(const char *fmt, ...)
  62{
  63	va_list ap;
  64
  65	if (json_output)
  66		return;
  67
  68	va_start(ap, fmt);
  69	vfprintf(stderr, fmt, ap);
  70	fprintf(stderr, "\n");
  71	va_end(ap);
  72}
  73
  74static bool is_bpffs(const char *path)
  75{
  76	struct statfs st_fs;
  77
  78	if (statfs(path, &st_fs) < 0)
  79		return false;
  80
  81	return (unsigned long)st_fs.f_type == BPF_FS_MAGIC;
  82}
  83
  84/* Probe whether kernel switched from memlock-based (RLIMIT_MEMLOCK) to
  85 * memcg-based memory accounting for BPF maps and programs. This was done in
  86 * commit 97306be45fbe ("Merge branch 'switch to memcg-based memory
  87 * accounting'"), in Linux 5.11.
  88 *
  89 * Libbpf also offers to probe for memcg-based accounting vs rlimit, but does
  90 * so by checking for the availability of a given BPF helper and this has
  91 * failed on some kernels with backports in the past, see commit 6b4384ff1088
  92 * ("Revert "bpftool: Use libbpf 1.0 API mode instead of RLIMIT_MEMLOCK"").
  93 * Instead, we can probe by lowering the process-based rlimit to 0, trying to
  94 * load a BPF object, and resetting the rlimit. If the load succeeds then
  95 * memcg-based accounting is supported.
  96 *
  97 * This would be too dangerous to do in the library, because multithreaded
  98 * applications might attempt to load items while the rlimit is at 0. Given
  99 * that bpftool is single-threaded, this is fine to do here.
 100 */
 101static bool known_to_need_rlimit(void)
 102{
 103	struct rlimit rlim_init, rlim_cur_zero = {};
 104	struct bpf_insn insns[] = {
 105		BPF_MOV64_IMM(BPF_REG_0, 0),
 106		BPF_EXIT_INSN(),
 107	};
 108	size_t insn_cnt = ARRAY_SIZE(insns);
 109	union bpf_attr attr;
 110	int prog_fd, err;
 111
 112	memset(&attr, 0, sizeof(attr));
 113	attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
 114	attr.insns = ptr_to_u64(insns);
 115	attr.insn_cnt = insn_cnt;
 116	attr.license = ptr_to_u64("GPL");
 117
 118	if (getrlimit(RLIMIT_MEMLOCK, &rlim_init))
 119		return false;
 120
 121	/* Drop the soft limit to zero. We maintain the hard limit to its
 122	 * current value, because lowering it would be a permanent operation
 123	 * for unprivileged users.
 124	 */
 125	rlim_cur_zero.rlim_max = rlim_init.rlim_max;
 126	if (setrlimit(RLIMIT_MEMLOCK, &rlim_cur_zero))
 127		return false;
 128
 129	/* Do not use bpf_prog_load() from libbpf here, because it calls
 130	 * bump_rlimit_memlock(), interfering with the current probe.
 131	 */
 132	prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
 133	err = errno;
 134
 135	/* reset soft rlimit to its initial value */
 136	setrlimit(RLIMIT_MEMLOCK, &rlim_init);
 137
 138	if (prog_fd < 0)
 139		return err == EPERM;
 140
 141	close(prog_fd);
 142	return false;
 143}
 144
 145void set_max_rlimit(void)
 146{
 147	struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
 148
 149	if (known_to_need_rlimit())
 150		setrlimit(RLIMIT_MEMLOCK, &rinf);
 151}
 152
 153static int
 154mnt_fs(const char *target, const char *type, char *buff, size_t bufflen)
 155{
 156	bool bind_done = false;
 157
 158	while (mount("", target, "none", MS_PRIVATE | MS_REC, NULL)) {
 159		if (errno != EINVAL || bind_done) {
 160			snprintf(buff, bufflen,
 161				 "mount --make-private %s failed: %s",
 162				 target, strerror(errno));
 163			return -1;
 164		}
 165
 166		if (mount(target, target, "none", MS_BIND, NULL)) {
 167			snprintf(buff, bufflen,
 168				 "mount --bind %s %s failed: %s",
 169				 target, target, strerror(errno));
 170			return -1;
 171		}
 172
 173		bind_done = true;
 174	}
 175
 176	if (mount(type, target, type, 0, "mode=0700")) {
 177		snprintf(buff, bufflen, "mount -t %s %s %s failed: %s",
 178			 type, type, target, strerror(errno));
 179		return -1;
 180	}
 181
 182	return 0;
 183}
 184
 185int mount_tracefs(const char *target)
 186{
 187	char err_str[ERR_MAX_LEN];
 188	int err;
 189
 190	err = mnt_fs(target, "tracefs", err_str, ERR_MAX_LEN);
 191	if (err) {
 192		err_str[ERR_MAX_LEN - 1] = '\0';
 193		p_err("can't mount tracefs: %s", err_str);
 194	}
 195
 196	return err;
 197}
 198
 199int open_obj_pinned(const char *path, bool quiet,
 200		    const struct bpf_obj_get_opts *opts)
 201{
 202	char *pname;
 203	int fd = -1;
 204
 205	pname = strdup(path);
 206	if (!pname) {
 207		if (!quiet)
 208			p_err("mem alloc failed");
 209		goto out_ret;
 210	}
 211
 212	fd = bpf_obj_get_opts(pname, opts);
 213	if (fd < 0) {
 214		if (!quiet)
 215			p_err("bpf obj get (%s): %s", pname,
 216			      errno == EACCES && !is_bpffs(dirname(pname)) ?
 217			    "directory not in bpf file system (bpffs)" :
 218			    strerror(errno));
 219		goto out_free;
 220	}
 221
 222out_free:
 223	free(pname);
 224out_ret:
 225	return fd;
 226}
 227
 228int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type,
 229			const struct bpf_obj_get_opts *opts)
 230{
 231	enum bpf_obj_type type;
 232	int fd;
 233
 234	fd = open_obj_pinned(path, false, opts);
 235	if (fd < 0)
 236		return -1;
 237
 238	type = get_fd_type(fd);
 239	if (type < 0) {
 240		close(fd);
 241		return type;
 242	}
 243	if (type != exp_type) {
 244		p_err("incorrect object type: %s", get_fd_type_name(type));
 245		close(fd);
 246		return -1;
 247	}
 248
 249	return fd;
 250}
 251
 252int create_and_mount_bpffs_dir(const char *dir_name)
 253{
 254	char err_str[ERR_MAX_LEN];
 255	bool dir_exists;
 256	int err = 0;
 257
 258	if (is_bpffs(dir_name))
 259		return err;
 260
 261	dir_exists = access(dir_name, F_OK) == 0;
 262
 263	if (!dir_exists) {
 264		char *temp_name;
 265		char *parent_name;
 266
 267		temp_name = strdup(dir_name);
 268		if (!temp_name) {
 269			p_err("mem alloc failed");
 270			return -1;
 271		}
 272
 273		parent_name = dirname(temp_name);
 274
 275		if (is_bpffs(parent_name)) {
 276			/* nothing to do if already mounted */
 277			free(temp_name);
 278			return err;
 279		}
 280
 281		if (access(parent_name, F_OK) == -1) {
 282			p_err("can't create dir '%s' to pin BPF object: parent dir '%s' doesn't exist",
 283			      dir_name, parent_name);
 284			free(temp_name);
 285			return -1;
 286		}
 287
 288		free(temp_name);
 289	}
 290
 291	if (block_mount) {
 292		p_err("no BPF file system found, not mounting it due to --nomount option");
 293		return -1;
 294	}
 295
 296	if (!dir_exists) {
 297		err = mkdir(dir_name, S_IRWXU);
 298		if (err) {
 299			p_err("failed to create dir '%s': %s", dir_name, strerror(errno));
 300			return err;
 301		}
 302	}
 303
 304	err = mnt_fs(dir_name, "bpf", err_str, ERR_MAX_LEN);
 305	if (err) {
 306		err_str[ERR_MAX_LEN - 1] = '\0';
 307		p_err("can't mount BPF file system on given dir '%s': %s",
 308		      dir_name, err_str);
 309
 310		if (!dir_exists)
 311			rmdir(dir_name);
 312	}
 313
 314	return err;
 315}
 316
 317int mount_bpffs_for_file(const char *file_name)
 318{
 319	char err_str[ERR_MAX_LEN];
 320	char *temp_name;
 321	char *dir;
 322	int err = 0;
 323
 324	if (access(file_name, F_OK) != -1) {
 325		p_err("can't pin BPF object: path '%s' already exists", file_name);
 326		return -1;
 327	}
 328
 329	temp_name = strdup(file_name);
 330	if (!temp_name) {
 331		p_err("mem alloc failed");
 332		return -1;
 333	}
 334
 335	dir = dirname(temp_name);
 336
 337	if (is_bpffs(dir))
 338		/* nothing to do if already mounted */
 339		goto out_free;
 340
 341	if (access(dir, F_OK) == -1) {
 342		p_err("can't pin BPF object: dir '%s' doesn't exist", dir);
 343		err = -1;
 344		goto out_free;
 345	}
 346
 347	if (block_mount) {
 348		p_err("no BPF file system found, not mounting it due to --nomount option");
 349		err = -1;
 350		goto out_free;
 351	}
 352
 353	err = mnt_fs(dir, "bpf", err_str, ERR_MAX_LEN);
 354	if (err) {
 355		err_str[ERR_MAX_LEN - 1] = '\0';
 356		p_err("can't mount BPF file system to pin the object '%s': %s",
 357		      file_name, err_str);
 358	}
 359
 360out_free:
 361	free(temp_name);
 362	return err;
 363}
 364
 365int do_pin_fd(int fd, const char *name)
 366{
 367	int err;
 368
 369	err = mount_bpffs_for_file(name);
 370	if (err)
 371		return err;
 372
 373	err = bpf_obj_pin(fd, name);
 374	if (err)
 375		p_err("can't pin the object (%s): %s", name, strerror(errno));
 376
 377	return err;
 378}
 379
 380int do_pin_any(int argc, char **argv, int (*get_fd)(int *, char ***))
 381{
 382	int err;
 383	int fd;
 384
 385	if (!REQ_ARGS(3))
 386		return -EINVAL;
 387
 388	fd = get_fd(&argc, &argv);
 389	if (fd < 0)
 390		return fd;
 391
 392	err = do_pin_fd(fd, *argv);
 393
 394	close(fd);
 395	return err;
 396}
 397
 398const char *get_fd_type_name(enum bpf_obj_type type)
 399{
 400	static const char * const names[] = {
 401		[BPF_OBJ_UNKNOWN]	= "unknown",
 402		[BPF_OBJ_PROG]		= "prog",
 403		[BPF_OBJ_MAP]		= "map",
 404		[BPF_OBJ_LINK]		= "link",
 405	};
 406
 407	if (type < 0 || type >= ARRAY_SIZE(names) || !names[type])
 408		return names[BPF_OBJ_UNKNOWN];
 409
 410	return names[type];
 411}
 412
 413void get_prog_full_name(const struct bpf_prog_info *prog_info, int prog_fd,
 414			char *name_buff, size_t buff_len)
 415{
 416	const char *prog_name = prog_info->name;
 417	const struct btf_type *func_type;
 418	struct bpf_func_info finfo = {};
 419	struct bpf_prog_info info = {};
 420	__u32 info_len = sizeof(info);
 421	struct btf *prog_btf = NULL;
 422
 423	if (buff_len <= BPF_OBJ_NAME_LEN ||
 424	    strlen(prog_info->name) < BPF_OBJ_NAME_LEN - 1)
 425		goto copy_name;
 426
 427	if (!prog_info->btf_id || prog_info->nr_func_info == 0)
 428		goto copy_name;
 429
 430	info.nr_func_info = 1;
 431	info.func_info_rec_size = prog_info->func_info_rec_size;
 432	if (info.func_info_rec_size > sizeof(finfo))
 433		info.func_info_rec_size = sizeof(finfo);
 434	info.func_info = ptr_to_u64(&finfo);
 435
 436	if (bpf_prog_get_info_by_fd(prog_fd, &info, &info_len))
 437		goto copy_name;
 438
 439	prog_btf = btf__load_from_kernel_by_id(info.btf_id);
 440	if (!prog_btf)
 441		goto copy_name;
 442
 443	func_type = btf__type_by_id(prog_btf, finfo.type_id);
 444	if (!func_type || !btf_is_func(func_type))
 445		goto copy_name;
 446
 447	prog_name = btf__name_by_offset(prog_btf, func_type->name_off);
 448
 449copy_name:
 450	snprintf(name_buff, buff_len, "%s", prog_name);
 451
 452	if (prog_btf)
 453		btf__free(prog_btf);
 454}
 455
 456int get_fd_type(int fd)
 457{
 458	char path[PATH_MAX];
 459	char buf[512];
 460	ssize_t n;
 461
 462	snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
 463
 464	n = readlink(path, buf, sizeof(buf));
 465	if (n < 0) {
 466		p_err("can't read link type: %s", strerror(errno));
 467		return -1;
 468	}
 469	if (n == sizeof(buf)) {
 470		p_err("can't read link type: path too long!");
 471		return -1;
 472	}
 473	buf[n] = '\0';
 474
 475	if (strstr(buf, "bpf-map"))
 476		return BPF_OBJ_MAP;
 477	else if (strstr(buf, "bpf-prog"))
 478		return BPF_OBJ_PROG;
 479	else if (strstr(buf, "bpf-link"))
 480		return BPF_OBJ_LINK;
 481
 482	return BPF_OBJ_UNKNOWN;
 483}
 484
 485char *get_fdinfo(int fd, const char *key)
 486{
 487	char path[PATH_MAX];
 488	char *line = NULL;
 489	size_t line_n = 0;
 490	ssize_t n;
 491	FILE *fdi;
 492
 493	snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", fd);
 494
 495	fdi = fopen(path, "r");
 496	if (!fdi)
 497		return NULL;
 498
 499	while ((n = getline(&line, &line_n, fdi)) > 0) {
 500		char *value;
 501		int len;
 502
 503		if (!strstr(line, key))
 504			continue;
 505
 506		fclose(fdi);
 507
 508		value = strchr(line, '\t');
 509		if (!value || !value[1]) {
 510			free(line);
 511			return NULL;
 512		}
 513		value++;
 514
 515		len = strlen(value);
 516		memmove(line, value, len);
 517		line[len - 1] = '\0';
 518
 519		return line;
 520	}
 521
 522	free(line);
 523	fclose(fdi);
 524	return NULL;
 525}
 526
 527void print_data_json(uint8_t *data, size_t len)
 528{
 529	unsigned int i;
 530
 531	jsonw_start_array(json_wtr);
 532	for (i = 0; i < len; i++)
 533		jsonw_printf(json_wtr, "%d", data[i]);
 534	jsonw_end_array(json_wtr);
 535}
 536
 537void print_hex_data_json(uint8_t *data, size_t len)
 538{
 539	unsigned int i;
 540
 541	jsonw_start_array(json_wtr);
 542	for (i = 0; i < len; i++)
 543		jsonw_printf(json_wtr, "\"0x%02hhx\"", data[i]);
 544	jsonw_end_array(json_wtr);
 545}
 546
 547/* extra params for nftw cb */
 548static struct hashmap *build_fn_table;
 549static enum bpf_obj_type build_fn_type;
 550
 551static int do_build_table_cb(const char *fpath, const struct stat *sb,
 552			     int typeflag, struct FTW *ftwbuf)
 553{
 554	struct bpf_prog_info pinned_info;
 555	__u32 len = sizeof(pinned_info);
 556	enum bpf_obj_type objtype;
 557	int fd, err = 0;
 558	char *path;
 559
 560	if (typeflag != FTW_F)
 561		goto out_ret;
 562
 563	fd = open_obj_pinned(fpath, true, NULL);
 564	if (fd < 0)
 565		goto out_ret;
 566
 567	objtype = get_fd_type(fd);
 568	if (objtype != build_fn_type)
 569		goto out_close;
 570
 571	memset(&pinned_info, 0, sizeof(pinned_info));
 572	if (bpf_prog_get_info_by_fd(fd, &pinned_info, &len))
 573		goto out_close;
 574
 575	path = strdup(fpath);
 576	if (!path) {
 577		err = -1;
 578		goto out_close;
 579	}
 580
 581	err = hashmap__append(build_fn_table, pinned_info.id, path);
 582	if (err) {
 583		p_err("failed to append entry to hashmap for ID %u, path '%s': %s",
 584		      pinned_info.id, path, strerror(errno));
 585		free(path);
 586		goto out_close;
 587	}
 588
 589out_close:
 590	close(fd);
 591out_ret:
 592	return err;
 593}
 594
 595int build_pinned_obj_table(struct hashmap *tab,
 596			   enum bpf_obj_type type)
 597{
 598	struct mntent *mntent = NULL;
 599	FILE *mntfile = NULL;
 600	int flags = FTW_PHYS;
 601	int nopenfd = 16;
 602	int err = 0;
 603
 604	mntfile = setmntent("/proc/mounts", "r");
 605	if (!mntfile)
 606		return -1;
 607
 608	build_fn_table = tab;
 609	build_fn_type = type;
 610
 611	while ((mntent = getmntent(mntfile))) {
 612		char *path = mntent->mnt_dir;
 613
 614		if (strncmp(mntent->mnt_type, "bpf", 3) != 0)
 615			continue;
 616		err = nftw(path, do_build_table_cb, nopenfd, flags);
 617		if (err)
 618			break;
 619	}
 620	fclose(mntfile);
 621	return err;
 622}
 623
 624void delete_pinned_obj_table(struct hashmap *map)
 625{
 626	struct hashmap_entry *entry;
 627	size_t bkt;
 628
 629	if (!map)
 630		return;
 631
 632	hashmap__for_each_entry(map, entry, bkt)
 633		free(entry->pvalue);
 634
 635	hashmap__free(map);
 636}
 637
 638unsigned int get_page_size(void)
 639{
 640	static int result;
 641
 642	if (!result)
 643		result = getpagesize();
 644	return result;
 645}
 646
 647unsigned int get_possible_cpus(void)
 648{
 649	int cpus = libbpf_num_possible_cpus();
 650
 651	if (cpus < 0) {
 652		p_err("Can't get # of possible cpus: %s", strerror(-cpus));
 653		exit(-1);
 654	}
 655	return cpus;
 656}
 657
 658static char *
 659ifindex_to_name_ns(__u32 ifindex, __u32 ns_dev, __u32 ns_ino, char *buf)
 660{
 661	struct stat st;
 662	int err;
 663
 664	err = stat("/proc/self/ns/net", &st);
 665	if (err) {
 666		p_err("Can't stat /proc/self: %s", strerror(errno));
 667		return NULL;
 668	}
 669
 670	if (st.st_dev != ns_dev || st.st_ino != ns_ino)
 671		return NULL;
 672
 673	return if_indextoname(ifindex, buf);
 674}
 675
 676static int read_sysfs_hex_int(char *path)
 677{
 678	char vendor_id_buf[8];
 679	int len;
 680	int fd;
 681
 682	fd = open(path, O_RDONLY);
 683	if (fd < 0) {
 684		p_err("Can't open %s: %s", path, strerror(errno));
 685		return -1;
 686	}
 687
 688	len = read(fd, vendor_id_buf, sizeof(vendor_id_buf));
 689	close(fd);
 690	if (len < 0) {
 691		p_err("Can't read %s: %s", path, strerror(errno));
 692		return -1;
 693	}
 694	if (len >= (int)sizeof(vendor_id_buf)) {
 695		p_err("Value in %s too long", path);
 696		return -1;
 697	}
 698
 699	vendor_id_buf[len] = 0;
 700
 701	return strtol(vendor_id_buf, NULL, 0);
 702}
 703
 704static int read_sysfs_netdev_hex_int(char *devname, const char *entry_name)
 705{
 706	char full_path[64];
 707
 708	snprintf(full_path, sizeof(full_path), "/sys/class/net/%s/device/%s",
 709		 devname, entry_name);
 710
 711	return read_sysfs_hex_int(full_path);
 712}
 713
 714const char *
 715ifindex_to_arch(__u32 ifindex, __u64 ns_dev, __u64 ns_ino, const char **opt)
 716{
 717	__maybe_unused int device_id;
 718	char devname[IF_NAMESIZE];
 719	int vendor_id;
 720
 721	if (!ifindex_to_name_ns(ifindex, ns_dev, ns_ino, devname)) {
 722		p_err("Can't get net device name for ifindex %u: %s", ifindex,
 723		      strerror(errno));
 724		return NULL;
 725	}
 726
 727	vendor_id = read_sysfs_netdev_hex_int(devname, "vendor");
 728	if (vendor_id < 0) {
 729		p_err("Can't get device vendor id for %s", devname);
 730		return NULL;
 731	}
 732
 733	switch (vendor_id) {
 734#ifdef HAVE_LIBBFD_SUPPORT
 735	case 0x19ee:
 736		device_id = read_sysfs_netdev_hex_int(devname, "device");
 737		if (device_id != 0x4000 &&
 738		    device_id != 0x6000 &&
 739		    device_id != 0x6003)
 740			p_info("Unknown NFP device ID, assuming it is NFP-6xxx arch");
 741		*opt = "ctx4";
 742		return "NFP-6xxx";
 743#endif /* HAVE_LIBBFD_SUPPORT */
 744	/* No NFP support in LLVM, we have no valid triple to return. */
 745	default:
 746		p_err("Can't get arch name for device vendor id 0x%04x",
 747		      (unsigned int)vendor_id);
 748		return NULL;
 749	}
 750}
 751
 752void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
 753{
 754	char name[IF_NAMESIZE];
 755
 756	if (!ifindex)
 757		return;
 758
 759	printf("  offloaded_to ");
 760	if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
 761		printf("%s", name);
 762	else
 763		printf("ifindex %u ns_dev %llu ns_ino %llu",
 764		       ifindex, ns_dev, ns_inode);
 765}
 766
 767void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
 768{
 769	char name[IF_NAMESIZE];
 770
 771	if (!ifindex)
 772		return;
 773
 774	jsonw_name(json_wtr, "dev");
 775	jsonw_start_object(json_wtr);
 776	jsonw_uint_field(json_wtr, "ifindex", ifindex);
 777	jsonw_uint_field(json_wtr, "ns_dev", ns_dev);
 778	jsonw_uint_field(json_wtr, "ns_inode", ns_inode);
 779	if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
 780		jsonw_string_field(json_wtr, "ifname", name);
 781	jsonw_end_object(json_wtr);
 782}
 783
 784int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what)
 785{
 786	char *endptr;
 787
 788	NEXT_ARGP();
 789
 790	if (*val) {
 791		p_err("%s already specified", what);
 792		return -1;
 793	}
 794
 795	*val = strtoul(**argv, &endptr, 0);
 796	if (*endptr) {
 797		p_err("can't parse %s as %s", **argv, what);
 798		return -1;
 799	}
 800	NEXT_ARGP();
 801
 802	return 0;
 803}
 804
 805int __printf(2, 0)
 806print_all_levels(__maybe_unused enum libbpf_print_level level,
 807		 const char *format, va_list args)
 808{
 809	return vfprintf(stderr, format, args);
 810}
 811
 812static int prog_fd_by_nametag(void *nametag, int **fds, bool tag)
 813{
 814	char prog_name[MAX_PROG_FULL_NAME];
 815	unsigned int id = 0;
 816	int fd, nb_fds = 0;
 817	void *tmp;
 818	int err;
 819
 820	while (true) {
 821		struct bpf_prog_info info = {};
 822		__u32 len = sizeof(info);
 823
 824		err = bpf_prog_get_next_id(id, &id);
 825		if (err) {
 826			if (errno != ENOENT) {
 827				p_err("%s", strerror(errno));
 828				goto err_close_fds;
 829			}
 830			return nb_fds;
 831		}
 832
 833		fd = bpf_prog_get_fd_by_id(id);
 834		if (fd < 0) {
 835			p_err("can't get prog by id (%u): %s",
 836			      id, strerror(errno));
 837			goto err_close_fds;
 838		}
 839
 840		err = bpf_prog_get_info_by_fd(fd, &info, &len);
 841		if (err) {
 842			p_err("can't get prog info (%u): %s",
 843			      id, strerror(errno));
 844			goto err_close_fd;
 845		}
 846
 847		if (tag && memcmp(nametag, info.tag, BPF_TAG_SIZE)) {
 848			close(fd);
 849			continue;
 850		}
 851
 852		if (!tag) {
 853			get_prog_full_name(&info, fd, prog_name,
 854					   sizeof(prog_name));
 855			if (strncmp(nametag, prog_name, sizeof(prog_name))) {
 856				close(fd);
 857				continue;
 858			}
 859		}
 860
 861		if (nb_fds > 0) {
 862			tmp = realloc(*fds, (nb_fds + 1) * sizeof(int));
 863			if (!tmp) {
 864				p_err("failed to realloc");
 865				goto err_close_fd;
 866			}
 867			*fds = tmp;
 868		}
 869		(*fds)[nb_fds++] = fd;
 870	}
 871
 872err_close_fd:
 873	close(fd);
 874err_close_fds:
 875	while (--nb_fds >= 0)
 876		close((*fds)[nb_fds]);
 877	return -1;
 878}
 879
 880int prog_parse_fds(int *argc, char ***argv, int **fds)
 881{
 882	if (is_prefix(**argv, "id")) {
 883		unsigned int id;
 884		char *endptr;
 885
 886		NEXT_ARGP();
 887
 888		id = strtoul(**argv, &endptr, 0);
 889		if (*endptr) {
 890			p_err("can't parse %s as ID", **argv);
 891			return -1;
 892		}
 893		NEXT_ARGP();
 894
 895		(*fds)[0] = bpf_prog_get_fd_by_id(id);
 896		if ((*fds)[0] < 0) {
 897			p_err("get by id (%u): %s", id, strerror(errno));
 898			return -1;
 899		}
 900		return 1;
 901	} else if (is_prefix(**argv, "tag")) {
 902		unsigned char tag[BPF_TAG_SIZE];
 903
 904		NEXT_ARGP();
 905
 906		if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2,
 907			   tag + 3, tag + 4, tag + 5, tag + 6, tag + 7)
 908		    != BPF_TAG_SIZE) {
 909			p_err("can't parse tag");
 910			return -1;
 911		}
 912		NEXT_ARGP();
 913
 914		return prog_fd_by_nametag(tag, fds, true);
 915	} else if (is_prefix(**argv, "name")) {
 916		char *name;
 917
 918		NEXT_ARGP();
 919
 920		name = **argv;
 921		if (strlen(name) > MAX_PROG_FULL_NAME - 1) {
 922			p_err("can't parse name");
 923			return -1;
 924		}
 925		NEXT_ARGP();
 926
 927		return prog_fd_by_nametag(name, fds, false);
 928	} else if (is_prefix(**argv, "pinned")) {
 929		char *path;
 930
 931		NEXT_ARGP();
 932
 933		path = **argv;
 934		NEXT_ARGP();
 935
 936		(*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_PROG, NULL);
 937		if ((*fds)[0] < 0)
 938			return -1;
 939		return 1;
 940	}
 941
 942	p_err("expected 'id', 'tag', 'name' or 'pinned', got: '%s'?", **argv);
 943	return -1;
 944}
 945
 946int prog_parse_fd(int *argc, char ***argv)
 947{
 948	int *fds = NULL;
 949	int nb_fds, fd;
 950
 951	fds = malloc(sizeof(int));
 952	if (!fds) {
 953		p_err("mem alloc failed");
 954		return -1;
 955	}
 956	nb_fds = prog_parse_fds(argc, argv, &fds);
 957	if (nb_fds != 1) {
 958		if (nb_fds > 1) {
 959			p_err("several programs match this handle");
 960			while (nb_fds--)
 961				close(fds[nb_fds]);
 962		}
 963		fd = -1;
 964		goto exit_free;
 965	}
 966
 967	fd = fds[0];
 968exit_free:
 969	free(fds);
 970	return fd;
 971}
 972
 973static int map_fd_by_name(char *name, int **fds,
 974			  const struct bpf_get_fd_by_id_opts *opts)
 975{
 976	unsigned int id = 0;
 977	int fd, nb_fds = 0;
 978	void *tmp;
 979	int err;
 980
 981	while (true) {
 982		LIBBPF_OPTS(bpf_get_fd_by_id_opts, opts_ro);
 983		struct bpf_map_info info = {};
 984		__u32 len = sizeof(info);
 985
 986		err = bpf_map_get_next_id(id, &id);
 987		if (err) {
 988			if (errno != ENOENT) {
 989				p_err("%s", strerror(errno));
 990				goto err_close_fds;
 991			}
 992			return nb_fds;
 993		}
 994
 995		/* Request a read-only fd to query the map info */
 996		opts_ro.open_flags = BPF_F_RDONLY;
 997		fd = bpf_map_get_fd_by_id_opts(id, &opts_ro);
 998		if (fd < 0) {
 999			p_err("can't get map by id (%u): %s",
1000			      id, strerror(errno));
1001			goto err_close_fds;
1002		}
1003
1004		err = bpf_map_get_info_by_fd(fd, &info, &len);
1005		if (err) {
1006			p_err("can't get map info (%u): %s",
1007			      id, strerror(errno));
1008			goto err_close_fd;
1009		}
1010
1011		if (strncmp(name, info.name, BPF_OBJ_NAME_LEN)) {
1012			close(fd);
1013			continue;
1014		}
1015
1016		/* Get an fd with the requested options, if they differ
1017		 * from the read-only options used to get the fd above.
1018		 */
1019		if (memcmp(opts, &opts_ro, sizeof(opts_ro))) {
1020			close(fd);
1021			fd = bpf_map_get_fd_by_id_opts(id, opts);
1022			if (fd < 0) {
1023				p_err("can't get map by id (%u): %s", id,
1024					strerror(errno));
1025				goto err_close_fds;
1026			}
1027		}
1028
1029		if (nb_fds > 0) {
1030			tmp = realloc(*fds, (nb_fds + 1) * sizeof(int));
1031			if (!tmp) {
1032				p_err("failed to realloc");
1033				goto err_close_fd;
1034			}
1035			*fds = tmp;
1036		}
1037		(*fds)[nb_fds++] = fd;
1038	}
1039
1040err_close_fd:
1041	close(fd);
1042err_close_fds:
1043	while (--nb_fds >= 0)
1044		close((*fds)[nb_fds]);
1045	return -1;
1046}
1047
1048int map_parse_fds(int *argc, char ***argv, int **fds, __u32 open_flags)
1049{
1050	LIBBPF_OPTS(bpf_get_fd_by_id_opts, opts);
1051
1052	assert((open_flags & ~BPF_F_RDONLY) == 0);
1053	opts.open_flags = open_flags;
1054
1055	if (is_prefix(**argv, "id")) {
1056		unsigned int id;
1057		char *endptr;
1058
1059		NEXT_ARGP();
1060
1061		id = strtoul(**argv, &endptr, 0);
1062		if (*endptr) {
1063			p_err("can't parse %s as ID", **argv);
1064			return -1;
1065		}
1066		NEXT_ARGP();
1067
1068		(*fds)[0] = bpf_map_get_fd_by_id_opts(id, &opts);
1069		if ((*fds)[0] < 0) {
1070			p_err("get map by id (%u): %s", id, strerror(errno));
1071			return -1;
1072		}
1073		return 1;
1074	} else if (is_prefix(**argv, "name")) {
1075		char *name;
1076
1077		NEXT_ARGP();
1078
1079		name = **argv;
1080		if (strlen(name) > BPF_OBJ_NAME_LEN - 1) {
1081			p_err("can't parse name");
1082			return -1;
1083		}
1084		NEXT_ARGP();
1085
1086		return map_fd_by_name(name, fds, &opts);
1087	} else if (is_prefix(**argv, "pinned")) {
1088		char *path;
1089		LIBBPF_OPTS(bpf_obj_get_opts, get_opts);
1090		get_opts.file_flags = open_flags;
1091
1092		NEXT_ARGP();
1093
1094		path = **argv;
1095		NEXT_ARGP();
1096
1097		(*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_MAP, &get_opts);
1098		if ((*fds)[0] < 0)
1099			return -1;
1100		return 1;
1101	}
1102
1103	p_err("expected 'id', 'name' or 'pinned', got: '%s'?", **argv);
1104	return -1;
1105}
1106
1107int map_parse_fd(int *argc, char ***argv, __u32 open_flags)
1108{
1109	int *fds = NULL;
1110	int nb_fds, fd;
1111
1112	fds = malloc(sizeof(int));
1113	if (!fds) {
1114		p_err("mem alloc failed");
1115		return -1;
1116	}
1117	nb_fds = map_parse_fds(argc, argv, &fds, open_flags);
1118	if (nb_fds != 1) {
1119		if (nb_fds > 1) {
1120			p_err("several maps match this handle");
1121			while (nb_fds--)
1122				close(fds[nb_fds]);
1123		}
1124		fd = -1;
1125		goto exit_free;
1126	}
1127
1128	fd = fds[0];
1129exit_free:
1130	free(fds);
1131	return fd;
1132}
1133
1134int map_parse_fd_and_info(int *argc, char ***argv, struct bpf_map_info *info,
1135			  __u32 *info_len, __u32 open_flags)
1136{
1137	int err;
1138	int fd;
1139
1140	fd = map_parse_fd(argc, argv, open_flags);
1141	if (fd < 0)
1142		return -1;
1143
1144	err = bpf_map_get_info_by_fd(fd, info, info_len);
1145	if (err) {
1146		p_err("can't get map info: %s", strerror(errno));
1147		close(fd);
1148		return err;
1149	}
1150
1151	return fd;
1152}
1153
1154size_t hash_fn_for_key_as_id(long key, void *ctx)
1155{
1156	return key;
1157}
1158
1159bool equal_fn_for_key_as_id(long k1, long k2, void *ctx)
1160{
1161	return k1 == k2;
1162}
1163
1164const char *bpf_attach_type_input_str(enum bpf_attach_type t)
1165{
1166	switch (t) {
1167	case BPF_CGROUP_INET_INGRESS:		return "ingress";
1168	case BPF_CGROUP_INET_EGRESS:		return "egress";
1169	case BPF_CGROUP_INET_SOCK_CREATE:	return "sock_create";
1170	case BPF_CGROUP_INET_SOCK_RELEASE:	return "sock_release";
1171	case BPF_CGROUP_SOCK_OPS:		return "sock_ops";
1172	case BPF_CGROUP_DEVICE:			return "device";
1173	case BPF_CGROUP_INET4_BIND:		return "bind4";
1174	case BPF_CGROUP_INET6_BIND:		return "bind6";
1175	case BPF_CGROUP_INET4_CONNECT:		return "connect4";
1176	case BPF_CGROUP_INET6_CONNECT:		return "connect6";
1177	case BPF_CGROUP_INET4_POST_BIND:	return "post_bind4";
1178	case BPF_CGROUP_INET6_POST_BIND:	return "post_bind6";
1179	case BPF_CGROUP_INET4_GETPEERNAME:	return "getpeername4";
1180	case BPF_CGROUP_INET6_GETPEERNAME:	return "getpeername6";
1181	case BPF_CGROUP_INET4_GETSOCKNAME:	return "getsockname4";
1182	case BPF_CGROUP_INET6_GETSOCKNAME:	return "getsockname6";
1183	case BPF_CGROUP_UDP4_SENDMSG:		return "sendmsg4";
1184	case BPF_CGROUP_UDP6_SENDMSG:		return "sendmsg6";
1185	case BPF_CGROUP_SYSCTL:			return "sysctl";
1186	case BPF_CGROUP_UDP4_RECVMSG:		return "recvmsg4";
1187	case BPF_CGROUP_UDP6_RECVMSG:		return "recvmsg6";
1188	case BPF_CGROUP_GETSOCKOPT:		return "getsockopt";
1189	case BPF_CGROUP_SETSOCKOPT:		return "setsockopt";
1190	case BPF_TRACE_RAW_TP:			return "raw_tp";
1191	case BPF_TRACE_FENTRY:			return "fentry";
1192	case BPF_TRACE_FEXIT:			return "fexit";
1193	case BPF_MODIFY_RETURN:			return "mod_ret";
1194	case BPF_SK_REUSEPORT_SELECT:		return "sk_skb_reuseport_select";
1195	case BPF_SK_REUSEPORT_SELECT_OR_MIGRATE:	return "sk_skb_reuseport_select_or_migrate";
1196	default:	return libbpf_bpf_attach_type_str(t);
1197	}
1198}
1199
1200int pathname_concat(char *buf, int buf_sz, const char *path,
1201		    const char *name)
1202{
1203	int len;
1204
1205	len = snprintf(buf, buf_sz, "%s/%s", path, name);
1206	if (len < 0)
1207		return -EINVAL;
1208	if (len >= buf_sz)
1209		return -ENAMETOOLONG;
1210
1211	return 0;
1212}
1213
1214static bool read_next_kernel_config_option(gzFile file, char *buf, size_t n,
1215					   char **value)
1216{
1217	char *sep;
1218
1219	while (gzgets(file, buf, n)) {
1220		if (strncmp(buf, "CONFIG_", 7))
1221			continue;
1222
1223		sep = strchr(buf, '=');
1224		if (!sep)
1225			continue;
1226
1227		/* Trim ending '\n' */
1228		buf[strlen(buf) - 1] = '\0';
1229
1230		/* Split on '=' and ensure that a value is present. */
1231		*sep = '\0';
1232		if (!sep[1])
1233			continue;
1234
1235		*value = sep + 1;
1236		return true;
1237	}
1238
1239	return false;
1240}
1241
1242int read_kernel_config(const struct kernel_config_option *requested_options,
1243		       size_t num_options, char **out_values,
1244		       const char *define_prefix)
1245{
1246	struct utsname utsn;
1247	char path[PATH_MAX];
1248	gzFile file = NULL;
1249	char buf[4096];
1250	char *value;
1251	size_t i;
1252	int ret = 0;
1253
1254	if (!requested_options || !out_values || num_options == 0)
1255		return -1;
1256
1257	if (!uname(&utsn)) {
1258		snprintf(path, sizeof(path), "/boot/config-%s", utsn.release);
1259
1260		/* gzopen also accepts uncompressed files. */
1261		file = gzopen(path, "r");
1262	}
1263
1264	if (!file) {
1265		/* Some distributions build with CONFIG_IKCONFIG=y and put the
1266		 * config file at /proc/config.gz.
1267		 */
1268		file = gzopen("/proc/config.gz", "r");
1269	}
1270
1271	if (!file) {
1272		p_info("skipping kernel config, can't open file: %s",
1273			strerror(errno));
1274		return -1;
1275	}
1276
1277	if (!gzgets(file, buf, sizeof(buf)) || !gzgets(file, buf, sizeof(buf))) {
1278		p_info("skipping kernel config, can't read from file: %s",
1279			strerror(errno));
1280		ret = -1;
1281		goto end_parse;
1282	}
1283
1284	if (strcmp(buf, "# Automatically generated file; DO NOT EDIT.\n")) {
1285		p_info("skipping kernel config, can't find correct file");
1286		ret = -1;
1287		goto end_parse;
1288	}
1289
1290	while (read_next_kernel_config_option(file, buf, sizeof(buf), &value)) {
1291		for (i = 0; i < num_options; i++) {
1292			if ((define_prefix && !requested_options[i].macro_dump) ||
1293			     out_values[i] || strcmp(buf, requested_options[i].name))
1294				continue;
1295
1296			out_values[i] = strdup(value);
1297		}
1298	}
1299
1300end_parse:
1301	gzclose(file);
1302	return ret;
1303}
~/ Read-only file preview.