2018-12-13 11:59:25 +08:00
|
|
|
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
|
|
|
/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
|
2017-10-05 11:10:04 +08:00
|
|
|
|
2018-05-04 09:37:15 +08:00
|
|
|
#include <ctype.h>
|
2017-10-05 11:10:04 +08:00
|
|
|
#include <errno.h>
|
2018-01-17 08:05:21 +08:00
|
|
|
#include <fcntl.h>
|
tools: bpftool: show filenames of pinned objects
Added support to show filenames of pinned objects.
For example:
root@test# ./bpftool prog
3: tracepoint name tracepoint__irq tag f677a7dd722299a3
loaded_at Oct 26/11:39 uid 0
xlated 160B not jited memlock 4096B map_ids 4
pinned /sys/fs/bpf/softirq_prog
4: tracepoint name tracepoint__irq tag ea5dc530d00b92b6
loaded_at Oct 26/11:39 uid 0
xlated 392B not jited memlock 4096B map_ids 4,6
root@test# ./bpftool --json --pretty prog
[{
"id": 3,
"type": "tracepoint",
"name": "tracepoint__irq",
"tag": "f677a7dd722299a3",
"loaded_at": "Oct 26/11:39",
"uid": 0,
"bytes_xlated": 160,
"jited": false,
"bytes_memlock": 4096,
"map_ids": [4
],
"pinned": ["/sys/fs/bpf/softirq_prog"
]
},{
"id": 4,
"type": "tracepoint",
"name": "tracepoint__irq",
"tag": "ea5dc530d00b92b6",
"loaded_at": "Oct 26/11:39",
"uid": 0,
"bytes_xlated": 392,
"jited": false,
"bytes_memlock": 4096,
"map_ids": [4,6
],
"pinned": []
}
]
root@test# ./bpftool map
4: hash name start flags 0x0
key 4B value 16B max_entries 10240 memlock 1003520B
pinned /sys/fs/bpf/softirq_map1
5: hash name iptr flags 0x0
key 4B value 8B max_entries 10240 memlock 921600B
root@test# ./bpftool --json --pretty map
[{
"id": 4,
"type": "hash",
"name": "start",
"flags": 0,
"bytes_key": 4,
"bytes_value": 16,
"max_entries": 10240,
"bytes_memlock": 1003520,
"pinned": ["/sys/fs/bpf/softirq_map1"
]
},{
"id": 5,
"type": "hash",
"name": "iptr",
"flags": 0,
"bytes_key": 4,
"bytes_value": 8,
"max_entries": 10240,
"bytes_memlock": 921600,
"pinned": []
}
]
Signed-off-by: Prashant Bhole <bhole_prashant_q7@lab.ntt.co.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-11-08 12:55:48 +08:00
|
|
|
#include <fts.h>
|
2017-10-05 11:10:04 +08:00
|
|
|
#include <libgen.h>
|
tools: bpftool: show filenames of pinned objects
Added support to show filenames of pinned objects.
For example:
root@test# ./bpftool prog
3: tracepoint name tracepoint__irq tag f677a7dd722299a3
loaded_at Oct 26/11:39 uid 0
xlated 160B not jited memlock 4096B map_ids 4
pinned /sys/fs/bpf/softirq_prog
4: tracepoint name tracepoint__irq tag ea5dc530d00b92b6
loaded_at Oct 26/11:39 uid 0
xlated 392B not jited memlock 4096B map_ids 4,6
root@test# ./bpftool --json --pretty prog
[{
"id": 3,
"type": "tracepoint",
"name": "tracepoint__irq",
"tag": "f677a7dd722299a3",
"loaded_at": "Oct 26/11:39",
"uid": 0,
"bytes_xlated": 160,
"jited": false,
"bytes_memlock": 4096,
"map_ids": [4
],
"pinned": ["/sys/fs/bpf/softirq_prog"
]
},{
"id": 4,
"type": "tracepoint",
"name": "tracepoint__irq",
"tag": "ea5dc530d00b92b6",
"loaded_at": "Oct 26/11:39",
"uid": 0,
"bytes_xlated": 392,
"jited": false,
"bytes_memlock": 4096,
"map_ids": [4,6
],
"pinned": []
}
]
root@test# ./bpftool map
4: hash name start flags 0x0
key 4B value 16B max_entries 10240 memlock 1003520B
pinned /sys/fs/bpf/softirq_map1
5: hash name iptr flags 0x0
key 4B value 8B max_entries 10240 memlock 921600B
root@test# ./bpftool --json --pretty map
[{
"id": 4,
"type": "hash",
"name": "start",
"flags": 0,
"bytes_key": 4,
"bytes_value": 16,
"max_entries": 10240,
"bytes_memlock": 1003520,
"pinned": ["/sys/fs/bpf/softirq_map1"
]
},{
"id": 5,
"type": "hash",
"name": "iptr",
"flags": 0,
"bytes_key": 4,
"bytes_value": 8,
"max_entries": 10240,
"bytes_memlock": 921600,
"pinned": []
}
]
Signed-off-by: Prashant Bhole <bhole_prashant_q7@lab.ntt.co.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-11-08 12:55:48 +08:00
|
|
|
#include <mntent.h>
|
2017-10-05 11:10:04 +08:00
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <linux/limits.h>
|
|
|
|
#include <linux/magic.h>
|
2017-12-28 10:39:10 +08:00
|
|
|
#include <net/if.h>
|
2017-10-25 11:11:28 +08:00
|
|
|
#include <sys/mount.h>
|
tools: bpftool: adjust rlimit RLIMIT_MEMLOCK when loading programs, maps
The limit for memory locked in the kernel by a process is usually set to
64 kbytes by default. This can be an issue when creating large BPF maps
and/or loading many programs. A workaround is to raise this limit for
the current process before trying to create a new BPF map. Changing the
hard limit requires the CAP_SYS_RESOURCE and can usually only be done by
root user (for non-root users, a call to setrlimit fails (and sets
errno) and the program simply goes on with its rlimit unchanged).
There is no API to get the current amount of memory locked for a user,
therefore we cannot raise the limit only when required. One solution,
used by bcc, is to try to create the map, and on getting a EPERM error,
raising the limit to infinity before giving another try. Another
approach, used in iproute2, is to raise the limit in all cases, before
trying to create the map.
Here we do the same as in iproute2: the rlimit is raised to infinity
before trying to load programs or to create maps with bpftool.
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
2018-11-07 20:29:30 +08:00
|
|
|
#include <sys/resource.h>
|
2017-12-28 10:39:10 +08:00
|
|
|
#include <sys/stat.h>
|
2017-10-05 11:10:04 +08:00
|
|
|
#include <sys/vfs.h>
|
|
|
|
|
|
|
|
#include <bpf.h>
|
2019-06-11 08:56:52 +08:00
|
|
|
#include <libbpf.h> /* libbpf_num_possible_cpus */
|
2017-10-05 11:10:04 +08:00
|
|
|
|
|
|
|
#include "main.h"
|
|
|
|
|
2018-03-06 21:50:10 +08:00
|
|
|
#ifndef BPF_FS_MAGIC
|
|
|
|
#define BPF_FS_MAGIC 0xcafe4a11
|
|
|
|
#endif
|
|
|
|
|
2018-12-14 21:56:01 +08:00
|
|
|
void __printf(1, 2) p_err(const char *fmt, ...)
|
2017-11-04 04:59:07 +08:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
if (json_output) {
|
|
|
|
jsonw_start_object(json_wtr);
|
|
|
|
jsonw_name(json_wtr, "error");
|
|
|
|
jsonw_vprintf_enquote(json_wtr, fmt, ap);
|
|
|
|
jsonw_end_object(json_wtr);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Error: ");
|
|
|
|
vfprintf(stderr, fmt, ap);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2018-12-14 21:56:01 +08:00
|
|
|
void __printf(1, 2) p_info(const char *fmt, ...)
|
2017-11-04 04:59:07 +08:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
if (json_output)
|
|
|
|
return;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vfprintf(stderr, fmt, ap);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2017-10-05 11:10:04 +08:00
|
|
|
static bool is_bpffs(char *path)
|
|
|
|
{
|
|
|
|
struct statfs st_fs;
|
|
|
|
|
|
|
|
if (statfs(path, &st_fs) < 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return (unsigned long)st_fs.f_type == BPF_FS_MAGIC;
|
|
|
|
}
|
|
|
|
|
tools: bpftool: adjust rlimit RLIMIT_MEMLOCK when loading programs, maps
The limit for memory locked in the kernel by a process is usually set to
64 kbytes by default. This can be an issue when creating large BPF maps
and/or loading many programs. A workaround is to raise this limit for
the current process before trying to create a new BPF map. Changing the
hard limit requires the CAP_SYS_RESOURCE and can usually only be done by
root user (for non-root users, a call to setrlimit fails (and sets
errno) and the program simply goes on with its rlimit unchanged).
There is no API to get the current amount of memory locked for a user,
therefore we cannot raise the limit only when required. One solution,
used by bcc, is to try to create the map, and on getting a EPERM error,
raising the limit to infinity before giving another try. Another
approach, used in iproute2, is to raise the limit in all cases, before
trying to create the map.
Here we do the same as in iproute2: the rlimit is raised to infinity
before trying to load programs or to create maps with bpftool.
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
2018-11-07 20:29:30 +08:00
|
|
|
void set_max_rlimit(void)
|
|
|
|
{
|
|
|
|
struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
|
|
|
|
|
|
|
|
setrlimit(RLIMIT_MEMLOCK, &rinf);
|
|
|
|
}
|
|
|
|
|
2018-12-18 18:13:18 +08:00
|
|
|
static int
|
|
|
|
mnt_fs(const char *target, const char *type, char *buff, size_t bufflen)
|
2017-10-25 11:11:28 +08:00
|
|
|
{
|
|
|
|
bool bind_done = false;
|
|
|
|
|
|
|
|
while (mount("", target, "none", MS_PRIVATE | MS_REC, NULL)) {
|
|
|
|
if (errno != EINVAL || bind_done) {
|
|
|
|
snprintf(buff, bufflen,
|
|
|
|
"mount --make-private %s failed: %s",
|
|
|
|
target, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mount(target, target, "none", MS_BIND, NULL)) {
|
|
|
|
snprintf(buff, bufflen,
|
|
|
|
"mount --bind %s %s failed: %s",
|
|
|
|
target, target, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bind_done = true;
|
|
|
|
}
|
|
|
|
|
2018-12-18 18:13:18 +08:00
|
|
|
if (mount(type, target, type, 0, "mode=0700")) {
|
|
|
|
snprintf(buff, bufflen, "mount -t %s %s %s failed: %s",
|
|
|
|
type, type, target, strerror(errno));
|
2017-10-25 11:11:28 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-12-18 18:13:18 +08:00
|
|
|
int mount_tracefs(const char *target)
|
|
|
|
{
|
|
|
|
char err_str[ERR_MAX_LEN];
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = mnt_fs(target, "tracefs", err_str, ERR_MAX_LEN);
|
|
|
|
if (err) {
|
|
|
|
err_str[ERR_MAX_LEN - 1] = '\0';
|
|
|
|
p_err("can't mount tracefs: %s", err_str);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
tools: bpftool: pass an argument to silence open_obj_pinned()
Function open_obj_pinned() prints error messages when it fails to open a
link in the BPF virtual file system. However, in some occasions it is
not desirable to print an error, for example when we parse all links
under the bpffs root, and the error is due to some paths actually being
symbolic links.
Example output:
# ls -l /sys/fs/bpf/
lrwxrwxrwx 1 root root 0 Oct 18 19:00 ip -> /sys/fs/bpf/tc/
drwx------ 3 root root 0 Oct 18 19:00 tc
lrwxrwxrwx 1 root root 0 Oct 18 19:00 xdp -> /sys/fs/bpf/tc/
# bpftool --bpffs prog show
Error: bpf obj get (/sys/fs/bpf): Permission denied
Error: bpf obj get (/sys/fs/bpf): Permission denied
# strace -e bpf bpftool --bpffs prog show
bpf(BPF_OBJ_GET, {pathname="/sys/fs/bpf/ip", bpf_fd=0}, 72) = -1 EACCES (Permission denied)
Error: bpf obj get (/sys/fs/bpf): Permission denied
bpf(BPF_OBJ_GET, {pathname="/sys/fs/bpf/xdp", bpf_fd=0}, 72) = -1 EACCES (Permission denied)
Error: bpf obj get (/sys/fs/bpf): Permission denied
...
To fix it, pass a bool as a second argument to the function, and prevent
it from printing an error when the argument is set to true.
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
2018-11-08 19:52:27 +08:00
|
|
|
int open_obj_pinned(char *path, bool quiet)
|
2017-10-05 11:10:04 +08:00
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = bpf_obj_get(path);
|
|
|
|
if (fd < 0) {
|
tools: bpftool: pass an argument to silence open_obj_pinned()
Function open_obj_pinned() prints error messages when it fails to open a
link in the BPF virtual file system. However, in some occasions it is
not desirable to print an error, for example when we parse all links
under the bpffs root, and the error is due to some paths actually being
symbolic links.
Example output:
# ls -l /sys/fs/bpf/
lrwxrwxrwx 1 root root 0 Oct 18 19:00 ip -> /sys/fs/bpf/tc/
drwx------ 3 root root 0 Oct 18 19:00 tc
lrwxrwxrwx 1 root root 0 Oct 18 19:00 xdp -> /sys/fs/bpf/tc/
# bpftool --bpffs prog show
Error: bpf obj get (/sys/fs/bpf): Permission denied
Error: bpf obj get (/sys/fs/bpf): Permission denied
# strace -e bpf bpftool --bpffs prog show
bpf(BPF_OBJ_GET, {pathname="/sys/fs/bpf/ip", bpf_fd=0}, 72) = -1 EACCES (Permission denied)
Error: bpf obj get (/sys/fs/bpf): Permission denied
bpf(BPF_OBJ_GET, {pathname="/sys/fs/bpf/xdp", bpf_fd=0}, 72) = -1 EACCES (Permission denied)
Error: bpf obj get (/sys/fs/bpf): Permission denied
...
To fix it, pass a bool as a second argument to the function, and prevent
it from printing an error when the argument is set to true.
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
2018-11-08 19:52:27 +08:00
|
|
|
if (!quiet)
|
|
|
|
p_err("bpf obj get (%s): %s", path,
|
|
|
|
errno == EACCES && !is_bpffs(dirname(path)) ?
|
|
|
|
"directory not in bpf file system (bpffs)" :
|
|
|
|
strerror(errno));
|
2017-10-05 11:10:04 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-11-08 12:55:47 +08:00
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
|
|
|
|
{
|
|
|
|
enum bpf_obj_type type;
|
|
|
|
int fd;
|
|
|
|
|
tools: bpftool: pass an argument to silence open_obj_pinned()
Function open_obj_pinned() prints error messages when it fails to open a
link in the BPF virtual file system. However, in some occasions it is
not desirable to print an error, for example when we parse all links
under the bpffs root, and the error is due to some paths actually being
symbolic links.
Example output:
# ls -l /sys/fs/bpf/
lrwxrwxrwx 1 root root 0 Oct 18 19:00 ip -> /sys/fs/bpf/tc/
drwx------ 3 root root 0 Oct 18 19:00 tc
lrwxrwxrwx 1 root root 0 Oct 18 19:00 xdp -> /sys/fs/bpf/tc/
# bpftool --bpffs prog show
Error: bpf obj get (/sys/fs/bpf): Permission denied
Error: bpf obj get (/sys/fs/bpf): Permission denied
# strace -e bpf bpftool --bpffs prog show
bpf(BPF_OBJ_GET, {pathname="/sys/fs/bpf/ip", bpf_fd=0}, 72) = -1 EACCES (Permission denied)
Error: bpf obj get (/sys/fs/bpf): Permission denied
bpf(BPF_OBJ_GET, {pathname="/sys/fs/bpf/xdp", bpf_fd=0}, 72) = -1 EACCES (Permission denied)
Error: bpf obj get (/sys/fs/bpf): Permission denied
...
To fix it, pass a bool as a second argument to the function, and prevent
it from printing an error when the argument is set to true.
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
2018-11-08 19:52:27 +08:00
|
|
|
fd = open_obj_pinned(path, false);
|
2017-11-08 12:55:47 +08:00
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
2017-10-05 11:10:04 +08:00
|
|
|
type = get_fd_type(fd);
|
|
|
|
if (type < 0) {
|
|
|
|
close(fd);
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
if (type != exp_type) {
|
2017-10-24 00:24:13 +08:00
|
|
|
p_err("incorrect object type: %s", get_fd_type_name(type));
|
2017-10-05 11:10:04 +08:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2018-11-10 00:21:44 +08:00
|
|
|
int mount_bpffs_for_pin(const char *name)
|
2017-10-05 11:10:04 +08:00
|
|
|
{
|
2017-10-25 11:11:28 +08:00
|
|
|
char err_str[ERR_MAX_LEN];
|
|
|
|
char *file;
|
|
|
|
char *dir;
|
2017-12-13 23:18:53 +08:00
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
file = malloc(strlen(name) + 1);
|
|
|
|
strcpy(file, name);
|
|
|
|
dir = dirname(file);
|
|
|
|
|
2018-11-10 00:21:44 +08:00
|
|
|
if (is_bpffs(dir))
|
|
|
|
/* nothing to do if already mounted */
|
2017-12-13 23:18:53 +08:00
|
|
|
goto out_free;
|
|
|
|
|
2018-12-18 18:13:19 +08:00
|
|
|
if (block_mount) {
|
|
|
|
p_err("no BPF file system found, not mounting it due to --nomount option");
|
|
|
|
err = -1;
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
|
2018-12-18 18:13:18 +08:00
|
|
|
err = mnt_fs(dir, "bpf", err_str, ERR_MAX_LEN);
|
2018-11-10 00:21:44 +08:00
|
|
|
if (err) {
|
2017-12-13 23:18:53 +08:00
|
|
|
err_str[ERR_MAX_LEN - 1] = '\0';
|
|
|
|
p_err("can't mount BPF file system to pin the object (%s): %s",
|
|
|
|
name, err_str);
|
|
|
|
}
|
|
|
|
|
|
|
|
out_free:
|
|
|
|
free(file);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2018-11-10 00:21:44 +08:00
|
|
|
int do_pin_fd(int fd, const char *name)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = mount_bpffs_for_pin(name);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
return bpf_obj_pin(fd, name);
|
|
|
|
}
|
|
|
|
|
2017-12-13 23:18:53 +08:00
|
|
|
int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
|
|
|
|
{
|
|
|
|
unsigned int id;
|
|
|
|
char *endptr;
|
2017-10-05 11:10:04 +08:00
|
|
|
int err;
|
|
|
|
int fd;
|
|
|
|
|
2018-07-19 20:10:05 +08:00
|
|
|
if (argc < 3) {
|
|
|
|
p_err("too few arguments, id ID and FILE path is required");
|
|
|
|
return -1;
|
|
|
|
} else if (argc > 3) {
|
|
|
|
p_err("too many arguments");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-10-05 11:10:04 +08:00
|
|
|
if (!is_prefix(*argv, "id")) {
|
2017-10-24 00:24:13 +08:00
|
|
|
p_err("expected 'id' got %s", *argv);
|
2017-10-05 11:10:04 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
NEXT_ARG();
|
|
|
|
|
|
|
|
id = strtoul(*argv, &endptr, 0);
|
|
|
|
if (*endptr) {
|
2017-10-24 00:24:13 +08:00
|
|
|
p_err("can't parse %s as ID", *argv);
|
2017-10-05 11:10:04 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
NEXT_ARG();
|
|
|
|
|
|
|
|
fd = get_fd_by_id(id);
|
|
|
|
if (fd < 0) {
|
2017-10-24 00:24:13 +08:00
|
|
|
p_err("can't get prog by id (%u): %s", id, strerror(errno));
|
2017-10-05 11:10:04 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-12-13 23:18:53 +08:00
|
|
|
err = do_pin_fd(fd, *argv);
|
2017-10-05 11:10:04 +08:00
|
|
|
|
2017-10-25 11:11:28 +08:00
|
|
|
close(fd);
|
|
|
|
return err;
|
2017-10-05 11:10:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *get_fd_type_name(enum bpf_obj_type type)
|
|
|
|
{
|
|
|
|
static const char * const names[] = {
|
|
|
|
[BPF_OBJ_UNKNOWN] = "unknown",
|
|
|
|
[BPF_OBJ_PROG] = "prog",
|
|
|
|
[BPF_OBJ_MAP] = "map",
|
|
|
|
};
|
|
|
|
|
|
|
|
if (type < 0 || type >= ARRAY_SIZE(names) || !names[type])
|
|
|
|
return names[BPF_OBJ_UNKNOWN];
|
|
|
|
|
|
|
|
return names[type];
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_fd_type(int fd)
|
|
|
|
{
|
|
|
|
char path[PATH_MAX];
|
|
|
|
char buf[512];
|
|
|
|
ssize_t n;
|
|
|
|
|
2018-12-01 00:25:44 +08:00
|
|
|
snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
|
2017-10-05 11:10:04 +08:00
|
|
|
|
|
|
|
n = readlink(path, buf, sizeof(buf));
|
|
|
|
if (n < 0) {
|
2017-10-24 00:24:13 +08:00
|
|
|
p_err("can't read link type: %s", strerror(errno));
|
2017-10-05 11:10:04 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (n == sizeof(path)) {
|
2017-10-24 00:24:13 +08:00
|
|
|
p_err("can't read link type: path too long!");
|
2017-10-05 11:10:04 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strstr(buf, "bpf-map"))
|
|
|
|
return BPF_OBJ_MAP;
|
|
|
|
else if (strstr(buf, "bpf-prog"))
|
|
|
|
return BPF_OBJ_PROG;
|
|
|
|
|
|
|
|
return BPF_OBJ_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *get_fdinfo(int fd, const char *key)
|
|
|
|
{
|
|
|
|
char path[PATH_MAX];
|
|
|
|
char *line = NULL;
|
|
|
|
size_t line_n = 0;
|
|
|
|
ssize_t n;
|
|
|
|
FILE *fdi;
|
|
|
|
|
2018-12-01 00:25:44 +08:00
|
|
|
snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", fd);
|
2017-10-05 11:10:04 +08:00
|
|
|
|
|
|
|
fdi = fopen(path, "r");
|
2019-01-29 02:01:21 +08:00
|
|
|
if (!fdi)
|
2017-10-05 11:10:04 +08:00
|
|
|
return NULL;
|
|
|
|
|
2018-11-08 19:52:25 +08:00
|
|
|
while ((n = getline(&line, &line_n, fdi)) > 0) {
|
2017-10-05 11:10:04 +08:00
|
|
|
char *value;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (!strstr(line, key))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
fclose(fdi);
|
|
|
|
|
|
|
|
value = strchr(line, '\t');
|
|
|
|
if (!value || !value[1]) {
|
|
|
|
free(line);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
value++;
|
|
|
|
|
|
|
|
len = strlen(value);
|
|
|
|
memmove(line, value, len);
|
|
|
|
line[len - 1] = '\0';
|
|
|
|
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(line);
|
|
|
|
fclose(fdi);
|
|
|
|
return NULL;
|
|
|
|
}
|
tools: bpftool: add JSON output for `bpftool prog dump xlated *` command
Add a new printing function to dump translated eBPF instructions as
JSON. As for plain output, opcodes are printed only on request (when
`opcodes` is provided on the command line).
The disassembled output is generated by the same code that is used by
the kernel verifier.
Example output:
$ bpftool --json --pretty prog dump xlated id 1
[{
"disasm": "(bf) r6 = r1"
},{
"disasm": "(61) r7 = *(u32 *)(r6 +16)"
},{
"disasm": "(95) exit"
}
]
$ bpftool --json --pretty prog dump xlated id 1 opcodes
[{
"disasm": "(bf) r6 = r1",
"opcodes": {
"code": "0xbf",
"src_reg": "0x1",
"dst_reg": "0x6",
"off": ["0x00","0x00"
],
"imm": ["0x00","0x00","0x00","0x00"
]
}
},{
"disasm": "(61) r7 = *(u32 *)(r6 +16)",
"opcodes": {
"code": "0x61",
"src_reg": "0x6",
"dst_reg": "0x7",
"off": ["0x10","0x00"
],
"imm": ["0x00","0x00","0x00","0x00"
]
}
},{
"disasm": "(95) exit",
"opcodes": {
"code": "0x95",
"src_reg": "0x0",
"dst_reg": "0x0",
"off": ["0x00","0x00"
],
"imm": ["0x00","0x00","0x00","0x00"
]
}
}
]
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-10-24 00:24:10 +08:00
|
|
|
|
2018-05-04 09:37:16 +08:00
|
|
|
void print_data_json(uint8_t *data, size_t len)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
jsonw_start_array(json_wtr);
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
jsonw_printf(json_wtr, "%d", data[i]);
|
|
|
|
jsonw_end_array(json_wtr);
|
|
|
|
}
|
|
|
|
|
tools: bpftool: add JSON output for `bpftool prog dump xlated *` command
Add a new printing function to dump translated eBPF instructions as
JSON. As for plain output, opcodes are printed only on request (when
`opcodes` is provided on the command line).
The disassembled output is generated by the same code that is used by
the kernel verifier.
Example output:
$ bpftool --json --pretty prog dump xlated id 1
[{
"disasm": "(bf) r6 = r1"
},{
"disasm": "(61) r7 = *(u32 *)(r6 +16)"
},{
"disasm": "(95) exit"
}
]
$ bpftool --json --pretty prog dump xlated id 1 opcodes
[{
"disasm": "(bf) r6 = r1",
"opcodes": {
"code": "0xbf",
"src_reg": "0x1",
"dst_reg": "0x6",
"off": ["0x00","0x00"
],
"imm": ["0x00","0x00","0x00","0x00"
]
}
},{
"disasm": "(61) r7 = *(u32 *)(r6 +16)",
"opcodes": {
"code": "0x61",
"src_reg": "0x6",
"dst_reg": "0x7",
"off": ["0x10","0x00"
],
"imm": ["0x00","0x00","0x00","0x00"
]
}
},{
"disasm": "(95) exit",
"opcodes": {
"code": "0x95",
"src_reg": "0x0",
"dst_reg": "0x0",
"off": ["0x00","0x00"
],
"imm": ["0x00","0x00","0x00","0x00"
]
}
}
]
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-10-24 00:24:10 +08:00
|
|
|
void print_hex_data_json(uint8_t *data, size_t len)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
jsonw_start_array(json_wtr);
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
jsonw_printf(json_wtr, "\"0x%02hhx\"", data[i]);
|
|
|
|
jsonw_end_array(json_wtr);
|
|
|
|
}
|
tools: bpftool: show filenames of pinned objects
Added support to show filenames of pinned objects.
For example:
root@test# ./bpftool prog
3: tracepoint name tracepoint__irq tag f677a7dd722299a3
loaded_at Oct 26/11:39 uid 0
xlated 160B not jited memlock 4096B map_ids 4
pinned /sys/fs/bpf/softirq_prog
4: tracepoint name tracepoint__irq tag ea5dc530d00b92b6
loaded_at Oct 26/11:39 uid 0
xlated 392B not jited memlock 4096B map_ids 4,6
root@test# ./bpftool --json --pretty prog
[{
"id": 3,
"type": "tracepoint",
"name": "tracepoint__irq",
"tag": "f677a7dd722299a3",
"loaded_at": "Oct 26/11:39",
"uid": 0,
"bytes_xlated": 160,
"jited": false,
"bytes_memlock": 4096,
"map_ids": [4
],
"pinned": ["/sys/fs/bpf/softirq_prog"
]
},{
"id": 4,
"type": "tracepoint",
"name": "tracepoint__irq",
"tag": "ea5dc530d00b92b6",
"loaded_at": "Oct 26/11:39",
"uid": 0,
"bytes_xlated": 392,
"jited": false,
"bytes_memlock": 4096,
"map_ids": [4,6
],
"pinned": []
}
]
root@test# ./bpftool map
4: hash name start flags 0x0
key 4B value 16B max_entries 10240 memlock 1003520B
pinned /sys/fs/bpf/softirq_map1
5: hash name iptr flags 0x0
key 4B value 8B max_entries 10240 memlock 921600B
root@test# ./bpftool --json --pretty map
[{
"id": 4,
"type": "hash",
"name": "start",
"flags": 0,
"bytes_key": 4,
"bytes_value": 16,
"max_entries": 10240,
"bytes_memlock": 1003520,
"pinned": ["/sys/fs/bpf/softirq_map1"
]
},{
"id": 5,
"type": "hash",
"name": "iptr",
"flags": 0,
"bytes_key": 4,
"bytes_value": 8,
"max_entries": 10240,
"bytes_memlock": 921600,
"pinned": []
}
]
Signed-off-by: Prashant Bhole <bhole_prashant_q7@lab.ntt.co.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-11-08 12:55:48 +08:00
|
|
|
|
|
|
|
int build_pinned_obj_table(struct pinned_obj_table *tab,
|
|
|
|
enum bpf_obj_type type)
|
|
|
|
{
|
|
|
|
struct bpf_prog_info pinned_info = {};
|
|
|
|
struct pinned_obj *obj_node = NULL;
|
|
|
|
__u32 len = sizeof(pinned_info);
|
|
|
|
struct mntent *mntent = NULL;
|
|
|
|
enum bpf_obj_type objtype;
|
|
|
|
FILE *mntfile = NULL;
|
|
|
|
FTSENT *ftse = NULL;
|
|
|
|
FTS *fts = NULL;
|
|
|
|
int fd, err;
|
|
|
|
|
|
|
|
mntfile = setmntent("/proc/mounts", "r");
|
|
|
|
if (!mntfile)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while ((mntent = getmntent(mntfile))) {
|
|
|
|
char *path[] = { mntent->mnt_dir, NULL };
|
|
|
|
|
|
|
|
if (strncmp(mntent->mnt_type, "bpf", 3) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
fts = fts_open(path, 0, NULL);
|
|
|
|
if (!fts)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
while ((ftse = fts_read(fts))) {
|
|
|
|
if (!(ftse->fts_info & FTS_F))
|
|
|
|
continue;
|
tools: bpftool: pass an argument to silence open_obj_pinned()
Function open_obj_pinned() prints error messages when it fails to open a
link in the BPF virtual file system. However, in some occasions it is
not desirable to print an error, for example when we parse all links
under the bpffs root, and the error is due to some paths actually being
symbolic links.
Example output:
# ls -l /sys/fs/bpf/
lrwxrwxrwx 1 root root 0 Oct 18 19:00 ip -> /sys/fs/bpf/tc/
drwx------ 3 root root 0 Oct 18 19:00 tc
lrwxrwxrwx 1 root root 0 Oct 18 19:00 xdp -> /sys/fs/bpf/tc/
# bpftool --bpffs prog show
Error: bpf obj get (/sys/fs/bpf): Permission denied
Error: bpf obj get (/sys/fs/bpf): Permission denied
# strace -e bpf bpftool --bpffs prog show
bpf(BPF_OBJ_GET, {pathname="/sys/fs/bpf/ip", bpf_fd=0}, 72) = -1 EACCES (Permission denied)
Error: bpf obj get (/sys/fs/bpf): Permission denied
bpf(BPF_OBJ_GET, {pathname="/sys/fs/bpf/xdp", bpf_fd=0}, 72) = -1 EACCES (Permission denied)
Error: bpf obj get (/sys/fs/bpf): Permission denied
...
To fix it, pass a bool as a second argument to the function, and prevent
it from printing an error when the argument is set to true.
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
2018-11-08 19:52:27 +08:00
|
|
|
fd = open_obj_pinned(ftse->fts_path, true);
|
tools: bpftool: show filenames of pinned objects
Added support to show filenames of pinned objects.
For example:
root@test# ./bpftool prog
3: tracepoint name tracepoint__irq tag f677a7dd722299a3
loaded_at Oct 26/11:39 uid 0
xlated 160B not jited memlock 4096B map_ids 4
pinned /sys/fs/bpf/softirq_prog
4: tracepoint name tracepoint__irq tag ea5dc530d00b92b6
loaded_at Oct 26/11:39 uid 0
xlated 392B not jited memlock 4096B map_ids 4,6
root@test# ./bpftool --json --pretty prog
[{
"id": 3,
"type": "tracepoint",
"name": "tracepoint__irq",
"tag": "f677a7dd722299a3",
"loaded_at": "Oct 26/11:39",
"uid": 0,
"bytes_xlated": 160,
"jited": false,
"bytes_memlock": 4096,
"map_ids": [4
],
"pinned": ["/sys/fs/bpf/softirq_prog"
]
},{
"id": 4,
"type": "tracepoint",
"name": "tracepoint__irq",
"tag": "ea5dc530d00b92b6",
"loaded_at": "Oct 26/11:39",
"uid": 0,
"bytes_xlated": 392,
"jited": false,
"bytes_memlock": 4096,
"map_ids": [4,6
],
"pinned": []
}
]
root@test# ./bpftool map
4: hash name start flags 0x0
key 4B value 16B max_entries 10240 memlock 1003520B
pinned /sys/fs/bpf/softirq_map1
5: hash name iptr flags 0x0
key 4B value 8B max_entries 10240 memlock 921600B
root@test# ./bpftool --json --pretty map
[{
"id": 4,
"type": "hash",
"name": "start",
"flags": 0,
"bytes_key": 4,
"bytes_value": 16,
"max_entries": 10240,
"bytes_memlock": 1003520,
"pinned": ["/sys/fs/bpf/softirq_map1"
]
},{
"id": 5,
"type": "hash",
"name": "iptr",
"flags": 0,
"bytes_key": 4,
"bytes_value": 8,
"max_entries": 10240,
"bytes_memlock": 921600,
"pinned": []
}
]
Signed-off-by: Prashant Bhole <bhole_prashant_q7@lab.ntt.co.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-11-08 12:55:48 +08:00
|
|
|
if (fd < 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
objtype = get_fd_type(fd);
|
|
|
|
if (objtype != type) {
|
|
|
|
close(fd);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
memset(&pinned_info, 0, sizeof(pinned_info));
|
|
|
|
err = bpf_obj_get_info_by_fd(fd, &pinned_info, &len);
|
|
|
|
if (err) {
|
|
|
|
close(fd);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
obj_node = malloc(sizeof(*obj_node));
|
|
|
|
if (!obj_node) {
|
|
|
|
close(fd);
|
|
|
|
fts_close(fts);
|
|
|
|
fclose(mntfile);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(obj_node, 0, sizeof(*obj_node));
|
|
|
|
obj_node->id = pinned_info.id;
|
|
|
|
obj_node->path = strdup(ftse->fts_path);
|
|
|
|
hash_add(tab->table, &obj_node->hash, obj_node->id);
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
fts_close(fts);
|
|
|
|
}
|
|
|
|
fclose(mntfile);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void delete_pinned_obj_table(struct pinned_obj_table *tab)
|
|
|
|
{
|
|
|
|
struct pinned_obj *obj;
|
|
|
|
struct hlist_node *tmp;
|
|
|
|
unsigned int bkt;
|
|
|
|
|
|
|
|
hash_for_each_safe(tab->table, bkt, tmp, obj, hash) {
|
|
|
|
hash_del(&obj->hash);
|
|
|
|
free(obj->path);
|
|
|
|
free(obj);
|
|
|
|
}
|
|
|
|
}
|
2017-12-28 10:39:10 +08:00
|
|
|
|
2018-05-04 09:37:16 +08:00
|
|
|
unsigned int get_page_size(void)
|
|
|
|
{
|
|
|
|
static int result;
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
result = getpagesize();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-05-04 09:37:15 +08:00
|
|
|
unsigned int get_possible_cpus(void)
|
|
|
|
{
|
2019-06-11 08:56:52 +08:00
|
|
|
int cpus = libbpf_num_possible_cpus();
|
2018-05-04 09:37:15 +08:00
|
|
|
|
2019-06-11 08:56:52 +08:00
|
|
|
if (cpus < 0) {
|
|
|
|
p_err("Can't get # of possible cpus: %s", strerror(-cpus));
|
2018-05-04 09:37:15 +08:00
|
|
|
exit(-1);
|
|
|
|
}
|
2019-06-11 08:56:52 +08:00
|
|
|
return cpus;
|
2018-05-04 09:37:15 +08:00
|
|
|
}
|
|
|
|
|
2017-12-28 10:39:10 +08:00
|
|
|
static char *
|
|
|
|
ifindex_to_name_ns(__u32 ifindex, __u32 ns_dev, __u32 ns_ino, char *buf)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = stat("/proc/self/ns/net", &st);
|
|
|
|
if (err) {
|
|
|
|
p_err("Can't stat /proc/self: %s", strerror(errno));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (st.st_dev != ns_dev || st.st_ino != ns_ino)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return if_indextoname(ifindex, buf);
|
|
|
|
}
|
|
|
|
|
2018-01-17 08:05:21 +08:00
|
|
|
static int read_sysfs_hex_int(char *path)
|
|
|
|
{
|
|
|
|
char vendor_id_buf[8];
|
|
|
|
int len;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = open(path, O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
|
|
p_err("Can't open %s: %s", path, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = read(fd, vendor_id_buf, sizeof(vendor_id_buf));
|
|
|
|
close(fd);
|
|
|
|
if (len < 0) {
|
|
|
|
p_err("Can't read %s: %s", path, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (len >= (int)sizeof(vendor_id_buf)) {
|
|
|
|
p_err("Value in %s too long", path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
vendor_id_buf[len] = 0;
|
|
|
|
|
|
|
|
return strtol(vendor_id_buf, NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int read_sysfs_netdev_hex_int(char *devname, const char *entry_name)
|
|
|
|
{
|
|
|
|
char full_path[64];
|
|
|
|
|
|
|
|
snprintf(full_path, sizeof(full_path), "/sys/class/net/%s/device/%s",
|
|
|
|
devname, entry_name);
|
|
|
|
|
|
|
|
return read_sysfs_hex_int(full_path);
|
|
|
|
}
|
|
|
|
|
2018-10-19 02:34:55 +08:00
|
|
|
const char *
|
|
|
|
ifindex_to_bfd_params(__u32 ifindex, __u64 ns_dev, __u64 ns_ino,
|
|
|
|
const char **opt)
|
2018-01-17 08:05:21 +08:00
|
|
|
{
|
|
|
|
char devname[IF_NAMESIZE];
|
|
|
|
int vendor_id;
|
|
|
|
int device_id;
|
|
|
|
|
|
|
|
if (!ifindex_to_name_ns(ifindex, ns_dev, ns_ino, devname)) {
|
|
|
|
p_err("Can't get net device name for ifindex %d: %s", ifindex,
|
|
|
|
strerror(errno));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
vendor_id = read_sysfs_netdev_hex_int(devname, "vendor");
|
|
|
|
if (vendor_id < 0) {
|
|
|
|
p_err("Can't get device vendor id for %s", devname);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (vendor_id) {
|
|
|
|
case 0x19ee:
|
|
|
|
device_id = read_sysfs_netdev_hex_int(devname, "device");
|
|
|
|
if (device_id != 0x4000 &&
|
|
|
|
device_id != 0x6000 &&
|
|
|
|
device_id != 0x6003)
|
|
|
|
p_info("Unknown NFP device ID, assuming it is NFP-6xxx arch");
|
2018-10-19 02:34:55 +08:00
|
|
|
*opt = "ctx4";
|
2018-01-17 08:05:21 +08:00
|
|
|
return "NFP-6xxx";
|
|
|
|
default:
|
|
|
|
p_err("Can't get bfd arch name for device vendor id 0x%04x",
|
|
|
|
vendor_id);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-28 10:39:10 +08:00
|
|
|
void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
|
|
|
|
{
|
|
|
|
char name[IF_NAMESIZE];
|
|
|
|
|
|
|
|
if (!ifindex)
|
|
|
|
return;
|
|
|
|
|
2018-12-01 00:25:47 +08:00
|
|
|
printf(" offloaded_to ");
|
2017-12-28 10:39:10 +08:00
|
|
|
if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
|
|
|
|
printf("%s", name);
|
|
|
|
else
|
|
|
|
printf("ifindex %u ns_dev %llu ns_ino %llu",
|
|
|
|
ifindex, ns_dev, ns_inode);
|
|
|
|
}
|
|
|
|
|
|
|
|
void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
|
|
|
|
{
|
|
|
|
char name[IF_NAMESIZE];
|
|
|
|
|
|
|
|
if (!ifindex)
|
|
|
|
return;
|
|
|
|
|
|
|
|
jsonw_name(json_wtr, "dev");
|
|
|
|
jsonw_start_object(json_wtr);
|
|
|
|
jsonw_uint_field(json_wtr, "ifindex", ifindex);
|
|
|
|
jsonw_uint_field(json_wtr, "ns_dev", ns_dev);
|
|
|
|
jsonw_uint_field(json_wtr, "ns_inode", ns_inode);
|
|
|
|
if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
|
|
|
|
jsonw_string_field(json_wtr, "ifname", name);
|
|
|
|
jsonw_end_object(json_wtr);
|
|
|
|
}
|
2018-10-16 07:30:36 +08:00
|
|
|
|
|
|
|
int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what)
|
|
|
|
{
|
|
|
|
char *endptr;
|
|
|
|
|
|
|
|
NEXT_ARGP();
|
|
|
|
|
|
|
|
if (*val) {
|
|
|
|
p_err("%s already specified", what);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*val = strtoul(**argv, &endptr, 0);
|
|
|
|
if (*endptr) {
|
|
|
|
p_err("can't parse %s as %s", **argv, what);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
NEXT_ARGP();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|