Skip to content

Commit

Permalink
tools: bpftool: print netfilter link info
Browse files Browse the repository at this point in the history
Dump protocol family, hook and priority value:
$ bpftool link
2: netfilter  prog 14
        ip input prio -128
        pids install(3264)
5: netfilter  prog 14
        ip6 forward prio 21
        pids a.out(3387)
9: netfilter  prog 14
        ip prerouting prio 123
        pids a.out(5700)
10: netfilter  prog 14
        ip input prio 21
        pids test2(5701)

v2: Quentin Monnet suggested to also add 'bpftool net' support:

$ bpftool net
xdp:

tc:

flow_dissector:

netfilter:

        ip prerouting prio 21 prog_id 14
        ip input prio -128 prog_id 14
        ip input prio 21 prog_id 14
        ip forward prio 21 prog_id 14
        ip output prio 21 prog_id 14
        ip postrouting prio 21 prog_id 14

'bpftool net' only dumps netfilter link type, links are sorted by protocol
family, hook and priority.

v5: fix bpf ci failure: libbpf needs small update to prog_type_name[]
    and probe_prog_load helper.
v4: don't fail with -EOPNOTSUPP in libbpf probe_prog_load, update
    prog_type_name[] with "netfilter" entry (bpf ci)
v3: fix bpf.h copy, 'reserved' member was removed (Alexei)
    use p_err, not fprintf (Quentin)

Suggested-by: Quentin Monnet <quentin@isovalent.com>
Link: https://lore.kernel.org/bpf/eeeaac99-9053-90c2-aa33-cc1ecb1ae9ca@isovalent.com/
Reviewed-by: Quentin Monnet <quentin@isovalent.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Link: https://lore.kernel.org/r/20230421170300.24115-6-fw@strlen.de
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
  • Loading branch information
Florian Westphal authored and qmonnet committed May 26, 2023
1 parent 6b24c3e commit 103f441
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 0 deletions.
14 changes: 14 additions & 0 deletions include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_LSM,
BPF_PROG_TYPE_SK_LOOKUP,
BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */
BPF_PROG_TYPE_NETFILTER,
};

enum bpf_attach_type {
Expand Down Expand Up @@ -1050,6 +1051,7 @@ enum bpf_link_type {
BPF_LINK_TYPE_PERF_EVENT = 7,
BPF_LINK_TYPE_KPROBE_MULTI = 8,
BPF_LINK_TYPE_STRUCT_OPS = 9,
BPF_LINK_TYPE_NETFILTER = 10,

MAX_BPF_LINK_TYPE,
};
Expand Down Expand Up @@ -1560,6 +1562,12 @@ union bpf_attr {
*/
__u64 cookie;
} tracing;
struct {
__u32 pf;
__u32 hooknum;
__s32 priority;
__u32 flags;
} netfilter;
};
} link_create;

Expand Down Expand Up @@ -6410,6 +6418,12 @@ struct bpf_link_info {
struct {
__u32 map_id;
} struct_ops;
struct {
__u32 pf;
__u32 hooknum;
__s32 priority;
__u32 flags;
} netfilter;
};
} __attribute__((aligned(8)));

Expand Down
83 changes: 83 additions & 0 deletions src/link.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#include <errno.h>
#include <linux/err.h>
#include <linux/netfilter.h>
#include <linux/netfilter_arp.h>
#include <net/if.h>
#include <stdio.h>
#include <unistd.h>
Expand Down Expand Up @@ -135,6 +137,18 @@ static void show_iter_json(struct bpf_link_info *info, json_writer_t *wtr)
}
}

void netfilter_dump_json(const struct bpf_link_info *info, json_writer_t *wtr)
{
jsonw_uint_field(json_wtr, "pf",
info->netfilter.pf);
jsonw_uint_field(json_wtr, "hook",
info->netfilter.hooknum);
jsonw_int_field(json_wtr, "prio",
info->netfilter.priority);
jsonw_uint_field(json_wtr, "flags",
info->netfilter.flags);
}

static int get_prog_info(int prog_id, struct bpf_prog_info *info)
{
__u32 len = sizeof(*info);
Expand Down Expand Up @@ -195,6 +209,10 @@ static int show_link_close_json(int fd, struct bpf_link_info *info)
info->netns.netns_ino);
show_link_attach_type_json(info->netns.attach_type, json_wtr);
break;
case BPF_LINK_TYPE_NETFILTER:
netfilter_dump_json(info, json_wtr);
break;

default:
break;
}
Expand Down Expand Up @@ -263,6 +281,68 @@ static void show_iter_plain(struct bpf_link_info *info)
}
}

static const char * const pf2name[] = {
[NFPROTO_INET] = "inet",
[NFPROTO_IPV4] = "ip",
[NFPROTO_ARP] = "arp",
[NFPROTO_NETDEV] = "netdev",
[NFPROTO_BRIDGE] = "bridge",
[NFPROTO_IPV6] = "ip6",
};

static const char * const inethook2name[] = {
[NF_INET_PRE_ROUTING] = "prerouting",
[NF_INET_LOCAL_IN] = "input",
[NF_INET_FORWARD] = "forward",
[NF_INET_LOCAL_OUT] = "output",
[NF_INET_POST_ROUTING] = "postrouting",
};

static const char * const arphook2name[] = {
[NF_ARP_IN] = "input",
[NF_ARP_OUT] = "output",
};

void netfilter_dump_plain(const struct bpf_link_info *info)
{
const char *hookname = NULL, *pfname = NULL;
unsigned int hook = info->netfilter.hooknum;
unsigned int pf = info->netfilter.pf;

if (pf < ARRAY_SIZE(pf2name))
pfname = pf2name[pf];

switch (pf) {
case NFPROTO_BRIDGE: /* bridge shares numbers with enum nf_inet_hooks */
case NFPROTO_IPV4:
case NFPROTO_IPV6:
case NFPROTO_INET:
if (hook < ARRAY_SIZE(inethook2name))
hookname = inethook2name[hook];
break;
case NFPROTO_ARP:
if (hook < ARRAY_SIZE(arphook2name))
hookname = arphook2name[hook];
default:
break;
}

if (pfname)
printf("\n\t%s", pfname);
else
printf("\n\tpf: %d", pf);

if (hookname)
printf(" %s", hookname);
else
printf(", hook %u,", hook);

printf(" prio %d", info->netfilter.priority);

if (info->netfilter.flags)
printf(" flags 0x%x", info->netfilter.flags);
}

static int show_link_close_plain(int fd, struct bpf_link_info *info)
{
struct bpf_prog_info prog_info;
Expand Down Expand Up @@ -301,6 +381,9 @@ static int show_link_close_plain(int fd, struct bpf_link_info *info)
printf("\n\tnetns_ino %u ", info->netns.netns_ino);
show_link_attach_type_plain(info->netns.attach_type);
break;
case BPF_LINK_TYPE_NETFILTER:
netfilter_dump_plain(info);
break;
default:
break;
}
Expand Down
3 changes: 3 additions & 0 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,4 +267,7 @@ static inline bool hashmap__empty(struct hashmap *map)
int pathname_concat(char *buf, int buf_sz, const char *path,
const char *name);

/* print netfilter bpf_link info */
void netfilter_dump_plain(const struct bpf_link_info *info);
void netfilter_dump_json(const struct bpf_link_info *info, json_writer_t *wtr);
#endif
106 changes: 106 additions & 0 deletions src/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,108 @@ static int do_detach(int argc, char **argv)
return 0;
}

static int netfilter_link_compar(const void *a, const void *b)
{
const struct bpf_link_info *nfa = a;
const struct bpf_link_info *nfb = b;
int delta;

delta = nfa->netfilter.pf - nfb->netfilter.pf;
if (delta)
return delta;

delta = nfa->netfilter.hooknum - nfb->netfilter.hooknum;
if (delta)
return delta;

if (nfa->netfilter.priority < nfb->netfilter.priority)
return -1;
if (nfa->netfilter.priority > nfb->netfilter.priority)
return 1;

return nfa->netfilter.flags - nfb->netfilter.flags;
}

static void show_link_netfilter(void)
{
unsigned int nf_link_len = 0, nf_link_count = 0;
struct bpf_link_info *nf_link_info = NULL;
__u32 id = 0;

while (true) {
struct bpf_link_info info;
int fd, err;
__u32 len;

err = bpf_link_get_next_id(id, &id);
if (err) {
if (errno == ENOENT)
break;
p_err("can't get next link: %s (id %d)", strerror(errno), id);
break;
}

fd = bpf_link_get_fd_by_id(id);
if (fd < 0) {
p_err("can't get link by id (%u): %s", id, strerror(errno));
continue;
}

memset(&info, 0, sizeof(info));
len = sizeof(info);

err = bpf_link_get_info_by_fd(fd, &info, &len);

close(fd);

if (err) {
p_err("can't get link info for fd %d: %s", fd, strerror(errno));
continue;
}

if (info.type != BPF_LINK_TYPE_NETFILTER)
continue;

if (nf_link_count >= nf_link_len) {
static const unsigned int max_link_count = INT_MAX / sizeof(info);
struct bpf_link_info *expand;

if (nf_link_count > max_link_count) {
p_err("cannot handle more than %u links\n", max_link_count);
break;
}

nf_link_len += 16;

expand = realloc(nf_link_info, nf_link_len * sizeof(info));
if (!expand) {
p_err("realloc: %s", strerror(errno));
break;
}

nf_link_info = expand;
}

nf_link_info[nf_link_count] = info;
nf_link_count++;
}

qsort(nf_link_info, nf_link_count, sizeof(*nf_link_info), netfilter_link_compar);

for (id = 0; id < nf_link_count; id++) {
NET_START_OBJECT;
if (json_output)
netfilter_dump_json(&nf_link_info[id], json_wtr);
else
netfilter_dump_plain(&nf_link_info[id]);

NET_DUMP_UINT("id", " prog_id %u", nf_link_info[id].prog_id);
NET_END_OBJECT;
}

free(nf_link_info);
}

static int do_show(int argc, char **argv)
{
struct bpf_attach_info attach_info = {};
Expand Down Expand Up @@ -701,6 +803,10 @@ static int do_show(int argc, char **argv)
NET_DUMP_UINT("id", "id %u", attach_info.flow_dissector_id);
NET_END_ARRAY("\n");

NET_START_ARRAY("netfilter", "%s:\n");
show_link_netfilter();
NET_END_ARRAY("\n");

NET_END_OBJECT;
if (json_output)
jsonw_end_array(json_wtr);
Expand Down

0 comments on commit 103f441

Please sign in to comment.