root/tools/bpf/bpftool/netlink_dumper.c
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
// Copyright (C) 2018 Facebook

#include <stdlib.h>
#include <string.h>
#include <bpf/libbpf.h>
#include <linux/rtnetlink.h>
#include <linux/tc_act/tc_bpf.h>

#include "bpf/nlattr.h"
#include "main.h"
#include "netlink_dumper.h"

static void xdp_dump_prog_id(struct nlattr **tb, int attr,
                             const char *mode,
                             bool new_json_object)
{
        if (!tb[attr])
                return;

        if (new_json_object)
                NET_START_OBJECT
        NET_DUMP_STR("mode", " %s", mode);
        NET_DUMP_UINT("id", " id %u", libbpf_nla_getattr_u32(tb[attr]))
        if (new_json_object)
                NET_END_OBJECT
}

static int do_xdp_dump_one(struct nlattr *attr, unsigned int ifindex,
                           const char *name)
{
        struct nlattr *tb[IFLA_XDP_MAX + 1];
        unsigned char mode;

        if (libbpf_nla_parse_nested(tb, IFLA_XDP_MAX, attr, NULL) < 0)
                return -1;

        if (!tb[IFLA_XDP_ATTACHED])
                return 0;

        mode = libbpf_nla_getattr_u8(tb[IFLA_XDP_ATTACHED]);
        if (mode == XDP_ATTACHED_NONE)
                return 0;

        NET_START_OBJECT;
        if (name)
                NET_DUMP_STR("devname", "%s", name);
        NET_DUMP_UINT("ifindex", "(%u)", ifindex);

        if (mode == XDP_ATTACHED_MULTI) {
                if (json_output) {
                        jsonw_name(json_wtr, "multi_attachments");
                        jsonw_start_array(json_wtr);
                }
                xdp_dump_prog_id(tb, IFLA_XDP_SKB_PROG_ID, "generic", true);
                xdp_dump_prog_id(tb, IFLA_XDP_DRV_PROG_ID, "driver", true);
                xdp_dump_prog_id(tb, IFLA_XDP_HW_PROG_ID, "offload", true);
                if (json_output)
                        jsonw_end_array(json_wtr);
        } else if (mode == XDP_ATTACHED_DRV) {
                xdp_dump_prog_id(tb, IFLA_XDP_PROG_ID, "driver", false);
        } else if (mode == XDP_ATTACHED_SKB) {
                xdp_dump_prog_id(tb, IFLA_XDP_PROG_ID, "generic", false);
        } else if (mode == XDP_ATTACHED_HW) {
                xdp_dump_prog_id(tb, IFLA_XDP_PROG_ID, "offload", false);
        }

        NET_END_OBJECT_FINAL;
        return 0;
}

int do_xdp_dump(struct ifinfomsg *ifinfo, struct nlattr **tb)
{
        if (!tb[IFLA_XDP])
                return 0;

        return do_xdp_dump_one(tb[IFLA_XDP], (unsigned int)ifinfo->ifi_index,
                               libbpf_nla_getattr_str(tb[IFLA_IFNAME]));
}

static int do_bpf_dump_one_act(struct nlattr *attr)
{
        struct nlattr *tb[TCA_ACT_BPF_MAX + 1];

        if (libbpf_nla_parse_nested(tb, TCA_ACT_BPF_MAX, attr, NULL) < 0)
                return -LIBBPF_ERRNO__NLPARSE;

        if (!tb[TCA_ACT_BPF_PARMS])
                return -LIBBPF_ERRNO__NLPARSE;

        NET_START_OBJECT_NESTED2;
        if (tb[TCA_ACT_BPF_NAME])
                NET_DUMP_STR("name", "%s",
                             libbpf_nla_getattr_str(tb[TCA_ACT_BPF_NAME]));
        if (tb[TCA_ACT_BPF_ID])
                NET_DUMP_UINT("id", " id %u",
                              libbpf_nla_getattr_u32(tb[TCA_ACT_BPF_ID]));
        NET_END_OBJECT_NESTED;
        return 0;
}

static int do_dump_one_act(struct nlattr *attr)
{
        struct nlattr *tb[TCA_ACT_MAX + 1];

        if (!attr)
                return 0;

        if (libbpf_nla_parse_nested(tb, TCA_ACT_MAX, attr, NULL) < 0)
                return -LIBBPF_ERRNO__NLPARSE;

        if (tb[TCA_ACT_KIND] &&
            strcmp(libbpf_nla_data(tb[TCA_ACT_KIND]), "bpf") == 0)
                return do_bpf_dump_one_act(tb[TCA_ACT_OPTIONS]);

        return 0;
}

static int do_bpf_act_dump(struct nlattr *attr)
{
        struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
        int act, ret;

        if (libbpf_nla_parse_nested(tb, TCA_ACT_MAX_PRIO, attr, NULL) < 0)
                return -LIBBPF_ERRNO__NLPARSE;

        NET_START_ARRAY("act", " %s [");
        for (act = 0; act <= TCA_ACT_MAX_PRIO; act++) {
                ret = do_dump_one_act(tb[act]);
                if (ret)
                        break;
        }
        NET_END_ARRAY("] ");

        return ret;
}

static int do_bpf_filter_dump(struct nlattr *attr)
{
        struct nlattr *tb[TCA_BPF_MAX + 1];
        int ret;

        if (libbpf_nla_parse_nested(tb, TCA_BPF_MAX, attr, NULL) < 0)
                return -LIBBPF_ERRNO__NLPARSE;

        if (tb[TCA_BPF_NAME])
                NET_DUMP_STR("name", " %s",
                             libbpf_nla_getattr_str(tb[TCA_BPF_NAME]));
        if (tb[TCA_BPF_ID])
                NET_DUMP_UINT("id", " id %u",
                              libbpf_nla_getattr_u32(tb[TCA_BPF_ID]));
        if (tb[TCA_BPF_ACT]) {
                ret = do_bpf_act_dump(tb[TCA_BPF_ACT]);
                if (ret)
                        return ret;
        }

        return 0;
}

int do_filter_dump(struct tcmsg *info, struct nlattr **tb, const char *kind,
                   const char *devname, int ifindex)
{
        int ret = 0;

        if (tb[TCA_OPTIONS] &&
            strcmp(libbpf_nla_data(tb[TCA_KIND]), "bpf") == 0) {
                NET_START_OBJECT;
                if (devname[0] != '\0')
                        NET_DUMP_STR("devname", "%s", devname);
                NET_DUMP_UINT("ifindex", "(%u)", (unsigned int)ifindex);
                NET_DUMP_STR("kind", " %s", kind);
                ret = do_bpf_filter_dump(tb[TCA_OPTIONS]);
                NET_END_OBJECT_FINAL;
        }

        return ret;
}