xref: /openbmc/linux/tools/bpf/bpftool/net.c (revision 6c33a6f4)
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 // Copyright (C) 2018 Facebook
3 
4 #define _GNU_SOURCE
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <bpf/bpf.h>
11 #include <bpf/libbpf.h>
12 #include <net/if.h>
13 #include <linux/if.h>
14 #include <linux/rtnetlink.h>
15 #include <linux/tc_act/tc_bpf.h>
16 #include <sys/socket.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 
20 #include "bpf/nlattr.h"
21 #include "bpf/libbpf_internal.h"
22 #include "main.h"
23 #include "netlink_dumper.h"
24 
25 struct ip_devname_ifindex {
26 	char	devname[64];
27 	int	ifindex;
28 };
29 
30 struct bpf_netdev_t {
31 	struct ip_devname_ifindex *devices;
32 	int	used_len;
33 	int	array_len;
34 	int	filter_idx;
35 };
36 
37 struct tc_kind_handle {
38 	char	kind[64];
39 	int	handle;
40 };
41 
42 struct bpf_tcinfo_t {
43 	struct tc_kind_handle	*handle_array;
44 	int			used_len;
45 	int			array_len;
46 	bool			is_qdisc;
47 };
48 
49 struct bpf_filter_t {
50 	const char	*kind;
51 	const char	*devname;
52 	int		ifindex;
53 };
54 
55 struct bpf_attach_info {
56 	__u32 flow_dissector_id;
57 };
58 
59 enum net_attach_type {
60 	NET_ATTACH_TYPE_XDP,
61 	NET_ATTACH_TYPE_XDP_GENERIC,
62 	NET_ATTACH_TYPE_XDP_DRIVER,
63 	NET_ATTACH_TYPE_XDP_OFFLOAD,
64 };
65 
66 static const char * const attach_type_strings[] = {
67 	[NET_ATTACH_TYPE_XDP]		= "xdp",
68 	[NET_ATTACH_TYPE_XDP_GENERIC]	= "xdpgeneric",
69 	[NET_ATTACH_TYPE_XDP_DRIVER]	= "xdpdrv",
70 	[NET_ATTACH_TYPE_XDP_OFFLOAD]	= "xdpoffload",
71 };
72 
73 const size_t net_attach_type_size = ARRAY_SIZE(attach_type_strings);
74 
75 static enum net_attach_type parse_attach_type(const char *str)
76 {
77 	enum net_attach_type type;
78 
79 	for (type = 0; type < net_attach_type_size; type++) {
80 		if (attach_type_strings[type] &&
81 		    is_prefix(str, attach_type_strings[type]))
82 			return type;
83 	}
84 
85 	return net_attach_type_size;
86 }
87 
88 static int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb)
89 {
90 	struct bpf_netdev_t *netinfo = cookie;
91 	struct ifinfomsg *ifinfo = msg;
92 
93 	if (netinfo->filter_idx > 0 && netinfo->filter_idx != ifinfo->ifi_index)
94 		return 0;
95 
96 	if (netinfo->used_len == netinfo->array_len) {
97 		netinfo->devices = realloc(netinfo->devices,
98 			(netinfo->array_len + 16) *
99 			sizeof(struct ip_devname_ifindex));
100 		if (!netinfo->devices)
101 			return -ENOMEM;
102 
103 		netinfo->array_len += 16;
104 	}
105 	netinfo->devices[netinfo->used_len].ifindex = ifinfo->ifi_index;
106 	snprintf(netinfo->devices[netinfo->used_len].devname,
107 		 sizeof(netinfo->devices[netinfo->used_len].devname),
108 		 "%s",
109 		 tb[IFLA_IFNAME]
110 			 ? libbpf_nla_getattr_str(tb[IFLA_IFNAME])
111 			 : "");
112 	netinfo->used_len++;
113 
114 	return do_xdp_dump(ifinfo, tb);
115 }
116 
117 static int dump_class_qdisc_nlmsg(void *cookie, void *msg, struct nlattr **tb)
118 {
119 	struct bpf_tcinfo_t *tcinfo = cookie;
120 	struct tcmsg *info = msg;
121 
122 	if (tcinfo->is_qdisc) {
123 		/* skip clsact qdisc */
124 		if (tb[TCA_KIND] &&
125 		    strcmp(libbpf_nla_data(tb[TCA_KIND]), "clsact") == 0)
126 			return 0;
127 		if (info->tcm_handle == 0)
128 			return 0;
129 	}
130 
131 	if (tcinfo->used_len == tcinfo->array_len) {
132 		tcinfo->handle_array = realloc(tcinfo->handle_array,
133 			(tcinfo->array_len + 16) * sizeof(struct tc_kind_handle));
134 		if (!tcinfo->handle_array)
135 			return -ENOMEM;
136 
137 		tcinfo->array_len += 16;
138 	}
139 	tcinfo->handle_array[tcinfo->used_len].handle = info->tcm_handle;
140 	snprintf(tcinfo->handle_array[tcinfo->used_len].kind,
141 		 sizeof(tcinfo->handle_array[tcinfo->used_len].kind),
142 		 "%s",
143 		 tb[TCA_KIND]
144 			 ? libbpf_nla_getattr_str(tb[TCA_KIND])
145 			 : "unknown");
146 	tcinfo->used_len++;
147 
148 	return 0;
149 }
150 
151 static int dump_filter_nlmsg(void *cookie, void *msg, struct nlattr **tb)
152 {
153 	const struct bpf_filter_t *filter_info = cookie;
154 
155 	return do_filter_dump((struct tcmsg *)msg, tb, filter_info->kind,
156 			      filter_info->devname, filter_info->ifindex);
157 }
158 
159 static int show_dev_tc_bpf(int sock, unsigned int nl_pid,
160 			   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 	tcinfo.handle_array = NULL;
167 	tcinfo.used_len = 0;
168 	tcinfo.array_len = 0;
169 
170 	tcinfo.is_qdisc = false;
171 	ret = libbpf_nl_get_class(sock, nl_pid, dev->ifindex,
172 				  dump_class_qdisc_nlmsg, &tcinfo);
173 	if (ret)
174 		goto out;
175 
176 	tcinfo.is_qdisc = true;
177 	ret = libbpf_nl_get_qdisc(sock, nl_pid, dev->ifindex,
178 				  dump_class_qdisc_nlmsg, &tcinfo);
179 	if (ret)
180 		goto out;
181 
182 	filter_info.devname = dev->devname;
183 	filter_info.ifindex = dev->ifindex;
184 	for (i = 0; i < tcinfo.used_len; i++) {
185 		filter_info.kind = tcinfo.handle_array[i].kind;
186 		ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex,
187 					   tcinfo.handle_array[i].handle,
188 					   dump_filter_nlmsg, &filter_info);
189 		if (ret)
190 			goto out;
191 	}
192 
193 	/* root, ingress and egress handle */
194 	handle = TC_H_ROOT;
195 	filter_info.kind = "root";
196 	ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, handle,
197 				   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 	ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, handle,
204 				   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 	ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, handle,
211 				   dump_filter_nlmsg, &filter_info);
212 	if (ret)
213 		goto out;
214 
215 out:
216 	free(tcinfo.handle_array);
217 	return 0;
218 }
219 
220 static int query_flow_dissector(struct bpf_attach_info *attach_info)
221 {
222 	__u32 attach_flags;
223 	__u32 prog_ids[1];
224 	__u32 prog_cnt;
225 	int err;
226 	int fd;
227 
228 	fd = open("/proc/self/ns/net", O_RDONLY);
229 	if (fd < 0) {
230 		p_err("can't open /proc/self/ns/net: %s",
231 		      strerror(errno));
232 		return -1;
233 	}
234 	prog_cnt = ARRAY_SIZE(prog_ids);
235 	err = bpf_prog_query(fd, BPF_FLOW_DISSECTOR, 0,
236 			     &attach_flags, prog_ids, &prog_cnt);
237 	close(fd);
238 	if (err) {
239 		if (errno == EINVAL) {
240 			/* Older kernel's don't support querying
241 			 * flow dissector programs.
242 			 */
243 			errno = 0;
244 			return 0;
245 		}
246 		p_err("can't query prog: %s", strerror(errno));
247 		return -1;
248 	}
249 
250 	if (prog_cnt == 1)
251 		attach_info->flow_dissector_id = prog_ids[0];
252 
253 	return 0;
254 }
255 
256 static int net_parse_dev(int *argc, char ***argv)
257 {
258 	int ifindex;
259 
260 	if (is_prefix(**argv, "dev")) {
261 		NEXT_ARGP();
262 
263 		ifindex = if_nametoindex(**argv);
264 		if (!ifindex)
265 			p_err("invalid devname %s", **argv);
266 
267 		NEXT_ARGP();
268 	} else {
269 		p_err("expected 'dev', got: '%s'?", **argv);
270 		return -1;
271 	}
272 
273 	return ifindex;
274 }
275 
276 static int do_attach_detach_xdp(int progfd, enum net_attach_type attach_type,
277 				int ifindex, bool overwrite)
278 {
279 	__u32 flags = 0;
280 
281 	if (!overwrite)
282 		flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
283 	if (attach_type == NET_ATTACH_TYPE_XDP_GENERIC)
284 		flags |= XDP_FLAGS_SKB_MODE;
285 	if (attach_type == NET_ATTACH_TYPE_XDP_DRIVER)
286 		flags |= XDP_FLAGS_DRV_MODE;
287 	if (attach_type == NET_ATTACH_TYPE_XDP_OFFLOAD)
288 		flags |= XDP_FLAGS_HW_MODE;
289 
290 	return bpf_set_link_xdp_fd(ifindex, progfd, flags);
291 }
292 
293 static int do_attach(int argc, char **argv)
294 {
295 	enum net_attach_type attach_type;
296 	int progfd, ifindex, err = 0;
297 	bool overwrite = false;
298 
299 	/* parse attach args */
300 	if (!REQ_ARGS(5))
301 		return -EINVAL;
302 
303 	attach_type = parse_attach_type(*argv);
304 	if (attach_type == net_attach_type_size) {
305 		p_err("invalid net attach/detach type: %s", *argv);
306 		return -EINVAL;
307 	}
308 	NEXT_ARG();
309 
310 	progfd = prog_parse_fd(&argc, &argv);
311 	if (progfd < 0)
312 		return -EINVAL;
313 
314 	ifindex = net_parse_dev(&argc, &argv);
315 	if (ifindex < 1) {
316 		close(progfd);
317 		return -EINVAL;
318 	}
319 
320 	if (argc) {
321 		if (is_prefix(*argv, "overwrite")) {
322 			overwrite = true;
323 		} else {
324 			p_err("expected 'overwrite', got: '%s'?", *argv);
325 			close(progfd);
326 			return -EINVAL;
327 		}
328 	}
329 
330 	/* attach xdp prog */
331 	if (is_prefix("xdp", attach_type_strings[attach_type]))
332 		err = do_attach_detach_xdp(progfd, attach_type, ifindex,
333 					   overwrite);
334 
335 	if (err < 0) {
336 		p_err("interface %s attach failed: %s",
337 		      attach_type_strings[attach_type], strerror(-err));
338 		return err;
339 	}
340 
341 	if (json_output)
342 		jsonw_null(json_wtr);
343 
344 	return 0;
345 }
346 
347 static int do_detach(int argc, char **argv)
348 {
349 	enum net_attach_type attach_type;
350 	int progfd, ifindex, err = 0;
351 
352 	/* parse detach args */
353 	if (!REQ_ARGS(3))
354 		return -EINVAL;
355 
356 	attach_type = parse_attach_type(*argv);
357 	if (attach_type == net_attach_type_size) {
358 		p_err("invalid net attach/detach type: %s", *argv);
359 		return -EINVAL;
360 	}
361 	NEXT_ARG();
362 
363 	ifindex = net_parse_dev(&argc, &argv);
364 	if (ifindex < 1)
365 		return -EINVAL;
366 
367 	/* detach xdp prog */
368 	progfd = -1;
369 	if (is_prefix("xdp", attach_type_strings[attach_type]))
370 		err = do_attach_detach_xdp(progfd, attach_type, ifindex, NULL);
371 
372 	if (err < 0) {
373 		p_err("interface %s detach failed: %s",
374 		      attach_type_strings[attach_type], strerror(-err));
375 		return err;
376 	}
377 
378 	if (json_output)
379 		jsonw_null(json_wtr);
380 
381 	return 0;
382 }
383 
384 static int do_show(int argc, char **argv)
385 {
386 	struct bpf_attach_info attach_info = {};
387 	int i, sock, ret, filter_idx = -1;
388 	struct bpf_netdev_t dev_array;
389 	unsigned int nl_pid;
390 	char err_buf[256];
391 
392 	if (argc == 2) {
393 		filter_idx = net_parse_dev(&argc, &argv);
394 		if (filter_idx < 1)
395 			return -1;
396 	} else if (argc != 0) {
397 		usage();
398 	}
399 
400 	ret = query_flow_dissector(&attach_info);
401 	if (ret)
402 		return -1;
403 
404 	sock = libbpf_netlink_open(&nl_pid);
405 	if (sock < 0) {
406 		fprintf(stderr, "failed to open netlink sock\n");
407 		return -1;
408 	}
409 
410 	dev_array.devices = NULL;
411 	dev_array.used_len = 0;
412 	dev_array.array_len = 0;
413 	dev_array.filter_idx = filter_idx;
414 
415 	if (json_output)
416 		jsonw_start_array(json_wtr);
417 	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 	NET_END_ARRAY("\n");
421 
422 	if (!ret) {
423 		NET_START_ARRAY("tc", "%s:\n");
424 		for (i = 0; i < dev_array.used_len; i++) {
425 			ret = show_dev_tc_bpf(sock, nl_pid,
426 					      &dev_array.devices[i]);
427 			if (ret)
428 				break;
429 		}
430 		NET_END_ARRAY("\n");
431 	}
432 
433 	NET_START_ARRAY("flow_dissector", "%s:\n");
434 	if (attach_info.flow_dissector_id > 0)
435 		NET_DUMP_UINT("id", "id %u", attach_info.flow_dissector_id);
436 	NET_END_ARRAY("\n");
437 
438 	NET_END_OBJECT;
439 	if (json_output)
440 		jsonw_end_array(json_wtr);
441 
442 	if (ret) {
443 		if (json_output)
444 			jsonw_null(json_wtr);
445 		libbpf_strerror(ret, err_buf, sizeof(err_buf));
446 		fprintf(stderr, "Error: %s\n", err_buf);
447 	}
448 	free(dev_array.devices);
449 	close(sock);
450 	return ret;
451 }
452 
453 static int do_help(int argc, char **argv)
454 {
455 	if (json_output) {
456 		jsonw_null(json_wtr);
457 		return 0;
458 	}
459 
460 	fprintf(stderr,
461 		"Usage: %s %s { show | list } [dev <devname>]\n"
462 		"       %s %s attach ATTACH_TYPE PROG dev <devname> [ overwrite ]\n"
463 		"       %s %s detach ATTACH_TYPE dev <devname>\n"
464 		"       %s %s help\n"
465 		"\n"
466 		"       " HELP_SPEC_PROGRAM "\n"
467 		"       ATTACH_TYPE := { xdp | xdpgeneric | xdpdrv | xdpoffload }\n"
468 		"\n"
469 		"Note: Only xdp and tc attachments are supported now.\n"
470 		"      For progs attached to cgroups, use \"bpftool cgroup\"\n"
471 		"      to dump program attachments. For program types\n"
472 		"      sk_{filter,skb,msg,reuseport} and lwt/seg6, please\n"
473 		"      consult iproute2.\n",
474 		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
475 		bin_name, argv[-2]);
476 
477 	return 0;
478 }
479 
480 static const struct cmd cmds[] = {
481 	{ "show",	do_show },
482 	{ "list",	do_show },
483 	{ "attach",	do_attach },
484 	{ "detach",	do_detach },
485 	{ "help",	do_help },
486 	{ 0 }
487 };
488 
489 int do_net(int argc, char **argv)
490 {
491 	return cmd_select(cmds, argc, argv, do_help);
492 }
493