This is a followup patch for Commit f6f3bac08ff9 ("tools/bpf: bpftool: add net support"). Some improvements are made for the bpftool net output. Specially, plain output is more concise such that per attachment should nicely fit in one line. Compared to previous output, the prog tag is removed since it can be easily obtained with program id. Similar to xdp attachments, the device name is added to tc attachments. The bpf program attached through shared block mechanism is supported as well. $ ip link add dev v1 type veth peer name v2 $ tc qdisc add dev v1 ingress_block 10 egress_block 20 clsact $ tc qdisc add dev v2 ingress_block 10 egress_block 20 clsact $ tc filter add block 10 protocol ip prio 25 bpf obj bpf_shared.o sec ingress flowid 1:1 $ tc filter add block 20 protocol ip prio 30 bpf obj bpf_cyclic.o sec classifier flowid 1:1 $ bpftool net xdp: tc: v2(7) clsact/ingress bpf_shared.o:[ingress] id 23 v2(7) clsact/egress bpf_cyclic.o:[classifier] id 24 v1(8) clsact/ingress bpf_shared.o:[ingress] id 23 v1(8) clsact/egress bpf_cyclic.o:[classifier] id 24 The documentation and "bpftool net help" are updated to make it clear that current implementation only supports xdp and tc attachments. For programs attached to cgroups, "bpftool cgroup" can be used to dump attachments. For other programs e.g. sk_{filter,skb,msg,reuseport} and lwt/seg6, iproute2 tools should be used. The new output: $ bpftool net xdp: eth0(2) driver id 198 tc: eth0(2) clsact/ingress fbflow_icmp id 335 act [{icmp_action id 336}] eth0(2) clsact/egress fbflow_egress id 334 $ bpftool -jp net [{ "xdp": [{ "devname": "eth0", "ifindex": 2, "mode": "driver", "id": 198 } ], "tc": [{ "devname": "eth0", "ifindex": 2, "kind": "clsact/ingress", "name": "fbflow_icmp", "id": 335, "act": [{ "name": "icmp_action", "id": 336 } ] },{ "devname": "eth0", "ifindex": 2, "kind": "clsact/egress", "name": "fbflow_egress", "id": 334 } ] } ] Signed-off-by: Yonghong Song <yhs@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
175 lines
4.1 KiB
C
175 lines
4.1 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
// Copyright (C) 2018 Facebook
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <libbpf.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <linux/tc_act/tc_bpf.h>
|
|
|
|
#include <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", 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 (nla_parse_nested(tb, IFLA_XDP_MAX, attr, NULL) < 0)
|
|
return -1;
|
|
|
|
if (!tb[IFLA_XDP_ATTACHED])
|
|
return 0;
|
|
|
|
mode = 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", "(%d)", 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], ifinfo->ifi_index,
|
|
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 (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",
|
|
nla_getattr_str(tb[TCA_ACT_BPF_NAME]));
|
|
if (tb[TCA_ACT_BPF_ID])
|
|
NET_DUMP_UINT("id", " id %u",
|
|
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 (nla_parse_nested(tb, TCA_ACT_MAX, attr, NULL) < 0)
|
|
return -LIBBPF_ERRNO__NLPARSE;
|
|
|
|
if (tb[TCA_ACT_KIND] && strcmp(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 (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 (nla_parse_nested(tb, TCA_BPF_MAX, attr, NULL) < 0)
|
|
return -LIBBPF_ERRNO__NLPARSE;
|
|
|
|
if (tb[TCA_BPF_NAME])
|
|
NET_DUMP_STR("name", " %s", nla_getattr_str(tb[TCA_BPF_NAME]));
|
|
if (tb[TCA_BPF_ID])
|
|
NET_DUMP_UINT("id", " id %u", 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(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)", ifindex);
|
|
NET_DUMP_STR("kind", " %s", kind);
|
|
ret = do_bpf_filter_dump(tb[TCA_OPTIONS]);
|
|
NET_END_OBJECT_FINAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|