net.c 10.9 KB
Newer Older
1
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 3 4
// Copyright (C) 2018 Facebook

#define _GNU_SOURCE
5
#include <errno.h>
6
#include <fcntl.h>
7 8 9
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
10 11
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
12 13 14 15 16
#include <net/if.h>
#include <linux/if.h>
#include <linux/rtnetlink.h>
#include <linux/tc_act/tc_bpf.h>
#include <sys/socket.h>
17 18
#include <sys/stat.h>
#include <sys/types.h>
19

20 21
#include "bpf/nlattr.h"
#include "bpf/libbpf_internal.h"
22 23 24
#include "main.h"
#include "netlink_dumper.h"

25 26 27 28 29
struct ip_devname_ifindex {
	char	devname[64];
	int	ifindex;
};

30
struct bpf_netdev_t {
31
	struct ip_devname_ifindex *devices;
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
	int	used_len;
	int	array_len;
	int	filter_idx;
};

struct tc_kind_handle {
	char	kind[64];
	int	handle;
};

struct bpf_tcinfo_t {
	struct tc_kind_handle	*handle_array;
	int			used_len;
	int			array_len;
	bool			is_qdisc;
};

49 50 51 52 53 54
struct bpf_filter_t {
	const char	*kind;
	const char	*devname;
	int		ifindex;
};

55 56 57 58
struct bpf_attach_info {
	__u32 flow_dissector_id;
};

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
enum net_attach_type {
	NET_ATTACH_TYPE_XDP,
	NET_ATTACH_TYPE_XDP_GENERIC,
	NET_ATTACH_TYPE_XDP_DRIVER,
	NET_ATTACH_TYPE_XDP_OFFLOAD,
};

static const char * const attach_type_strings[] = {
	[NET_ATTACH_TYPE_XDP]		= "xdp",
	[NET_ATTACH_TYPE_XDP_GENERIC]	= "xdpgeneric",
	[NET_ATTACH_TYPE_XDP_DRIVER]	= "xdpdrv",
	[NET_ATTACH_TYPE_XDP_OFFLOAD]	= "xdpoffload",
};

const size_t net_attach_type_size = ARRAY_SIZE(attach_type_strings);

static enum net_attach_type parse_attach_type(const char *str)
{
	enum net_attach_type type;

	for (type = 0; type < net_attach_type_size; type++) {
		if (attach_type_strings[type] &&
		    is_prefix(str, attach_type_strings[type]))
			return type;
	}

	return net_attach_type_size;
}

88 89 90 91 92 93 94 95 96
static int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb)
{
	struct bpf_netdev_t *netinfo = cookie;
	struct ifinfomsg *ifinfo = msg;

	if (netinfo->filter_idx > 0 && netinfo->filter_idx != ifinfo->ifi_index)
		return 0;

	if (netinfo->used_len == netinfo->array_len) {
97 98 99 100 101 102
		netinfo->devices = realloc(netinfo->devices,
			(netinfo->array_len + 16) *
			sizeof(struct ip_devname_ifindex));
		if (!netinfo->devices)
			return -ENOMEM;

103 104
		netinfo->array_len += 16;
	}
105 106 107 108
	netinfo->devices[netinfo->used_len].ifindex = ifinfo->ifi_index;
	snprintf(netinfo->devices[netinfo->used_len].devname,
		 sizeof(netinfo->devices[netinfo->used_len].devname),
		 "%s",
109 110 111
		 tb[IFLA_IFNAME]
			 ? libbpf_nla_getattr_str(tb[IFLA_IFNAME])
			 : "");
112
	netinfo->used_len++;
113 114 115 116 117 118 119 120 121 122 123 124

	return do_xdp_dump(ifinfo, tb);
}

static int dump_class_qdisc_nlmsg(void *cookie, void *msg, struct nlattr **tb)
{
	struct bpf_tcinfo_t *tcinfo = cookie;
	struct tcmsg *info = msg;

	if (tcinfo->is_qdisc) {
		/* skip clsact qdisc */
		if (tb[TCA_KIND] &&
125
		    strcmp(libbpf_nla_data(tb[TCA_KIND]), "clsact") == 0)
126 127 128 129 130 131 132 133
			return 0;
		if (info->tcm_handle == 0)
			return 0;
	}

	if (tcinfo->used_len == tcinfo->array_len) {
		tcinfo->handle_array = realloc(tcinfo->handle_array,
			(tcinfo->array_len + 16) * sizeof(struct tc_kind_handle));
134 135 136
		if (!tcinfo->handle_array)
			return -ENOMEM;

137 138 139 140 141
		tcinfo->array_len += 16;
	}
	tcinfo->handle_array[tcinfo->used_len].handle = info->tcm_handle;
	snprintf(tcinfo->handle_array[tcinfo->used_len].kind,
		 sizeof(tcinfo->handle_array[tcinfo->used_len].kind),
142
		 "%s",
143 144 145
		 tb[TCA_KIND]
			 ? libbpf_nla_getattr_str(tb[TCA_KIND])
			 : "unknown");
146 147 148 149 150 151 152
	tcinfo->used_len++;

	return 0;
}

static int dump_filter_nlmsg(void *cookie, void *msg, struct nlattr **tb)
{
153
	const struct bpf_filter_t *filter_info = cookie;
154

155 156
	return do_filter_dump((struct tcmsg *)msg, tb, filter_info->kind,
			      filter_info->devname, filter_info->ifindex);
157 158
}

159 160
static int show_dev_tc_bpf(int sock, unsigned int nl_pid,
			   struct ip_devname_ifindex *dev)
161
{
162
	struct bpf_filter_t filter_info;
163
	struct bpf_tcinfo_t tcinfo;
164
	int i, handle, ret = 0;
165 166 167 168 169 170

	tcinfo.handle_array = NULL;
	tcinfo.used_len = 0;
	tcinfo.array_len = 0;

	tcinfo.is_qdisc = false;
171 172
	ret = libbpf_nl_get_class(sock, nl_pid, dev->ifindex,
				  dump_class_qdisc_nlmsg, &tcinfo);
173
	if (ret)
174
		goto out;
175 176

	tcinfo.is_qdisc = true;
177 178
	ret = libbpf_nl_get_qdisc(sock, nl_pid, dev->ifindex,
				  dump_class_qdisc_nlmsg, &tcinfo);
179
	if (ret)
180
		goto out;
181

182 183
	filter_info.devname = dev->devname;
	filter_info.ifindex = dev->ifindex;
184
	for (i = 0; i < tcinfo.used_len; i++) {
185
		filter_info.kind = tcinfo.handle_array[i].kind;
186 187 188
		ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex,
					   tcinfo.handle_array[i].handle,
					   dump_filter_nlmsg, &filter_info);
189
		if (ret)
190
			goto out;
191 192 193 194
	}

	/* root, ingress and egress handle */
	handle = TC_H_ROOT;
195
	filter_info.kind = "root";
196 197
	ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, handle,
				   dump_filter_nlmsg, &filter_info);
198
	if (ret)
199
		goto out;
200 201

	handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
202
	filter_info.kind = "clsact/ingress";
203 204
	ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, handle,
				   dump_filter_nlmsg, &filter_info);
205
	if (ret)
206
		goto out;
207 208

	handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS);
209
	filter_info.kind = "clsact/egress";
210 211
	ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, handle,
				   dump_filter_nlmsg, &filter_info);
212
	if (ret)
213
		goto out;
214

215 216
out:
	free(tcinfo.handle_array);
217 218 219
	return 0;
}

220 221 222 223 224 225 226 227 228 229
static int query_flow_dissector(struct bpf_attach_info *attach_info)
{
	__u32 attach_flags;
	__u32 prog_ids[1];
	__u32 prog_cnt;
	int err;
	int fd;

	fd = open("/proc/self/ns/net", O_RDONLY);
	if (fd < 0) {
230
		p_err("can't open /proc/self/ns/net: %s",
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
		      strerror(errno));
		return -1;
	}
	prog_cnt = ARRAY_SIZE(prog_ids);
	err = bpf_prog_query(fd, BPF_FLOW_DISSECTOR, 0,
			     &attach_flags, prog_ids, &prog_cnt);
	close(fd);
	if (err) {
		if (errno == EINVAL) {
			/* Older kernel's don't support querying
			 * flow dissector programs.
			 */
			errno = 0;
			return 0;
		}
		p_err("can't query prog: %s", strerror(errno));
		return -1;
	}

	if (prog_cnt == 1)
		attach_info->flow_dissector_id = prog_ids[0];

	return 0;
}

256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
static int net_parse_dev(int *argc, char ***argv)
{
	int ifindex;

	if (is_prefix(**argv, "dev")) {
		NEXT_ARGP();

		ifindex = if_nametoindex(**argv);
		if (!ifindex)
			p_err("invalid devname %s", **argv);

		NEXT_ARGP();
	} else {
		p_err("expected 'dev', got: '%s'?", **argv);
		return -1;
	}

	return ifindex;
}

static int do_attach_detach_xdp(int progfd, enum net_attach_type attach_type,
				int ifindex, bool overwrite)
{
	__u32 flags = 0;

	if (!overwrite)
		flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
	if (attach_type == NET_ATTACH_TYPE_XDP_GENERIC)
		flags |= XDP_FLAGS_SKB_MODE;
	if (attach_type == NET_ATTACH_TYPE_XDP_DRIVER)
		flags |= XDP_FLAGS_DRV_MODE;
	if (attach_type == NET_ATTACH_TYPE_XDP_OFFLOAD)
		flags |= XDP_FLAGS_HW_MODE;

	return bpf_set_link_xdp_fd(ifindex, progfd, flags);
}

static int do_attach(int argc, char **argv)
{
	enum net_attach_type attach_type;
	int progfd, ifindex, err = 0;
	bool overwrite = false;

	/* parse attach args */
	if (!REQ_ARGS(5))
		return -EINVAL;

	attach_type = parse_attach_type(*argv);
	if (attach_type == net_attach_type_size) {
		p_err("invalid net attach/detach type: %s", *argv);
		return -EINVAL;
	}
	NEXT_ARG();

	progfd = prog_parse_fd(&argc, &argv);
	if (progfd < 0)
		return -EINVAL;

	ifindex = net_parse_dev(&argc, &argv);
	if (ifindex < 1) {
		close(progfd);
		return -EINVAL;
	}

	if (argc) {
		if (is_prefix(*argv, "overwrite")) {
			overwrite = true;
		} else {
			p_err("expected 'overwrite', got: '%s'?", *argv);
			close(progfd);
			return -EINVAL;
		}
	}

	/* attach xdp prog */
	if (is_prefix("xdp", attach_type_strings[attach_type]))
		err = do_attach_detach_xdp(progfd, attach_type, ifindex,
					   overwrite);

	if (err < 0) {
		p_err("interface %s attach failed: %s",
		      attach_type_strings[attach_type], strerror(-err));
		return err;
	}

	if (json_output)
		jsonw_null(json_wtr);

	return 0;
}

347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
static int do_detach(int argc, char **argv)
{
	enum net_attach_type attach_type;
	int progfd, ifindex, err = 0;

	/* parse detach args */
	if (!REQ_ARGS(3))
		return -EINVAL;

	attach_type = parse_attach_type(*argv);
	if (attach_type == net_attach_type_size) {
		p_err("invalid net attach/detach type: %s", *argv);
		return -EINVAL;
	}
	NEXT_ARG();

	ifindex = net_parse_dev(&argc, &argv);
	if (ifindex < 1)
		return -EINVAL;

	/* detach xdp prog */
	progfd = -1;
	if (is_prefix("xdp", attach_type_strings[attach_type]))
		err = do_attach_detach_xdp(progfd, attach_type, ifindex, NULL);

	if (err < 0) {
		p_err("interface %s detach failed: %s",
		      attach_type_strings[attach_type], strerror(-err));
		return err;
	}

	if (json_output)
		jsonw_null(json_wtr);

	return 0;
}

384 385
static int do_show(int argc, char **argv)
{
386
	struct bpf_attach_info attach_info = {};
387 388 389 390 391 392
	int i, sock, ret, filter_idx = -1;
	struct bpf_netdev_t dev_array;
	unsigned int nl_pid;
	char err_buf[256];

	if (argc == 2) {
393 394
		filter_idx = net_parse_dev(&argc, &argv);
		if (filter_idx < 1)
395 396 397 398 399
			return -1;
	} else if (argc != 0) {
		usage();
	}

400 401 402 403
	ret = query_flow_dissector(&attach_info);
	if (ret)
		return -1;

404
	sock = libbpf_netlink_open(&nl_pid);
405 406 407 408 409
	if (sock < 0) {
		fprintf(stderr, "failed to open netlink sock\n");
		return -1;
	}

410
	dev_array.devices = NULL;
411 412 413 414 415 416 417
	dev_array.used_len = 0;
	dev_array.array_len = 0;
	dev_array.filter_idx = filter_idx;

	if (json_output)
		jsonw_start_array(json_wtr);
	NET_START_OBJECT;
418
	NET_START_ARRAY("xdp", "%s:\n");
419
	ret = libbpf_nl_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array);
420 421 422
	NET_END_ARRAY("\n");

	if (!ret) {
423
		NET_START_ARRAY("tc", "%s:\n");
424 425
		for (i = 0; i < dev_array.used_len; i++) {
			ret = show_dev_tc_bpf(sock, nl_pid,
426
					      &dev_array.devices[i]);
427 428 429 430 431
			if (ret)
				break;
		}
		NET_END_ARRAY("\n");
	}
432 433 434 435 436 437

	NET_START_ARRAY("flow_dissector", "%s:\n");
	if (attach_info.flow_dissector_id > 0)
		NET_DUMP_UINT("id", "id %u", attach_info.flow_dissector_id);
	NET_END_ARRAY("\n");

438 439 440 441 442 443 444 445 446 447
	NET_END_OBJECT;
	if (json_output)
		jsonw_end_array(json_wtr);

	if (ret) {
		if (json_output)
			jsonw_null(json_wtr);
		libbpf_strerror(ret, err_buf, sizeof(err_buf));
		fprintf(stderr, "Error: %s\n", err_buf);
	}
448
	free(dev_array.devices);
449 450 451 452 453 454 455 456 457 458 459 460
	close(sock);
	return ret;
}

static int do_help(int argc, char **argv)
{
	if (json_output) {
		jsonw_null(json_wtr);
		return 0;
	}

	fprintf(stderr,
461 462 463 464
		"Usage: %1$s %2$s { show | list } [dev <devname>]\n"
		"       %1$s %2$s attach ATTACH_TYPE PROG dev <devname> [ overwrite ]\n"
		"       %1$s %2$s detach ATTACH_TYPE dev <devname>\n"
		"       %1$s %2$s help\n"
465 466 467 468
		"\n"
		"       " HELP_SPEC_PROGRAM "\n"
		"       ATTACH_TYPE := { xdp | xdpgeneric | xdpdrv | xdpoffload }\n"
		"\n"
469 470 471 472
		"Note: Only xdp and tc attachments are supported now.\n"
		"      For progs attached to cgroups, use \"bpftool cgroup\"\n"
		"      to dump program attachments. For program types\n"
		"      sk_{filter,skb,msg,reuseport} and lwt/seg6, please\n"
473 474
		"      consult iproute2.\n"
		"",
475
		bin_name, argv[-2]);
476 477 478 479 480 481 482

	return 0;
}

static const struct cmd cmds[] = {
	{ "show",	do_show },
	{ "list",	do_show },
483
	{ "attach",	do_attach },
484
	{ "detach",	do_detach },
485 486 487 488 489 490 491 492
	{ "help",	do_help },
	{ 0 }
};

int do_net(int argc, char **argv)
{
	return cmd_select(cmds, argc, argv, do_help);
}