162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
262306a36Sopenharmony_ci// Copyright (C) 2018 Facebook
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#ifndef _GNU_SOURCE
562306a36Sopenharmony_ci#define _GNU_SOURCE
662306a36Sopenharmony_ci#endif
762306a36Sopenharmony_ci#include <errno.h>
862306a36Sopenharmony_ci#include <fcntl.h>
962306a36Sopenharmony_ci#include <stdlib.h>
1062306a36Sopenharmony_ci#include <string.h>
1162306a36Sopenharmony_ci#include <time.h>
1262306a36Sopenharmony_ci#include <unistd.h>
1362306a36Sopenharmony_ci#include <bpf/bpf.h>
1462306a36Sopenharmony_ci#include <bpf/libbpf.h>
1562306a36Sopenharmony_ci#include <net/if.h>
1662306a36Sopenharmony_ci#include <linux/rtnetlink.h>
1762306a36Sopenharmony_ci#include <linux/socket.h>
1862306a36Sopenharmony_ci#include <linux/tc_act/tc_bpf.h>
1962306a36Sopenharmony_ci#include <sys/socket.h>
2062306a36Sopenharmony_ci#include <sys/stat.h>
2162306a36Sopenharmony_ci#include <sys/types.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "bpf/nlattr.h"
2462306a36Sopenharmony_ci#include "main.h"
2562306a36Sopenharmony_ci#include "netlink_dumper.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#ifndef SOL_NETLINK
2862306a36Sopenharmony_ci#define SOL_NETLINK 270
2962306a36Sopenharmony_ci#endif
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistruct ip_devname_ifindex {
3262306a36Sopenharmony_ci	char	devname[64];
3362306a36Sopenharmony_ci	int	ifindex;
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct bpf_netdev_t {
3762306a36Sopenharmony_ci	struct ip_devname_ifindex *devices;
3862306a36Sopenharmony_ci	int	used_len;
3962306a36Sopenharmony_ci	int	array_len;
4062306a36Sopenharmony_ci	int	filter_idx;
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistruct tc_kind_handle {
4462306a36Sopenharmony_ci	char	kind[64];
4562306a36Sopenharmony_ci	int	handle;
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistruct bpf_tcinfo_t {
4962306a36Sopenharmony_ci	struct tc_kind_handle	*handle_array;
5062306a36Sopenharmony_ci	int			used_len;
5162306a36Sopenharmony_ci	int			array_len;
5262306a36Sopenharmony_ci	bool			is_qdisc;
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistruct bpf_filter_t {
5662306a36Sopenharmony_ci	const char	*kind;
5762306a36Sopenharmony_ci	const char	*devname;
5862306a36Sopenharmony_ci	int		ifindex;
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistruct bpf_attach_info {
6262306a36Sopenharmony_ci	__u32 flow_dissector_id;
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cienum net_attach_type {
6662306a36Sopenharmony_ci	NET_ATTACH_TYPE_XDP,
6762306a36Sopenharmony_ci	NET_ATTACH_TYPE_XDP_GENERIC,
6862306a36Sopenharmony_ci	NET_ATTACH_TYPE_XDP_DRIVER,
6962306a36Sopenharmony_ci	NET_ATTACH_TYPE_XDP_OFFLOAD,
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic const char * const attach_type_strings[] = {
7362306a36Sopenharmony_ci	[NET_ATTACH_TYPE_XDP]		= "xdp",
7462306a36Sopenharmony_ci	[NET_ATTACH_TYPE_XDP_GENERIC]	= "xdpgeneric",
7562306a36Sopenharmony_ci	[NET_ATTACH_TYPE_XDP_DRIVER]	= "xdpdrv",
7662306a36Sopenharmony_ci	[NET_ATTACH_TYPE_XDP_OFFLOAD]	= "xdpoffload",
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic const char * const attach_loc_strings[] = {
8062306a36Sopenharmony_ci	[BPF_TCX_INGRESS]		= "tcx/ingress",
8162306a36Sopenharmony_ci	[BPF_TCX_EGRESS]		= "tcx/egress",
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ciconst size_t net_attach_type_size = ARRAY_SIZE(attach_type_strings);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic enum net_attach_type parse_attach_type(const char *str)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	enum net_attach_type type;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	for (type = 0; type < net_attach_type_size; type++) {
9162306a36Sopenharmony_ci		if (attach_type_strings[type] &&
9262306a36Sopenharmony_ci		    is_prefix(str, attach_type_strings[type]))
9362306a36Sopenharmony_ci			return type;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return net_attach_type_size;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_citypedef int (*dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_citypedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, dump_nlmsg_t, void *cookie);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic int netlink_open(__u32 *nl_pid)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct sockaddr_nl sa;
10662306a36Sopenharmony_ci	socklen_t addrlen;
10762306a36Sopenharmony_ci	int one = 1, ret;
10862306a36Sopenharmony_ci	int sock;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	memset(&sa, 0, sizeof(sa));
11162306a36Sopenharmony_ci	sa.nl_family = AF_NETLINK;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
11462306a36Sopenharmony_ci	if (sock < 0)
11562306a36Sopenharmony_ci		return -errno;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK,
11862306a36Sopenharmony_ci		       &one, sizeof(one)) < 0) {
11962306a36Sopenharmony_ci		p_err("Netlink error reporting not supported");
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
12362306a36Sopenharmony_ci		ret = -errno;
12462306a36Sopenharmony_ci		goto cleanup;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	addrlen = sizeof(sa);
12862306a36Sopenharmony_ci	if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) {
12962306a36Sopenharmony_ci		ret = -errno;
13062306a36Sopenharmony_ci		goto cleanup;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (addrlen != sizeof(sa)) {
13462306a36Sopenharmony_ci		ret = -LIBBPF_ERRNO__INTERNAL;
13562306a36Sopenharmony_ci		goto cleanup;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	*nl_pid = sa.nl_pid;
13962306a36Sopenharmony_ci	return sock;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cicleanup:
14262306a36Sopenharmony_ci	close(sock);
14362306a36Sopenharmony_ci	return ret;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic int netlink_recv(int sock, __u32 nl_pid, __u32 seq,
14762306a36Sopenharmony_ci			    __dump_nlmsg_t _fn, dump_nlmsg_t fn,
14862306a36Sopenharmony_ci			    void *cookie)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	bool multipart = true;
15162306a36Sopenharmony_ci	struct nlmsgerr *err;
15262306a36Sopenharmony_ci	struct nlmsghdr *nh;
15362306a36Sopenharmony_ci	char buf[4096];
15462306a36Sopenharmony_ci	int len, ret;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	while (multipart) {
15762306a36Sopenharmony_ci		multipart = false;
15862306a36Sopenharmony_ci		len = recv(sock, buf, sizeof(buf), 0);
15962306a36Sopenharmony_ci		if (len < 0) {
16062306a36Sopenharmony_ci			ret = -errno;
16162306a36Sopenharmony_ci			goto done;
16262306a36Sopenharmony_ci		}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		if (len == 0)
16562306a36Sopenharmony_ci			break;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, (unsigned int)len);
16862306a36Sopenharmony_ci		     nh = NLMSG_NEXT(nh, len)) {
16962306a36Sopenharmony_ci			if (nh->nlmsg_pid != nl_pid) {
17062306a36Sopenharmony_ci				ret = -LIBBPF_ERRNO__WRNGPID;
17162306a36Sopenharmony_ci				goto done;
17262306a36Sopenharmony_ci			}
17362306a36Sopenharmony_ci			if (nh->nlmsg_seq != seq) {
17462306a36Sopenharmony_ci				ret = -LIBBPF_ERRNO__INVSEQ;
17562306a36Sopenharmony_ci				goto done;
17662306a36Sopenharmony_ci			}
17762306a36Sopenharmony_ci			if (nh->nlmsg_flags & NLM_F_MULTI)
17862306a36Sopenharmony_ci				multipart = true;
17962306a36Sopenharmony_ci			switch (nh->nlmsg_type) {
18062306a36Sopenharmony_ci			case NLMSG_ERROR:
18162306a36Sopenharmony_ci				err = (struct nlmsgerr *)NLMSG_DATA(nh);
18262306a36Sopenharmony_ci				if (!err->error)
18362306a36Sopenharmony_ci					continue;
18462306a36Sopenharmony_ci				ret = err->error;
18562306a36Sopenharmony_ci				libbpf_nla_dump_errormsg(nh);
18662306a36Sopenharmony_ci				goto done;
18762306a36Sopenharmony_ci			case NLMSG_DONE:
18862306a36Sopenharmony_ci				return 0;
18962306a36Sopenharmony_ci			default:
19062306a36Sopenharmony_ci				break;
19162306a36Sopenharmony_ci			}
19262306a36Sopenharmony_ci			if (_fn) {
19362306a36Sopenharmony_ci				ret = _fn(nh, fn, cookie);
19462306a36Sopenharmony_ci				if (ret)
19562306a36Sopenharmony_ci					return ret;
19662306a36Sopenharmony_ci			}
19762306a36Sopenharmony_ci		}
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci	ret = 0;
20062306a36Sopenharmony_cidone:
20162306a36Sopenharmony_ci	return ret;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic int __dump_class_nlmsg(struct nlmsghdr *nlh,
20562306a36Sopenharmony_ci			      dump_nlmsg_t dump_class_nlmsg,
20662306a36Sopenharmony_ci			      void *cookie)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct nlattr *tb[TCA_MAX + 1], *attr;
20962306a36Sopenharmony_ci	struct tcmsg *t = NLMSG_DATA(nlh);
21062306a36Sopenharmony_ci	int len;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
21362306a36Sopenharmony_ci	attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
21462306a36Sopenharmony_ci	if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
21562306a36Sopenharmony_ci		return -LIBBPF_ERRNO__NLPARSE;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	return dump_class_nlmsg(cookie, t, tb);
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic int netlink_get_class(int sock, unsigned int nl_pid, int ifindex,
22162306a36Sopenharmony_ci			     dump_nlmsg_t dump_class_nlmsg, void *cookie)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	struct {
22462306a36Sopenharmony_ci		struct nlmsghdr nlh;
22562306a36Sopenharmony_ci		struct tcmsg t;
22662306a36Sopenharmony_ci	} req = {
22762306a36Sopenharmony_ci		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
22862306a36Sopenharmony_ci		.nlh.nlmsg_type = RTM_GETTCLASS,
22962306a36Sopenharmony_ci		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
23062306a36Sopenharmony_ci		.t.tcm_family = AF_UNSPEC,
23162306a36Sopenharmony_ci		.t.tcm_ifindex = ifindex,
23262306a36Sopenharmony_ci	};
23362306a36Sopenharmony_ci	int seq = time(NULL);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	req.nlh.nlmsg_seq = seq;
23662306a36Sopenharmony_ci	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
23762306a36Sopenharmony_ci		return -errno;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	return netlink_recv(sock, nl_pid, seq, __dump_class_nlmsg,
24062306a36Sopenharmony_ci			    dump_class_nlmsg, cookie);
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic int __dump_qdisc_nlmsg(struct nlmsghdr *nlh,
24462306a36Sopenharmony_ci			      dump_nlmsg_t dump_qdisc_nlmsg,
24562306a36Sopenharmony_ci			      void *cookie)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct nlattr *tb[TCA_MAX + 1], *attr;
24862306a36Sopenharmony_ci	struct tcmsg *t = NLMSG_DATA(nlh);
24962306a36Sopenharmony_ci	int len;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
25262306a36Sopenharmony_ci	attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
25362306a36Sopenharmony_ci	if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
25462306a36Sopenharmony_ci		return -LIBBPF_ERRNO__NLPARSE;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return dump_qdisc_nlmsg(cookie, t, tb);
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic int netlink_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
26062306a36Sopenharmony_ci			     dump_nlmsg_t dump_qdisc_nlmsg, void *cookie)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct {
26362306a36Sopenharmony_ci		struct nlmsghdr nlh;
26462306a36Sopenharmony_ci		struct tcmsg t;
26562306a36Sopenharmony_ci	} req = {
26662306a36Sopenharmony_ci		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
26762306a36Sopenharmony_ci		.nlh.nlmsg_type = RTM_GETQDISC,
26862306a36Sopenharmony_ci		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
26962306a36Sopenharmony_ci		.t.tcm_family = AF_UNSPEC,
27062306a36Sopenharmony_ci		.t.tcm_ifindex = ifindex,
27162306a36Sopenharmony_ci	};
27262306a36Sopenharmony_ci	int seq = time(NULL);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	req.nlh.nlmsg_seq = seq;
27562306a36Sopenharmony_ci	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
27662306a36Sopenharmony_ci		return -errno;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	return netlink_recv(sock, nl_pid, seq, __dump_qdisc_nlmsg,
27962306a36Sopenharmony_ci			    dump_qdisc_nlmsg, cookie);
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic int __dump_filter_nlmsg(struct nlmsghdr *nlh,
28362306a36Sopenharmony_ci			       dump_nlmsg_t dump_filter_nlmsg,
28462306a36Sopenharmony_ci			       void *cookie)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct nlattr *tb[TCA_MAX + 1], *attr;
28762306a36Sopenharmony_ci	struct tcmsg *t = NLMSG_DATA(nlh);
28862306a36Sopenharmony_ci	int len;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
29162306a36Sopenharmony_ci	attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
29262306a36Sopenharmony_ci	if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
29362306a36Sopenharmony_ci		return -LIBBPF_ERRNO__NLPARSE;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return dump_filter_nlmsg(cookie, t, tb);
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic int netlink_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
29962306a36Sopenharmony_ci			      dump_nlmsg_t dump_filter_nlmsg, void *cookie)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct {
30262306a36Sopenharmony_ci		struct nlmsghdr nlh;
30362306a36Sopenharmony_ci		struct tcmsg t;
30462306a36Sopenharmony_ci	} req = {
30562306a36Sopenharmony_ci		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
30662306a36Sopenharmony_ci		.nlh.nlmsg_type = RTM_GETTFILTER,
30762306a36Sopenharmony_ci		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
30862306a36Sopenharmony_ci		.t.tcm_family = AF_UNSPEC,
30962306a36Sopenharmony_ci		.t.tcm_ifindex = ifindex,
31062306a36Sopenharmony_ci		.t.tcm_parent = handle,
31162306a36Sopenharmony_ci	};
31262306a36Sopenharmony_ci	int seq = time(NULL);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	req.nlh.nlmsg_seq = seq;
31562306a36Sopenharmony_ci	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
31662306a36Sopenharmony_ci		return -errno;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return netlink_recv(sock, nl_pid, seq, __dump_filter_nlmsg,
31962306a36Sopenharmony_ci			    dump_filter_nlmsg, cookie);
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic int __dump_link_nlmsg(struct nlmsghdr *nlh,
32362306a36Sopenharmony_ci			     dump_nlmsg_t dump_link_nlmsg, void *cookie)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	struct nlattr *tb[IFLA_MAX + 1], *attr;
32662306a36Sopenharmony_ci	struct ifinfomsg *ifi = NLMSG_DATA(nlh);
32762306a36Sopenharmony_ci	int len;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
33062306a36Sopenharmony_ci	attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
33162306a36Sopenharmony_ci	if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
33262306a36Sopenharmony_ci		return -LIBBPF_ERRNO__NLPARSE;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return dump_link_nlmsg(cookie, ifi, tb);
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic int netlink_get_link(int sock, unsigned int nl_pid,
33862306a36Sopenharmony_ci			    dump_nlmsg_t dump_link_nlmsg, void *cookie)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	struct {
34162306a36Sopenharmony_ci		struct nlmsghdr nlh;
34262306a36Sopenharmony_ci		struct ifinfomsg ifm;
34362306a36Sopenharmony_ci	} req = {
34462306a36Sopenharmony_ci		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
34562306a36Sopenharmony_ci		.nlh.nlmsg_type = RTM_GETLINK,
34662306a36Sopenharmony_ci		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
34762306a36Sopenharmony_ci		.ifm.ifi_family = AF_PACKET,
34862306a36Sopenharmony_ci	};
34962306a36Sopenharmony_ci	int seq = time(NULL);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	req.nlh.nlmsg_seq = seq;
35262306a36Sopenharmony_ci	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
35362306a36Sopenharmony_ci		return -errno;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg,
35662306a36Sopenharmony_ci			    dump_link_nlmsg, cookie);
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	struct bpf_netdev_t *netinfo = cookie;
36262306a36Sopenharmony_ci	struct ifinfomsg *ifinfo = msg;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (netinfo->filter_idx > 0 && netinfo->filter_idx != ifinfo->ifi_index)
36562306a36Sopenharmony_ci		return 0;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (netinfo->used_len == netinfo->array_len) {
36862306a36Sopenharmony_ci		netinfo->devices = realloc(netinfo->devices,
36962306a36Sopenharmony_ci			(netinfo->array_len + 16) *
37062306a36Sopenharmony_ci			sizeof(struct ip_devname_ifindex));
37162306a36Sopenharmony_ci		if (!netinfo->devices)
37262306a36Sopenharmony_ci			return -ENOMEM;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci		netinfo->array_len += 16;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci	netinfo->devices[netinfo->used_len].ifindex = ifinfo->ifi_index;
37762306a36Sopenharmony_ci	snprintf(netinfo->devices[netinfo->used_len].devname,
37862306a36Sopenharmony_ci		 sizeof(netinfo->devices[netinfo->used_len].devname),
37962306a36Sopenharmony_ci		 "%s",
38062306a36Sopenharmony_ci		 tb[IFLA_IFNAME]
38162306a36Sopenharmony_ci			 ? libbpf_nla_getattr_str(tb[IFLA_IFNAME])
38262306a36Sopenharmony_ci			 : "");
38362306a36Sopenharmony_ci	netinfo->used_len++;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	return do_xdp_dump(ifinfo, tb);
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic int dump_class_qdisc_nlmsg(void *cookie, void *msg, struct nlattr **tb)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct bpf_tcinfo_t *tcinfo = cookie;
39162306a36Sopenharmony_ci	struct tcmsg *info = msg;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (tcinfo->is_qdisc) {
39462306a36Sopenharmony_ci		/* skip clsact qdisc */
39562306a36Sopenharmony_ci		if (tb[TCA_KIND] &&
39662306a36Sopenharmony_ci		    strcmp(libbpf_nla_data(tb[TCA_KIND]), "clsact") == 0)
39762306a36Sopenharmony_ci			return 0;
39862306a36Sopenharmony_ci		if (info->tcm_handle == 0)
39962306a36Sopenharmony_ci			return 0;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (tcinfo->used_len == tcinfo->array_len) {
40362306a36Sopenharmony_ci		tcinfo->handle_array = realloc(tcinfo->handle_array,
40462306a36Sopenharmony_ci			(tcinfo->array_len + 16) * sizeof(struct tc_kind_handle));
40562306a36Sopenharmony_ci		if (!tcinfo->handle_array)
40662306a36Sopenharmony_ci			return -ENOMEM;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		tcinfo->array_len += 16;
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci	tcinfo->handle_array[tcinfo->used_len].handle = info->tcm_handle;
41162306a36Sopenharmony_ci	snprintf(tcinfo->handle_array[tcinfo->used_len].kind,
41262306a36Sopenharmony_ci		 sizeof(tcinfo->handle_array[tcinfo->used_len].kind),
41362306a36Sopenharmony_ci		 "%s",
41462306a36Sopenharmony_ci		 tb[TCA_KIND]
41562306a36Sopenharmony_ci			 ? libbpf_nla_getattr_str(tb[TCA_KIND])
41662306a36Sopenharmony_ci			 : "unknown");
41762306a36Sopenharmony_ci	tcinfo->used_len++;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	return 0;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic int dump_filter_nlmsg(void *cookie, void *msg, struct nlattr **tb)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	const struct bpf_filter_t *filter_info = cookie;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	return do_filter_dump((struct tcmsg *)msg, tb, filter_info->kind,
42762306a36Sopenharmony_ci			      filter_info->devname, filter_info->ifindex);
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic int __show_dev_tc_bpf_name(__u32 id, char *name, size_t len)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	struct bpf_prog_info info = {};
43362306a36Sopenharmony_ci	__u32 ilen = sizeof(info);
43462306a36Sopenharmony_ci	int fd, ret;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	fd = bpf_prog_get_fd_by_id(id);
43762306a36Sopenharmony_ci	if (fd < 0)
43862306a36Sopenharmony_ci		return fd;
43962306a36Sopenharmony_ci	ret = bpf_obj_get_info_by_fd(fd, &info, &ilen);
44062306a36Sopenharmony_ci	if (ret < 0)
44162306a36Sopenharmony_ci		goto out;
44262306a36Sopenharmony_ci	ret = -ENOENT;
44362306a36Sopenharmony_ci	if (info.name[0]) {
44462306a36Sopenharmony_ci		get_prog_full_name(&info, fd, name, len);
44562306a36Sopenharmony_ci		ret = 0;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ciout:
44862306a36Sopenharmony_ci	close(fd);
44962306a36Sopenharmony_ci	return ret;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic void __show_dev_tc_bpf(const struct ip_devname_ifindex *dev,
45362306a36Sopenharmony_ci			      const enum bpf_attach_type loc)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	__u32 prog_flags[64] = {}, link_flags[64] = {}, i, j;
45662306a36Sopenharmony_ci	__u32 prog_ids[64] = {}, link_ids[64] = {};
45762306a36Sopenharmony_ci	LIBBPF_OPTS(bpf_prog_query_opts, optq);
45862306a36Sopenharmony_ci	char prog_name[MAX_PROG_FULL_NAME];
45962306a36Sopenharmony_ci	int ret;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	optq.prog_ids = prog_ids;
46262306a36Sopenharmony_ci	optq.prog_attach_flags = prog_flags;
46362306a36Sopenharmony_ci	optq.link_ids = link_ids;
46462306a36Sopenharmony_ci	optq.link_attach_flags = link_flags;
46562306a36Sopenharmony_ci	optq.count = ARRAY_SIZE(prog_ids);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	ret = bpf_prog_query_opts(dev->ifindex, loc, &optq);
46862306a36Sopenharmony_ci	if (ret)
46962306a36Sopenharmony_ci		return;
47062306a36Sopenharmony_ci	for (i = 0; i < optq.count; i++) {
47162306a36Sopenharmony_ci		NET_START_OBJECT;
47262306a36Sopenharmony_ci		NET_DUMP_STR("devname", "%s", dev->devname);
47362306a36Sopenharmony_ci		NET_DUMP_UINT("ifindex", "(%u)", dev->ifindex);
47462306a36Sopenharmony_ci		NET_DUMP_STR("kind", " %s", attach_loc_strings[loc]);
47562306a36Sopenharmony_ci		ret = __show_dev_tc_bpf_name(prog_ids[i], prog_name,
47662306a36Sopenharmony_ci					     sizeof(prog_name));
47762306a36Sopenharmony_ci		if (!ret)
47862306a36Sopenharmony_ci			NET_DUMP_STR("name", " %s", prog_name);
47962306a36Sopenharmony_ci		NET_DUMP_UINT("prog_id", " prog_id %u ", prog_ids[i]);
48062306a36Sopenharmony_ci		if (prog_flags[i] || json_output) {
48162306a36Sopenharmony_ci			NET_START_ARRAY("prog_flags", "%s ");
48262306a36Sopenharmony_ci			for (j = 0; prog_flags[i] && j < 32; j++) {
48362306a36Sopenharmony_ci				if (!(prog_flags[i] & (1 << j)))
48462306a36Sopenharmony_ci					continue;
48562306a36Sopenharmony_ci				NET_DUMP_UINT_ONLY(1 << j);
48662306a36Sopenharmony_ci			}
48762306a36Sopenharmony_ci			NET_END_ARRAY("");
48862306a36Sopenharmony_ci		}
48962306a36Sopenharmony_ci		if (link_ids[i] || json_output) {
49062306a36Sopenharmony_ci			NET_DUMP_UINT("link_id", "link_id %u ", link_ids[i]);
49162306a36Sopenharmony_ci			if (link_flags[i] || json_output) {
49262306a36Sopenharmony_ci				NET_START_ARRAY("link_flags", "%s ");
49362306a36Sopenharmony_ci				for (j = 0; link_flags[i] && j < 32; j++) {
49462306a36Sopenharmony_ci					if (!(link_flags[i] & (1 << j)))
49562306a36Sopenharmony_ci						continue;
49662306a36Sopenharmony_ci					NET_DUMP_UINT_ONLY(1 << j);
49762306a36Sopenharmony_ci				}
49862306a36Sopenharmony_ci				NET_END_ARRAY("");
49962306a36Sopenharmony_ci			}
50062306a36Sopenharmony_ci		}
50162306a36Sopenharmony_ci		NET_END_OBJECT_FINAL;
50262306a36Sopenharmony_ci	}
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic void show_dev_tc_bpf(struct ip_devname_ifindex *dev)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	__show_dev_tc_bpf(dev, BPF_TCX_INGRESS);
50862306a36Sopenharmony_ci	__show_dev_tc_bpf(dev, BPF_TCX_EGRESS);
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic int show_dev_tc_bpf_classic(int sock, unsigned int nl_pid,
51262306a36Sopenharmony_ci				   struct ip_devname_ifindex *dev)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	struct bpf_filter_t filter_info;
51562306a36Sopenharmony_ci	struct bpf_tcinfo_t tcinfo;
51662306a36Sopenharmony_ci	int i, handle, ret = 0;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	tcinfo.handle_array = NULL;
51962306a36Sopenharmony_ci	tcinfo.used_len = 0;
52062306a36Sopenharmony_ci	tcinfo.array_len = 0;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	tcinfo.is_qdisc = false;
52362306a36Sopenharmony_ci	ret = netlink_get_class(sock, nl_pid, dev->ifindex,
52462306a36Sopenharmony_ci				dump_class_qdisc_nlmsg, &tcinfo);
52562306a36Sopenharmony_ci	if (ret)
52662306a36Sopenharmony_ci		goto out;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	tcinfo.is_qdisc = true;
52962306a36Sopenharmony_ci	ret = netlink_get_qdisc(sock, nl_pid, dev->ifindex,
53062306a36Sopenharmony_ci				dump_class_qdisc_nlmsg, &tcinfo);
53162306a36Sopenharmony_ci	if (ret)
53262306a36Sopenharmony_ci		goto out;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	filter_info.devname = dev->devname;
53562306a36Sopenharmony_ci	filter_info.ifindex = dev->ifindex;
53662306a36Sopenharmony_ci	for (i = 0; i < tcinfo.used_len; i++) {
53762306a36Sopenharmony_ci		filter_info.kind = tcinfo.handle_array[i].kind;
53862306a36Sopenharmony_ci		ret = netlink_get_filter(sock, nl_pid, dev->ifindex,
53962306a36Sopenharmony_ci					 tcinfo.handle_array[i].handle,
54062306a36Sopenharmony_ci					 dump_filter_nlmsg, &filter_info);
54162306a36Sopenharmony_ci		if (ret)
54262306a36Sopenharmony_ci			goto out;
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	/* root, ingress and egress handle */
54662306a36Sopenharmony_ci	handle = TC_H_ROOT;
54762306a36Sopenharmony_ci	filter_info.kind = "root";
54862306a36Sopenharmony_ci	ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle,
54962306a36Sopenharmony_ci				 dump_filter_nlmsg, &filter_info);
55062306a36Sopenharmony_ci	if (ret)
55162306a36Sopenharmony_ci		goto out;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
55462306a36Sopenharmony_ci	filter_info.kind = "clsact/ingress";
55562306a36Sopenharmony_ci	ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle,
55662306a36Sopenharmony_ci				 dump_filter_nlmsg, &filter_info);
55762306a36Sopenharmony_ci	if (ret)
55862306a36Sopenharmony_ci		goto out;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS);
56162306a36Sopenharmony_ci	filter_info.kind = "clsact/egress";
56262306a36Sopenharmony_ci	ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle,
56362306a36Sopenharmony_ci				 dump_filter_nlmsg, &filter_info);
56462306a36Sopenharmony_ci	if (ret)
56562306a36Sopenharmony_ci		goto out;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ciout:
56862306a36Sopenharmony_ci	free(tcinfo.handle_array);
56962306a36Sopenharmony_ci	return 0;
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic int query_flow_dissector(struct bpf_attach_info *attach_info)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	__u32 attach_flags;
57562306a36Sopenharmony_ci	__u32 prog_ids[1];
57662306a36Sopenharmony_ci	__u32 prog_cnt;
57762306a36Sopenharmony_ci	int err;
57862306a36Sopenharmony_ci	int fd;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	fd = open("/proc/self/ns/net", O_RDONLY);
58162306a36Sopenharmony_ci	if (fd < 0) {
58262306a36Sopenharmony_ci		p_err("can't open /proc/self/ns/net: %s",
58362306a36Sopenharmony_ci		      strerror(errno));
58462306a36Sopenharmony_ci		return -1;
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci	prog_cnt = ARRAY_SIZE(prog_ids);
58762306a36Sopenharmony_ci	err = bpf_prog_query(fd, BPF_FLOW_DISSECTOR, 0,
58862306a36Sopenharmony_ci			     &attach_flags, prog_ids, &prog_cnt);
58962306a36Sopenharmony_ci	close(fd);
59062306a36Sopenharmony_ci	if (err) {
59162306a36Sopenharmony_ci		if (errno == EINVAL) {
59262306a36Sopenharmony_ci			/* Older kernel's don't support querying
59362306a36Sopenharmony_ci			 * flow dissector programs.
59462306a36Sopenharmony_ci			 */
59562306a36Sopenharmony_ci			errno = 0;
59662306a36Sopenharmony_ci			return 0;
59762306a36Sopenharmony_ci		}
59862306a36Sopenharmony_ci		p_err("can't query prog: %s", strerror(errno));
59962306a36Sopenharmony_ci		return -1;
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	if (prog_cnt == 1)
60362306a36Sopenharmony_ci		attach_info->flow_dissector_id = prog_ids[0];
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	return 0;
60662306a36Sopenharmony_ci}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_cistatic int net_parse_dev(int *argc, char ***argv)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	int ifindex;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	if (is_prefix(**argv, "dev")) {
61362306a36Sopenharmony_ci		NEXT_ARGP();
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		ifindex = if_nametoindex(**argv);
61662306a36Sopenharmony_ci		if (!ifindex)
61762306a36Sopenharmony_ci			p_err("invalid devname %s", **argv);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci		NEXT_ARGP();
62062306a36Sopenharmony_ci	} else {
62162306a36Sopenharmony_ci		p_err("expected 'dev', got: '%s'?", **argv);
62262306a36Sopenharmony_ci		return -1;
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	return ifindex;
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_cistatic int do_attach_detach_xdp(int progfd, enum net_attach_type attach_type,
62962306a36Sopenharmony_ci				int ifindex, bool overwrite)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	__u32 flags = 0;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	if (!overwrite)
63462306a36Sopenharmony_ci		flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
63562306a36Sopenharmony_ci	if (attach_type == NET_ATTACH_TYPE_XDP_GENERIC)
63662306a36Sopenharmony_ci		flags |= XDP_FLAGS_SKB_MODE;
63762306a36Sopenharmony_ci	if (attach_type == NET_ATTACH_TYPE_XDP_DRIVER)
63862306a36Sopenharmony_ci		flags |= XDP_FLAGS_DRV_MODE;
63962306a36Sopenharmony_ci	if (attach_type == NET_ATTACH_TYPE_XDP_OFFLOAD)
64062306a36Sopenharmony_ci		flags |= XDP_FLAGS_HW_MODE;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	return bpf_xdp_attach(ifindex, progfd, flags, NULL);
64362306a36Sopenharmony_ci}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_cistatic int do_attach(int argc, char **argv)
64662306a36Sopenharmony_ci{
64762306a36Sopenharmony_ci	enum net_attach_type attach_type;
64862306a36Sopenharmony_ci	int progfd, ifindex, err = 0;
64962306a36Sopenharmony_ci	bool overwrite = false;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	/* parse attach args */
65262306a36Sopenharmony_ci	if (!REQ_ARGS(5))
65362306a36Sopenharmony_ci		return -EINVAL;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	attach_type = parse_attach_type(*argv);
65662306a36Sopenharmony_ci	if (attach_type == net_attach_type_size) {
65762306a36Sopenharmony_ci		p_err("invalid net attach/detach type: %s", *argv);
65862306a36Sopenharmony_ci		return -EINVAL;
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci	NEXT_ARG();
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	progfd = prog_parse_fd(&argc, &argv);
66362306a36Sopenharmony_ci	if (progfd < 0)
66462306a36Sopenharmony_ci		return -EINVAL;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	ifindex = net_parse_dev(&argc, &argv);
66762306a36Sopenharmony_ci	if (ifindex < 1) {
66862306a36Sopenharmony_ci		err = -EINVAL;
66962306a36Sopenharmony_ci		goto cleanup;
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	if (argc) {
67362306a36Sopenharmony_ci		if (is_prefix(*argv, "overwrite")) {
67462306a36Sopenharmony_ci			overwrite = true;
67562306a36Sopenharmony_ci		} else {
67662306a36Sopenharmony_ci			p_err("expected 'overwrite', got: '%s'?", *argv);
67762306a36Sopenharmony_ci			err = -EINVAL;
67862306a36Sopenharmony_ci			goto cleanup;
67962306a36Sopenharmony_ci		}
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	/* attach xdp prog */
68362306a36Sopenharmony_ci	if (is_prefix("xdp", attach_type_strings[attach_type]))
68462306a36Sopenharmony_ci		err = do_attach_detach_xdp(progfd, attach_type, ifindex,
68562306a36Sopenharmony_ci					   overwrite);
68662306a36Sopenharmony_ci	if (err) {
68762306a36Sopenharmony_ci		p_err("interface %s attach failed: %s",
68862306a36Sopenharmony_ci		      attach_type_strings[attach_type], strerror(-err));
68962306a36Sopenharmony_ci		goto cleanup;
69062306a36Sopenharmony_ci	}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	if (json_output)
69362306a36Sopenharmony_ci		jsonw_null(json_wtr);
69462306a36Sopenharmony_cicleanup:
69562306a36Sopenharmony_ci	close(progfd);
69662306a36Sopenharmony_ci	return err;
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic int do_detach(int argc, char **argv)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	enum net_attach_type attach_type;
70262306a36Sopenharmony_ci	int progfd, ifindex, err = 0;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	/* parse detach args */
70562306a36Sopenharmony_ci	if (!REQ_ARGS(3))
70662306a36Sopenharmony_ci		return -EINVAL;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	attach_type = parse_attach_type(*argv);
70962306a36Sopenharmony_ci	if (attach_type == net_attach_type_size) {
71062306a36Sopenharmony_ci		p_err("invalid net attach/detach type: %s", *argv);
71162306a36Sopenharmony_ci		return -EINVAL;
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci	NEXT_ARG();
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	ifindex = net_parse_dev(&argc, &argv);
71662306a36Sopenharmony_ci	if (ifindex < 1)
71762306a36Sopenharmony_ci		return -EINVAL;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	/* detach xdp prog */
72062306a36Sopenharmony_ci	progfd = -1;
72162306a36Sopenharmony_ci	if (is_prefix("xdp", attach_type_strings[attach_type]))
72262306a36Sopenharmony_ci		err = do_attach_detach_xdp(progfd, attach_type, ifindex, NULL);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	if (err < 0) {
72562306a36Sopenharmony_ci		p_err("interface %s detach failed: %s",
72662306a36Sopenharmony_ci		      attach_type_strings[attach_type], strerror(-err));
72762306a36Sopenharmony_ci		return err;
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	if (json_output)
73162306a36Sopenharmony_ci		jsonw_null(json_wtr);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	return 0;
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_cistatic int netfilter_link_compar(const void *a, const void *b)
73762306a36Sopenharmony_ci{
73862306a36Sopenharmony_ci	const struct bpf_link_info *nfa = a;
73962306a36Sopenharmony_ci	const struct bpf_link_info *nfb = b;
74062306a36Sopenharmony_ci	int delta;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	delta = nfa->netfilter.pf - nfb->netfilter.pf;
74362306a36Sopenharmony_ci	if (delta)
74462306a36Sopenharmony_ci		return delta;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	delta = nfa->netfilter.hooknum - nfb->netfilter.hooknum;
74762306a36Sopenharmony_ci	if (delta)
74862306a36Sopenharmony_ci		return delta;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	if (nfa->netfilter.priority < nfb->netfilter.priority)
75162306a36Sopenharmony_ci		return -1;
75262306a36Sopenharmony_ci	if (nfa->netfilter.priority > nfb->netfilter.priority)
75362306a36Sopenharmony_ci		return 1;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	return nfa->netfilter.flags - nfb->netfilter.flags;
75662306a36Sopenharmony_ci}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_cistatic void show_link_netfilter(void)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	unsigned int nf_link_len = 0, nf_link_count = 0;
76162306a36Sopenharmony_ci	struct bpf_link_info *nf_link_info = NULL;
76262306a36Sopenharmony_ci	__u32 id = 0;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	while (true) {
76562306a36Sopenharmony_ci		struct bpf_link_info info;
76662306a36Sopenharmony_ci		int fd, err;
76762306a36Sopenharmony_ci		__u32 len;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci		err = bpf_link_get_next_id(id, &id);
77062306a36Sopenharmony_ci		if (err) {
77162306a36Sopenharmony_ci			if (errno == ENOENT)
77262306a36Sopenharmony_ci				break;
77362306a36Sopenharmony_ci			p_err("can't get next link: %s (id %d)", strerror(errno), id);
77462306a36Sopenharmony_ci			break;
77562306a36Sopenharmony_ci		}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci		fd = bpf_link_get_fd_by_id(id);
77862306a36Sopenharmony_ci		if (fd < 0) {
77962306a36Sopenharmony_ci			p_err("can't get link by id (%u): %s", id, strerror(errno));
78062306a36Sopenharmony_ci			continue;
78162306a36Sopenharmony_ci		}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci		memset(&info, 0, sizeof(info));
78462306a36Sopenharmony_ci		len = sizeof(info);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci		err = bpf_link_get_info_by_fd(fd, &info, &len);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci		close(fd);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci		if (err) {
79162306a36Sopenharmony_ci			p_err("can't get link info for fd %d: %s", fd, strerror(errno));
79262306a36Sopenharmony_ci			continue;
79362306a36Sopenharmony_ci		}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci		if (info.type != BPF_LINK_TYPE_NETFILTER)
79662306a36Sopenharmony_ci			continue;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci		if (nf_link_count >= nf_link_len) {
79962306a36Sopenharmony_ci			static const unsigned int max_link_count = INT_MAX / sizeof(info);
80062306a36Sopenharmony_ci			struct bpf_link_info *expand;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci			if (nf_link_count > max_link_count) {
80362306a36Sopenharmony_ci				p_err("cannot handle more than %u links\n", max_link_count);
80462306a36Sopenharmony_ci				break;
80562306a36Sopenharmony_ci			}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci			nf_link_len += 16;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci			expand = realloc(nf_link_info, nf_link_len * sizeof(info));
81062306a36Sopenharmony_ci			if (!expand) {
81162306a36Sopenharmony_ci				p_err("realloc: %s",  strerror(errno));
81262306a36Sopenharmony_ci				break;
81362306a36Sopenharmony_ci			}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci			nf_link_info = expand;
81662306a36Sopenharmony_ci		}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci		nf_link_info[nf_link_count] = info;
81962306a36Sopenharmony_ci		nf_link_count++;
82062306a36Sopenharmony_ci	}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	qsort(nf_link_info, nf_link_count, sizeof(*nf_link_info), netfilter_link_compar);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	for (id = 0; id < nf_link_count; id++) {
82562306a36Sopenharmony_ci		NET_START_OBJECT;
82662306a36Sopenharmony_ci		if (json_output)
82762306a36Sopenharmony_ci			netfilter_dump_json(&nf_link_info[id], json_wtr);
82862306a36Sopenharmony_ci		else
82962306a36Sopenharmony_ci			netfilter_dump_plain(&nf_link_info[id]);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci		NET_DUMP_UINT("id", " prog_id %u", nf_link_info[id].prog_id);
83262306a36Sopenharmony_ci		NET_END_OBJECT;
83362306a36Sopenharmony_ci	}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	free(nf_link_info);
83662306a36Sopenharmony_ci}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_cistatic int do_show(int argc, char **argv)
83962306a36Sopenharmony_ci{
84062306a36Sopenharmony_ci	struct bpf_attach_info attach_info = {};
84162306a36Sopenharmony_ci	int i, sock, ret, filter_idx = -1;
84262306a36Sopenharmony_ci	struct bpf_netdev_t dev_array;
84362306a36Sopenharmony_ci	unsigned int nl_pid = 0;
84462306a36Sopenharmony_ci	char err_buf[256];
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	if (argc == 2) {
84762306a36Sopenharmony_ci		filter_idx = net_parse_dev(&argc, &argv);
84862306a36Sopenharmony_ci		if (filter_idx < 1)
84962306a36Sopenharmony_ci			return -1;
85062306a36Sopenharmony_ci	} else if (argc != 0) {
85162306a36Sopenharmony_ci		usage();
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	ret = query_flow_dissector(&attach_info);
85562306a36Sopenharmony_ci	if (ret)
85662306a36Sopenharmony_ci		return -1;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	sock = netlink_open(&nl_pid);
85962306a36Sopenharmony_ci	if (sock < 0) {
86062306a36Sopenharmony_ci		fprintf(stderr, "failed to open netlink sock\n");
86162306a36Sopenharmony_ci		return -1;
86262306a36Sopenharmony_ci	}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	dev_array.devices = NULL;
86562306a36Sopenharmony_ci	dev_array.used_len = 0;
86662306a36Sopenharmony_ci	dev_array.array_len = 0;
86762306a36Sopenharmony_ci	dev_array.filter_idx = filter_idx;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	if (json_output)
87062306a36Sopenharmony_ci		jsonw_start_array(json_wtr);
87162306a36Sopenharmony_ci	NET_START_OBJECT;
87262306a36Sopenharmony_ci	NET_START_ARRAY("xdp", "%s:\n");
87362306a36Sopenharmony_ci	ret = netlink_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array);
87462306a36Sopenharmony_ci	NET_END_ARRAY("\n");
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	if (!ret) {
87762306a36Sopenharmony_ci		NET_START_ARRAY("tc", "%s:\n");
87862306a36Sopenharmony_ci		for (i = 0; i < dev_array.used_len; i++) {
87962306a36Sopenharmony_ci			show_dev_tc_bpf(&dev_array.devices[i]);
88062306a36Sopenharmony_ci			ret = show_dev_tc_bpf_classic(sock, nl_pid,
88162306a36Sopenharmony_ci						      &dev_array.devices[i]);
88262306a36Sopenharmony_ci			if (ret)
88362306a36Sopenharmony_ci				break;
88462306a36Sopenharmony_ci		}
88562306a36Sopenharmony_ci		NET_END_ARRAY("\n");
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	NET_START_ARRAY("flow_dissector", "%s:\n");
88962306a36Sopenharmony_ci	if (attach_info.flow_dissector_id > 0)
89062306a36Sopenharmony_ci		NET_DUMP_UINT("id", "id %u", attach_info.flow_dissector_id);
89162306a36Sopenharmony_ci	NET_END_ARRAY("\n");
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	NET_START_ARRAY("netfilter", "%s:\n");
89462306a36Sopenharmony_ci	show_link_netfilter();
89562306a36Sopenharmony_ci	NET_END_ARRAY("\n");
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	NET_END_OBJECT;
89862306a36Sopenharmony_ci	if (json_output)
89962306a36Sopenharmony_ci		jsonw_end_array(json_wtr);
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	if (ret) {
90262306a36Sopenharmony_ci		if (json_output)
90362306a36Sopenharmony_ci			jsonw_null(json_wtr);
90462306a36Sopenharmony_ci		libbpf_strerror(ret, err_buf, sizeof(err_buf));
90562306a36Sopenharmony_ci		fprintf(stderr, "Error: %s\n", err_buf);
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci	free(dev_array.devices);
90862306a36Sopenharmony_ci	close(sock);
90962306a36Sopenharmony_ci	return ret;
91062306a36Sopenharmony_ci}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_cistatic int do_help(int argc, char **argv)
91362306a36Sopenharmony_ci{
91462306a36Sopenharmony_ci	if (json_output) {
91562306a36Sopenharmony_ci		jsonw_null(json_wtr);
91662306a36Sopenharmony_ci		return 0;
91762306a36Sopenharmony_ci	}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	fprintf(stderr,
92062306a36Sopenharmony_ci		"Usage: %1$s %2$s { show | list } [dev <devname>]\n"
92162306a36Sopenharmony_ci		"       %1$s %2$s attach ATTACH_TYPE PROG dev <devname> [ overwrite ]\n"
92262306a36Sopenharmony_ci		"       %1$s %2$s detach ATTACH_TYPE dev <devname>\n"
92362306a36Sopenharmony_ci		"       %1$s %2$s help\n"
92462306a36Sopenharmony_ci		"\n"
92562306a36Sopenharmony_ci		"       " HELP_SPEC_PROGRAM "\n"
92662306a36Sopenharmony_ci		"       ATTACH_TYPE := { xdp | xdpgeneric | xdpdrv | xdpoffload }\n"
92762306a36Sopenharmony_ci		"       " HELP_SPEC_OPTIONS " }\n"
92862306a36Sopenharmony_ci		"\n"
92962306a36Sopenharmony_ci		"Note: Only xdp, tcx, tc, flow_dissector and netfilter attachments\n"
93062306a36Sopenharmony_ci		"      are currently supported.\n"
93162306a36Sopenharmony_ci		"      For progs attached to cgroups, use \"bpftool cgroup\"\n"
93262306a36Sopenharmony_ci		"      to dump program attachments. For program types\n"
93362306a36Sopenharmony_ci		"      sk_{filter,skb,msg,reuseport} and lwt/seg6, please\n"
93462306a36Sopenharmony_ci		"      consult iproute2.\n"
93562306a36Sopenharmony_ci		"",
93662306a36Sopenharmony_ci		bin_name, argv[-2]);
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	return 0;
93962306a36Sopenharmony_ci}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_cistatic const struct cmd cmds[] = {
94262306a36Sopenharmony_ci	{ "show",	do_show },
94362306a36Sopenharmony_ci	{ "list",	do_show },
94462306a36Sopenharmony_ci	{ "attach",	do_attach },
94562306a36Sopenharmony_ci	{ "detach",	do_detach },
94662306a36Sopenharmony_ci	{ "help",	do_help },
94762306a36Sopenharmony_ci	{ 0 }
94862306a36Sopenharmony_ci};
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ciint do_net(int argc, char **argv)
95162306a36Sopenharmony_ci{
95262306a36Sopenharmony_ci	return cmd_select(cmds, argc, argv, do_help);
95362306a36Sopenharmony_ci}
954