1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 // Copyright (C) 2018 Facebook
14 #include <bpf/libbpf.h>
16 #include <linux/rtnetlink.h>
17 #include <linux/socket.h>
18 #include <linux/tc_act/tc_bpf.h>
19 #include <sys/socket.h>
21 #include <sys/types.h>
23 #include "bpf/nlattr.h"
25 #include "netlink_dumper.h"
28 #define SOL_NETLINK 270
31 struct ip_devname_ifindex
{
37 struct ip_devname_ifindex
*devices
;
43 struct tc_kind_handle
{
49 struct tc_kind_handle
*handle_array
;
61 struct bpf_attach_info
{
62 __u32 flow_dissector_id
;
65 enum net_attach_type
{
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
,
74 static 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",
83 static 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",
90 const size_t net_attach_type_size
= ARRAY_SIZE(attach_type_strings
);
92 static enum net_attach_type
parse_attach_type(const char *str
)
94 enum net_attach_type type
;
96 for (type
= 0; type
< net_attach_type_size
; type
++) {
97 if (attach_type_strings
[type
] &&
98 is_prefix(str
, attach_type_strings
[type
]))
102 return net_attach_type_size
;
105 typedef int (*dump_nlmsg_t
)(void *cookie
, void *msg
, struct nlattr
**tb
);
107 typedef int (*__dump_nlmsg_t
)(struct nlmsghdr
*nlmsg
, dump_nlmsg_t
, void *cookie
);
109 static int netlink_open(__u32
*nl_pid
)
111 struct sockaddr_nl sa
;
116 memset(&sa
, 0, sizeof(sa
));
117 sa
.nl_family
= AF_NETLINK
;
119 sock
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
123 if (setsockopt(sock
, SOL_NETLINK
, NETLINK_EXT_ACK
,
124 &one
, sizeof(one
)) < 0) {
125 p_err("Netlink error reporting not supported");
128 if (bind(sock
, (struct sockaddr
*)&sa
, sizeof(sa
)) < 0) {
133 addrlen
= sizeof(sa
);
134 if (getsockname(sock
, (struct sockaddr
*)&sa
, &addrlen
) < 0) {
139 if (addrlen
!= sizeof(sa
)) {
140 ret
= -LIBBPF_ERRNO__INTERNAL
;
152 static int netlink_recv(int sock
, __u32 nl_pid
, __u32 seq
,
153 __dump_nlmsg_t _fn
, dump_nlmsg_t fn
,
156 bool multipart
= true;
157 struct nlmsgerr
*err
;
164 len
= recv(sock
, buf
, sizeof(buf
), 0);
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
;
179 if (nh
->nlmsg_seq
!= seq
) {
180 ret
= -LIBBPF_ERRNO__INVSEQ
;
183 if (nh
->nlmsg_flags
& NLM_F_MULTI
)
185 switch (nh
->nlmsg_type
) {
187 err
= (struct nlmsgerr
*)NLMSG_DATA(nh
);
191 libbpf_nla_dump_errormsg(nh
);
199 ret
= _fn(nh
, fn
, cookie
);
210 static int __dump_class_nlmsg(struct nlmsghdr
*nlh
,
211 dump_nlmsg_t dump_class_nlmsg
,
214 struct nlattr
*tb
[TCA_MAX
+ 1], *attr
;
215 struct tcmsg
*t
= NLMSG_DATA(nlh
);
218 len
= nlh
->nlmsg_len
- NLMSG_LENGTH(sizeof(*t
));
219 attr
= (struct nlattr
*) ((void *) t
+ NLMSG_ALIGN(sizeof(*t
)));
220 if (libbpf_nla_parse(tb
, TCA_MAX
, attr
, len
, NULL
) != 0)
221 return -LIBBPF_ERRNO__NLPARSE
;
223 return dump_class_nlmsg(cookie
, t
, tb
);
226 static int netlink_get_class(int sock
, unsigned int nl_pid
, int ifindex
,
227 dump_nlmsg_t dump_class_nlmsg
, void *cookie
)
233 .nlh
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct tcmsg
)),
234 .nlh
.nlmsg_type
= RTM_GETTCLASS
,
235 .nlh
.nlmsg_flags
= NLM_F_DUMP
| NLM_F_REQUEST
,
236 .t
.tcm_family
= AF_UNSPEC
,
237 .t
.tcm_ifindex
= ifindex
,
239 int seq
= time(NULL
);
241 req
.nlh
.nlmsg_seq
= seq
;
242 if (send(sock
, &req
, req
.nlh
.nlmsg_len
, 0) < 0)
245 return netlink_recv(sock
, nl_pid
, seq
, __dump_class_nlmsg
,
246 dump_class_nlmsg
, cookie
);
249 static int __dump_qdisc_nlmsg(struct nlmsghdr
*nlh
,
250 dump_nlmsg_t dump_qdisc_nlmsg
,
253 struct nlattr
*tb
[TCA_MAX
+ 1], *attr
;
254 struct tcmsg
*t
= NLMSG_DATA(nlh
);
257 len
= nlh
->nlmsg_len
- NLMSG_LENGTH(sizeof(*t
));
258 attr
= (struct nlattr
*) ((void *) t
+ NLMSG_ALIGN(sizeof(*t
)));
259 if (libbpf_nla_parse(tb
, TCA_MAX
, attr
, len
, NULL
) != 0)
260 return -LIBBPF_ERRNO__NLPARSE
;
262 return dump_qdisc_nlmsg(cookie
, t
, tb
);
265 static int netlink_get_qdisc(int sock
, unsigned int nl_pid
, int ifindex
,
266 dump_nlmsg_t dump_qdisc_nlmsg
, void *cookie
)
272 .nlh
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct tcmsg
)),
273 .nlh
.nlmsg_type
= RTM_GETQDISC
,
274 .nlh
.nlmsg_flags
= NLM_F_DUMP
| NLM_F_REQUEST
,
275 .t
.tcm_family
= AF_UNSPEC
,
276 .t
.tcm_ifindex
= ifindex
,
278 int seq
= time(NULL
);
280 req
.nlh
.nlmsg_seq
= seq
;
281 if (send(sock
, &req
, req
.nlh
.nlmsg_len
, 0) < 0)
284 return netlink_recv(sock
, nl_pid
, seq
, __dump_qdisc_nlmsg
,
285 dump_qdisc_nlmsg
, cookie
);
288 static int __dump_filter_nlmsg(struct nlmsghdr
*nlh
,
289 dump_nlmsg_t dump_filter_nlmsg
,
292 struct nlattr
*tb
[TCA_MAX
+ 1], *attr
;
293 struct tcmsg
*t
= NLMSG_DATA(nlh
);
296 len
= nlh
->nlmsg_len
- NLMSG_LENGTH(sizeof(*t
));
297 attr
= (struct nlattr
*) ((void *) t
+ NLMSG_ALIGN(sizeof(*t
)));
298 if (libbpf_nla_parse(tb
, TCA_MAX
, attr
, len
, NULL
) != 0)
299 return -LIBBPF_ERRNO__NLPARSE
;
301 return dump_filter_nlmsg(cookie
, t
, tb
);
304 static int netlink_get_filter(int sock
, unsigned int nl_pid
, int ifindex
, int handle
,
305 dump_nlmsg_t dump_filter_nlmsg
, void *cookie
)
311 .nlh
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct tcmsg
)),
312 .nlh
.nlmsg_type
= RTM_GETTFILTER
,
313 .nlh
.nlmsg_flags
= NLM_F_DUMP
| NLM_F_REQUEST
,
314 .t
.tcm_family
= AF_UNSPEC
,
315 .t
.tcm_ifindex
= ifindex
,
316 .t
.tcm_parent
= handle
,
318 int seq
= time(NULL
);
320 req
.nlh
.nlmsg_seq
= seq
;
321 if (send(sock
, &req
, req
.nlh
.nlmsg_len
, 0) < 0)
324 return netlink_recv(sock
, nl_pid
, seq
, __dump_filter_nlmsg
,
325 dump_filter_nlmsg
, cookie
);
328 static int __dump_link_nlmsg(struct nlmsghdr
*nlh
,
329 dump_nlmsg_t dump_link_nlmsg
, void *cookie
)
331 struct nlattr
*tb
[IFLA_MAX
+ 1], *attr
;
332 struct ifinfomsg
*ifi
= NLMSG_DATA(nlh
);
335 len
= nlh
->nlmsg_len
- NLMSG_LENGTH(sizeof(*ifi
));
336 attr
= (struct nlattr
*) ((void *) ifi
+ NLMSG_ALIGN(sizeof(*ifi
)));
337 if (libbpf_nla_parse(tb
, IFLA_MAX
, attr
, len
, NULL
) != 0)
338 return -LIBBPF_ERRNO__NLPARSE
;
340 return dump_link_nlmsg(cookie
, ifi
, tb
);
343 static int netlink_get_link(int sock
, unsigned int nl_pid
,
344 dump_nlmsg_t dump_link_nlmsg
, void *cookie
)
348 struct ifinfomsg ifm
;
350 .nlh
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct ifinfomsg
)),
351 .nlh
.nlmsg_type
= RTM_GETLINK
,
352 .nlh
.nlmsg_flags
= NLM_F_DUMP
| NLM_F_REQUEST
,
353 .ifm
.ifi_family
= AF_PACKET
,
355 int seq
= time(NULL
);
357 req
.nlh
.nlmsg_seq
= seq
;
358 if (send(sock
, &req
, req
.nlh
.nlmsg_len
, 0) < 0)
361 return netlink_recv(sock
, nl_pid
, seq
, __dump_link_nlmsg
,
362 dump_link_nlmsg
, cookie
);
365 static int dump_link_nlmsg(void *cookie
, void *msg
, struct nlattr
**tb
)
367 struct bpf_netdev_t
*netinfo
= cookie
;
368 struct ifinfomsg
*ifinfo
= msg
;
370 if (netinfo
->filter_idx
> 0 && netinfo
->filter_idx
!= ifinfo
->ifi_index
)
373 if (netinfo
->used_len
== netinfo
->array_len
) {
374 netinfo
->devices
= realloc(netinfo
->devices
,
375 (netinfo
->array_len
+ 16) *
376 sizeof(struct ip_devname_ifindex
));
377 if (!netinfo
->devices
)
380 netinfo
->array_len
+= 16;
382 netinfo
->devices
[netinfo
->used_len
].ifindex
= ifinfo
->ifi_index
;
383 snprintf(netinfo
->devices
[netinfo
->used_len
].devname
,
384 sizeof(netinfo
->devices
[netinfo
->used_len
].devname
),
387 ? libbpf_nla_getattr_str(tb
[IFLA_IFNAME
])
391 return do_xdp_dump(ifinfo
, tb
);
394 static int dump_class_qdisc_nlmsg(void *cookie
, void *msg
, struct nlattr
**tb
)
396 struct bpf_tcinfo_t
*tcinfo
= cookie
;
397 struct tcmsg
*info
= msg
;
399 if (tcinfo
->is_qdisc
) {
400 /* skip clsact qdisc */
402 strcmp(libbpf_nla_data(tb
[TCA_KIND
]), "clsact") == 0)
404 if (info
->tcm_handle
== 0)
408 if (tcinfo
->used_len
== tcinfo
->array_len
) {
409 tcinfo
->handle_array
= realloc(tcinfo
->handle_array
,
410 (tcinfo
->array_len
+ 16) * sizeof(struct tc_kind_handle
));
411 if (!tcinfo
->handle_array
)
414 tcinfo
->array_len
+= 16;
416 tcinfo
->handle_array
[tcinfo
->used_len
].handle
= info
->tcm_handle
;
417 snprintf(tcinfo
->handle_array
[tcinfo
->used_len
].kind
,
418 sizeof(tcinfo
->handle_array
[tcinfo
->used_len
].kind
),
421 ? libbpf_nla_getattr_str(tb
[TCA_KIND
])
428 static int dump_filter_nlmsg(void *cookie
, void *msg
, struct nlattr
**tb
)
430 const struct bpf_filter_t
*filter_info
= cookie
;
432 return do_filter_dump((struct tcmsg
*)msg
, tb
, filter_info
->kind
,
433 filter_info
->devname
, filter_info
->ifindex
);
436 static int __show_dev_tc_bpf_name(__u32 id
, char *name
, size_t len
)
438 struct bpf_prog_info info
= {};
439 __u32 ilen
= sizeof(info
);
442 fd
= bpf_prog_get_fd_by_id(id
);
445 ret
= bpf_obj_get_info_by_fd(fd
, &info
, &ilen
);
450 get_prog_full_name(&info
, fd
, name
, len
);
458 static void __show_dev_tc_bpf(const struct ip_devname_ifindex
*dev
,
459 const enum bpf_attach_type loc
)
461 __u32 prog_flags
[64] = {}, link_flags
[64] = {}, i
, j
;
462 __u32 prog_ids
[64] = {}, link_ids
[64] = {};
463 LIBBPF_OPTS(bpf_prog_query_opts
, optq
);
464 char prog_name
[MAX_PROG_FULL_NAME
];
467 optq
.prog_ids
= prog_ids
;
468 optq
.prog_attach_flags
= prog_flags
;
469 optq
.link_ids
= link_ids
;
470 optq
.link_attach_flags
= link_flags
;
471 optq
.count
= ARRAY_SIZE(prog_ids
);
473 ret
= bpf_prog_query_opts(dev
->ifindex
, loc
, &optq
);
476 for (i
= 0; i
< optq
.count
; i
++) {
478 NET_DUMP_STR("devname", "%s", dev
->devname
);
479 NET_DUMP_UINT("ifindex", "(%u)", dev
->ifindex
);
480 NET_DUMP_STR("kind", " %s", attach_loc_strings
[loc
]);
481 ret
= __show_dev_tc_bpf_name(prog_ids
[i
], prog_name
,
484 NET_DUMP_STR("name", " %s", prog_name
);
485 NET_DUMP_UINT("prog_id", " prog_id %u ", prog_ids
[i
]);
486 if (prog_flags
[i
] || json_output
) {
487 NET_START_ARRAY("prog_flags", "%s ");
488 for (j
= 0; prog_flags
[i
] && j
< 32; j
++) {
489 if (!(prog_flags
[i
] & (1U << j
)))
491 NET_DUMP_UINT_ONLY(1U << j
);
495 if (link_ids
[i
] || json_output
) {
496 NET_DUMP_UINT("link_id", "link_id %u ", link_ids
[i
]);
497 if (link_flags
[i
] || json_output
) {
498 NET_START_ARRAY("link_flags", "%s ");
499 for (j
= 0; link_flags
[i
] && j
< 32; j
++) {
500 if (!(link_flags
[i
] & (1U << j
)))
502 NET_DUMP_UINT_ONLY(1U << j
);
507 NET_END_OBJECT_FINAL
;
511 static void show_dev_tc_bpf(struct ip_devname_ifindex
*dev
)
513 __show_dev_tc_bpf(dev
, BPF_TCX_INGRESS
);
514 __show_dev_tc_bpf(dev
, BPF_TCX_EGRESS
);
516 __show_dev_tc_bpf(dev
, BPF_NETKIT_PRIMARY
);
517 __show_dev_tc_bpf(dev
, BPF_NETKIT_PEER
);
520 static int show_dev_tc_bpf_classic(int sock
, unsigned int nl_pid
,
521 struct ip_devname_ifindex
*dev
)
523 struct bpf_filter_t filter_info
;
524 struct bpf_tcinfo_t tcinfo
;
525 int i
, handle
, ret
= 0;
527 tcinfo
.handle_array
= NULL
;
529 tcinfo
.array_len
= 0;
531 tcinfo
.is_qdisc
= false;
532 ret
= netlink_get_class(sock
, nl_pid
, dev
->ifindex
,
533 dump_class_qdisc_nlmsg
, &tcinfo
);
537 tcinfo
.is_qdisc
= true;
538 ret
= netlink_get_qdisc(sock
, nl_pid
, dev
->ifindex
,
539 dump_class_qdisc_nlmsg
, &tcinfo
);
543 filter_info
.devname
= dev
->devname
;
544 filter_info
.ifindex
= dev
->ifindex
;
545 for (i
= 0; i
< tcinfo
.used_len
; i
++) {
546 filter_info
.kind
= tcinfo
.handle_array
[i
].kind
;
547 ret
= netlink_get_filter(sock
, nl_pid
, dev
->ifindex
,
548 tcinfo
.handle_array
[i
].handle
,
549 dump_filter_nlmsg
, &filter_info
);
554 /* root, ingress and egress handle */
556 filter_info
.kind
= "root";
557 ret
= netlink_get_filter(sock
, nl_pid
, dev
->ifindex
, handle
,
558 dump_filter_nlmsg
, &filter_info
);
562 handle
= TC_H_MAKE(TC_H_CLSACT
, TC_H_MIN_INGRESS
);
563 filter_info
.kind
= "clsact/ingress";
564 ret
= netlink_get_filter(sock
, nl_pid
, dev
->ifindex
, handle
,
565 dump_filter_nlmsg
, &filter_info
);
569 handle
= TC_H_MAKE(TC_H_CLSACT
, TC_H_MIN_EGRESS
);
570 filter_info
.kind
= "clsact/egress";
571 ret
= netlink_get_filter(sock
, nl_pid
, dev
->ifindex
, handle
,
572 dump_filter_nlmsg
, &filter_info
);
577 free(tcinfo
.handle_array
);
581 static int query_flow_dissector(struct bpf_attach_info
*attach_info
)
589 fd
= open("/proc/self/ns/net", O_RDONLY
);
591 p_err("can't open /proc/self/ns/net: %s",
595 prog_cnt
= ARRAY_SIZE(prog_ids
);
596 err
= bpf_prog_query(fd
, BPF_FLOW_DISSECTOR
, 0,
597 &attach_flags
, prog_ids
, &prog_cnt
);
600 if (errno
== EINVAL
) {
601 /* Older kernel's don't support querying
602 * flow dissector programs.
607 p_err("can't query prog: %s", strerror(errno
));
612 attach_info
->flow_dissector_id
= prog_ids
[0];
617 static int net_parse_dev(int *argc
, char ***argv
)
621 if (is_prefix(**argv
, "dev")) {
624 ifindex
= if_nametoindex(**argv
);
626 p_err("invalid devname %s", **argv
);
630 p_err("expected 'dev', got: '%s'?", **argv
);
637 static int do_attach_detach_xdp(int progfd
, enum net_attach_type attach_type
,
638 int ifindex
, bool overwrite
)
643 flags
= XDP_FLAGS_UPDATE_IF_NOEXIST
;
644 if (attach_type
== NET_ATTACH_TYPE_XDP_GENERIC
)
645 flags
|= XDP_FLAGS_SKB_MODE
;
646 if (attach_type
== NET_ATTACH_TYPE_XDP_DRIVER
)
647 flags
|= XDP_FLAGS_DRV_MODE
;
648 if (attach_type
== NET_ATTACH_TYPE_XDP_OFFLOAD
)
649 flags
|= XDP_FLAGS_HW_MODE
;
651 return bpf_xdp_attach(ifindex
, progfd
, flags
, NULL
);
654 static int get_tcx_type(enum net_attach_type attach_type
)
656 switch (attach_type
) {
657 case NET_ATTACH_TYPE_TCX_INGRESS
:
658 return BPF_TCX_INGRESS
;
659 case NET_ATTACH_TYPE_TCX_EGRESS
:
660 return BPF_TCX_EGRESS
;
666 static int do_attach_tcx(int progfd
, enum net_attach_type attach_type
, int ifindex
)
668 int type
= get_tcx_type(attach_type
);
670 return bpf_prog_attach(progfd
, ifindex
, type
, 0);
673 static int do_detach_tcx(int targetfd
, enum net_attach_type attach_type
)
675 int type
= get_tcx_type(attach_type
);
677 return bpf_prog_detach(targetfd
, type
);
680 static int do_attach(int argc
, char **argv
)
682 enum net_attach_type attach_type
;
683 int progfd
, ifindex
, err
= 0;
684 bool overwrite
= false;
686 /* parse attach args */
690 attach_type
= parse_attach_type(*argv
);
691 if (attach_type
== net_attach_type_size
) {
692 p_err("invalid net attach/detach type: %s", *argv
);
697 progfd
= prog_parse_fd(&argc
, &argv
);
701 ifindex
= net_parse_dev(&argc
, &argv
);
708 if (is_prefix(*argv
, "overwrite")) {
711 p_err("expected 'overwrite', got: '%s'?", *argv
);
717 switch (attach_type
) {
718 /* attach xdp prog */
719 case NET_ATTACH_TYPE_XDP
:
720 case NET_ATTACH_TYPE_XDP_GENERIC
:
721 case NET_ATTACH_TYPE_XDP_DRIVER
:
722 case NET_ATTACH_TYPE_XDP_OFFLOAD
:
723 err
= do_attach_detach_xdp(progfd
, attach_type
, ifindex
, overwrite
);
725 /* attach tcx prog */
726 case NET_ATTACH_TYPE_TCX_INGRESS
:
727 case NET_ATTACH_TYPE_TCX_EGRESS
:
728 err
= do_attach_tcx(progfd
, attach_type
, ifindex
);
735 p_err("interface %s attach failed: %s",
736 attach_type_strings
[attach_type
], strerror(-err
));
741 jsonw_null(json_wtr
);
747 static int do_detach(int argc
, char **argv
)
749 enum net_attach_type attach_type
;
750 int progfd
, ifindex
, err
= 0;
752 /* parse detach args */
756 attach_type
= parse_attach_type(*argv
);
757 if (attach_type
== net_attach_type_size
) {
758 p_err("invalid net attach/detach type: %s", *argv
);
763 ifindex
= net_parse_dev(&argc
, &argv
);
767 switch (attach_type
) {
768 /* detach xdp prog */
769 case NET_ATTACH_TYPE_XDP
:
770 case NET_ATTACH_TYPE_XDP_GENERIC
:
771 case NET_ATTACH_TYPE_XDP_DRIVER
:
772 case NET_ATTACH_TYPE_XDP_OFFLOAD
:
774 err
= do_attach_detach_xdp(progfd
, attach_type
, ifindex
, NULL
);
776 /* detach tcx prog */
777 case NET_ATTACH_TYPE_TCX_INGRESS
:
778 case NET_ATTACH_TYPE_TCX_EGRESS
:
779 err
= do_detach_tcx(ifindex
, attach_type
);
786 p_err("interface %s detach failed: %s",
787 attach_type_strings
[attach_type
], strerror(-err
));
792 jsonw_null(json_wtr
);
797 static int netfilter_link_compar(const void *a
, const void *b
)
799 const struct bpf_link_info
*nfa
= a
;
800 const struct bpf_link_info
*nfb
= b
;
803 delta
= nfa
->netfilter
.pf
- nfb
->netfilter
.pf
;
807 delta
= nfa
->netfilter
.hooknum
- nfb
->netfilter
.hooknum
;
811 if (nfa
->netfilter
.priority
< nfb
->netfilter
.priority
)
813 if (nfa
->netfilter
.priority
> nfb
->netfilter
.priority
)
816 return nfa
->netfilter
.flags
- nfb
->netfilter
.flags
;
819 static void show_link_netfilter(void)
821 unsigned int nf_link_len
= 0, nf_link_count
= 0;
822 struct bpf_link_info
*nf_link_info
= NULL
;
826 struct bpf_link_info info
;
830 err
= bpf_link_get_next_id(id
, &id
);
834 p_err("can't get next link: %s (id %d)", strerror(errno
), id
);
838 fd
= bpf_link_get_fd_by_id(id
);
840 p_err("can't get link by id (%u): %s", id
, strerror(errno
));
844 memset(&info
, 0, sizeof(info
));
847 err
= bpf_link_get_info_by_fd(fd
, &info
, &len
);
852 p_err("can't get link info for fd %d: %s", fd
, strerror(errno
));
856 if (info
.type
!= BPF_LINK_TYPE_NETFILTER
)
859 if (nf_link_count
>= nf_link_len
) {
860 static const unsigned int max_link_count
= INT_MAX
/ sizeof(info
);
861 struct bpf_link_info
*expand
;
863 if (nf_link_count
> max_link_count
) {
864 p_err("cannot handle more than %u links\n", max_link_count
);
870 expand
= realloc(nf_link_info
, nf_link_len
* sizeof(info
));
872 p_err("realloc: %s", strerror(errno
));
876 nf_link_info
= expand
;
879 nf_link_info
[nf_link_count
] = info
;
886 qsort(nf_link_info
, nf_link_count
, sizeof(*nf_link_info
), netfilter_link_compar
);
888 for (id
= 0; id
< nf_link_count
; id
++) {
891 netfilter_dump_json(&nf_link_info
[id
], json_wtr
);
893 netfilter_dump_plain(&nf_link_info
[id
]);
895 NET_DUMP_UINT("id", " prog_id %u", nf_link_info
[id
].prog_id
);
902 static int do_show(int argc
, char **argv
)
904 struct bpf_attach_info attach_info
= {};
905 int i
, sock
, ret
, filter_idx
= -1;
906 struct bpf_netdev_t dev_array
;
907 unsigned int nl_pid
= 0;
911 filter_idx
= net_parse_dev(&argc
, &argv
);
914 } else if (argc
!= 0) {
918 ret
= query_flow_dissector(&attach_info
);
922 sock
= netlink_open(&nl_pid
);
924 fprintf(stderr
, "failed to open netlink sock\n");
928 dev_array
.devices
= NULL
;
929 dev_array
.used_len
= 0;
930 dev_array
.array_len
= 0;
931 dev_array
.filter_idx
= filter_idx
;
934 jsonw_start_array(json_wtr
);
936 NET_START_ARRAY("xdp", "%s:\n");
937 ret
= netlink_get_link(sock
, nl_pid
, dump_link_nlmsg
, &dev_array
);
941 NET_START_ARRAY("tc", "%s:\n");
942 for (i
= 0; i
< dev_array
.used_len
; i
++) {
943 show_dev_tc_bpf(&dev_array
.devices
[i
]);
944 ret
= show_dev_tc_bpf_classic(sock
, nl_pid
,
945 &dev_array
.devices
[i
]);
952 NET_START_ARRAY("flow_dissector", "%s:\n");
953 if (attach_info
.flow_dissector_id
> 0)
954 NET_DUMP_UINT("id", "id %u", attach_info
.flow_dissector_id
);
957 NET_START_ARRAY("netfilter", "%s:\n");
958 show_link_netfilter();
963 jsonw_end_array(json_wtr
);
967 jsonw_null(json_wtr
);
968 libbpf_strerror(ret
, err_buf
, sizeof(err_buf
));
969 fprintf(stderr
, "Error: %s\n", err_buf
);
971 free(dev_array
.devices
);
976 static int do_help(int argc
, char **argv
)
979 jsonw_null(json_wtr
);
984 "Usage: %1$s %2$s { show | list } [dev <devname>]\n"
985 " %1$s %2$s attach ATTACH_TYPE PROG dev <devname> [ overwrite ]\n"
986 " %1$s %2$s detach ATTACH_TYPE dev <devname>\n"
989 " " HELP_SPEC_PROGRAM
"\n"
990 " ATTACH_TYPE := { xdp | xdpgeneric | xdpdrv | xdpoffload | tcx_ingress\n"
992 " " HELP_SPEC_OPTIONS
" }\n"
994 "Note: Only xdp, tcx, tc, netkit, flow_dissector and netfilter attachments\n"
995 " are currently supported.\n"
996 " For progs attached to cgroups, use \"bpftool cgroup\"\n"
997 " to dump program attachments. For program types\n"
998 " sk_{filter,skb,msg,reuseport} and lwt/seg6, please\n"
999 " consult iproute2.\n"
1001 bin_name
, argv
[-2]);
1006 static const struct cmd cmds
[] = {
1007 { "show", do_show
},
1008 { "list", do_show
},
1009 { "attach", do_attach
},
1010 { "detach", do_detach
},
1011 { "help", do_help
},
1015 int do_net(int argc
, char **argv
)
1017 return cmd_select(cmds
, argc
, argv
, do_help
);