f836d115d7d6a4789b0cf46e587d38a58d08305a
[releases.git] / net.c
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 <time.h>
10 #include <unistd.h>
11 #include <bpf/bpf.h>
12 #include <bpf/libbpf.h>
13 #include <net/if.h>
14 #include <linux/rtnetlink.h>
15 #include <linux/socket.h>
16 #include <linux/tc_act/tc_bpf.h>
17 #include <sys/socket.h>
18 #include <sys/stat.h>
19 #include <sys/types.h>
20
21 #include "bpf/nlattr.h"
22 #include "main.h"
23 #include "netlink_dumper.h"
24
25 #ifndef SOL_NETLINK
26 #define SOL_NETLINK 270
27 #endif
28
29 struct ip_devname_ifindex {
30         char    devname[64];
31         int     ifindex;
32 };
33
34 struct bpf_netdev_t {
35         struct ip_devname_ifindex *devices;
36         int     used_len;
37         int     array_len;
38         int     filter_idx;
39 };
40
41 struct tc_kind_handle {
42         char    kind[64];
43         int     handle;
44 };
45
46 struct bpf_tcinfo_t {
47         struct tc_kind_handle   *handle_array;
48         int                     used_len;
49         int                     array_len;
50         bool                    is_qdisc;
51 };
52
53 struct bpf_filter_t {
54         const char      *kind;
55         const char      *devname;
56         int             ifindex;
57 };
58
59 struct bpf_attach_info {
60         __u32 flow_dissector_id;
61 };
62
63 enum net_attach_type {
64         NET_ATTACH_TYPE_XDP,
65         NET_ATTACH_TYPE_XDP_GENERIC,
66         NET_ATTACH_TYPE_XDP_DRIVER,
67         NET_ATTACH_TYPE_XDP_OFFLOAD,
68 };
69
70 static const char * const attach_type_strings[] = {
71         [NET_ATTACH_TYPE_XDP]           = "xdp",
72         [NET_ATTACH_TYPE_XDP_GENERIC]   = "xdpgeneric",
73         [NET_ATTACH_TYPE_XDP_DRIVER]    = "xdpdrv",
74         [NET_ATTACH_TYPE_XDP_OFFLOAD]   = "xdpoffload",
75 };
76
77 const size_t net_attach_type_size = ARRAY_SIZE(attach_type_strings);
78
79 static enum net_attach_type parse_attach_type(const char *str)
80 {
81         enum net_attach_type type;
82
83         for (type = 0; type < net_attach_type_size; type++) {
84                 if (attach_type_strings[type] &&
85                     is_prefix(str, attach_type_strings[type]))
86                         return type;
87         }
88
89         return net_attach_type_size;
90 }
91
92 typedef int (*dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb);
93
94 typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, dump_nlmsg_t, void *cookie);
95
96 static int netlink_open(__u32 *nl_pid)
97 {
98         struct sockaddr_nl sa;
99         socklen_t addrlen;
100         int one = 1, ret;
101         int sock;
102
103         memset(&sa, 0, sizeof(sa));
104         sa.nl_family = AF_NETLINK;
105
106         sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
107         if (sock < 0)
108                 return -errno;
109
110         if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK,
111                        &one, sizeof(one)) < 0) {
112                 p_err("Netlink error reporting not supported");
113         }
114
115         if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
116                 ret = -errno;
117                 goto cleanup;
118         }
119
120         addrlen = sizeof(sa);
121         if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) {
122                 ret = -errno;
123                 goto cleanup;
124         }
125
126         if (addrlen != sizeof(sa)) {
127                 ret = -LIBBPF_ERRNO__INTERNAL;
128                 goto cleanup;
129         }
130
131         *nl_pid = sa.nl_pid;
132         return sock;
133
134 cleanup:
135         close(sock);
136         return ret;
137 }
138
139 static int netlink_recv(int sock, __u32 nl_pid, __u32 seq,
140                             __dump_nlmsg_t _fn, dump_nlmsg_t fn,
141                             void *cookie)
142 {
143         bool multipart = true;
144         struct nlmsgerr *err;
145         struct nlmsghdr *nh;
146         char buf[4096];
147         int len, ret;
148
149         while (multipart) {
150                 multipart = false;
151                 len = recv(sock, buf, sizeof(buf), 0);
152                 if (len < 0) {
153                         ret = -errno;
154                         goto done;
155                 }
156
157                 if (len == 0)
158                         break;
159
160                 for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, (unsigned int)len);
161                      nh = NLMSG_NEXT(nh, len)) {
162                         if (nh->nlmsg_pid != nl_pid) {
163                                 ret = -LIBBPF_ERRNO__WRNGPID;
164                                 goto done;
165                         }
166                         if (nh->nlmsg_seq != seq) {
167                                 ret = -LIBBPF_ERRNO__INVSEQ;
168                                 goto done;
169                         }
170                         if (nh->nlmsg_flags & NLM_F_MULTI)
171                                 multipart = true;
172                         switch (nh->nlmsg_type) {
173                         case NLMSG_ERROR:
174                                 err = (struct nlmsgerr *)NLMSG_DATA(nh);
175                                 if (!err->error)
176                                         continue;
177                                 ret = err->error;
178                                 libbpf_nla_dump_errormsg(nh);
179                                 goto done;
180                         case NLMSG_DONE:
181                                 return 0;
182                         default:
183                                 break;
184                         }
185                         if (_fn) {
186                                 ret = _fn(nh, fn, cookie);
187                                 if (ret)
188                                         return ret;
189                         }
190                 }
191         }
192         ret = 0;
193 done:
194         return ret;
195 }
196
197 static int __dump_class_nlmsg(struct nlmsghdr *nlh,
198                               dump_nlmsg_t dump_class_nlmsg,
199                               void *cookie)
200 {
201         struct nlattr *tb[TCA_MAX + 1], *attr;
202         struct tcmsg *t = NLMSG_DATA(nlh);
203         int len;
204
205         len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
206         attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
207         if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
208                 return -LIBBPF_ERRNO__NLPARSE;
209
210         return dump_class_nlmsg(cookie, t, tb);
211 }
212
213 static int netlink_get_class(int sock, unsigned int nl_pid, int ifindex,
214                              dump_nlmsg_t dump_class_nlmsg, void *cookie)
215 {
216         struct {
217                 struct nlmsghdr nlh;
218                 struct tcmsg t;
219         } req = {
220                 .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
221                 .nlh.nlmsg_type = RTM_GETTCLASS,
222                 .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
223                 .t.tcm_family = AF_UNSPEC,
224                 .t.tcm_ifindex = ifindex,
225         };
226         int seq = time(NULL);
227
228         req.nlh.nlmsg_seq = seq;
229         if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
230                 return -errno;
231
232         return netlink_recv(sock, nl_pid, seq, __dump_class_nlmsg,
233                             dump_class_nlmsg, cookie);
234 }
235
236 static int __dump_qdisc_nlmsg(struct nlmsghdr *nlh,
237                               dump_nlmsg_t dump_qdisc_nlmsg,
238                               void *cookie)
239 {
240         struct nlattr *tb[TCA_MAX + 1], *attr;
241         struct tcmsg *t = NLMSG_DATA(nlh);
242         int len;
243
244         len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
245         attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
246         if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
247                 return -LIBBPF_ERRNO__NLPARSE;
248
249         return dump_qdisc_nlmsg(cookie, t, tb);
250 }
251
252 static int netlink_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
253                              dump_nlmsg_t dump_qdisc_nlmsg, void *cookie)
254 {
255         struct {
256                 struct nlmsghdr nlh;
257                 struct tcmsg t;
258         } req = {
259                 .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
260                 .nlh.nlmsg_type = RTM_GETQDISC,
261                 .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
262                 .t.tcm_family = AF_UNSPEC,
263                 .t.tcm_ifindex = ifindex,
264         };
265         int seq = time(NULL);
266
267         req.nlh.nlmsg_seq = seq;
268         if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
269                 return -errno;
270
271         return netlink_recv(sock, nl_pid, seq, __dump_qdisc_nlmsg,
272                             dump_qdisc_nlmsg, cookie);
273 }
274
275 static int __dump_filter_nlmsg(struct nlmsghdr *nlh,
276                                dump_nlmsg_t dump_filter_nlmsg,
277                                void *cookie)
278 {
279         struct nlattr *tb[TCA_MAX + 1], *attr;
280         struct tcmsg *t = NLMSG_DATA(nlh);
281         int len;
282
283         len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
284         attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
285         if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
286                 return -LIBBPF_ERRNO__NLPARSE;
287
288         return dump_filter_nlmsg(cookie, t, tb);
289 }
290
291 static int netlink_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
292                               dump_nlmsg_t dump_filter_nlmsg, void *cookie)
293 {
294         struct {
295                 struct nlmsghdr nlh;
296                 struct tcmsg t;
297         } req = {
298                 .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
299                 .nlh.nlmsg_type = RTM_GETTFILTER,
300                 .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
301                 .t.tcm_family = AF_UNSPEC,
302                 .t.tcm_ifindex = ifindex,
303                 .t.tcm_parent = handle,
304         };
305         int seq = time(NULL);
306
307         req.nlh.nlmsg_seq = seq;
308         if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
309                 return -errno;
310
311         return netlink_recv(sock, nl_pid, seq, __dump_filter_nlmsg,
312                             dump_filter_nlmsg, cookie);
313 }
314
315 static int __dump_link_nlmsg(struct nlmsghdr *nlh,
316                              dump_nlmsg_t dump_link_nlmsg, void *cookie)
317 {
318         struct nlattr *tb[IFLA_MAX + 1], *attr;
319         struct ifinfomsg *ifi = NLMSG_DATA(nlh);
320         int len;
321
322         len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
323         attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
324         if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
325                 return -LIBBPF_ERRNO__NLPARSE;
326
327         return dump_link_nlmsg(cookie, ifi, tb);
328 }
329
330 static int netlink_get_link(int sock, unsigned int nl_pid,
331                             dump_nlmsg_t dump_link_nlmsg, void *cookie)
332 {
333         struct {
334                 struct nlmsghdr nlh;
335                 struct ifinfomsg ifm;
336         } req = {
337                 .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
338                 .nlh.nlmsg_type = RTM_GETLINK,
339                 .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
340                 .ifm.ifi_family = AF_PACKET,
341         };
342         int seq = time(NULL);
343
344         req.nlh.nlmsg_seq = seq;
345         if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
346                 return -errno;
347
348         return netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg,
349                             dump_link_nlmsg, cookie);
350 }
351
352 static int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb)
353 {
354         struct bpf_netdev_t *netinfo = cookie;
355         struct ifinfomsg *ifinfo = msg;
356
357         if (netinfo->filter_idx > 0 && netinfo->filter_idx != ifinfo->ifi_index)
358                 return 0;
359
360         if (netinfo->used_len == netinfo->array_len) {
361                 netinfo->devices = realloc(netinfo->devices,
362                         (netinfo->array_len + 16) *
363                         sizeof(struct ip_devname_ifindex));
364                 if (!netinfo->devices)
365                         return -ENOMEM;
366
367                 netinfo->array_len += 16;
368         }
369         netinfo->devices[netinfo->used_len].ifindex = ifinfo->ifi_index;
370         snprintf(netinfo->devices[netinfo->used_len].devname,
371                  sizeof(netinfo->devices[netinfo->used_len].devname),
372                  "%s",
373                  tb[IFLA_IFNAME]
374                          ? libbpf_nla_getattr_str(tb[IFLA_IFNAME])
375                          : "");
376         netinfo->used_len++;
377
378         return do_xdp_dump(ifinfo, tb);
379 }
380
381 static int dump_class_qdisc_nlmsg(void *cookie, void *msg, struct nlattr **tb)
382 {
383         struct bpf_tcinfo_t *tcinfo = cookie;
384         struct tcmsg *info = msg;
385
386         if (tcinfo->is_qdisc) {
387                 /* skip clsact qdisc */
388                 if (tb[TCA_KIND] &&
389                     strcmp(libbpf_nla_data(tb[TCA_KIND]), "clsact") == 0)
390                         return 0;
391                 if (info->tcm_handle == 0)
392                         return 0;
393         }
394
395         if (tcinfo->used_len == tcinfo->array_len) {
396                 tcinfo->handle_array = realloc(tcinfo->handle_array,
397                         (tcinfo->array_len + 16) * sizeof(struct tc_kind_handle));
398                 if (!tcinfo->handle_array)
399                         return -ENOMEM;
400
401                 tcinfo->array_len += 16;
402         }
403         tcinfo->handle_array[tcinfo->used_len].handle = info->tcm_handle;
404         snprintf(tcinfo->handle_array[tcinfo->used_len].kind,
405                  sizeof(tcinfo->handle_array[tcinfo->used_len].kind),
406                  "%s",
407                  tb[TCA_KIND]
408                          ? libbpf_nla_getattr_str(tb[TCA_KIND])
409                          : "unknown");
410         tcinfo->used_len++;
411
412         return 0;
413 }
414
415 static int dump_filter_nlmsg(void *cookie, void *msg, struct nlattr **tb)
416 {
417         const struct bpf_filter_t *filter_info = cookie;
418
419         return do_filter_dump((struct tcmsg *)msg, tb, filter_info->kind,
420                               filter_info->devname, filter_info->ifindex);
421 }
422
423 static int show_dev_tc_bpf(int sock, unsigned int nl_pid,
424                            struct ip_devname_ifindex *dev)
425 {
426         struct bpf_filter_t filter_info;
427         struct bpf_tcinfo_t tcinfo;
428         int i, handle, ret = 0;
429
430         tcinfo.handle_array = NULL;
431         tcinfo.used_len = 0;
432         tcinfo.array_len = 0;
433
434         tcinfo.is_qdisc = false;
435         ret = netlink_get_class(sock, nl_pid, dev->ifindex,
436                                 dump_class_qdisc_nlmsg, &tcinfo);
437         if (ret)
438                 goto out;
439
440         tcinfo.is_qdisc = true;
441         ret = netlink_get_qdisc(sock, nl_pid, dev->ifindex,
442                                 dump_class_qdisc_nlmsg, &tcinfo);
443         if (ret)
444                 goto out;
445
446         filter_info.devname = dev->devname;
447         filter_info.ifindex = dev->ifindex;
448         for (i = 0; i < tcinfo.used_len; i++) {
449                 filter_info.kind = tcinfo.handle_array[i].kind;
450                 ret = netlink_get_filter(sock, nl_pid, dev->ifindex,
451                                          tcinfo.handle_array[i].handle,
452                                          dump_filter_nlmsg, &filter_info);
453                 if (ret)
454                         goto out;
455         }
456
457         /* root, ingress and egress handle */
458         handle = TC_H_ROOT;
459         filter_info.kind = "root";
460         ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle,
461                                  dump_filter_nlmsg, &filter_info);
462         if (ret)
463                 goto out;
464
465         handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
466         filter_info.kind = "clsact/ingress";
467         ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle,
468                                  dump_filter_nlmsg, &filter_info);
469         if (ret)
470                 goto out;
471
472         handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS);
473         filter_info.kind = "clsact/egress";
474         ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle,
475                                  dump_filter_nlmsg, &filter_info);
476         if (ret)
477                 goto out;
478
479 out:
480         free(tcinfo.handle_array);
481         return 0;
482 }
483
484 static int query_flow_dissector(struct bpf_attach_info *attach_info)
485 {
486         __u32 attach_flags;
487         __u32 prog_ids[1];
488         __u32 prog_cnt;
489         int err;
490         int fd;
491
492         fd = open("/proc/self/ns/net", O_RDONLY);
493         if (fd < 0) {
494                 p_err("can't open /proc/self/ns/net: %s",
495                       strerror(errno));
496                 return -1;
497         }
498         prog_cnt = ARRAY_SIZE(prog_ids);
499         err = bpf_prog_query(fd, BPF_FLOW_DISSECTOR, 0,
500                              &attach_flags, prog_ids, &prog_cnt);
501         close(fd);
502         if (err) {
503                 if (errno == EINVAL) {
504                         /* Older kernel's don't support querying
505                          * flow dissector programs.
506                          */
507                         errno = 0;
508                         return 0;
509                 }
510                 p_err("can't query prog: %s", strerror(errno));
511                 return -1;
512         }
513
514         if (prog_cnt == 1)
515                 attach_info->flow_dissector_id = prog_ids[0];
516
517         return 0;
518 }
519
520 static int net_parse_dev(int *argc, char ***argv)
521 {
522         int ifindex;
523
524         if (is_prefix(**argv, "dev")) {
525                 NEXT_ARGP();
526
527                 ifindex = if_nametoindex(**argv);
528                 if (!ifindex)
529                         p_err("invalid devname %s", **argv);
530
531                 NEXT_ARGP();
532         } else {
533                 p_err("expected 'dev', got: '%s'?", **argv);
534                 return -1;
535         }
536
537         return ifindex;
538 }
539
540 static int do_attach_detach_xdp(int progfd, enum net_attach_type attach_type,
541                                 int ifindex, bool overwrite)
542 {
543         __u32 flags = 0;
544
545         if (!overwrite)
546                 flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
547         if (attach_type == NET_ATTACH_TYPE_XDP_GENERIC)
548                 flags |= XDP_FLAGS_SKB_MODE;
549         if (attach_type == NET_ATTACH_TYPE_XDP_DRIVER)
550                 flags |= XDP_FLAGS_DRV_MODE;
551         if (attach_type == NET_ATTACH_TYPE_XDP_OFFLOAD)
552                 flags |= XDP_FLAGS_HW_MODE;
553
554         return bpf_set_link_xdp_fd(ifindex, progfd, flags);
555 }
556
557 static int do_attach(int argc, char **argv)
558 {
559         enum net_attach_type attach_type;
560         int progfd, ifindex, err = 0;
561         bool overwrite = false;
562
563         /* parse attach args */
564         if (!REQ_ARGS(5))
565                 return -EINVAL;
566
567         attach_type = parse_attach_type(*argv);
568         if (attach_type == net_attach_type_size) {
569                 p_err("invalid net attach/detach type: %s", *argv);
570                 return -EINVAL;
571         }
572         NEXT_ARG();
573
574         progfd = prog_parse_fd(&argc, &argv);
575         if (progfd < 0)
576                 return -EINVAL;
577
578         ifindex = net_parse_dev(&argc, &argv);
579         if (ifindex < 1) {
580                 err = -EINVAL;
581                 goto cleanup;
582         }
583
584         if (argc) {
585                 if (is_prefix(*argv, "overwrite")) {
586                         overwrite = true;
587                 } else {
588                         p_err("expected 'overwrite', got: '%s'?", *argv);
589                         err = -EINVAL;
590                         goto cleanup;
591                 }
592         }
593
594         /* attach xdp prog */
595         if (is_prefix("xdp", attach_type_strings[attach_type]))
596                 err = do_attach_detach_xdp(progfd, attach_type, ifindex,
597                                            overwrite);
598         if (err) {
599                 p_err("interface %s attach failed: %s",
600                       attach_type_strings[attach_type], strerror(-err));
601                 goto cleanup;
602         }
603
604         if (json_output)
605                 jsonw_null(json_wtr);
606 cleanup:
607         close(progfd);
608         return err;
609 }
610
611 static int do_detach(int argc, char **argv)
612 {
613         enum net_attach_type attach_type;
614         int progfd, ifindex, err = 0;
615
616         /* parse detach args */
617         if (!REQ_ARGS(3))
618                 return -EINVAL;
619
620         attach_type = parse_attach_type(*argv);
621         if (attach_type == net_attach_type_size) {
622                 p_err("invalid net attach/detach type: %s", *argv);
623                 return -EINVAL;
624         }
625         NEXT_ARG();
626
627         ifindex = net_parse_dev(&argc, &argv);
628         if (ifindex < 1)
629                 return -EINVAL;
630
631         /* detach xdp prog */
632         progfd = -1;
633         if (is_prefix("xdp", attach_type_strings[attach_type]))
634                 err = do_attach_detach_xdp(progfd, attach_type, ifindex, NULL);
635
636         if (err < 0) {
637                 p_err("interface %s detach failed: %s",
638                       attach_type_strings[attach_type], strerror(-err));
639                 return err;
640         }
641
642         if (json_output)
643                 jsonw_null(json_wtr);
644
645         return 0;
646 }
647
648 static int do_show(int argc, char **argv)
649 {
650         struct bpf_attach_info attach_info = {};
651         int i, sock, ret, filter_idx = -1;
652         struct bpf_netdev_t dev_array;
653         unsigned int nl_pid = 0;
654         char err_buf[256];
655
656         if (argc == 2) {
657                 filter_idx = net_parse_dev(&argc, &argv);
658                 if (filter_idx < 1)
659                         return -1;
660         } else if (argc != 0) {
661                 usage();
662         }
663
664         ret = query_flow_dissector(&attach_info);
665         if (ret)
666                 return -1;
667
668         sock = netlink_open(&nl_pid);
669         if (sock < 0) {
670                 fprintf(stderr, "failed to open netlink sock\n");
671                 return -1;
672         }
673
674         dev_array.devices = NULL;
675         dev_array.used_len = 0;
676         dev_array.array_len = 0;
677         dev_array.filter_idx = filter_idx;
678
679         if (json_output)
680                 jsonw_start_array(json_wtr);
681         NET_START_OBJECT;
682         NET_START_ARRAY("xdp", "%s:\n");
683         ret = netlink_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array);
684         NET_END_ARRAY("\n");
685
686         if (!ret) {
687                 NET_START_ARRAY("tc", "%s:\n");
688                 for (i = 0; i < dev_array.used_len; i++) {
689                         ret = show_dev_tc_bpf(sock, nl_pid,
690                                               &dev_array.devices[i]);
691                         if (ret)
692                                 break;
693                 }
694                 NET_END_ARRAY("\n");
695         }
696
697         NET_START_ARRAY("flow_dissector", "%s:\n");
698         if (attach_info.flow_dissector_id > 0)
699                 NET_DUMP_UINT("id", "id %u", attach_info.flow_dissector_id);
700         NET_END_ARRAY("\n");
701
702         NET_END_OBJECT;
703         if (json_output)
704                 jsonw_end_array(json_wtr);
705
706         if (ret) {
707                 if (json_output)
708                         jsonw_null(json_wtr);
709                 libbpf_strerror(ret, err_buf, sizeof(err_buf));
710                 fprintf(stderr, "Error: %s\n", err_buf);
711         }
712         free(dev_array.devices);
713         close(sock);
714         return ret;
715 }
716
717 static int do_help(int argc, char **argv)
718 {
719         if (json_output) {
720                 jsonw_null(json_wtr);
721                 return 0;
722         }
723
724         fprintf(stderr,
725                 "Usage: %1$s %2$s { show | list } [dev <devname>]\n"
726                 "       %1$s %2$s attach ATTACH_TYPE PROG dev <devname> [ overwrite ]\n"
727                 "       %1$s %2$s detach ATTACH_TYPE dev <devname>\n"
728                 "       %1$s %2$s help\n"
729                 "\n"
730                 "       " HELP_SPEC_PROGRAM "\n"
731                 "       ATTACH_TYPE := { xdp | xdpgeneric | xdpdrv | xdpoffload }\n"
732                 "\n"
733                 "Note: Only xdp and tc attachments are supported now.\n"
734                 "      For progs attached to cgroups, use \"bpftool cgroup\"\n"
735                 "      to dump program attachments. For program types\n"
736                 "      sk_{filter,skb,msg,reuseport} and lwt/seg6, please\n"
737                 "      consult iproute2.\n"
738                 "",
739                 bin_name, argv[-2]);
740
741         return 0;
742 }
743
744 static const struct cmd cmds[] = {
745         { "show",       do_show },
746         { "list",       do_show },
747         { "attach",     do_attach },
748         { "detach",     do_detach },
749         { "help",       do_help },
750         { 0 }
751 };
752
753 int do_net(int argc, char **argv)
754 {
755         return cmd_select(cmds, argc, argv, do_help);
756 }