File Index
Files and directories
Sign in
FILE

net.c

File preview with syntax highlighting.

linux-6.19.14/tools/bpf/bpftool/net.c C
   1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
   2// Copyright (C) 2018 Facebook
   3
   4#ifndef _GNU_SOURCE
   5#define _GNU_SOURCE
   6#endif
   7#include <errno.h>
   8#include <fcntl.h>
   9#include <stdlib.h>
  10#include <string.h>
  11#include <time.h>
  12#include <unistd.h>
  13#include <bpf/bpf.h>
  14#include <bpf/libbpf.h>
  15#include <net/if.h>
  16#include <linux/rtnetlink.h>
  17#include <linux/socket.h>
  18#include <linux/tc_act/tc_bpf.h>
  19#include <sys/socket.h>
  20#include <sys/stat.h>
  21#include <sys/types.h>
  22
  23#include "bpf/nlattr.h"
  24#include "main.h"
  25#include "netlink_dumper.h"
  26
  27#ifndef SOL_NETLINK
  28#define SOL_NETLINK 270
  29#endif
  30
  31struct ip_devname_ifindex {
  32	char	devname[64];
  33	int	ifindex;
  34};
  35
  36struct bpf_netdev_t {
  37	struct ip_devname_ifindex *devices;
  38	int	used_len;
  39	int	array_len;
  40	int	filter_idx;
  41};
  42
  43struct tc_kind_handle {
  44	char	kind[64];
  45	int	handle;
  46};
  47
  48struct bpf_tcinfo_t {
  49	struct tc_kind_handle	*handle_array;
  50	int			used_len;
  51	int			array_len;
  52	bool			is_qdisc;
  53};
  54
  55struct bpf_filter_t {
  56	const char	*kind;
  57	const char	*devname;
  58	int		ifindex;
  59};
  60
  61struct bpf_attach_info {
  62	__u32 flow_dissector_id;
  63};
  64
  65enum net_attach_type {
  66	NET_ATTACH_TYPE_XDP,
  67	NET_ATTACH_TYPE_XDP_GENERIC,
  68	NET_ATTACH_TYPE_XDP_DRIVER,
  69	NET_ATTACH_TYPE_XDP_OFFLOAD,
  70	NET_ATTACH_TYPE_TCX_INGRESS,
  71	NET_ATTACH_TYPE_TCX_EGRESS,
  72};
  73
  74static const char * const attach_type_strings[] = {
  75	[NET_ATTACH_TYPE_XDP]		= "xdp",
  76	[NET_ATTACH_TYPE_XDP_GENERIC]	= "xdpgeneric",
  77	[NET_ATTACH_TYPE_XDP_DRIVER]	= "xdpdrv",
  78	[NET_ATTACH_TYPE_XDP_OFFLOAD]	= "xdpoffload",
  79	[NET_ATTACH_TYPE_TCX_INGRESS]	= "tcx_ingress",
  80	[NET_ATTACH_TYPE_TCX_EGRESS]	= "tcx_egress",
  81};
  82
  83static const char * const attach_loc_strings[] = {
  84	[BPF_TCX_INGRESS]		= "tcx/ingress",
  85	[BPF_TCX_EGRESS]		= "tcx/egress",
  86	[BPF_NETKIT_PRIMARY]		= "netkit/primary",
  87	[BPF_NETKIT_PEER]		= "netkit/peer",
  88};
  89
  90const size_t net_attach_type_size = ARRAY_SIZE(attach_type_strings);
  91
  92static enum net_attach_type parse_attach_type(const char *str)
  93{
  94	enum net_attach_type type;
  95
  96	for (type = 0; type < net_attach_type_size; type++) {
  97		if (attach_type_strings[type] &&
  98		    is_prefix(str, attach_type_strings[type]))
  99			return type;
 100	}
 101
 102	return net_attach_type_size;
 103}
 104
 105typedef int (*dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb);
 106
 107typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, dump_nlmsg_t, void *cookie);
 108
 109static int netlink_open(__u32 *nl_pid)
 110{
 111	struct sockaddr_nl sa;
 112	socklen_t addrlen;
 113	int one = 1, ret;
 114	int sock;
 115
 116	memset(&sa, 0, sizeof(sa));
 117	sa.nl_family = AF_NETLINK;
 118
 119	sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
 120	if (sock < 0)
 121		return -errno;
 122
 123	if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK,
 124		       &one, sizeof(one)) < 0) {
 125		p_err("Netlink error reporting not supported");
 126	}
 127
 128	if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
 129		ret = -errno;
 130		goto cleanup;
 131	}
 132
 133	addrlen = sizeof(sa);
 134	if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) {
 135		ret = -errno;
 136		goto cleanup;
 137	}
 138
 139	if (addrlen != sizeof(sa)) {
 140		ret = -LIBBPF_ERRNO__INTERNAL;
 141		goto cleanup;
 142	}
 143
 144	*nl_pid = sa.nl_pid;
 145	return sock;
 146
 147cleanup:
 148	close(sock);
 149	return ret;
 150}
 151
 152static int netlink_recv(int sock, __u32 nl_pid, __u32 seq,
 153			    __dump_nlmsg_t _fn, dump_nlmsg_t fn,
 154			    void *cookie)
 155{
 156	bool multipart = true;
 157	struct nlmsgerr *err;
 158	struct nlmsghdr *nh;
 159	char buf[8192];
 160	int len, ret;
 161
 162	while (multipart) {
 163		multipart = false;
 164		len = recv(sock, buf, sizeof(buf), 0);
 165		if (len < 0) {
 166			ret = -errno;
 167			goto done;
 168		}
 169
 170		if (len == 0)
 171			break;
 172
 173		for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, (unsigned int)len);
 174		     nh = NLMSG_NEXT(nh, len)) {
 175			if (nh->nlmsg_pid != nl_pid) {
 176				ret = -LIBBPF_ERRNO__WRNGPID;
 177				goto done;
 178			}
 179			if (nh->nlmsg_seq != seq) {
 180				ret = -LIBBPF_ERRNO__INVSEQ;
 181				goto done;
 182			}
 183			if (nh->nlmsg_flags & NLM_F_MULTI)
 184				multipart = true;
 185			switch (nh->nlmsg_type) {
 186			case NLMSG_ERROR:
 187				err = (struct nlmsgerr *)NLMSG_DATA(nh);
 188				if (!err->error)
 189					continue;
 190				ret = err->error;
 191				libbpf_nla_dump_errormsg(nh);
 192				goto done;
 193			case NLMSG_DONE:
 194				return 0;
 195			default:
 196				break;
 197			}
 198			if (_fn) {
 199				ret = _fn(nh, fn, cookie);
 200				if (ret)
 201					return ret;
 202			}
 203		}
 204
 205		if (len)
 206			p_err("Invalid message or trailing data in Netlink response: %d bytes left", len);
 207	}
 208	ret = 0;
 209done:
 210	return ret;
 211}
 212
 213static int __dump_class_nlmsg(struct nlmsghdr *nlh,
 214			      dump_nlmsg_t dump_class_nlmsg,
 215			      void *cookie)
 216{
 217	struct nlattr *tb[TCA_MAX + 1], *attr;
 218	struct tcmsg *t = NLMSG_DATA(nlh);
 219	int len;
 220
 221	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
 222	attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
 223	if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
 224		return -LIBBPF_ERRNO__NLPARSE;
 225
 226	return dump_class_nlmsg(cookie, t, tb);
 227}
 228
 229static int netlink_get_class(int sock, unsigned int nl_pid, int ifindex,
 230			     dump_nlmsg_t dump_class_nlmsg, void *cookie)
 231{
 232	struct {
 233		struct nlmsghdr nlh;
 234		struct tcmsg t;
 235	} req = {
 236		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
 237		.nlh.nlmsg_type = RTM_GETTCLASS,
 238		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
 239		.t.tcm_family = AF_UNSPEC,
 240		.t.tcm_ifindex = ifindex,
 241	};
 242	int seq = time(NULL);
 243
 244	req.nlh.nlmsg_seq = seq;
 245	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
 246		return -errno;
 247
 248	return netlink_recv(sock, nl_pid, seq, __dump_class_nlmsg,
 249			    dump_class_nlmsg, cookie);
 250}
 251
 252static int __dump_qdisc_nlmsg(struct nlmsghdr *nlh,
 253			      dump_nlmsg_t dump_qdisc_nlmsg,
 254			      void *cookie)
 255{
 256	struct nlattr *tb[TCA_MAX + 1], *attr;
 257	struct tcmsg *t = NLMSG_DATA(nlh);
 258	int len;
 259
 260	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
 261	attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
 262	if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
 263		return -LIBBPF_ERRNO__NLPARSE;
 264
 265	return dump_qdisc_nlmsg(cookie, t, tb);
 266}
 267
 268static int netlink_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
 269			     dump_nlmsg_t dump_qdisc_nlmsg, void *cookie)
 270{
 271	struct {
 272		struct nlmsghdr nlh;
 273		struct tcmsg t;
 274	} req = {
 275		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
 276		.nlh.nlmsg_type = RTM_GETQDISC,
 277		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
 278		.t.tcm_family = AF_UNSPEC,
 279		.t.tcm_ifindex = ifindex,
 280	};
 281	int seq = time(NULL);
 282
 283	req.nlh.nlmsg_seq = seq;
 284	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
 285		return -errno;
 286
 287	return netlink_recv(sock, nl_pid, seq, __dump_qdisc_nlmsg,
 288			    dump_qdisc_nlmsg, cookie);
 289}
 290
 291static int __dump_filter_nlmsg(struct nlmsghdr *nlh,
 292			       dump_nlmsg_t dump_filter_nlmsg,
 293			       void *cookie)
 294{
 295	struct nlattr *tb[TCA_MAX + 1], *attr;
 296	struct tcmsg *t = NLMSG_DATA(nlh);
 297	int len;
 298
 299	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
 300	attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
 301	if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
 302		return -LIBBPF_ERRNO__NLPARSE;
 303
 304	return dump_filter_nlmsg(cookie, t, tb);
 305}
 306
 307static int netlink_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
 308			      dump_nlmsg_t dump_filter_nlmsg, void *cookie)
 309{
 310	struct {
 311		struct nlmsghdr nlh;
 312		struct tcmsg t;
 313	} req = {
 314		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
 315		.nlh.nlmsg_type = RTM_GETTFILTER,
 316		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
 317		.t.tcm_family = AF_UNSPEC,
 318		.t.tcm_ifindex = ifindex,
 319		.t.tcm_parent = handle,
 320	};
 321	int seq = time(NULL);
 322
 323	req.nlh.nlmsg_seq = seq;
 324	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
 325		return -errno;
 326
 327	return netlink_recv(sock, nl_pid, seq, __dump_filter_nlmsg,
 328			    dump_filter_nlmsg, cookie);
 329}
 330
 331static int __dump_link_nlmsg(struct nlmsghdr *nlh,
 332			     dump_nlmsg_t dump_link_nlmsg, void *cookie)
 333{
 334	struct nlattr *tb[IFLA_MAX + 1], *attr;
 335	struct ifinfomsg *ifi = NLMSG_DATA(nlh);
 336	int len;
 337
 338	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
 339	attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
 340	if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
 341		return -LIBBPF_ERRNO__NLPARSE;
 342
 343	return dump_link_nlmsg(cookie, ifi, tb);
 344}
 345
 346static int netlink_get_link(int sock, unsigned int nl_pid,
 347			    dump_nlmsg_t dump_link_nlmsg, void *cookie)
 348{
 349	struct {
 350		struct nlmsghdr nlh;
 351		struct ifinfomsg ifm;
 352	} req = {
 353		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
 354		.nlh.nlmsg_type = RTM_GETLINK,
 355		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
 356		.ifm.ifi_family = AF_PACKET,
 357	};
 358	int seq = time(NULL);
 359
 360	req.nlh.nlmsg_seq = seq;
 361	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
 362		return -errno;
 363
 364	return netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg,
 365			    dump_link_nlmsg, cookie);
 366}
 367
 368static int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb)
 369{
 370	struct bpf_netdev_t *netinfo = cookie;
 371	struct ifinfomsg *ifinfo = msg;
 372	struct ip_devname_ifindex *tmp;
 373
 374	if (netinfo->filter_idx > 0 && netinfo->filter_idx != ifinfo->ifi_index)
 375		return 0;
 376
 377	if (netinfo->used_len == netinfo->array_len) {
 378		tmp = realloc(netinfo->devices,
 379			(netinfo->array_len + 16) * sizeof(struct ip_devname_ifindex));
 380		if (!tmp)
 381			return -ENOMEM;
 382
 383		netinfo->devices = tmp;
 384		netinfo->array_len += 16;
 385	}
 386	netinfo->devices[netinfo->used_len].ifindex = ifinfo->ifi_index;
 387	snprintf(netinfo->devices[netinfo->used_len].devname,
 388		 sizeof(netinfo->devices[netinfo->used_len].devname),
 389		 "%s",
 390		 tb[IFLA_IFNAME]
 391			 ? libbpf_nla_getattr_str(tb[IFLA_IFNAME])
 392			 : "");
 393	netinfo->used_len++;
 394
 395	return do_xdp_dump(ifinfo, tb);
 396}
 397
 398static int dump_class_qdisc_nlmsg(void *cookie, void *msg, struct nlattr **tb)
 399{
 400	struct bpf_tcinfo_t *tcinfo = cookie;
 401	struct tcmsg *info = msg;
 402	struct tc_kind_handle *tmp;
 403
 404	if (tcinfo->is_qdisc) {
 405		/* skip clsact qdisc */
 406		if (tb[TCA_KIND] &&
 407		    strcmp(libbpf_nla_data(tb[TCA_KIND]), "clsact") == 0)
 408			return 0;
 409		if (info->tcm_handle == 0)
 410			return 0;
 411	}
 412
 413	if (tcinfo->used_len == tcinfo->array_len) {
 414		tmp = realloc(tcinfo->handle_array,
 415			(tcinfo->array_len + 16) * sizeof(struct tc_kind_handle));
 416		if (!tmp)
 417			return -ENOMEM;
 418
 419		tcinfo->handle_array = tmp;
 420		tcinfo->array_len += 16;
 421	}
 422	tcinfo->handle_array[tcinfo->used_len].handle = info->tcm_handle;
 423	snprintf(tcinfo->handle_array[tcinfo->used_len].kind,
 424		 sizeof(tcinfo->handle_array[tcinfo->used_len].kind),
 425		 "%s",
 426		 tb[TCA_KIND]
 427			 ? libbpf_nla_getattr_str(tb[TCA_KIND])
 428			 : "unknown");
 429	tcinfo->used_len++;
 430
 431	return 0;
 432}
 433
 434static int dump_filter_nlmsg(void *cookie, void *msg, struct nlattr **tb)
 435{
 436	const struct bpf_filter_t *filter_info = cookie;
 437
 438	return do_filter_dump((struct tcmsg *)msg, tb, filter_info->kind,
 439			      filter_info->devname, filter_info->ifindex);
 440}
 441
 442static int __show_dev_tc_bpf_name(__u32 id, char *name, size_t len)
 443{
 444	struct bpf_prog_info info = {};
 445	__u32 ilen = sizeof(info);
 446	int fd, ret;
 447
 448	fd = bpf_prog_get_fd_by_id(id);
 449	if (fd < 0)
 450		return fd;
 451	ret = bpf_obj_get_info_by_fd(fd, &info, &ilen);
 452	if (ret < 0)
 453		goto out;
 454	ret = -ENOENT;
 455	if (info.name[0]) {
 456		get_prog_full_name(&info, fd, name, len);
 457		ret = 0;
 458	}
 459out:
 460	close(fd);
 461	return ret;
 462}
 463
 464static void __show_dev_tc_bpf(const struct ip_devname_ifindex *dev,
 465			      const enum bpf_attach_type loc)
 466{
 467	__u32 prog_flags[64] = {}, link_flags[64] = {}, i, j;
 468	__u32 prog_ids[64] = {}, link_ids[64] = {};
 469	LIBBPF_OPTS(bpf_prog_query_opts, optq);
 470	char prog_name[MAX_PROG_FULL_NAME];
 471	int ret;
 472
 473	optq.prog_ids = prog_ids;
 474	optq.prog_attach_flags = prog_flags;
 475	optq.link_ids = link_ids;
 476	optq.link_attach_flags = link_flags;
 477	optq.count = ARRAY_SIZE(prog_ids);
 478
 479	ret = bpf_prog_query_opts(dev->ifindex, loc, &optq);
 480	if (ret)
 481		return;
 482	for (i = 0; i < optq.count; i++) {
 483		NET_START_OBJECT;
 484		NET_DUMP_STR("devname", "%s", dev->devname);
 485		NET_DUMP_UINT("ifindex", "(%u)", (unsigned int)dev->ifindex);
 486		NET_DUMP_STR("kind", " %s", attach_loc_strings[loc]);
 487		ret = __show_dev_tc_bpf_name(prog_ids[i], prog_name,
 488					     sizeof(prog_name));
 489		if (!ret)
 490			NET_DUMP_STR("name", " %s", prog_name);
 491		NET_DUMP_UINT("prog_id", " prog_id %u ", prog_ids[i]);
 492		if (prog_flags[i] || json_output) {
 493			NET_START_ARRAY("prog_flags", "%s ");
 494			for (j = 0; prog_flags[i] && j < 32; j++) {
 495				if (!(prog_flags[i] & (1U << j)))
 496					continue;
 497				NET_DUMP_UINT_ONLY(1U << j);
 498			}
 499			NET_END_ARRAY("");
 500		}
 501		if (link_ids[i] || json_output) {
 502			NET_DUMP_UINT("link_id", "link_id %u ", link_ids[i]);
 503			if (link_flags[i] || json_output) {
 504				NET_START_ARRAY("link_flags", "%s ");
 505				for (j = 0; link_flags[i] && j < 32; j++) {
 506					if (!(link_flags[i] & (1U << j)))
 507						continue;
 508					NET_DUMP_UINT_ONLY(1U << j);
 509				}
 510				NET_END_ARRAY("");
 511			}
 512		}
 513		NET_END_OBJECT_FINAL;
 514	}
 515}
 516
 517static void show_dev_tc_bpf(struct ip_devname_ifindex *dev)
 518{
 519	__show_dev_tc_bpf(dev, BPF_TCX_INGRESS);
 520	__show_dev_tc_bpf(dev, BPF_TCX_EGRESS);
 521
 522	__show_dev_tc_bpf(dev, BPF_NETKIT_PRIMARY);
 523	__show_dev_tc_bpf(dev, BPF_NETKIT_PEER);
 524}
 525
 526static int show_dev_tc_bpf_classic(int sock, unsigned int nl_pid,
 527				   struct ip_devname_ifindex *dev)
 528{
 529	struct bpf_filter_t filter_info;
 530	struct bpf_tcinfo_t tcinfo;
 531	int i, handle, ret = 0;
 532
 533	tcinfo.handle_array = NULL;
 534	tcinfo.used_len = 0;
 535	tcinfo.array_len = 0;
 536
 537	tcinfo.is_qdisc = false;
 538	ret = netlink_get_class(sock, nl_pid, dev->ifindex,
 539				dump_class_qdisc_nlmsg, &tcinfo);
 540	if (ret)
 541		goto out;
 542
 543	tcinfo.is_qdisc = true;
 544	ret = netlink_get_qdisc(sock, nl_pid, dev->ifindex,
 545				dump_class_qdisc_nlmsg, &tcinfo);
 546	if (ret)
 547		goto out;
 548
 549	filter_info.devname = dev->devname;
 550	filter_info.ifindex = dev->ifindex;
 551	for (i = 0; i < tcinfo.used_len; i++) {
 552		filter_info.kind = tcinfo.handle_array[i].kind;
 553		ret = netlink_get_filter(sock, nl_pid, dev->ifindex,
 554					 tcinfo.handle_array[i].handle,
 555					 dump_filter_nlmsg, &filter_info);
 556		if (ret)
 557			goto out;
 558	}
 559
 560	/* root, ingress and egress handle */
 561	handle = TC_H_ROOT;
 562	filter_info.kind = "root";
 563	ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle,
 564				 dump_filter_nlmsg, &filter_info);
 565	if (ret)
 566		goto out;
 567
 568	handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
 569	filter_info.kind = "clsact/ingress";
 570	ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle,
 571				 dump_filter_nlmsg, &filter_info);
 572	if (ret)
 573		goto out;
 574
 575	handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS);
 576	filter_info.kind = "clsact/egress";
 577	ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle,
 578				 dump_filter_nlmsg, &filter_info);
 579	if (ret)
 580		goto out;
 581
 582out:
 583	free(tcinfo.handle_array);
 584	return 0;
 585}
 586
 587static int query_flow_dissector(struct bpf_attach_info *attach_info)
 588{
 589	__u32 attach_flags;
 590	__u32 prog_ids[1];
 591	__u32 prog_cnt;
 592	int err;
 593	int fd;
 594
 595	fd = open("/proc/self/ns/net", O_RDONLY);
 596	if (fd < 0) {
 597		p_err("can't open /proc/self/ns/net: %s",
 598		      strerror(errno));
 599		return -1;
 600	}
 601	prog_cnt = ARRAY_SIZE(prog_ids);
 602	err = bpf_prog_query(fd, BPF_FLOW_DISSECTOR, 0,
 603			     &attach_flags, prog_ids, &prog_cnt);
 604	close(fd);
 605	if (err) {
 606		if (errno == EINVAL) {
 607			/* Older kernel's don't support querying
 608			 * flow dissector programs.
 609			 */
 610			errno = 0;
 611			return 0;
 612		}
 613		p_err("can't query prog: %s", strerror(errno));
 614		return -1;
 615	}
 616
 617	if (prog_cnt == 1)
 618		attach_info->flow_dissector_id = prog_ids[0];
 619
 620	return 0;
 621}
 622
 623static int net_parse_dev(int *argc, char ***argv)
 624{
 625	int ifindex;
 626
 627	if (is_prefix(**argv, "dev")) {
 628		NEXT_ARGP();
 629
 630		ifindex = if_nametoindex(**argv);
 631		if (!ifindex)
 632			p_err("invalid devname %s", **argv);
 633
 634		NEXT_ARGP();
 635	} else {
 636		p_err("expected 'dev', got: '%s'?", **argv);
 637		return -1;
 638	}
 639
 640	return ifindex;
 641}
 642
 643static int do_attach_detach_xdp(int progfd, enum net_attach_type attach_type,
 644				int ifindex, bool overwrite)
 645{
 646	__u32 flags = 0;
 647
 648	if (!overwrite)
 649		flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
 650	if (attach_type == NET_ATTACH_TYPE_XDP_GENERIC)
 651		flags |= XDP_FLAGS_SKB_MODE;
 652	if (attach_type == NET_ATTACH_TYPE_XDP_DRIVER)
 653		flags |= XDP_FLAGS_DRV_MODE;
 654	if (attach_type == NET_ATTACH_TYPE_XDP_OFFLOAD)
 655		flags |= XDP_FLAGS_HW_MODE;
 656
 657	return bpf_xdp_attach(ifindex, progfd, flags, NULL);
 658}
 659
 660static int get_tcx_type(enum net_attach_type attach_type)
 661{
 662	switch (attach_type) {
 663	case NET_ATTACH_TYPE_TCX_INGRESS:
 664		return BPF_TCX_INGRESS;
 665	case NET_ATTACH_TYPE_TCX_EGRESS:
 666		return BPF_TCX_EGRESS;
 667	default:
 668		return -1;
 669	}
 670}
 671
 672static int do_attach_tcx(int progfd, enum net_attach_type attach_type, int ifindex)
 673{
 674	int type = get_tcx_type(attach_type);
 675
 676	return bpf_prog_attach(progfd, ifindex, type, 0);
 677}
 678
 679static int do_detach_tcx(int targetfd, enum net_attach_type attach_type)
 680{
 681	int type = get_tcx_type(attach_type);
 682
 683	return bpf_prog_detach(targetfd, type);
 684}
 685
 686static int do_attach(int argc, char **argv)
 687{
 688	enum net_attach_type attach_type;
 689	int progfd, ifindex, err = 0;
 690	bool overwrite = false;
 691
 692	/* parse attach args */
 693	if (!REQ_ARGS(5))
 694		return -EINVAL;
 695
 696	attach_type = parse_attach_type(*argv);
 697	if (attach_type == net_attach_type_size) {
 698		p_err("invalid net attach/detach type: %s", *argv);
 699		return -EINVAL;
 700	}
 701	NEXT_ARG();
 702
 703	progfd = prog_parse_fd(&argc, &argv);
 704	if (progfd < 0)
 705		return -EINVAL;
 706
 707	ifindex = net_parse_dev(&argc, &argv);
 708	if (ifindex < 1) {
 709		err = -EINVAL;
 710		goto cleanup;
 711	}
 712
 713	if (argc) {
 714		if (is_prefix(*argv, "overwrite")) {
 715			overwrite = true;
 716		} else {
 717			p_err("expected 'overwrite', got: '%s'?", *argv);
 718			err = -EINVAL;
 719			goto cleanup;
 720		}
 721	}
 722
 723	switch (attach_type) {
 724	/* attach xdp prog */
 725	case NET_ATTACH_TYPE_XDP:
 726	case NET_ATTACH_TYPE_XDP_GENERIC:
 727	case NET_ATTACH_TYPE_XDP_DRIVER:
 728	case NET_ATTACH_TYPE_XDP_OFFLOAD:
 729		err = do_attach_detach_xdp(progfd, attach_type, ifindex, overwrite);
 730		break;
 731	/* attach tcx prog */
 732	case NET_ATTACH_TYPE_TCX_INGRESS:
 733	case NET_ATTACH_TYPE_TCX_EGRESS:
 734		err = do_attach_tcx(progfd, attach_type, ifindex);
 735		break;
 736	default:
 737		break;
 738	}
 739
 740	if (err) {
 741		p_err("interface %s attach failed: %s",
 742		      attach_type_strings[attach_type], strerror(-err));
 743		goto cleanup;
 744	}
 745
 746	if (json_output)
 747		jsonw_null(json_wtr);
 748cleanup:
 749	close(progfd);
 750	return err;
 751}
 752
 753static int do_detach(int argc, char **argv)
 754{
 755	enum net_attach_type attach_type;
 756	int progfd, ifindex, err = 0;
 757
 758	/* parse detach args */
 759	if (!REQ_ARGS(3))
 760		return -EINVAL;
 761
 762	attach_type = parse_attach_type(*argv);
 763	if (attach_type == net_attach_type_size) {
 764		p_err("invalid net attach/detach type: %s", *argv);
 765		return -EINVAL;
 766	}
 767	NEXT_ARG();
 768
 769	ifindex = net_parse_dev(&argc, &argv);
 770	if (ifindex < 1)
 771		return -EINVAL;
 772
 773	switch (attach_type) {
 774	/* detach xdp prog */
 775	case NET_ATTACH_TYPE_XDP:
 776	case NET_ATTACH_TYPE_XDP_GENERIC:
 777	case NET_ATTACH_TYPE_XDP_DRIVER:
 778	case NET_ATTACH_TYPE_XDP_OFFLOAD:
 779		progfd = -1;
 780		err = do_attach_detach_xdp(progfd, attach_type, ifindex, NULL);
 781		break;
 782	/* detach tcx prog */
 783	case NET_ATTACH_TYPE_TCX_INGRESS:
 784	case NET_ATTACH_TYPE_TCX_EGRESS:
 785		err = do_detach_tcx(ifindex, attach_type);
 786		break;
 787	default:
 788		break;
 789	}
 790
 791	if (err < 0) {
 792		p_err("interface %s detach failed: %s",
 793		      attach_type_strings[attach_type], strerror(-err));
 794		return err;
 795	}
 796
 797	if (json_output)
 798		jsonw_null(json_wtr);
 799
 800	return 0;
 801}
 802
 803static int netfilter_link_compar(const void *a, const void *b)
 804{
 805	const struct bpf_link_info *nfa = a;
 806	const struct bpf_link_info *nfb = b;
 807	int delta;
 808
 809	delta = nfa->netfilter.pf - nfb->netfilter.pf;
 810	if (delta)
 811		return delta;
 812
 813	delta = nfa->netfilter.hooknum - nfb->netfilter.hooknum;
 814	if (delta)
 815		return delta;
 816
 817	if (nfa->netfilter.priority < nfb->netfilter.priority)
 818		return -1;
 819	if (nfa->netfilter.priority > nfb->netfilter.priority)
 820		return 1;
 821
 822	return nfa->netfilter.flags - nfb->netfilter.flags;
 823}
 824
 825static void show_link_netfilter(void)
 826{
 827	unsigned int nf_link_len = 0, nf_link_count = 0;
 828	struct bpf_link_info *nf_link_info = NULL;
 829	__u32 id = 0;
 830
 831	while (true) {
 832		struct bpf_link_info info;
 833		int fd, err;
 834		__u32 len;
 835
 836		err = bpf_link_get_next_id(id, &id);
 837		if (err) {
 838			if (errno == ENOENT)
 839				break;
 840			p_err("can't get next link: %s (id %u)", strerror(errno), id);
 841			break;
 842		}
 843
 844		fd = bpf_link_get_fd_by_id(id);
 845		if (fd < 0) {
 846			p_err("can't get link by id (%u): %s", id, strerror(errno));
 847			continue;
 848		}
 849
 850		memset(&info, 0, sizeof(info));
 851		len = sizeof(info);
 852
 853		err = bpf_link_get_info_by_fd(fd, &info, &len);
 854
 855		close(fd);
 856
 857		if (err) {
 858			p_err("can't get link info for fd %d: %s", fd, strerror(errno));
 859			continue;
 860		}
 861
 862		if (info.type != BPF_LINK_TYPE_NETFILTER)
 863			continue;
 864
 865		if (nf_link_count >= nf_link_len) {
 866			static const unsigned int max_link_count = INT_MAX / sizeof(info);
 867			struct bpf_link_info *expand;
 868
 869			if (nf_link_count > max_link_count) {
 870				p_err("cannot handle more than %u links\n", max_link_count);
 871				break;
 872			}
 873
 874			nf_link_len += 16;
 875
 876			expand = realloc(nf_link_info, nf_link_len * sizeof(info));
 877			if (!expand) {
 878				p_err("realloc: %s",  strerror(errno));
 879				break;
 880			}
 881
 882			nf_link_info = expand;
 883		}
 884
 885		nf_link_info[nf_link_count] = info;
 886		nf_link_count++;
 887	}
 888
 889	if (!nf_link_info)
 890		return;
 891
 892	qsort(nf_link_info, nf_link_count, sizeof(*nf_link_info), netfilter_link_compar);
 893
 894	for (id = 0; id < nf_link_count; id++) {
 895		NET_START_OBJECT;
 896		if (json_output)
 897			netfilter_dump_json(&nf_link_info[id], json_wtr);
 898		else
 899			netfilter_dump_plain(&nf_link_info[id]);
 900
 901		NET_DUMP_UINT("id", " prog_id %u", nf_link_info[id].prog_id);
 902		NET_END_OBJECT;
 903	}
 904
 905	free(nf_link_info);
 906}
 907
 908static int do_show(int argc, char **argv)
 909{
 910	struct bpf_attach_info attach_info = {};
 911	int i, sock, ret, filter_idx = -1;
 912	struct bpf_netdev_t dev_array;
 913	unsigned int nl_pid = 0;
 914	char err_buf[256];
 915
 916	if (argc == 2) {
 917		filter_idx = net_parse_dev(&argc, &argv);
 918		if (filter_idx < 1)
 919			return -1;
 920	} else if (argc != 0) {
 921		usage();
 922	}
 923
 924	ret = query_flow_dissector(&attach_info);
 925	if (ret)
 926		return -1;
 927
 928	sock = netlink_open(&nl_pid);
 929	if (sock < 0) {
 930		fprintf(stderr, "failed to open netlink sock\n");
 931		return -1;
 932	}
 933
 934	dev_array.devices = NULL;
 935	dev_array.used_len = 0;
 936	dev_array.array_len = 0;
 937	dev_array.filter_idx = filter_idx;
 938
 939	if (json_output)
 940		jsonw_start_array(json_wtr);
 941	NET_START_OBJECT;
 942	NET_START_ARRAY("xdp", "%s:\n");
 943	ret = netlink_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array);
 944	NET_END_ARRAY("\n");
 945
 946	if (!ret) {
 947		NET_START_ARRAY("tc", "%s:\n");
 948		for (i = 0; i < dev_array.used_len; i++) {
 949			show_dev_tc_bpf(&dev_array.devices[i]);
 950			ret = show_dev_tc_bpf_classic(sock, nl_pid,
 951						      &dev_array.devices[i]);
 952			if (ret)
 953				break;
 954		}
 955		NET_END_ARRAY("\n");
 956	}
 957
 958	NET_START_ARRAY("flow_dissector", "%s:\n");
 959	if (attach_info.flow_dissector_id > 0)
 960		NET_DUMP_UINT("id", "id %u", attach_info.flow_dissector_id);
 961	NET_END_ARRAY("\n");
 962
 963	NET_START_ARRAY("netfilter", "%s:\n");
 964	show_link_netfilter();
 965	NET_END_ARRAY("\n");
 966
 967	NET_END_OBJECT;
 968	if (json_output)
 969		jsonw_end_array(json_wtr);
 970
 971	if (ret) {
 972		if (json_output)
 973			jsonw_null(json_wtr);
 974		libbpf_strerror(ret, err_buf, sizeof(err_buf));
 975		fprintf(stderr, "Error: %s\n", err_buf);
 976	}
 977	free(dev_array.devices);
 978	close(sock);
 979	return ret;
 980}
 981
 982static int do_help(int argc, char **argv)
 983{
 984	if (json_output) {
 985		jsonw_null(json_wtr);
 986		return 0;
 987	}
 988
 989	fprintf(stderr,
 990		"Usage: %1$s %2$s { show | list } [dev <devname>]\n"
 991		"       %1$s %2$s attach ATTACH_TYPE PROG dev <devname> [ overwrite ]\n"
 992		"       %1$s %2$s detach ATTACH_TYPE dev <devname>\n"
 993		"       %1$s %2$s help\n"
 994		"\n"
 995		"       " HELP_SPEC_PROGRAM "\n"
 996		"       ATTACH_TYPE := { xdp | xdpgeneric | xdpdrv | xdpoffload | tcx_ingress\n"
 997		"                        | tcx_egress }\n"
 998		"       " HELP_SPEC_OPTIONS " }\n"
 999		"\n"
1000		"Note: Only xdp, tcx, tc, netkit, flow_dissector and netfilter attachments\n"
1001		"      are currently supported.\n"
1002		"      For progs attached to cgroups, use \"bpftool cgroup\"\n"
1003		"      to dump program attachments. For program types\n"
1004		"      sk_{filter,skb,msg,reuseport} and lwt/seg6, please\n"
1005		"      consult iproute2.\n"
1006		"",
1007		bin_name, argv[-2]);
1008
1009	return 0;
1010}
1011
1012static const struct cmd cmds[] = {
1013	{ "show",	do_show },
1014	{ "list",	do_show },
1015	{ "attach",	do_attach },
1016	{ "detach",	do_detach },
1017	{ "help",	do_help },
1018	{ 0 }
1019};
1020
1021int do_net(int argc, char **argv)
1022{
1023	return cmd_select(cmds, argc, argv, do_help);
1024}
~/ Read-only file preview.