xref: /openbmc/linux/tools/bpf/bpftool/net.c (revision 57c61da8)
1907b2236SJakub Kicinski // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2f6f3bac0SYonghong Song // Copyright (C) 2018 Facebook
3f6f3bac0SYonghong Song 
4b3d84af7SQuentin Monnet #ifndef _GNU_SOURCE
5f6f3bac0SYonghong Song #define _GNU_SOURCE
6b3d84af7SQuentin Monnet #endif
77900efc1SYonghong Song #include <errno.h>
87f0c57feSStanislav Fomichev #include <fcntl.h>
9f6f3bac0SYonghong Song #include <stdlib.h>
10f6f3bac0SYonghong Song #include <string.h>
117084566aSAndrii Nakryiko #include <time.h>
12f6f3bac0SYonghong Song #include <unistd.h>
13229c3b47SToke Høiland-Jørgensen #include <bpf/bpf.h>
14229c3b47SToke Høiland-Jørgensen #include <bpf/libbpf.h>
15f6f3bac0SYonghong Song #include <net/if.h>
16f6f3bac0SYonghong Song #include <linux/rtnetlink.h>
177084566aSAndrii Nakryiko #include <linux/socket.h>
18f6f3bac0SYonghong Song #include <linux/tc_act/tc_bpf.h>
19f6f3bac0SYonghong Song #include <sys/socket.h>
207f0c57feSStanislav Fomichev #include <sys/stat.h>
217f0c57feSStanislav Fomichev #include <sys/types.h>
22f6f3bac0SYonghong Song 
23229c3b47SToke Høiland-Jørgensen #include "bpf/nlattr.h"
24f6f3bac0SYonghong Song #include "main.h"
25f6f3bac0SYonghong Song #include "netlink_dumper.h"
26f6f3bac0SYonghong Song 
277084566aSAndrii Nakryiko #ifndef SOL_NETLINK
287084566aSAndrii Nakryiko #define SOL_NETLINK 270
297084566aSAndrii Nakryiko #endif
307084566aSAndrii Nakryiko 
317900efc1SYonghong Song struct ip_devname_ifindex {
327900efc1SYonghong Song 	char	devname[64];
337900efc1SYonghong Song 	int	ifindex;
347900efc1SYonghong Song };
357900efc1SYonghong Song 
36f6f3bac0SYonghong Song struct bpf_netdev_t {
377900efc1SYonghong Song 	struct ip_devname_ifindex *devices;
38f6f3bac0SYonghong Song 	int	used_len;
39f6f3bac0SYonghong Song 	int	array_len;
40f6f3bac0SYonghong Song 	int	filter_idx;
41f6f3bac0SYonghong Song };
42f6f3bac0SYonghong Song 
43f6f3bac0SYonghong Song struct tc_kind_handle {
44f6f3bac0SYonghong Song 	char	kind[64];
45f6f3bac0SYonghong Song 	int	handle;
46f6f3bac0SYonghong Song };
47f6f3bac0SYonghong Song 
48f6f3bac0SYonghong Song struct bpf_tcinfo_t {
49f6f3bac0SYonghong Song 	struct tc_kind_handle	*handle_array;
50f6f3bac0SYonghong Song 	int			used_len;
51f6f3bac0SYonghong Song 	int			array_len;
52f6f3bac0SYonghong Song 	bool			is_qdisc;
53f6f3bac0SYonghong Song };
54f6f3bac0SYonghong Song 
557900efc1SYonghong Song struct bpf_filter_t {
567900efc1SYonghong Song 	const char	*kind;
577900efc1SYonghong Song 	const char	*devname;
587900efc1SYonghong Song 	int		ifindex;
597900efc1SYonghong Song };
607900efc1SYonghong Song 
617f0c57feSStanislav Fomichev struct bpf_attach_info {
627f0c57feSStanislav Fomichev 	__u32 flow_dissector_id;
637f0c57feSStanislav Fomichev };
647f0c57feSStanislav Fomichev 
6504949cccSDaniel T. Lee enum net_attach_type {
6604949cccSDaniel T. Lee 	NET_ATTACH_TYPE_XDP,
6704949cccSDaniel T. Lee 	NET_ATTACH_TYPE_XDP_GENERIC,
6804949cccSDaniel T. Lee 	NET_ATTACH_TYPE_XDP_DRIVER,
6904949cccSDaniel T. Lee 	NET_ATTACH_TYPE_XDP_OFFLOAD,
7004949cccSDaniel T. Lee };
7104949cccSDaniel T. Lee 
7204949cccSDaniel T. Lee static const char * const attach_type_strings[] = {
7304949cccSDaniel T. Lee 	[NET_ATTACH_TYPE_XDP]		= "xdp",
7404949cccSDaniel T. Lee 	[NET_ATTACH_TYPE_XDP_GENERIC]	= "xdpgeneric",
7504949cccSDaniel T. Lee 	[NET_ATTACH_TYPE_XDP_DRIVER]	= "xdpdrv",
7604949cccSDaniel T. Lee 	[NET_ATTACH_TYPE_XDP_OFFLOAD]	= "xdpoffload",
7704949cccSDaniel T. Lee };
7804949cccSDaniel T. Lee 
79*57c61da8SDaniel Borkmann static const char * const attach_loc_strings[] = {
80*57c61da8SDaniel Borkmann 	[BPF_TCX_INGRESS]		= "tcx/ingress",
81*57c61da8SDaniel Borkmann 	[BPF_TCX_EGRESS]		= "tcx/egress",
82*57c61da8SDaniel Borkmann };
83*57c61da8SDaniel Borkmann 
8404949cccSDaniel T. Lee const size_t net_attach_type_size = ARRAY_SIZE(attach_type_strings);
8504949cccSDaniel T. Lee 
parse_attach_type(const char * str)8604949cccSDaniel T. Lee static enum net_attach_type parse_attach_type(const char *str)
8704949cccSDaniel T. Lee {
8804949cccSDaniel T. Lee 	enum net_attach_type type;
8904949cccSDaniel T. Lee 
9004949cccSDaniel T. Lee 	for (type = 0; type < net_attach_type_size; type++) {
9104949cccSDaniel T. Lee 		if (attach_type_strings[type] &&
9204949cccSDaniel T. Lee 		    is_prefix(str, attach_type_strings[type]))
9304949cccSDaniel T. Lee 			return type;
9404949cccSDaniel T. Lee 	}
9504949cccSDaniel T. Lee 
9604949cccSDaniel T. Lee 	return net_attach_type_size;
9704949cccSDaniel T. Lee }
9804949cccSDaniel T. Lee 
997084566aSAndrii Nakryiko typedef int (*dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb);
1007084566aSAndrii Nakryiko 
1017084566aSAndrii Nakryiko typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, dump_nlmsg_t, void *cookie);
1027084566aSAndrii Nakryiko 
netlink_open(__u32 * nl_pid)1037084566aSAndrii Nakryiko static int netlink_open(__u32 *nl_pid)
1047084566aSAndrii Nakryiko {
1057084566aSAndrii Nakryiko 	struct sockaddr_nl sa;
1067084566aSAndrii Nakryiko 	socklen_t addrlen;
1077084566aSAndrii Nakryiko 	int one = 1, ret;
1087084566aSAndrii Nakryiko 	int sock;
1097084566aSAndrii Nakryiko 
1107084566aSAndrii Nakryiko 	memset(&sa, 0, sizeof(sa));
1117084566aSAndrii Nakryiko 	sa.nl_family = AF_NETLINK;
1127084566aSAndrii Nakryiko 
1137084566aSAndrii Nakryiko 	sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
1147084566aSAndrii Nakryiko 	if (sock < 0)
1157084566aSAndrii Nakryiko 		return -errno;
1167084566aSAndrii Nakryiko 
1177084566aSAndrii Nakryiko 	if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK,
1187084566aSAndrii Nakryiko 		       &one, sizeof(one)) < 0) {
1197084566aSAndrii Nakryiko 		p_err("Netlink error reporting not supported");
1207084566aSAndrii Nakryiko 	}
1217084566aSAndrii Nakryiko 
1227084566aSAndrii Nakryiko 	if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
1237084566aSAndrii Nakryiko 		ret = -errno;
1247084566aSAndrii Nakryiko 		goto cleanup;
1257084566aSAndrii Nakryiko 	}
1267084566aSAndrii Nakryiko 
1277084566aSAndrii Nakryiko 	addrlen = sizeof(sa);
1287084566aSAndrii Nakryiko 	if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) {
1297084566aSAndrii Nakryiko 		ret = -errno;
1307084566aSAndrii Nakryiko 		goto cleanup;
1317084566aSAndrii Nakryiko 	}
1327084566aSAndrii Nakryiko 
1337084566aSAndrii Nakryiko 	if (addrlen != sizeof(sa)) {
1347084566aSAndrii Nakryiko 		ret = -LIBBPF_ERRNO__INTERNAL;
1357084566aSAndrii Nakryiko 		goto cleanup;
1367084566aSAndrii Nakryiko 	}
1377084566aSAndrii Nakryiko 
1387084566aSAndrii Nakryiko 	*nl_pid = sa.nl_pid;
1397084566aSAndrii Nakryiko 	return sock;
1407084566aSAndrii Nakryiko 
1417084566aSAndrii Nakryiko cleanup:
1427084566aSAndrii Nakryiko 	close(sock);
1437084566aSAndrii Nakryiko 	return ret;
1447084566aSAndrii Nakryiko }
1457084566aSAndrii Nakryiko 
netlink_recv(int sock,__u32 nl_pid,__u32 seq,__dump_nlmsg_t _fn,dump_nlmsg_t fn,void * cookie)1467084566aSAndrii Nakryiko static int netlink_recv(int sock, __u32 nl_pid, __u32 seq,
1477084566aSAndrii Nakryiko 			    __dump_nlmsg_t _fn, dump_nlmsg_t fn,
1487084566aSAndrii Nakryiko 			    void *cookie)
1497084566aSAndrii Nakryiko {
1507084566aSAndrii Nakryiko 	bool multipart = true;
1517084566aSAndrii Nakryiko 	struct nlmsgerr *err;
1527084566aSAndrii Nakryiko 	struct nlmsghdr *nh;
1537084566aSAndrii Nakryiko 	char buf[4096];
1547084566aSAndrii Nakryiko 	int len, ret;
1557084566aSAndrii Nakryiko 
1567084566aSAndrii Nakryiko 	while (multipart) {
1577084566aSAndrii Nakryiko 		multipart = false;
1587084566aSAndrii Nakryiko 		len = recv(sock, buf, sizeof(buf), 0);
1597084566aSAndrii Nakryiko 		if (len < 0) {
1607084566aSAndrii Nakryiko 			ret = -errno;
1617084566aSAndrii Nakryiko 			goto done;
1627084566aSAndrii Nakryiko 		}
1637084566aSAndrii Nakryiko 
1647084566aSAndrii Nakryiko 		if (len == 0)
1657084566aSAndrii Nakryiko 			break;
1667084566aSAndrii Nakryiko 
1678af50142SYonghong Song 		for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, (unsigned int)len);
1687084566aSAndrii Nakryiko 		     nh = NLMSG_NEXT(nh, len)) {
1697084566aSAndrii Nakryiko 			if (nh->nlmsg_pid != nl_pid) {
1707084566aSAndrii Nakryiko 				ret = -LIBBPF_ERRNO__WRNGPID;
1717084566aSAndrii Nakryiko 				goto done;
1727084566aSAndrii Nakryiko 			}
1737084566aSAndrii Nakryiko 			if (nh->nlmsg_seq != seq) {
1747084566aSAndrii Nakryiko 				ret = -LIBBPF_ERRNO__INVSEQ;
1757084566aSAndrii Nakryiko 				goto done;
1767084566aSAndrii Nakryiko 			}
1777084566aSAndrii Nakryiko 			if (nh->nlmsg_flags & NLM_F_MULTI)
1787084566aSAndrii Nakryiko 				multipart = true;
1797084566aSAndrii Nakryiko 			switch (nh->nlmsg_type) {
1807084566aSAndrii Nakryiko 			case NLMSG_ERROR:
1817084566aSAndrii Nakryiko 				err = (struct nlmsgerr *)NLMSG_DATA(nh);
1827084566aSAndrii Nakryiko 				if (!err->error)
1837084566aSAndrii Nakryiko 					continue;
1847084566aSAndrii Nakryiko 				ret = err->error;
1857084566aSAndrii Nakryiko 				libbpf_nla_dump_errormsg(nh);
1867084566aSAndrii Nakryiko 				goto done;
1877084566aSAndrii Nakryiko 			case NLMSG_DONE:
1887084566aSAndrii Nakryiko 				return 0;
1897084566aSAndrii Nakryiko 			default:
1907084566aSAndrii Nakryiko 				break;
1917084566aSAndrii Nakryiko 			}
1927084566aSAndrii Nakryiko 			if (_fn) {
1937084566aSAndrii Nakryiko 				ret = _fn(nh, fn, cookie);
1947084566aSAndrii Nakryiko 				if (ret)
1957084566aSAndrii Nakryiko 					return ret;
1967084566aSAndrii Nakryiko 			}
1977084566aSAndrii Nakryiko 		}
1987084566aSAndrii Nakryiko 	}
1997084566aSAndrii Nakryiko 	ret = 0;
2007084566aSAndrii Nakryiko done:
2017084566aSAndrii Nakryiko 	return ret;
2027084566aSAndrii Nakryiko }
2037084566aSAndrii Nakryiko 
__dump_class_nlmsg(struct nlmsghdr * nlh,dump_nlmsg_t dump_class_nlmsg,void * cookie)2047084566aSAndrii Nakryiko static int __dump_class_nlmsg(struct nlmsghdr *nlh,
2057084566aSAndrii Nakryiko 			      dump_nlmsg_t dump_class_nlmsg,
2067084566aSAndrii Nakryiko 			      void *cookie)
2077084566aSAndrii Nakryiko {
2087084566aSAndrii Nakryiko 	struct nlattr *tb[TCA_MAX + 1], *attr;
2097084566aSAndrii Nakryiko 	struct tcmsg *t = NLMSG_DATA(nlh);
2107084566aSAndrii Nakryiko 	int len;
2117084566aSAndrii Nakryiko 
2127084566aSAndrii Nakryiko 	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
2137084566aSAndrii Nakryiko 	attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
2147084566aSAndrii Nakryiko 	if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
2157084566aSAndrii Nakryiko 		return -LIBBPF_ERRNO__NLPARSE;
2167084566aSAndrii Nakryiko 
2177084566aSAndrii Nakryiko 	return dump_class_nlmsg(cookie, t, tb);
2187084566aSAndrii Nakryiko }
2197084566aSAndrii Nakryiko 
netlink_get_class(int sock,unsigned int nl_pid,int ifindex,dump_nlmsg_t dump_class_nlmsg,void * cookie)2207084566aSAndrii Nakryiko static int netlink_get_class(int sock, unsigned int nl_pid, int ifindex,
2217084566aSAndrii Nakryiko 			     dump_nlmsg_t dump_class_nlmsg, void *cookie)
2227084566aSAndrii Nakryiko {
2237084566aSAndrii Nakryiko 	struct {
2247084566aSAndrii Nakryiko 		struct nlmsghdr nlh;
2257084566aSAndrii Nakryiko 		struct tcmsg t;
2267084566aSAndrii Nakryiko 	} req = {
2277084566aSAndrii Nakryiko 		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
2287084566aSAndrii Nakryiko 		.nlh.nlmsg_type = RTM_GETTCLASS,
2297084566aSAndrii Nakryiko 		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
2307084566aSAndrii Nakryiko 		.t.tcm_family = AF_UNSPEC,
2317084566aSAndrii Nakryiko 		.t.tcm_ifindex = ifindex,
2327084566aSAndrii Nakryiko 	};
2337084566aSAndrii Nakryiko 	int seq = time(NULL);
2347084566aSAndrii Nakryiko 
2357084566aSAndrii Nakryiko 	req.nlh.nlmsg_seq = seq;
2367084566aSAndrii Nakryiko 	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
2377084566aSAndrii Nakryiko 		return -errno;
2387084566aSAndrii Nakryiko 
2397084566aSAndrii Nakryiko 	return netlink_recv(sock, nl_pid, seq, __dump_class_nlmsg,
2407084566aSAndrii Nakryiko 			    dump_class_nlmsg, cookie);
2417084566aSAndrii Nakryiko }
2427084566aSAndrii Nakryiko 
__dump_qdisc_nlmsg(struct nlmsghdr * nlh,dump_nlmsg_t dump_qdisc_nlmsg,void * cookie)2437084566aSAndrii Nakryiko static int __dump_qdisc_nlmsg(struct nlmsghdr *nlh,
2447084566aSAndrii Nakryiko 			      dump_nlmsg_t dump_qdisc_nlmsg,
2457084566aSAndrii Nakryiko 			      void *cookie)
2467084566aSAndrii Nakryiko {
2477084566aSAndrii Nakryiko 	struct nlattr *tb[TCA_MAX + 1], *attr;
2487084566aSAndrii Nakryiko 	struct tcmsg *t = NLMSG_DATA(nlh);
2497084566aSAndrii Nakryiko 	int len;
2507084566aSAndrii Nakryiko 
2517084566aSAndrii Nakryiko 	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
2527084566aSAndrii Nakryiko 	attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
2537084566aSAndrii Nakryiko 	if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
2547084566aSAndrii Nakryiko 		return -LIBBPF_ERRNO__NLPARSE;
2557084566aSAndrii Nakryiko 
2567084566aSAndrii Nakryiko 	return dump_qdisc_nlmsg(cookie, t, tb);
2577084566aSAndrii Nakryiko }
2587084566aSAndrii Nakryiko 
netlink_get_qdisc(int sock,unsigned int nl_pid,int ifindex,dump_nlmsg_t dump_qdisc_nlmsg,void * cookie)2597084566aSAndrii Nakryiko static int netlink_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
2607084566aSAndrii Nakryiko 			     dump_nlmsg_t dump_qdisc_nlmsg, void *cookie)
2617084566aSAndrii Nakryiko {
2627084566aSAndrii Nakryiko 	struct {
2637084566aSAndrii Nakryiko 		struct nlmsghdr nlh;
2647084566aSAndrii Nakryiko 		struct tcmsg t;
2657084566aSAndrii Nakryiko 	} req = {
2667084566aSAndrii Nakryiko 		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
2677084566aSAndrii Nakryiko 		.nlh.nlmsg_type = RTM_GETQDISC,
2687084566aSAndrii Nakryiko 		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
2697084566aSAndrii Nakryiko 		.t.tcm_family = AF_UNSPEC,
2707084566aSAndrii Nakryiko 		.t.tcm_ifindex = ifindex,
2717084566aSAndrii Nakryiko 	};
2727084566aSAndrii Nakryiko 	int seq = time(NULL);
2737084566aSAndrii Nakryiko 
2747084566aSAndrii Nakryiko 	req.nlh.nlmsg_seq = seq;
2757084566aSAndrii Nakryiko 	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
2767084566aSAndrii Nakryiko 		return -errno;
2777084566aSAndrii Nakryiko 
2787084566aSAndrii Nakryiko 	return netlink_recv(sock, nl_pid, seq, __dump_qdisc_nlmsg,
2797084566aSAndrii Nakryiko 			    dump_qdisc_nlmsg, cookie);
2807084566aSAndrii Nakryiko }
2817084566aSAndrii Nakryiko 
__dump_filter_nlmsg(struct nlmsghdr * nlh,dump_nlmsg_t dump_filter_nlmsg,void * cookie)2827084566aSAndrii Nakryiko static int __dump_filter_nlmsg(struct nlmsghdr *nlh,
2837084566aSAndrii Nakryiko 			       dump_nlmsg_t dump_filter_nlmsg,
2847084566aSAndrii Nakryiko 			       void *cookie)
2857084566aSAndrii Nakryiko {
2867084566aSAndrii Nakryiko 	struct nlattr *tb[TCA_MAX + 1], *attr;
2877084566aSAndrii Nakryiko 	struct tcmsg *t = NLMSG_DATA(nlh);
2887084566aSAndrii Nakryiko 	int len;
2897084566aSAndrii Nakryiko 
2907084566aSAndrii Nakryiko 	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
2917084566aSAndrii Nakryiko 	attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
2927084566aSAndrii Nakryiko 	if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
2937084566aSAndrii Nakryiko 		return -LIBBPF_ERRNO__NLPARSE;
2947084566aSAndrii Nakryiko 
2957084566aSAndrii Nakryiko 	return dump_filter_nlmsg(cookie, t, tb);
2967084566aSAndrii Nakryiko }
2977084566aSAndrii Nakryiko 
netlink_get_filter(int sock,unsigned int nl_pid,int ifindex,int handle,dump_nlmsg_t dump_filter_nlmsg,void * cookie)2987084566aSAndrii Nakryiko static int netlink_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
2997084566aSAndrii Nakryiko 			      dump_nlmsg_t dump_filter_nlmsg, void *cookie)
3007084566aSAndrii Nakryiko {
3017084566aSAndrii Nakryiko 	struct {
3027084566aSAndrii Nakryiko 		struct nlmsghdr nlh;
3037084566aSAndrii Nakryiko 		struct tcmsg t;
3047084566aSAndrii Nakryiko 	} req = {
3057084566aSAndrii Nakryiko 		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
3067084566aSAndrii Nakryiko 		.nlh.nlmsg_type = RTM_GETTFILTER,
3077084566aSAndrii Nakryiko 		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
3087084566aSAndrii Nakryiko 		.t.tcm_family = AF_UNSPEC,
3097084566aSAndrii Nakryiko 		.t.tcm_ifindex = ifindex,
3107084566aSAndrii Nakryiko 		.t.tcm_parent = handle,
3117084566aSAndrii Nakryiko 	};
3127084566aSAndrii Nakryiko 	int seq = time(NULL);
3137084566aSAndrii Nakryiko 
3147084566aSAndrii Nakryiko 	req.nlh.nlmsg_seq = seq;
3157084566aSAndrii Nakryiko 	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
3167084566aSAndrii Nakryiko 		return -errno;
3177084566aSAndrii Nakryiko 
3187084566aSAndrii Nakryiko 	return netlink_recv(sock, nl_pid, seq, __dump_filter_nlmsg,
3197084566aSAndrii Nakryiko 			    dump_filter_nlmsg, cookie);
3207084566aSAndrii Nakryiko }
3217084566aSAndrii Nakryiko 
__dump_link_nlmsg(struct nlmsghdr * nlh,dump_nlmsg_t dump_link_nlmsg,void * cookie)3227084566aSAndrii Nakryiko static int __dump_link_nlmsg(struct nlmsghdr *nlh,
3237084566aSAndrii Nakryiko 			     dump_nlmsg_t dump_link_nlmsg, void *cookie)
3247084566aSAndrii Nakryiko {
3257084566aSAndrii Nakryiko 	struct nlattr *tb[IFLA_MAX + 1], *attr;
3267084566aSAndrii Nakryiko 	struct ifinfomsg *ifi = NLMSG_DATA(nlh);
3277084566aSAndrii Nakryiko 	int len;
3287084566aSAndrii Nakryiko 
3297084566aSAndrii Nakryiko 	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
3307084566aSAndrii Nakryiko 	attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
3317084566aSAndrii Nakryiko 	if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
3327084566aSAndrii Nakryiko 		return -LIBBPF_ERRNO__NLPARSE;
3337084566aSAndrii Nakryiko 
3347084566aSAndrii Nakryiko 	return dump_link_nlmsg(cookie, ifi, tb);
3357084566aSAndrii Nakryiko }
3367084566aSAndrii Nakryiko 
netlink_get_link(int sock,unsigned int nl_pid,dump_nlmsg_t dump_link_nlmsg,void * cookie)3377084566aSAndrii Nakryiko static int netlink_get_link(int sock, unsigned int nl_pid,
3387084566aSAndrii Nakryiko 			    dump_nlmsg_t dump_link_nlmsg, void *cookie)
3397084566aSAndrii Nakryiko {
3407084566aSAndrii Nakryiko 	struct {
3417084566aSAndrii Nakryiko 		struct nlmsghdr nlh;
3427084566aSAndrii Nakryiko 		struct ifinfomsg ifm;
3437084566aSAndrii Nakryiko 	} req = {
3447084566aSAndrii Nakryiko 		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
3457084566aSAndrii Nakryiko 		.nlh.nlmsg_type = RTM_GETLINK,
3467084566aSAndrii Nakryiko 		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
3477084566aSAndrii Nakryiko 		.ifm.ifi_family = AF_PACKET,
3487084566aSAndrii Nakryiko 	};
3497084566aSAndrii Nakryiko 	int seq = time(NULL);
3507084566aSAndrii Nakryiko 
3517084566aSAndrii Nakryiko 	req.nlh.nlmsg_seq = seq;
3527084566aSAndrii Nakryiko 	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
3537084566aSAndrii Nakryiko 		return -errno;
3547084566aSAndrii Nakryiko 
3557084566aSAndrii Nakryiko 	return netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg,
3567084566aSAndrii Nakryiko 			    dump_link_nlmsg, cookie);
3577084566aSAndrii Nakryiko }
3587084566aSAndrii Nakryiko 
dump_link_nlmsg(void * cookie,void * msg,struct nlattr ** tb)359f6f3bac0SYonghong Song static int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb)
360f6f3bac0SYonghong Song {
361f6f3bac0SYonghong Song 	struct bpf_netdev_t *netinfo = cookie;
362f6f3bac0SYonghong Song 	struct ifinfomsg *ifinfo = msg;
363f6f3bac0SYonghong Song 
364f6f3bac0SYonghong Song 	if (netinfo->filter_idx > 0 && netinfo->filter_idx != ifinfo->ifi_index)
365f6f3bac0SYonghong Song 		return 0;
366f6f3bac0SYonghong Song 
367f6f3bac0SYonghong Song 	if (netinfo->used_len == netinfo->array_len) {
3687900efc1SYonghong Song 		netinfo->devices = realloc(netinfo->devices,
3697900efc1SYonghong Song 			(netinfo->array_len + 16) *
3707900efc1SYonghong Song 			sizeof(struct ip_devname_ifindex));
3717900efc1SYonghong Song 		if (!netinfo->devices)
3727900efc1SYonghong Song 			return -ENOMEM;
3737900efc1SYonghong Song 
374f6f3bac0SYonghong Song 		netinfo->array_len += 16;
375f6f3bac0SYonghong Song 	}
3767900efc1SYonghong Song 	netinfo->devices[netinfo->used_len].ifindex = ifinfo->ifi_index;
3777900efc1SYonghong Song 	snprintf(netinfo->devices[netinfo->used_len].devname,
3787900efc1SYonghong Song 		 sizeof(netinfo->devices[netinfo->used_len].devname),
3797900efc1SYonghong Song 		 "%s",
380f04bc8a4SAndrey Ignatov 		 tb[IFLA_IFNAME]
381f04bc8a4SAndrey Ignatov 			 ? libbpf_nla_getattr_str(tb[IFLA_IFNAME])
382f04bc8a4SAndrey Ignatov 			 : "");
3837900efc1SYonghong Song 	netinfo->used_len++;
384f6f3bac0SYonghong Song 
385f6f3bac0SYonghong Song 	return do_xdp_dump(ifinfo, tb);
386f6f3bac0SYonghong Song }
387f6f3bac0SYonghong Song 
dump_class_qdisc_nlmsg(void * cookie,void * msg,struct nlattr ** tb)388f6f3bac0SYonghong Song static int dump_class_qdisc_nlmsg(void *cookie, void *msg, struct nlattr **tb)
389f6f3bac0SYonghong Song {
390f6f3bac0SYonghong Song 	struct bpf_tcinfo_t *tcinfo = cookie;
391f6f3bac0SYonghong Song 	struct tcmsg *info = msg;
392f6f3bac0SYonghong Song 
393f6f3bac0SYonghong Song 	if (tcinfo->is_qdisc) {
394f6f3bac0SYonghong Song 		/* skip clsact qdisc */
395f6f3bac0SYonghong Song 		if (tb[TCA_KIND] &&
396f04bc8a4SAndrey Ignatov 		    strcmp(libbpf_nla_data(tb[TCA_KIND]), "clsact") == 0)
397f6f3bac0SYonghong Song 			return 0;
398f6f3bac0SYonghong Song 		if (info->tcm_handle == 0)
399f6f3bac0SYonghong Song 			return 0;
400f6f3bac0SYonghong Song 	}
401f6f3bac0SYonghong Song 
402f6f3bac0SYonghong Song 	if (tcinfo->used_len == tcinfo->array_len) {
403f6f3bac0SYonghong Song 		tcinfo->handle_array = realloc(tcinfo->handle_array,
404f6f3bac0SYonghong Song 			(tcinfo->array_len + 16) * sizeof(struct tc_kind_handle));
4057900efc1SYonghong Song 		if (!tcinfo->handle_array)
4067900efc1SYonghong Song 			return -ENOMEM;
4077900efc1SYonghong Song 
408f6f3bac0SYonghong Song 		tcinfo->array_len += 16;
409f6f3bac0SYonghong Song 	}
410f6f3bac0SYonghong Song 	tcinfo->handle_array[tcinfo->used_len].handle = info->tcm_handle;
411f6f3bac0SYonghong Song 	snprintf(tcinfo->handle_array[tcinfo->used_len].kind,
412f6f3bac0SYonghong Song 		 sizeof(tcinfo->handle_array[tcinfo->used_len].kind),
4137900efc1SYonghong Song 		 "%s",
414f04bc8a4SAndrey Ignatov 		 tb[TCA_KIND]
415f04bc8a4SAndrey Ignatov 			 ? libbpf_nla_getattr_str(tb[TCA_KIND])
416f04bc8a4SAndrey Ignatov 			 : "unknown");
417f6f3bac0SYonghong Song 	tcinfo->used_len++;
418f6f3bac0SYonghong Song 
419f6f3bac0SYonghong Song 	return 0;
420f6f3bac0SYonghong Song }
421f6f3bac0SYonghong Song 
dump_filter_nlmsg(void * cookie,void * msg,struct nlattr ** tb)422f6f3bac0SYonghong Song static int dump_filter_nlmsg(void *cookie, void *msg, struct nlattr **tb)
423f6f3bac0SYonghong Song {
4247900efc1SYonghong Song 	const struct bpf_filter_t *filter_info = cookie;
425f6f3bac0SYonghong Song 
4267900efc1SYonghong Song 	return do_filter_dump((struct tcmsg *)msg, tb, filter_info->kind,
4277900efc1SYonghong Song 			      filter_info->devname, filter_info->ifindex);
428f6f3bac0SYonghong Song }
429f6f3bac0SYonghong Song 
__show_dev_tc_bpf_name(__u32 id,char * name,size_t len)430*57c61da8SDaniel Borkmann static int __show_dev_tc_bpf_name(__u32 id, char *name, size_t len)
431*57c61da8SDaniel Borkmann {
432*57c61da8SDaniel Borkmann 	struct bpf_prog_info info = {};
433*57c61da8SDaniel Borkmann 	__u32 ilen = sizeof(info);
434*57c61da8SDaniel Borkmann 	int fd, ret;
435*57c61da8SDaniel Borkmann 
436*57c61da8SDaniel Borkmann 	fd = bpf_prog_get_fd_by_id(id);
437*57c61da8SDaniel Borkmann 	if (fd < 0)
438*57c61da8SDaniel Borkmann 		return fd;
439*57c61da8SDaniel Borkmann 	ret = bpf_obj_get_info_by_fd(fd, &info, &ilen);
440*57c61da8SDaniel Borkmann 	if (ret < 0)
441*57c61da8SDaniel Borkmann 		goto out;
442*57c61da8SDaniel Borkmann 	ret = -ENOENT;
443*57c61da8SDaniel Borkmann 	if (info.name[0]) {
444*57c61da8SDaniel Borkmann 		get_prog_full_name(&info, fd, name, len);
445*57c61da8SDaniel Borkmann 		ret = 0;
446*57c61da8SDaniel Borkmann 	}
447*57c61da8SDaniel Borkmann out:
448*57c61da8SDaniel Borkmann 	close(fd);
449*57c61da8SDaniel Borkmann 	return ret;
450*57c61da8SDaniel Borkmann }
451*57c61da8SDaniel Borkmann 
__show_dev_tc_bpf(const struct ip_devname_ifindex * dev,const enum bpf_attach_type loc)452*57c61da8SDaniel Borkmann static void __show_dev_tc_bpf(const struct ip_devname_ifindex *dev,
453*57c61da8SDaniel Borkmann 			      const enum bpf_attach_type loc)
454*57c61da8SDaniel Borkmann {
455*57c61da8SDaniel Borkmann 	__u32 prog_flags[64] = {}, link_flags[64] = {}, i, j;
456*57c61da8SDaniel Borkmann 	__u32 prog_ids[64] = {}, link_ids[64] = {};
457*57c61da8SDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
458*57c61da8SDaniel Borkmann 	char prog_name[MAX_PROG_FULL_NAME];
459*57c61da8SDaniel Borkmann 	int ret;
460*57c61da8SDaniel Borkmann 
461*57c61da8SDaniel Borkmann 	optq.prog_ids = prog_ids;
462*57c61da8SDaniel Borkmann 	optq.prog_attach_flags = prog_flags;
463*57c61da8SDaniel Borkmann 	optq.link_ids = link_ids;
464*57c61da8SDaniel Borkmann 	optq.link_attach_flags = link_flags;
465*57c61da8SDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
466*57c61da8SDaniel Borkmann 
467*57c61da8SDaniel Borkmann 	ret = bpf_prog_query_opts(dev->ifindex, loc, &optq);
468*57c61da8SDaniel Borkmann 	if (ret)
469*57c61da8SDaniel Borkmann 		return;
470*57c61da8SDaniel Borkmann 	for (i = 0; i < optq.count; i++) {
471*57c61da8SDaniel Borkmann 		NET_START_OBJECT;
472*57c61da8SDaniel Borkmann 		NET_DUMP_STR("devname", "%s", dev->devname);
473*57c61da8SDaniel Borkmann 		NET_DUMP_UINT("ifindex", "(%u)", dev->ifindex);
474*57c61da8SDaniel Borkmann 		NET_DUMP_STR("kind", " %s", attach_loc_strings[loc]);
475*57c61da8SDaniel Borkmann 		ret = __show_dev_tc_bpf_name(prog_ids[i], prog_name,
476*57c61da8SDaniel Borkmann 					     sizeof(prog_name));
477*57c61da8SDaniel Borkmann 		if (!ret)
478*57c61da8SDaniel Borkmann 			NET_DUMP_STR("name", " %s", prog_name);
479*57c61da8SDaniel Borkmann 		NET_DUMP_UINT("prog_id", " prog_id %u ", prog_ids[i]);
480*57c61da8SDaniel Borkmann 		if (prog_flags[i] || json_output) {
481*57c61da8SDaniel Borkmann 			NET_START_ARRAY("prog_flags", "%s ");
482*57c61da8SDaniel Borkmann 			for (j = 0; prog_flags[i] && j < 32; j++) {
483*57c61da8SDaniel Borkmann 				if (!(prog_flags[i] & (1 << j)))
484*57c61da8SDaniel Borkmann 					continue;
485*57c61da8SDaniel Borkmann 				NET_DUMP_UINT_ONLY(1 << j);
486*57c61da8SDaniel Borkmann 			}
487*57c61da8SDaniel Borkmann 			NET_END_ARRAY("");
488*57c61da8SDaniel Borkmann 		}
489*57c61da8SDaniel Borkmann 		if (link_ids[i] || json_output) {
490*57c61da8SDaniel Borkmann 			NET_DUMP_UINT("link_id", "link_id %u ", link_ids[i]);
491*57c61da8SDaniel Borkmann 			if (link_flags[i] || json_output) {
492*57c61da8SDaniel Borkmann 				NET_START_ARRAY("link_flags", "%s ");
493*57c61da8SDaniel Borkmann 				for (j = 0; link_flags[i] && j < 32; j++) {
494*57c61da8SDaniel Borkmann 					if (!(link_flags[i] & (1 << j)))
495*57c61da8SDaniel Borkmann 						continue;
496*57c61da8SDaniel Borkmann 					NET_DUMP_UINT_ONLY(1 << j);
497*57c61da8SDaniel Borkmann 				}
498*57c61da8SDaniel Borkmann 				NET_END_ARRAY("");
499*57c61da8SDaniel Borkmann 			}
500*57c61da8SDaniel Borkmann 		}
501*57c61da8SDaniel Borkmann 		NET_END_OBJECT_FINAL;
502*57c61da8SDaniel Borkmann 	}
503*57c61da8SDaniel Borkmann }
504*57c61da8SDaniel Borkmann 
show_dev_tc_bpf(struct ip_devname_ifindex * dev)505*57c61da8SDaniel Borkmann static void show_dev_tc_bpf(struct ip_devname_ifindex *dev)
506*57c61da8SDaniel Borkmann {
507*57c61da8SDaniel Borkmann 	__show_dev_tc_bpf(dev, BPF_TCX_INGRESS);
508*57c61da8SDaniel Borkmann 	__show_dev_tc_bpf(dev, BPF_TCX_EGRESS);
509*57c61da8SDaniel Borkmann }
510*57c61da8SDaniel Borkmann 
show_dev_tc_bpf_classic(int sock,unsigned int nl_pid,struct ip_devname_ifindex * dev)511*57c61da8SDaniel Borkmann static int show_dev_tc_bpf_classic(int sock, unsigned int nl_pid,
5127900efc1SYonghong Song 				   struct ip_devname_ifindex *dev)
513f6f3bac0SYonghong Song {
5147900efc1SYonghong Song 	struct bpf_filter_t filter_info;
515f6f3bac0SYonghong Song 	struct bpf_tcinfo_t tcinfo;
5167900efc1SYonghong Song 	int i, handle, ret = 0;
517f6f3bac0SYonghong Song 
518f6f3bac0SYonghong Song 	tcinfo.handle_array = NULL;
519f6f3bac0SYonghong Song 	tcinfo.used_len = 0;
520f6f3bac0SYonghong Song 	tcinfo.array_len = 0;
521f6f3bac0SYonghong Song 
522f6f3bac0SYonghong Song 	tcinfo.is_qdisc = false;
5237084566aSAndrii Nakryiko 	ret = netlink_get_class(sock, nl_pid, dev->ifindex,
524aae57780SAndrey Ignatov 				dump_class_qdisc_nlmsg, &tcinfo);
525f6f3bac0SYonghong Song 	if (ret)
5267900efc1SYonghong Song 		goto out;
527f6f3bac0SYonghong Song 
528f6f3bac0SYonghong Song 	tcinfo.is_qdisc = true;
5297084566aSAndrii Nakryiko 	ret = netlink_get_qdisc(sock, nl_pid, dev->ifindex,
530aae57780SAndrey Ignatov 				dump_class_qdisc_nlmsg, &tcinfo);
531f6f3bac0SYonghong Song 	if (ret)
5327900efc1SYonghong Song 		goto out;
533f6f3bac0SYonghong Song 
5347900efc1SYonghong Song 	filter_info.devname = dev->devname;
5357900efc1SYonghong Song 	filter_info.ifindex = dev->ifindex;
536f6f3bac0SYonghong Song 	for (i = 0; i < tcinfo.used_len; i++) {
5377900efc1SYonghong Song 		filter_info.kind = tcinfo.handle_array[i].kind;
5387084566aSAndrii Nakryiko 		ret = netlink_get_filter(sock, nl_pid, dev->ifindex,
539f6f3bac0SYonghong Song 					 tcinfo.handle_array[i].handle,
540aae57780SAndrey Ignatov 					 dump_filter_nlmsg, &filter_info);
541f6f3bac0SYonghong Song 		if (ret)
5427900efc1SYonghong Song 			goto out;
543f6f3bac0SYonghong Song 	}
544f6f3bac0SYonghong Song 
545f6f3bac0SYonghong Song 	/* root, ingress and egress handle */
546f6f3bac0SYonghong Song 	handle = TC_H_ROOT;
5477900efc1SYonghong Song 	filter_info.kind = "root";
5487084566aSAndrii Nakryiko 	ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle,
5497900efc1SYonghong Song 				 dump_filter_nlmsg, &filter_info);
550f6f3bac0SYonghong Song 	if (ret)
5517900efc1SYonghong Song 		goto out;
552f6f3bac0SYonghong Song 
553f6f3bac0SYonghong Song 	handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
5547900efc1SYonghong Song 	filter_info.kind = "clsact/ingress";
5557084566aSAndrii Nakryiko 	ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle,
5567900efc1SYonghong Song 				 dump_filter_nlmsg, &filter_info);
557f6f3bac0SYonghong Song 	if (ret)
5587900efc1SYonghong Song 		goto out;
559f6f3bac0SYonghong Song 
560f6f3bac0SYonghong Song 	handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS);
5617900efc1SYonghong Song 	filter_info.kind = "clsact/egress";
5627084566aSAndrii Nakryiko 	ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle,
5637900efc1SYonghong Song 				 dump_filter_nlmsg, &filter_info);
564f6f3bac0SYonghong Song 	if (ret)
5657900efc1SYonghong Song 		goto out;
566f6f3bac0SYonghong Song 
5677900efc1SYonghong Song out:
5687900efc1SYonghong Song 	free(tcinfo.handle_array);
569f6f3bac0SYonghong Song 	return 0;
570f6f3bac0SYonghong Song }
571f6f3bac0SYonghong Song 
query_flow_dissector(struct bpf_attach_info * attach_info)5727f0c57feSStanislav Fomichev static int query_flow_dissector(struct bpf_attach_info *attach_info)
5737f0c57feSStanislav Fomichev {
5747f0c57feSStanislav Fomichev 	__u32 attach_flags;
5757f0c57feSStanislav Fomichev 	__u32 prog_ids[1];
5767f0c57feSStanislav Fomichev 	__u32 prog_cnt;
5777f0c57feSStanislav Fomichev 	int err;
5787f0c57feSStanislav Fomichev 	int fd;
5797f0c57feSStanislav Fomichev 
5807f0c57feSStanislav Fomichev 	fd = open("/proc/self/ns/net", O_RDONLY);
5817f0c57feSStanislav Fomichev 	if (fd < 0) {
5828a15d5ceSQuentin Monnet 		p_err("can't open /proc/self/ns/net: %s",
5837f0c57feSStanislav Fomichev 		      strerror(errno));
5847f0c57feSStanislav Fomichev 		return -1;
5857f0c57feSStanislav Fomichev 	}
5867f0c57feSStanislav Fomichev 	prog_cnt = ARRAY_SIZE(prog_ids);
5877f0c57feSStanislav Fomichev 	err = bpf_prog_query(fd, BPF_FLOW_DISSECTOR, 0,
5887f0c57feSStanislav Fomichev 			     &attach_flags, prog_ids, &prog_cnt);
5897f0c57feSStanislav Fomichev 	close(fd);
5907f0c57feSStanislav Fomichev 	if (err) {
5917f0c57feSStanislav Fomichev 		if (errno == EINVAL) {
5927f0c57feSStanislav Fomichev 			/* Older kernel's don't support querying
5937f0c57feSStanislav Fomichev 			 * flow dissector programs.
5947f0c57feSStanislav Fomichev 			 */
5957f0c57feSStanislav Fomichev 			errno = 0;
5967f0c57feSStanislav Fomichev 			return 0;
5977f0c57feSStanislav Fomichev 		}
5987f0c57feSStanislav Fomichev 		p_err("can't query prog: %s", strerror(errno));
5997f0c57feSStanislav Fomichev 		return -1;
6007f0c57feSStanislav Fomichev 	}
6017f0c57feSStanislav Fomichev 
6027f0c57feSStanislav Fomichev 	if (prog_cnt == 1)
6037f0c57feSStanislav Fomichev 		attach_info->flow_dissector_id = prog_ids[0];
6047f0c57feSStanislav Fomichev 
6057f0c57feSStanislav Fomichev 	return 0;
6067f0c57feSStanislav Fomichev }
6077f0c57feSStanislav Fomichev 
net_parse_dev(int * argc,char *** argv)60804949cccSDaniel T. Lee static int net_parse_dev(int *argc, char ***argv)
60904949cccSDaniel T. Lee {
61004949cccSDaniel T. Lee 	int ifindex;
61104949cccSDaniel T. Lee 
61204949cccSDaniel T. Lee 	if (is_prefix(**argv, "dev")) {
61304949cccSDaniel T. Lee 		NEXT_ARGP();
61404949cccSDaniel T. Lee 
61504949cccSDaniel T. Lee 		ifindex = if_nametoindex(**argv);
61604949cccSDaniel T. Lee 		if (!ifindex)
61704949cccSDaniel T. Lee 			p_err("invalid devname %s", **argv);
61804949cccSDaniel T. Lee 
61904949cccSDaniel T. Lee 		NEXT_ARGP();
62004949cccSDaniel T. Lee 	} else {
62104949cccSDaniel T. Lee 		p_err("expected 'dev', got: '%s'?", **argv);
62204949cccSDaniel T. Lee 		return -1;
62304949cccSDaniel T. Lee 	}
62404949cccSDaniel T. Lee 
62504949cccSDaniel T. Lee 	return ifindex;
62604949cccSDaniel T. Lee }
62704949cccSDaniel T. Lee 
do_attach_detach_xdp(int progfd,enum net_attach_type attach_type,int ifindex,bool overwrite)62804949cccSDaniel T. Lee static int do_attach_detach_xdp(int progfd, enum net_attach_type attach_type,
62904949cccSDaniel T. Lee 				int ifindex, bool overwrite)
63004949cccSDaniel T. Lee {
63104949cccSDaniel T. Lee 	__u32 flags = 0;
63204949cccSDaniel T. Lee 
63304949cccSDaniel T. Lee 	if (!overwrite)
63404949cccSDaniel T. Lee 		flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
63504949cccSDaniel T. Lee 	if (attach_type == NET_ATTACH_TYPE_XDP_GENERIC)
63604949cccSDaniel T. Lee 		flags |= XDP_FLAGS_SKB_MODE;
63704949cccSDaniel T. Lee 	if (attach_type == NET_ATTACH_TYPE_XDP_DRIVER)
63804949cccSDaniel T. Lee 		flags |= XDP_FLAGS_DRV_MODE;
63904949cccSDaniel T. Lee 	if (attach_type == NET_ATTACH_TYPE_XDP_OFFLOAD)
64004949cccSDaniel T. Lee 		flags |= XDP_FLAGS_HW_MODE;
64104949cccSDaniel T. Lee 
642c86575ecSAndrii Nakryiko 	return bpf_xdp_attach(ifindex, progfd, flags, NULL);
64304949cccSDaniel T. Lee }
64404949cccSDaniel T. Lee 
do_attach(int argc,char ** argv)64504949cccSDaniel T. Lee static int do_attach(int argc, char **argv)
64604949cccSDaniel T. Lee {
64704949cccSDaniel T. Lee 	enum net_attach_type attach_type;
64804949cccSDaniel T. Lee 	int progfd, ifindex, err = 0;
64904949cccSDaniel T. Lee 	bool overwrite = false;
65004949cccSDaniel T. Lee 
65104949cccSDaniel T. Lee 	/* parse attach args */
65204949cccSDaniel T. Lee 	if (!REQ_ARGS(5))
65304949cccSDaniel T. Lee 		return -EINVAL;
65404949cccSDaniel T. Lee 
65504949cccSDaniel T. Lee 	attach_type = parse_attach_type(*argv);
65604949cccSDaniel T. Lee 	if (attach_type == net_attach_type_size) {
65704949cccSDaniel T. Lee 		p_err("invalid net attach/detach type: %s", *argv);
65804949cccSDaniel T. Lee 		return -EINVAL;
65904949cccSDaniel T. Lee 	}
66004949cccSDaniel T. Lee 	NEXT_ARG();
66104949cccSDaniel T. Lee 
66204949cccSDaniel T. Lee 	progfd = prog_parse_fd(&argc, &argv);
66304949cccSDaniel T. Lee 	if (progfd < 0)
66404949cccSDaniel T. Lee 		return -EINVAL;
66504949cccSDaniel T. Lee 
66604949cccSDaniel T. Lee 	ifindex = net_parse_dev(&argc, &argv);
66704949cccSDaniel T. Lee 	if (ifindex < 1) {
66850431b45SWang Hai 		err = -EINVAL;
66950431b45SWang Hai 		goto cleanup;
67004949cccSDaniel T. Lee 	}
67104949cccSDaniel T. Lee 
67204949cccSDaniel T. Lee 	if (argc) {
67304949cccSDaniel T. Lee 		if (is_prefix(*argv, "overwrite")) {
67404949cccSDaniel T. Lee 			overwrite = true;
67504949cccSDaniel T. Lee 		} else {
67604949cccSDaniel T. Lee 			p_err("expected 'overwrite', got: '%s'?", *argv);
67750431b45SWang Hai 			err = -EINVAL;
67850431b45SWang Hai 			goto cleanup;
67904949cccSDaniel T. Lee 		}
68004949cccSDaniel T. Lee 	}
68104949cccSDaniel T. Lee 
68204949cccSDaniel T. Lee 	/* attach xdp prog */
68304949cccSDaniel T. Lee 	if (is_prefix("xdp", attach_type_strings[attach_type]))
68404949cccSDaniel T. Lee 		err = do_attach_detach_xdp(progfd, attach_type, ifindex,
68504949cccSDaniel T. Lee 					   overwrite);
68650431b45SWang Hai 	if (err) {
68704949cccSDaniel T. Lee 		p_err("interface %s attach failed: %s",
68804949cccSDaniel T. Lee 		      attach_type_strings[attach_type], strerror(-err));
68950431b45SWang Hai 		goto cleanup;
69004949cccSDaniel T. Lee 	}
69104949cccSDaniel T. Lee 
69204949cccSDaniel T. Lee 	if (json_output)
69304949cccSDaniel T. Lee 		jsonw_null(json_wtr);
69450431b45SWang Hai cleanup:
69550431b45SWang Hai 	close(progfd);
69650431b45SWang Hai 	return err;
69704949cccSDaniel T. Lee }
69804949cccSDaniel T. Lee 
do_detach(int argc,char ** argv)69937c7f863SDaniel T. Lee static int do_detach(int argc, char **argv)
70037c7f863SDaniel T. Lee {
70137c7f863SDaniel T. Lee 	enum net_attach_type attach_type;
70237c7f863SDaniel T. Lee 	int progfd, ifindex, err = 0;
70337c7f863SDaniel T. Lee 
70437c7f863SDaniel T. Lee 	/* parse detach args */
70537c7f863SDaniel T. Lee 	if (!REQ_ARGS(3))
70637c7f863SDaniel T. Lee 		return -EINVAL;
70737c7f863SDaniel T. Lee 
70837c7f863SDaniel T. Lee 	attach_type = parse_attach_type(*argv);
70937c7f863SDaniel T. Lee 	if (attach_type == net_attach_type_size) {
71037c7f863SDaniel T. Lee 		p_err("invalid net attach/detach type: %s", *argv);
71137c7f863SDaniel T. Lee 		return -EINVAL;
71237c7f863SDaniel T. Lee 	}
71337c7f863SDaniel T. Lee 	NEXT_ARG();
71437c7f863SDaniel T. Lee 
71537c7f863SDaniel T. Lee 	ifindex = net_parse_dev(&argc, &argv);
71637c7f863SDaniel T. Lee 	if (ifindex < 1)
71737c7f863SDaniel T. Lee 		return -EINVAL;
71837c7f863SDaniel T. Lee 
71937c7f863SDaniel T. Lee 	/* detach xdp prog */
72037c7f863SDaniel T. Lee 	progfd = -1;
72137c7f863SDaniel T. Lee 	if (is_prefix("xdp", attach_type_strings[attach_type]))
72237c7f863SDaniel T. Lee 		err = do_attach_detach_xdp(progfd, attach_type, ifindex, NULL);
72337c7f863SDaniel T. Lee 
72437c7f863SDaniel T. Lee 	if (err < 0) {
72537c7f863SDaniel T. Lee 		p_err("interface %s detach failed: %s",
72637c7f863SDaniel T. Lee 		      attach_type_strings[attach_type], strerror(-err));
72737c7f863SDaniel T. Lee 		return err;
72837c7f863SDaniel T. Lee 	}
72937c7f863SDaniel T. Lee 
73037c7f863SDaniel T. Lee 	if (json_output)
73137c7f863SDaniel T. Lee 		jsonw_null(json_wtr);
73237c7f863SDaniel T. Lee 
73337c7f863SDaniel T. Lee 	return 0;
73437c7f863SDaniel T. Lee }
73537c7f863SDaniel T. Lee 
netfilter_link_compar(const void * a,const void * b)736d0fe92fbSFlorian Westphal static int netfilter_link_compar(const void *a, const void *b)
737d0fe92fbSFlorian Westphal {
738d0fe92fbSFlorian Westphal 	const struct bpf_link_info *nfa = a;
739d0fe92fbSFlorian Westphal 	const struct bpf_link_info *nfb = b;
740d0fe92fbSFlorian Westphal 	int delta;
741d0fe92fbSFlorian Westphal 
742d0fe92fbSFlorian Westphal 	delta = nfa->netfilter.pf - nfb->netfilter.pf;
743d0fe92fbSFlorian Westphal 	if (delta)
744d0fe92fbSFlorian Westphal 		return delta;
745d0fe92fbSFlorian Westphal 
746d0fe92fbSFlorian Westphal 	delta = nfa->netfilter.hooknum - nfb->netfilter.hooknum;
747d0fe92fbSFlorian Westphal 	if (delta)
748d0fe92fbSFlorian Westphal 		return delta;
749d0fe92fbSFlorian Westphal 
750d0fe92fbSFlorian Westphal 	if (nfa->netfilter.priority < nfb->netfilter.priority)
751d0fe92fbSFlorian Westphal 		return -1;
752d0fe92fbSFlorian Westphal 	if (nfa->netfilter.priority > nfb->netfilter.priority)
753d0fe92fbSFlorian Westphal 		return 1;
754d0fe92fbSFlorian Westphal 
755d0fe92fbSFlorian Westphal 	return nfa->netfilter.flags - nfb->netfilter.flags;
756d0fe92fbSFlorian Westphal }
757d0fe92fbSFlorian Westphal 
show_link_netfilter(void)758d0fe92fbSFlorian Westphal static void show_link_netfilter(void)
759d0fe92fbSFlorian Westphal {
760d0fe92fbSFlorian Westphal 	unsigned int nf_link_len = 0, nf_link_count = 0;
761d0fe92fbSFlorian Westphal 	struct bpf_link_info *nf_link_info = NULL;
762d0fe92fbSFlorian Westphal 	__u32 id = 0;
763d0fe92fbSFlorian Westphal 
764d0fe92fbSFlorian Westphal 	while (true) {
765d0fe92fbSFlorian Westphal 		struct bpf_link_info info;
766d0fe92fbSFlorian Westphal 		int fd, err;
767d0fe92fbSFlorian Westphal 		__u32 len;
768d0fe92fbSFlorian Westphal 
769d0fe92fbSFlorian Westphal 		err = bpf_link_get_next_id(id, &id);
770d0fe92fbSFlorian Westphal 		if (err) {
771d0fe92fbSFlorian Westphal 			if (errno == ENOENT)
772d0fe92fbSFlorian Westphal 				break;
773d0fe92fbSFlorian Westphal 			p_err("can't get next link: %s (id %d)", strerror(errno), id);
774d0fe92fbSFlorian Westphal 			break;
775d0fe92fbSFlorian Westphal 		}
776d0fe92fbSFlorian Westphal 
777d0fe92fbSFlorian Westphal 		fd = bpf_link_get_fd_by_id(id);
778d0fe92fbSFlorian Westphal 		if (fd < 0) {
779d0fe92fbSFlorian Westphal 			p_err("can't get link by id (%u): %s", id, strerror(errno));
780d0fe92fbSFlorian Westphal 			continue;
781d0fe92fbSFlorian Westphal 		}
782d0fe92fbSFlorian Westphal 
783d0fe92fbSFlorian Westphal 		memset(&info, 0, sizeof(info));
784d0fe92fbSFlorian Westphal 		len = sizeof(info);
785d0fe92fbSFlorian Westphal 
786d0fe92fbSFlorian Westphal 		err = bpf_link_get_info_by_fd(fd, &info, &len);
787d0fe92fbSFlorian Westphal 
788d0fe92fbSFlorian Westphal 		close(fd);
789d0fe92fbSFlorian Westphal 
790d0fe92fbSFlorian Westphal 		if (err) {
791d0fe92fbSFlorian Westphal 			p_err("can't get link info for fd %d: %s", fd, strerror(errno));
792d0fe92fbSFlorian Westphal 			continue;
793d0fe92fbSFlorian Westphal 		}
794d0fe92fbSFlorian Westphal 
795d0fe92fbSFlorian Westphal 		if (info.type != BPF_LINK_TYPE_NETFILTER)
796d0fe92fbSFlorian Westphal 			continue;
797d0fe92fbSFlorian Westphal 
798d0fe92fbSFlorian Westphal 		if (nf_link_count >= nf_link_len) {
799d0fe92fbSFlorian Westphal 			static const unsigned int max_link_count = INT_MAX / sizeof(info);
800d0fe92fbSFlorian Westphal 			struct bpf_link_info *expand;
801d0fe92fbSFlorian Westphal 
802d0fe92fbSFlorian Westphal 			if (nf_link_count > max_link_count) {
803d0fe92fbSFlorian Westphal 				p_err("cannot handle more than %u links\n", max_link_count);
804d0fe92fbSFlorian Westphal 				break;
805d0fe92fbSFlorian Westphal 			}
806d0fe92fbSFlorian Westphal 
807d0fe92fbSFlorian Westphal 			nf_link_len += 16;
808d0fe92fbSFlorian Westphal 
809d0fe92fbSFlorian Westphal 			expand = realloc(nf_link_info, nf_link_len * sizeof(info));
810d0fe92fbSFlorian Westphal 			if (!expand) {
811d0fe92fbSFlorian Westphal 				p_err("realloc: %s",  strerror(errno));
812d0fe92fbSFlorian Westphal 				break;
813d0fe92fbSFlorian Westphal 			}
814d0fe92fbSFlorian Westphal 
815d0fe92fbSFlorian Westphal 			nf_link_info = expand;
816d0fe92fbSFlorian Westphal 		}
817d0fe92fbSFlorian Westphal 
818d0fe92fbSFlorian Westphal 		nf_link_info[nf_link_count] = info;
819d0fe92fbSFlorian Westphal 		nf_link_count++;
820d0fe92fbSFlorian Westphal 	}
821d0fe92fbSFlorian Westphal 
822d0fe92fbSFlorian Westphal 	qsort(nf_link_info, nf_link_count, sizeof(*nf_link_info), netfilter_link_compar);
823d0fe92fbSFlorian Westphal 
824d0fe92fbSFlorian Westphal 	for (id = 0; id < nf_link_count; id++) {
825d0fe92fbSFlorian Westphal 		NET_START_OBJECT;
826d0fe92fbSFlorian Westphal 		if (json_output)
827d0fe92fbSFlorian Westphal 			netfilter_dump_json(&nf_link_info[id], json_wtr);
828d0fe92fbSFlorian Westphal 		else
829d0fe92fbSFlorian Westphal 			netfilter_dump_plain(&nf_link_info[id]);
830d0fe92fbSFlorian Westphal 
831d0fe92fbSFlorian Westphal 		NET_DUMP_UINT("id", " prog_id %u", nf_link_info[id].prog_id);
832d0fe92fbSFlorian Westphal 		NET_END_OBJECT;
833d0fe92fbSFlorian Westphal 	}
834d0fe92fbSFlorian Westphal 
835d0fe92fbSFlorian Westphal 	free(nf_link_info);
836d0fe92fbSFlorian Westphal }
837d0fe92fbSFlorian Westphal 
do_show(int argc,char ** argv)838f6f3bac0SYonghong Song static int do_show(int argc, char **argv)
839f6f3bac0SYonghong Song {
8407f0c57feSStanislav Fomichev 	struct bpf_attach_info attach_info = {};
841f6f3bac0SYonghong Song 	int i, sock, ret, filter_idx = -1;
842f6f3bac0SYonghong Song 	struct bpf_netdev_t dev_array;
8437084566aSAndrii Nakryiko 	unsigned int nl_pid = 0;
844f6f3bac0SYonghong Song 	char err_buf[256];
845f6f3bac0SYonghong Song 
846f6f3bac0SYonghong Song 	if (argc == 2) {
84704949cccSDaniel T. Lee 		filter_idx = net_parse_dev(&argc, &argv);
84804949cccSDaniel T. Lee 		if (filter_idx < 1)
849f6f3bac0SYonghong Song 			return -1;
850f6f3bac0SYonghong Song 	} else if (argc != 0) {
851f6f3bac0SYonghong Song 		usage();
852f6f3bac0SYonghong Song 	}
853f6f3bac0SYonghong Song 
8547f0c57feSStanislav Fomichev 	ret = query_flow_dissector(&attach_info);
8557f0c57feSStanislav Fomichev 	if (ret)
8567f0c57feSStanislav Fomichev 		return -1;
8577f0c57feSStanislav Fomichev 
8587084566aSAndrii Nakryiko 	sock = netlink_open(&nl_pid);
859f6f3bac0SYonghong Song 	if (sock < 0) {
860f6f3bac0SYonghong Song 		fprintf(stderr, "failed to open netlink sock\n");
861f6f3bac0SYonghong Song 		return -1;
862f6f3bac0SYonghong Song 	}
863f6f3bac0SYonghong Song 
8647900efc1SYonghong Song 	dev_array.devices = NULL;
865f6f3bac0SYonghong Song 	dev_array.used_len = 0;
866f6f3bac0SYonghong Song 	dev_array.array_len = 0;
867f6f3bac0SYonghong Song 	dev_array.filter_idx = filter_idx;
868f6f3bac0SYonghong Song 
869f6f3bac0SYonghong Song 	if (json_output)
870f6f3bac0SYonghong Song 		jsonw_start_array(json_wtr);
871f6f3bac0SYonghong Song 	NET_START_OBJECT;
8727900efc1SYonghong Song 	NET_START_ARRAY("xdp", "%s:\n");
8737084566aSAndrii Nakryiko 	ret = netlink_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array);
874f6f3bac0SYonghong Song 	NET_END_ARRAY("\n");
875f6f3bac0SYonghong Song 
876f6f3bac0SYonghong Song 	if (!ret) {
8777900efc1SYonghong Song 		NET_START_ARRAY("tc", "%s:\n");
878f6f3bac0SYonghong Song 		for (i = 0; i < dev_array.used_len; i++) {
879*57c61da8SDaniel Borkmann 			show_dev_tc_bpf(&dev_array.devices[i]);
880*57c61da8SDaniel Borkmann 			ret = show_dev_tc_bpf_classic(sock, nl_pid,
8817900efc1SYonghong Song 						      &dev_array.devices[i]);
882f6f3bac0SYonghong Song 			if (ret)
883f6f3bac0SYonghong Song 				break;
884f6f3bac0SYonghong Song 		}
885f6f3bac0SYonghong Song 		NET_END_ARRAY("\n");
886f6f3bac0SYonghong Song 	}
8877f0c57feSStanislav Fomichev 
8887f0c57feSStanislav Fomichev 	NET_START_ARRAY("flow_dissector", "%s:\n");
8897f0c57feSStanislav Fomichev 	if (attach_info.flow_dissector_id > 0)
8907f0c57feSStanislav Fomichev 		NET_DUMP_UINT("id", "id %u", attach_info.flow_dissector_id);
8917f0c57feSStanislav Fomichev 	NET_END_ARRAY("\n");
8927f0c57feSStanislav Fomichev 
893d0fe92fbSFlorian Westphal 	NET_START_ARRAY("netfilter", "%s:\n");
894d0fe92fbSFlorian Westphal 	show_link_netfilter();
895d0fe92fbSFlorian Westphal 	NET_END_ARRAY("\n");
896d0fe92fbSFlorian Westphal 
897f6f3bac0SYonghong Song 	NET_END_OBJECT;
898f6f3bac0SYonghong Song 	if (json_output)
899f6f3bac0SYonghong Song 		jsonw_end_array(json_wtr);
900f6f3bac0SYonghong Song 
901f6f3bac0SYonghong Song 	if (ret) {
902f6f3bac0SYonghong Song 		if (json_output)
903f6f3bac0SYonghong Song 			jsonw_null(json_wtr);
904f6f3bac0SYonghong Song 		libbpf_strerror(ret, err_buf, sizeof(err_buf));
905f6f3bac0SYonghong Song 		fprintf(stderr, "Error: %s\n", err_buf);
906f6f3bac0SYonghong Song 	}
9077900efc1SYonghong Song 	free(dev_array.devices);
908f6f3bac0SYonghong Song 	close(sock);
909f6f3bac0SYonghong Song 	return ret;
910f6f3bac0SYonghong Song }
911f6f3bac0SYonghong Song 
do_help(int argc,char ** argv)912f6f3bac0SYonghong Song static int do_help(int argc, char **argv)
913f6f3bac0SYonghong Song {
914f6f3bac0SYonghong Song 	if (json_output) {
915f6f3bac0SYonghong Song 		jsonw_null(json_wtr);
916f6f3bac0SYonghong Song 		return 0;
917f6f3bac0SYonghong Song 	}
918f6f3bac0SYonghong Song 
919f6f3bac0SYonghong Song 	fprintf(stderr,
92090040351SQuentin Monnet 		"Usage: %1$s %2$s { show | list } [dev <devname>]\n"
92190040351SQuentin Monnet 		"       %1$s %2$s attach ATTACH_TYPE PROG dev <devname> [ overwrite ]\n"
92290040351SQuentin Monnet 		"       %1$s %2$s detach ATTACH_TYPE dev <devname>\n"
92390040351SQuentin Monnet 		"       %1$s %2$s help\n"
92404949cccSDaniel T. Lee 		"\n"
92504949cccSDaniel T. Lee 		"       " HELP_SPEC_PROGRAM "\n"
92604949cccSDaniel T. Lee 		"       ATTACH_TYPE := { xdp | xdpgeneric | xdpdrv | xdpoffload }\n"
927c07ba629SQuentin Monnet 		"       " HELP_SPEC_OPTIONS " }\n"
92804949cccSDaniel T. Lee 		"\n"
929*57c61da8SDaniel Borkmann 		"Note: Only xdp, tcx, tc, flow_dissector and netfilter attachments\n"
930*57c61da8SDaniel Borkmann 		"      are currently supported.\n"
9317900efc1SYonghong Song 		"      For progs attached to cgroups, use \"bpftool cgroup\"\n"
9327900efc1SYonghong Song 		"      to dump program attachments. For program types\n"
9337900efc1SYonghong Song 		"      sk_{filter,skb,msg,reuseport} and lwt/seg6, please\n"
93490040351SQuentin Monnet 		"      consult iproute2.\n"
93590040351SQuentin Monnet 		"",
93637c7f863SDaniel T. Lee 		bin_name, argv[-2]);
937f6f3bac0SYonghong Song 
938f6f3bac0SYonghong Song 	return 0;
939f6f3bac0SYonghong Song }
940f6f3bac0SYonghong Song 
941f6f3bac0SYonghong Song static const struct cmd cmds[] = {
942f6f3bac0SYonghong Song 	{ "show",	do_show },
943f6f3bac0SYonghong Song 	{ "list",	do_show },
94404949cccSDaniel T. Lee 	{ "attach",	do_attach },
94537c7f863SDaniel T. Lee 	{ "detach",	do_detach },
946f6f3bac0SYonghong Song 	{ "help",	do_help },
947f6f3bac0SYonghong Song 	{ 0 }
948f6f3bac0SYonghong Song };
949f6f3bac0SYonghong Song 
do_net(int argc,char ** argv)950f6f3bac0SYonghong Song int do_net(int argc, char **argv)
951f6f3bac0SYonghong Song {
952f6f3bac0SYonghong Song 	return cmd_select(cmds, argc, argv, do_help);
953f6f3bac0SYonghong Song }
954