tools: bpftool: print netfilter link info
authorFlorian Westphal <fw@strlen.de>
Fri, 21 Apr 2023 17:02:58 +0000 (19:02 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 21 Apr 2023 18:34:49 +0000 (11:34 -0700)
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>
tools/bpf/bpftool/link.c
tools/bpf/bpftool/main.h
tools/bpf/bpftool/net.c
tools/include/uapi/linux/bpf.h
tools/lib/bpf/libbpf.c
tools/lib/bpf/libbpf_probes.c

index f985b79..d98dbc5 100644 (file)
@@ -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>
@@ -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);
@@ -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;
        }
@@ -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;
@@ -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;
        }
index 9a95788..a49534d 100644 (file)
@@ -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
index c40e44c..26a4996 100644 (file)
@@ -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 = {};
@@ -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);
index 4b20a72..1bb11a6 100644 (file)
@@ -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 {
@@ -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,
 };
@@ -1560,6 +1562,12 @@ union bpf_attr {
                                 */
                                __u64           cookie;
                        } tracing;
+                       struct {
+                               __u32           pf;
+                               __u32           hooknum;
+                               __s32           priority;
+                               __u32           flags;
+                       } netfilter;
                };
        } link_create;
 
@@ -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)));
 
index 2600d83..1cbacf9 100644 (file)
@@ -130,6 +130,7 @@ static const char * const link_type_name[] = {
        [BPF_LINK_TYPE_PERF_EVENT]              = "perf_event",
        [BPF_LINK_TYPE_KPROBE_MULTI]            = "kprobe_multi",
        [BPF_LINK_TYPE_STRUCT_OPS]              = "struct_ops",
+       [BPF_LINK_TYPE_NETFILTER]               = "netfilter",
 };
 
 static const char * const map_type_name[] = {
@@ -201,6 +202,7 @@ static const char * const prog_type_name[] = {
        [BPF_PROG_TYPE_LSM]                     = "lsm",
        [BPF_PROG_TYPE_SK_LOOKUP]               = "sk_lookup",
        [BPF_PROG_TYPE_SYSCALL]                 = "syscall",
+       [BPF_PROG_TYPE_NETFILTER]               = "netfilter",
 };
 
 static int __base_pr(enum libbpf_print_level level, const char *format,
@@ -8710,6 +8712,7 @@ static const struct bpf_sec_def section_defs[] = {
        SEC_DEF("struct_ops+",          STRUCT_OPS, 0, SEC_NONE),
        SEC_DEF("struct_ops.s+",        STRUCT_OPS, 0, SEC_SLEEPABLE),
        SEC_DEF("sk_lookup",            SK_LOOKUP, BPF_SK_LOOKUP, SEC_ATTACHABLE),
+       SEC_DEF("netfilter",            NETFILTER, 0, SEC_NONE),
 };
 
 static size_t custom_sec_def_cnt;
index 4f3bc96..6065f40 100644 (file)
@@ -180,6 +180,7 @@ static int probe_prog_load(enum bpf_prog_type prog_type,
        case BPF_PROG_TYPE_SK_REUSEPORT:
        case BPF_PROG_TYPE_FLOW_DISSECTOR:
        case BPF_PROG_TYPE_CGROUP_SYSCTL:
+       case BPF_PROG_TYPE_NETFILTER:
                break;
        default:
                return -EOPNOTSUPP;