0001
0002
0003
0004 #define _GNU_SOURCE
0005 #include <errno.h>
0006 #include <fcntl.h>
0007 #include <stdlib.h>
0008 #include <string.h>
0009 #include <time.h>
0010 #include <unistd.h>
0011 #include <bpf/bpf.h>
0012 #include <bpf/libbpf.h>
0013 #include <net/if.h>
0014 #include <linux/rtnetlink.h>
0015 #include <linux/socket.h>
0016 #include <linux/tc_act/tc_bpf.h>
0017 #include <sys/socket.h>
0018 #include <sys/stat.h>
0019 #include <sys/types.h>
0020
0021 #include "bpf/nlattr.h"
0022 #include "main.h"
0023 #include "netlink_dumper.h"
0024
0025 #ifndef SOL_NETLINK
0026 #define SOL_NETLINK 270
0027 #endif
0028
0029 struct ip_devname_ifindex {
0030 char devname[64];
0031 int ifindex;
0032 };
0033
0034 struct bpf_netdev_t {
0035 struct ip_devname_ifindex *devices;
0036 int used_len;
0037 int array_len;
0038 int filter_idx;
0039 };
0040
0041 struct tc_kind_handle {
0042 char kind[64];
0043 int handle;
0044 };
0045
0046 struct bpf_tcinfo_t {
0047 struct tc_kind_handle *handle_array;
0048 int used_len;
0049 int array_len;
0050 bool is_qdisc;
0051 };
0052
0053 struct bpf_filter_t {
0054 const char *kind;
0055 const char *devname;
0056 int ifindex;
0057 };
0058
0059 struct bpf_attach_info {
0060 __u32 flow_dissector_id;
0061 };
0062
0063 enum net_attach_type {
0064 NET_ATTACH_TYPE_XDP,
0065 NET_ATTACH_TYPE_XDP_GENERIC,
0066 NET_ATTACH_TYPE_XDP_DRIVER,
0067 NET_ATTACH_TYPE_XDP_OFFLOAD,
0068 };
0069
0070 static const char * const attach_type_strings[] = {
0071 [NET_ATTACH_TYPE_XDP] = "xdp",
0072 [NET_ATTACH_TYPE_XDP_GENERIC] = "xdpgeneric",
0073 [NET_ATTACH_TYPE_XDP_DRIVER] = "xdpdrv",
0074 [NET_ATTACH_TYPE_XDP_OFFLOAD] = "xdpoffload",
0075 };
0076
0077 const size_t net_attach_type_size = ARRAY_SIZE(attach_type_strings);
0078
0079 static enum net_attach_type parse_attach_type(const char *str)
0080 {
0081 enum net_attach_type type;
0082
0083 for (type = 0; type < net_attach_type_size; type++) {
0084 if (attach_type_strings[type] &&
0085 is_prefix(str, attach_type_strings[type]))
0086 return type;
0087 }
0088
0089 return net_attach_type_size;
0090 }
0091
0092 typedef int (*dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb);
0093
0094 typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, dump_nlmsg_t, void *cookie);
0095
0096 static int netlink_open(__u32 *nl_pid)
0097 {
0098 struct sockaddr_nl sa;
0099 socklen_t addrlen;
0100 int one = 1, ret;
0101 int sock;
0102
0103 memset(&sa, 0, sizeof(sa));
0104 sa.nl_family = AF_NETLINK;
0105
0106 sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
0107 if (sock < 0)
0108 return -errno;
0109
0110 if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK,
0111 &one, sizeof(one)) < 0) {
0112 p_err("Netlink error reporting not supported");
0113 }
0114
0115 if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
0116 ret = -errno;
0117 goto cleanup;
0118 }
0119
0120 addrlen = sizeof(sa);
0121 if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) {
0122 ret = -errno;
0123 goto cleanup;
0124 }
0125
0126 if (addrlen != sizeof(sa)) {
0127 ret = -LIBBPF_ERRNO__INTERNAL;
0128 goto cleanup;
0129 }
0130
0131 *nl_pid = sa.nl_pid;
0132 return sock;
0133
0134 cleanup:
0135 close(sock);
0136 return ret;
0137 }
0138
0139 static int netlink_recv(int sock, __u32 nl_pid, __u32 seq,
0140 __dump_nlmsg_t _fn, dump_nlmsg_t fn,
0141 void *cookie)
0142 {
0143 bool multipart = true;
0144 struct nlmsgerr *err;
0145 struct nlmsghdr *nh;
0146 char buf[4096];
0147 int len, ret;
0148
0149 while (multipart) {
0150 multipart = false;
0151 len = recv(sock, buf, sizeof(buf), 0);
0152 if (len < 0) {
0153 ret = -errno;
0154 goto done;
0155 }
0156
0157 if (len == 0)
0158 break;
0159
0160 for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, (unsigned int)len);
0161 nh = NLMSG_NEXT(nh, len)) {
0162 if (nh->nlmsg_pid != nl_pid) {
0163 ret = -LIBBPF_ERRNO__WRNGPID;
0164 goto done;
0165 }
0166 if (nh->nlmsg_seq != seq) {
0167 ret = -LIBBPF_ERRNO__INVSEQ;
0168 goto done;
0169 }
0170 if (nh->nlmsg_flags & NLM_F_MULTI)
0171 multipart = true;
0172 switch (nh->nlmsg_type) {
0173 case NLMSG_ERROR:
0174 err = (struct nlmsgerr *)NLMSG_DATA(nh);
0175 if (!err->error)
0176 continue;
0177 ret = err->error;
0178 libbpf_nla_dump_errormsg(nh);
0179 goto done;
0180 case NLMSG_DONE:
0181 return 0;
0182 default:
0183 break;
0184 }
0185 if (_fn) {
0186 ret = _fn(nh, fn, cookie);
0187 if (ret)
0188 return ret;
0189 }
0190 }
0191 }
0192 ret = 0;
0193 done:
0194 return ret;
0195 }
0196
0197 static int __dump_class_nlmsg(struct nlmsghdr *nlh,
0198 dump_nlmsg_t dump_class_nlmsg,
0199 void *cookie)
0200 {
0201 struct nlattr *tb[TCA_MAX + 1], *attr;
0202 struct tcmsg *t = NLMSG_DATA(nlh);
0203 int len;
0204
0205 len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
0206 attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
0207 if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
0208 return -LIBBPF_ERRNO__NLPARSE;
0209
0210 return dump_class_nlmsg(cookie, t, tb);
0211 }
0212
0213 static int netlink_get_class(int sock, unsigned int nl_pid, int ifindex,
0214 dump_nlmsg_t dump_class_nlmsg, void *cookie)
0215 {
0216 struct {
0217 struct nlmsghdr nlh;
0218 struct tcmsg t;
0219 } req = {
0220 .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
0221 .nlh.nlmsg_type = RTM_GETTCLASS,
0222 .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
0223 .t.tcm_family = AF_UNSPEC,
0224 .t.tcm_ifindex = ifindex,
0225 };
0226 int seq = time(NULL);
0227
0228 req.nlh.nlmsg_seq = seq;
0229 if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
0230 return -errno;
0231
0232 return netlink_recv(sock, nl_pid, seq, __dump_class_nlmsg,
0233 dump_class_nlmsg, cookie);
0234 }
0235
0236 static int __dump_qdisc_nlmsg(struct nlmsghdr *nlh,
0237 dump_nlmsg_t dump_qdisc_nlmsg,
0238 void *cookie)
0239 {
0240 struct nlattr *tb[TCA_MAX + 1], *attr;
0241 struct tcmsg *t = NLMSG_DATA(nlh);
0242 int len;
0243
0244 len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
0245 attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
0246 if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
0247 return -LIBBPF_ERRNO__NLPARSE;
0248
0249 return dump_qdisc_nlmsg(cookie, t, tb);
0250 }
0251
0252 static int netlink_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
0253 dump_nlmsg_t dump_qdisc_nlmsg, void *cookie)
0254 {
0255 struct {
0256 struct nlmsghdr nlh;
0257 struct tcmsg t;
0258 } req = {
0259 .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
0260 .nlh.nlmsg_type = RTM_GETQDISC,
0261 .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
0262 .t.tcm_family = AF_UNSPEC,
0263 .t.tcm_ifindex = ifindex,
0264 };
0265 int seq = time(NULL);
0266
0267 req.nlh.nlmsg_seq = seq;
0268 if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
0269 return -errno;
0270
0271 return netlink_recv(sock, nl_pid, seq, __dump_qdisc_nlmsg,
0272 dump_qdisc_nlmsg, cookie);
0273 }
0274
0275 static int __dump_filter_nlmsg(struct nlmsghdr *nlh,
0276 dump_nlmsg_t dump_filter_nlmsg,
0277 void *cookie)
0278 {
0279 struct nlattr *tb[TCA_MAX + 1], *attr;
0280 struct tcmsg *t = NLMSG_DATA(nlh);
0281 int len;
0282
0283 len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
0284 attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
0285 if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
0286 return -LIBBPF_ERRNO__NLPARSE;
0287
0288 return dump_filter_nlmsg(cookie, t, tb);
0289 }
0290
0291 static int netlink_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
0292 dump_nlmsg_t dump_filter_nlmsg, void *cookie)
0293 {
0294 struct {
0295 struct nlmsghdr nlh;
0296 struct tcmsg t;
0297 } req = {
0298 .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
0299 .nlh.nlmsg_type = RTM_GETTFILTER,
0300 .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
0301 .t.tcm_family = AF_UNSPEC,
0302 .t.tcm_ifindex = ifindex,
0303 .t.tcm_parent = handle,
0304 };
0305 int seq = time(NULL);
0306
0307 req.nlh.nlmsg_seq = seq;
0308 if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
0309 return -errno;
0310
0311 return netlink_recv(sock, nl_pid, seq, __dump_filter_nlmsg,
0312 dump_filter_nlmsg, cookie);
0313 }
0314
0315 static int __dump_link_nlmsg(struct nlmsghdr *nlh,
0316 dump_nlmsg_t dump_link_nlmsg, void *cookie)
0317 {
0318 struct nlattr *tb[IFLA_MAX + 1], *attr;
0319 struct ifinfomsg *ifi = NLMSG_DATA(nlh);
0320 int len;
0321
0322 len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
0323 attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
0324 if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
0325 return -LIBBPF_ERRNO__NLPARSE;
0326
0327 return dump_link_nlmsg(cookie, ifi, tb);
0328 }
0329
0330 static int netlink_get_link(int sock, unsigned int nl_pid,
0331 dump_nlmsg_t dump_link_nlmsg, void *cookie)
0332 {
0333 struct {
0334 struct nlmsghdr nlh;
0335 struct ifinfomsg ifm;
0336 } req = {
0337 .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
0338 .nlh.nlmsg_type = RTM_GETLINK,
0339 .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
0340 .ifm.ifi_family = AF_PACKET,
0341 };
0342 int seq = time(NULL);
0343
0344 req.nlh.nlmsg_seq = seq;
0345 if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
0346 return -errno;
0347
0348 return netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg,
0349 dump_link_nlmsg, cookie);
0350 }
0351
0352 static int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb)
0353 {
0354 struct bpf_netdev_t *netinfo = cookie;
0355 struct ifinfomsg *ifinfo = msg;
0356
0357 if (netinfo->filter_idx > 0 && netinfo->filter_idx != ifinfo->ifi_index)
0358 return 0;
0359
0360 if (netinfo->used_len == netinfo->array_len) {
0361 netinfo->devices = realloc(netinfo->devices,
0362 (netinfo->array_len + 16) *
0363 sizeof(struct ip_devname_ifindex));
0364 if (!netinfo->devices)
0365 return -ENOMEM;
0366
0367 netinfo->array_len += 16;
0368 }
0369 netinfo->devices[netinfo->used_len].ifindex = ifinfo->ifi_index;
0370 snprintf(netinfo->devices[netinfo->used_len].devname,
0371 sizeof(netinfo->devices[netinfo->used_len].devname),
0372 "%s",
0373 tb[IFLA_IFNAME]
0374 ? libbpf_nla_getattr_str(tb[IFLA_IFNAME])
0375 : "");
0376 netinfo->used_len++;
0377
0378 return do_xdp_dump(ifinfo, tb);
0379 }
0380
0381 static int dump_class_qdisc_nlmsg(void *cookie, void *msg, struct nlattr **tb)
0382 {
0383 struct bpf_tcinfo_t *tcinfo = cookie;
0384 struct tcmsg *info = msg;
0385
0386 if (tcinfo->is_qdisc) {
0387
0388 if (tb[TCA_KIND] &&
0389 strcmp(libbpf_nla_data(tb[TCA_KIND]), "clsact") == 0)
0390 return 0;
0391 if (info->tcm_handle == 0)
0392 return 0;
0393 }
0394
0395 if (tcinfo->used_len == tcinfo->array_len) {
0396 tcinfo->handle_array = realloc(tcinfo->handle_array,
0397 (tcinfo->array_len + 16) * sizeof(struct tc_kind_handle));
0398 if (!tcinfo->handle_array)
0399 return -ENOMEM;
0400
0401 tcinfo->array_len += 16;
0402 }
0403 tcinfo->handle_array[tcinfo->used_len].handle = info->tcm_handle;
0404 snprintf(tcinfo->handle_array[tcinfo->used_len].kind,
0405 sizeof(tcinfo->handle_array[tcinfo->used_len].kind),
0406 "%s",
0407 tb[TCA_KIND]
0408 ? libbpf_nla_getattr_str(tb[TCA_KIND])
0409 : "unknown");
0410 tcinfo->used_len++;
0411
0412 return 0;
0413 }
0414
0415 static int dump_filter_nlmsg(void *cookie, void *msg, struct nlattr **tb)
0416 {
0417 const struct bpf_filter_t *filter_info = cookie;
0418
0419 return do_filter_dump((struct tcmsg *)msg, tb, filter_info->kind,
0420 filter_info->devname, filter_info->ifindex);
0421 }
0422
0423 static int show_dev_tc_bpf(int sock, unsigned int nl_pid,
0424 struct ip_devname_ifindex *dev)
0425 {
0426 struct bpf_filter_t filter_info;
0427 struct bpf_tcinfo_t tcinfo;
0428 int i, handle, ret = 0;
0429
0430 tcinfo.handle_array = NULL;
0431 tcinfo.used_len = 0;
0432 tcinfo.array_len = 0;
0433
0434 tcinfo.is_qdisc = false;
0435 ret = netlink_get_class(sock, nl_pid, dev->ifindex,
0436 dump_class_qdisc_nlmsg, &tcinfo);
0437 if (ret)
0438 goto out;
0439
0440 tcinfo.is_qdisc = true;
0441 ret = netlink_get_qdisc(sock, nl_pid, dev->ifindex,
0442 dump_class_qdisc_nlmsg, &tcinfo);
0443 if (ret)
0444 goto out;
0445
0446 filter_info.devname = dev->devname;
0447 filter_info.ifindex = dev->ifindex;
0448 for (i = 0; i < tcinfo.used_len; i++) {
0449 filter_info.kind = tcinfo.handle_array[i].kind;
0450 ret = netlink_get_filter(sock, nl_pid, dev->ifindex,
0451 tcinfo.handle_array[i].handle,
0452 dump_filter_nlmsg, &filter_info);
0453 if (ret)
0454 goto out;
0455 }
0456
0457
0458 handle = TC_H_ROOT;
0459 filter_info.kind = "root";
0460 ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle,
0461 dump_filter_nlmsg, &filter_info);
0462 if (ret)
0463 goto out;
0464
0465 handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
0466 filter_info.kind = "clsact/ingress";
0467 ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle,
0468 dump_filter_nlmsg, &filter_info);
0469 if (ret)
0470 goto out;
0471
0472 handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS);
0473 filter_info.kind = "clsact/egress";
0474 ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle,
0475 dump_filter_nlmsg, &filter_info);
0476 if (ret)
0477 goto out;
0478
0479 out:
0480 free(tcinfo.handle_array);
0481 return 0;
0482 }
0483
0484 static int query_flow_dissector(struct bpf_attach_info *attach_info)
0485 {
0486 __u32 attach_flags;
0487 __u32 prog_ids[1];
0488 __u32 prog_cnt;
0489 int err;
0490 int fd;
0491
0492 fd = open("/proc/self/ns/net", O_RDONLY);
0493 if (fd < 0) {
0494 p_err("can't open /proc/self/ns/net: %s",
0495 strerror(errno));
0496 return -1;
0497 }
0498 prog_cnt = ARRAY_SIZE(prog_ids);
0499 err = bpf_prog_query(fd, BPF_FLOW_DISSECTOR, 0,
0500 &attach_flags, prog_ids, &prog_cnt);
0501 close(fd);
0502 if (err) {
0503 if (errno == EINVAL) {
0504
0505
0506
0507 errno = 0;
0508 return 0;
0509 }
0510 p_err("can't query prog: %s", strerror(errno));
0511 return -1;
0512 }
0513
0514 if (prog_cnt == 1)
0515 attach_info->flow_dissector_id = prog_ids[0];
0516
0517 return 0;
0518 }
0519
0520 static int net_parse_dev(int *argc, char ***argv)
0521 {
0522 int ifindex;
0523
0524 if (is_prefix(**argv, "dev")) {
0525 NEXT_ARGP();
0526
0527 ifindex = if_nametoindex(**argv);
0528 if (!ifindex)
0529 p_err("invalid devname %s", **argv);
0530
0531 NEXT_ARGP();
0532 } else {
0533 p_err("expected 'dev', got: '%s'?", **argv);
0534 return -1;
0535 }
0536
0537 return ifindex;
0538 }
0539
0540 static int do_attach_detach_xdp(int progfd, enum net_attach_type attach_type,
0541 int ifindex, bool overwrite)
0542 {
0543 __u32 flags = 0;
0544
0545 if (!overwrite)
0546 flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
0547 if (attach_type == NET_ATTACH_TYPE_XDP_GENERIC)
0548 flags |= XDP_FLAGS_SKB_MODE;
0549 if (attach_type == NET_ATTACH_TYPE_XDP_DRIVER)
0550 flags |= XDP_FLAGS_DRV_MODE;
0551 if (attach_type == NET_ATTACH_TYPE_XDP_OFFLOAD)
0552 flags |= XDP_FLAGS_HW_MODE;
0553
0554 return bpf_xdp_attach(ifindex, progfd, flags, NULL);
0555 }
0556
0557 static int do_attach(int argc, char **argv)
0558 {
0559 enum net_attach_type attach_type;
0560 int progfd, ifindex, err = 0;
0561 bool overwrite = false;
0562
0563
0564 if (!REQ_ARGS(5))
0565 return -EINVAL;
0566
0567 attach_type = parse_attach_type(*argv);
0568 if (attach_type == net_attach_type_size) {
0569 p_err("invalid net attach/detach type: %s", *argv);
0570 return -EINVAL;
0571 }
0572 NEXT_ARG();
0573
0574 progfd = prog_parse_fd(&argc, &argv);
0575 if (progfd < 0)
0576 return -EINVAL;
0577
0578 ifindex = net_parse_dev(&argc, &argv);
0579 if (ifindex < 1) {
0580 err = -EINVAL;
0581 goto cleanup;
0582 }
0583
0584 if (argc) {
0585 if (is_prefix(*argv, "overwrite")) {
0586 overwrite = true;
0587 } else {
0588 p_err("expected 'overwrite', got: '%s'?", *argv);
0589 err = -EINVAL;
0590 goto cleanup;
0591 }
0592 }
0593
0594
0595 if (is_prefix("xdp", attach_type_strings[attach_type]))
0596 err = do_attach_detach_xdp(progfd, attach_type, ifindex,
0597 overwrite);
0598 if (err) {
0599 p_err("interface %s attach failed: %s",
0600 attach_type_strings[attach_type], strerror(-err));
0601 goto cleanup;
0602 }
0603
0604 if (json_output)
0605 jsonw_null(json_wtr);
0606 cleanup:
0607 close(progfd);
0608 return err;
0609 }
0610
0611 static int do_detach(int argc, char **argv)
0612 {
0613 enum net_attach_type attach_type;
0614 int progfd, ifindex, err = 0;
0615
0616
0617 if (!REQ_ARGS(3))
0618 return -EINVAL;
0619
0620 attach_type = parse_attach_type(*argv);
0621 if (attach_type == net_attach_type_size) {
0622 p_err("invalid net attach/detach type: %s", *argv);
0623 return -EINVAL;
0624 }
0625 NEXT_ARG();
0626
0627 ifindex = net_parse_dev(&argc, &argv);
0628 if (ifindex < 1)
0629 return -EINVAL;
0630
0631
0632 progfd = -1;
0633 if (is_prefix("xdp", attach_type_strings[attach_type]))
0634 err = do_attach_detach_xdp(progfd, attach_type, ifindex, NULL);
0635
0636 if (err < 0) {
0637 p_err("interface %s detach failed: %s",
0638 attach_type_strings[attach_type], strerror(-err));
0639 return err;
0640 }
0641
0642 if (json_output)
0643 jsonw_null(json_wtr);
0644
0645 return 0;
0646 }
0647
0648 static int do_show(int argc, char **argv)
0649 {
0650 struct bpf_attach_info attach_info = {};
0651 int i, sock, ret, filter_idx = -1;
0652 struct bpf_netdev_t dev_array;
0653 unsigned int nl_pid = 0;
0654 char err_buf[256];
0655
0656 if (argc == 2) {
0657 filter_idx = net_parse_dev(&argc, &argv);
0658 if (filter_idx < 1)
0659 return -1;
0660 } else if (argc != 0) {
0661 usage();
0662 }
0663
0664 ret = query_flow_dissector(&attach_info);
0665 if (ret)
0666 return -1;
0667
0668 sock = netlink_open(&nl_pid);
0669 if (sock < 0) {
0670 fprintf(stderr, "failed to open netlink sock\n");
0671 return -1;
0672 }
0673
0674 dev_array.devices = NULL;
0675 dev_array.used_len = 0;
0676 dev_array.array_len = 0;
0677 dev_array.filter_idx = filter_idx;
0678
0679 if (json_output)
0680 jsonw_start_array(json_wtr);
0681 NET_START_OBJECT;
0682 NET_START_ARRAY("xdp", "%s:\n");
0683 ret = netlink_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array);
0684 NET_END_ARRAY("\n");
0685
0686 if (!ret) {
0687 NET_START_ARRAY("tc", "%s:\n");
0688 for (i = 0; i < dev_array.used_len; i++) {
0689 ret = show_dev_tc_bpf(sock, nl_pid,
0690 &dev_array.devices[i]);
0691 if (ret)
0692 break;
0693 }
0694 NET_END_ARRAY("\n");
0695 }
0696
0697 NET_START_ARRAY("flow_dissector", "%s:\n");
0698 if (attach_info.flow_dissector_id > 0)
0699 NET_DUMP_UINT("id", "id %u", attach_info.flow_dissector_id);
0700 NET_END_ARRAY("\n");
0701
0702 NET_END_OBJECT;
0703 if (json_output)
0704 jsonw_end_array(json_wtr);
0705
0706 if (ret) {
0707 if (json_output)
0708 jsonw_null(json_wtr);
0709 libbpf_strerror(ret, err_buf, sizeof(err_buf));
0710 fprintf(stderr, "Error: %s\n", err_buf);
0711 }
0712 free(dev_array.devices);
0713 close(sock);
0714 return ret;
0715 }
0716
0717 static int do_help(int argc, char **argv)
0718 {
0719 if (json_output) {
0720 jsonw_null(json_wtr);
0721 return 0;
0722 }
0723
0724 fprintf(stderr,
0725 "Usage: %1$s %2$s { show | list } [dev <devname>]\n"
0726 " %1$s %2$s attach ATTACH_TYPE PROG dev <devname> [ overwrite ]\n"
0727 " %1$s %2$s detach ATTACH_TYPE dev <devname>\n"
0728 " %1$s %2$s help\n"
0729 "\n"
0730 " " HELP_SPEC_PROGRAM "\n"
0731 " ATTACH_TYPE := { xdp | xdpgeneric | xdpdrv | xdpoffload }\n"
0732 " " HELP_SPEC_OPTIONS " }\n"
0733 "\n"
0734 "Note: Only xdp and tc attachments are supported now.\n"
0735 " For progs attached to cgroups, use \"bpftool cgroup\"\n"
0736 " to dump program attachments. For program types\n"
0737 " sk_{filter,skb,msg,reuseport} and lwt/seg6, please\n"
0738 " consult iproute2.\n"
0739 "",
0740 bin_name, argv[-2]);
0741
0742 return 0;
0743 }
0744
0745 static const struct cmd cmds[] = {
0746 { "show", do_show },
0747 { "list", do_show },
0748 { "attach", do_attach },
0749 { "detach", do_detach },
0750 { "help", do_help },
0751 { 0 }
0752 };
0753
0754 int do_net(int argc, char **argv)
0755 {
0756 return cmd_select(cmds, argc, argv, do_help);
0757 }