162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 262306a36Sopenharmony_ci// Copyright (C) 2018 Facebook 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#ifndef _GNU_SOURCE 562306a36Sopenharmony_ci#define _GNU_SOURCE 662306a36Sopenharmony_ci#endif 762306a36Sopenharmony_ci#include <errno.h> 862306a36Sopenharmony_ci#include <fcntl.h> 962306a36Sopenharmony_ci#include <stdlib.h> 1062306a36Sopenharmony_ci#include <string.h> 1162306a36Sopenharmony_ci#include <time.h> 1262306a36Sopenharmony_ci#include <unistd.h> 1362306a36Sopenharmony_ci#include <bpf/bpf.h> 1462306a36Sopenharmony_ci#include <bpf/libbpf.h> 1562306a36Sopenharmony_ci#include <net/if.h> 1662306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1762306a36Sopenharmony_ci#include <linux/socket.h> 1862306a36Sopenharmony_ci#include <linux/tc_act/tc_bpf.h> 1962306a36Sopenharmony_ci#include <sys/socket.h> 2062306a36Sopenharmony_ci#include <sys/stat.h> 2162306a36Sopenharmony_ci#include <sys/types.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "bpf/nlattr.h" 2462306a36Sopenharmony_ci#include "main.h" 2562306a36Sopenharmony_ci#include "netlink_dumper.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#ifndef SOL_NETLINK 2862306a36Sopenharmony_ci#define SOL_NETLINK 270 2962306a36Sopenharmony_ci#endif 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct ip_devname_ifindex { 3262306a36Sopenharmony_ci char devname[64]; 3362306a36Sopenharmony_ci int ifindex; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct bpf_netdev_t { 3762306a36Sopenharmony_ci struct ip_devname_ifindex *devices; 3862306a36Sopenharmony_ci int used_len; 3962306a36Sopenharmony_ci int array_len; 4062306a36Sopenharmony_ci int filter_idx; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct tc_kind_handle { 4462306a36Sopenharmony_ci char kind[64]; 4562306a36Sopenharmony_ci int handle; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct bpf_tcinfo_t { 4962306a36Sopenharmony_ci struct tc_kind_handle *handle_array; 5062306a36Sopenharmony_ci int used_len; 5162306a36Sopenharmony_ci int array_len; 5262306a36Sopenharmony_ci bool is_qdisc; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct bpf_filter_t { 5662306a36Sopenharmony_ci const char *kind; 5762306a36Sopenharmony_ci const char *devname; 5862306a36Sopenharmony_ci int ifindex; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct bpf_attach_info { 6262306a36Sopenharmony_ci __u32 flow_dissector_id; 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cienum net_attach_type { 6662306a36Sopenharmony_ci NET_ATTACH_TYPE_XDP, 6762306a36Sopenharmony_ci NET_ATTACH_TYPE_XDP_GENERIC, 6862306a36Sopenharmony_ci NET_ATTACH_TYPE_XDP_DRIVER, 6962306a36Sopenharmony_ci NET_ATTACH_TYPE_XDP_OFFLOAD, 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic const char * const attach_type_strings[] = { 7362306a36Sopenharmony_ci [NET_ATTACH_TYPE_XDP] = "xdp", 7462306a36Sopenharmony_ci [NET_ATTACH_TYPE_XDP_GENERIC] = "xdpgeneric", 7562306a36Sopenharmony_ci [NET_ATTACH_TYPE_XDP_DRIVER] = "xdpdrv", 7662306a36Sopenharmony_ci [NET_ATTACH_TYPE_XDP_OFFLOAD] = "xdpoffload", 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic const char * const attach_loc_strings[] = { 8062306a36Sopenharmony_ci [BPF_TCX_INGRESS] = "tcx/ingress", 8162306a36Sopenharmony_ci [BPF_TCX_EGRESS] = "tcx/egress", 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ciconst size_t net_attach_type_size = ARRAY_SIZE(attach_type_strings); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic enum net_attach_type parse_attach_type(const char *str) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci enum net_attach_type type; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci for (type = 0; type < net_attach_type_size; type++) { 9162306a36Sopenharmony_ci if (attach_type_strings[type] && 9262306a36Sopenharmony_ci is_prefix(str, attach_type_strings[type])) 9362306a36Sopenharmony_ci return type; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return net_attach_type_size; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_citypedef int (*dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_citypedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, dump_nlmsg_t, void *cookie); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int netlink_open(__u32 *nl_pid) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct sockaddr_nl sa; 10662306a36Sopenharmony_ci socklen_t addrlen; 10762306a36Sopenharmony_ci int one = 1, ret; 10862306a36Sopenharmony_ci int sock; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci memset(&sa, 0, sizeof(sa)); 11162306a36Sopenharmony_ci sa.nl_family = AF_NETLINK; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 11462306a36Sopenharmony_ci if (sock < 0) 11562306a36Sopenharmony_ci return -errno; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, 11862306a36Sopenharmony_ci &one, sizeof(one)) < 0) { 11962306a36Sopenharmony_ci p_err("Netlink error reporting not supported"); 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 12362306a36Sopenharmony_ci ret = -errno; 12462306a36Sopenharmony_ci goto cleanup; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci addrlen = sizeof(sa); 12862306a36Sopenharmony_ci if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) { 12962306a36Sopenharmony_ci ret = -errno; 13062306a36Sopenharmony_ci goto cleanup; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (addrlen != sizeof(sa)) { 13462306a36Sopenharmony_ci ret = -LIBBPF_ERRNO__INTERNAL; 13562306a36Sopenharmony_ci goto cleanup; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci *nl_pid = sa.nl_pid; 13962306a36Sopenharmony_ci return sock; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cicleanup: 14262306a36Sopenharmony_ci close(sock); 14362306a36Sopenharmony_ci return ret; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int netlink_recv(int sock, __u32 nl_pid, __u32 seq, 14762306a36Sopenharmony_ci __dump_nlmsg_t _fn, dump_nlmsg_t fn, 14862306a36Sopenharmony_ci void *cookie) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci bool multipart = true; 15162306a36Sopenharmony_ci struct nlmsgerr *err; 15262306a36Sopenharmony_ci struct nlmsghdr *nh; 15362306a36Sopenharmony_ci char buf[4096]; 15462306a36Sopenharmony_ci int len, ret; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci while (multipart) { 15762306a36Sopenharmony_ci multipart = false; 15862306a36Sopenharmony_ci len = recv(sock, buf, sizeof(buf), 0); 15962306a36Sopenharmony_ci if (len < 0) { 16062306a36Sopenharmony_ci ret = -errno; 16162306a36Sopenharmony_ci goto done; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (len == 0) 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, (unsigned int)len); 16862306a36Sopenharmony_ci nh = NLMSG_NEXT(nh, len)) { 16962306a36Sopenharmony_ci if (nh->nlmsg_pid != nl_pid) { 17062306a36Sopenharmony_ci ret = -LIBBPF_ERRNO__WRNGPID; 17162306a36Sopenharmony_ci goto done; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci if (nh->nlmsg_seq != seq) { 17462306a36Sopenharmony_ci ret = -LIBBPF_ERRNO__INVSEQ; 17562306a36Sopenharmony_ci goto done; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci if (nh->nlmsg_flags & NLM_F_MULTI) 17862306a36Sopenharmony_ci multipart = true; 17962306a36Sopenharmony_ci switch (nh->nlmsg_type) { 18062306a36Sopenharmony_ci case NLMSG_ERROR: 18162306a36Sopenharmony_ci err = (struct nlmsgerr *)NLMSG_DATA(nh); 18262306a36Sopenharmony_ci if (!err->error) 18362306a36Sopenharmony_ci continue; 18462306a36Sopenharmony_ci ret = err->error; 18562306a36Sopenharmony_ci libbpf_nla_dump_errormsg(nh); 18662306a36Sopenharmony_ci goto done; 18762306a36Sopenharmony_ci case NLMSG_DONE: 18862306a36Sopenharmony_ci return 0; 18962306a36Sopenharmony_ci default: 19062306a36Sopenharmony_ci break; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci if (_fn) { 19362306a36Sopenharmony_ci ret = _fn(nh, fn, cookie); 19462306a36Sopenharmony_ci if (ret) 19562306a36Sopenharmony_ci return ret; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci ret = 0; 20062306a36Sopenharmony_cidone: 20162306a36Sopenharmony_ci return ret; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int __dump_class_nlmsg(struct nlmsghdr *nlh, 20562306a36Sopenharmony_ci dump_nlmsg_t dump_class_nlmsg, 20662306a36Sopenharmony_ci void *cookie) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct nlattr *tb[TCA_MAX + 1], *attr; 20962306a36Sopenharmony_ci struct tcmsg *t = NLMSG_DATA(nlh); 21062306a36Sopenharmony_ci int len; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t)); 21362306a36Sopenharmony_ci attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t))); 21462306a36Sopenharmony_ci if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0) 21562306a36Sopenharmony_ci return -LIBBPF_ERRNO__NLPARSE; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return dump_class_nlmsg(cookie, t, tb); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int netlink_get_class(int sock, unsigned int nl_pid, int ifindex, 22162306a36Sopenharmony_ci dump_nlmsg_t dump_class_nlmsg, void *cookie) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct { 22462306a36Sopenharmony_ci struct nlmsghdr nlh; 22562306a36Sopenharmony_ci struct tcmsg t; 22662306a36Sopenharmony_ci } req = { 22762306a36Sopenharmony_ci .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), 22862306a36Sopenharmony_ci .nlh.nlmsg_type = RTM_GETTCLASS, 22962306a36Sopenharmony_ci .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, 23062306a36Sopenharmony_ci .t.tcm_family = AF_UNSPEC, 23162306a36Sopenharmony_ci .t.tcm_ifindex = ifindex, 23262306a36Sopenharmony_ci }; 23362306a36Sopenharmony_ci int seq = time(NULL); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci req.nlh.nlmsg_seq = seq; 23662306a36Sopenharmony_ci if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) 23762306a36Sopenharmony_ci return -errno; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return netlink_recv(sock, nl_pid, seq, __dump_class_nlmsg, 24062306a36Sopenharmony_ci dump_class_nlmsg, cookie); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic int __dump_qdisc_nlmsg(struct nlmsghdr *nlh, 24462306a36Sopenharmony_ci dump_nlmsg_t dump_qdisc_nlmsg, 24562306a36Sopenharmony_ci void *cookie) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct nlattr *tb[TCA_MAX + 1], *attr; 24862306a36Sopenharmony_ci struct tcmsg *t = NLMSG_DATA(nlh); 24962306a36Sopenharmony_ci int len; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t)); 25262306a36Sopenharmony_ci attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t))); 25362306a36Sopenharmony_ci if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0) 25462306a36Sopenharmony_ci return -LIBBPF_ERRNO__NLPARSE; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return dump_qdisc_nlmsg(cookie, t, tb); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int netlink_get_qdisc(int sock, unsigned int nl_pid, int ifindex, 26062306a36Sopenharmony_ci dump_nlmsg_t dump_qdisc_nlmsg, void *cookie) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct { 26362306a36Sopenharmony_ci struct nlmsghdr nlh; 26462306a36Sopenharmony_ci struct tcmsg t; 26562306a36Sopenharmony_ci } req = { 26662306a36Sopenharmony_ci .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), 26762306a36Sopenharmony_ci .nlh.nlmsg_type = RTM_GETQDISC, 26862306a36Sopenharmony_ci .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, 26962306a36Sopenharmony_ci .t.tcm_family = AF_UNSPEC, 27062306a36Sopenharmony_ci .t.tcm_ifindex = ifindex, 27162306a36Sopenharmony_ci }; 27262306a36Sopenharmony_ci int seq = time(NULL); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci req.nlh.nlmsg_seq = seq; 27562306a36Sopenharmony_ci if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) 27662306a36Sopenharmony_ci return -errno; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return netlink_recv(sock, nl_pid, seq, __dump_qdisc_nlmsg, 27962306a36Sopenharmony_ci dump_qdisc_nlmsg, cookie); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic int __dump_filter_nlmsg(struct nlmsghdr *nlh, 28362306a36Sopenharmony_ci dump_nlmsg_t dump_filter_nlmsg, 28462306a36Sopenharmony_ci void *cookie) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct nlattr *tb[TCA_MAX + 1], *attr; 28762306a36Sopenharmony_ci struct tcmsg *t = NLMSG_DATA(nlh); 28862306a36Sopenharmony_ci int len; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t)); 29162306a36Sopenharmony_ci attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t))); 29262306a36Sopenharmony_ci if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0) 29362306a36Sopenharmony_ci return -LIBBPF_ERRNO__NLPARSE; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci return dump_filter_nlmsg(cookie, t, tb); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic int netlink_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle, 29962306a36Sopenharmony_ci dump_nlmsg_t dump_filter_nlmsg, void *cookie) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct { 30262306a36Sopenharmony_ci struct nlmsghdr nlh; 30362306a36Sopenharmony_ci struct tcmsg t; 30462306a36Sopenharmony_ci } req = { 30562306a36Sopenharmony_ci .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), 30662306a36Sopenharmony_ci .nlh.nlmsg_type = RTM_GETTFILTER, 30762306a36Sopenharmony_ci .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, 30862306a36Sopenharmony_ci .t.tcm_family = AF_UNSPEC, 30962306a36Sopenharmony_ci .t.tcm_ifindex = ifindex, 31062306a36Sopenharmony_ci .t.tcm_parent = handle, 31162306a36Sopenharmony_ci }; 31262306a36Sopenharmony_ci int seq = time(NULL); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci req.nlh.nlmsg_seq = seq; 31562306a36Sopenharmony_ci if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) 31662306a36Sopenharmony_ci return -errno; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return netlink_recv(sock, nl_pid, seq, __dump_filter_nlmsg, 31962306a36Sopenharmony_ci dump_filter_nlmsg, cookie); 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic int __dump_link_nlmsg(struct nlmsghdr *nlh, 32362306a36Sopenharmony_ci dump_nlmsg_t dump_link_nlmsg, void *cookie) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct nlattr *tb[IFLA_MAX + 1], *attr; 32662306a36Sopenharmony_ci struct ifinfomsg *ifi = NLMSG_DATA(nlh); 32762306a36Sopenharmony_ci int len; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); 33062306a36Sopenharmony_ci attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi))); 33162306a36Sopenharmony_ci if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0) 33262306a36Sopenharmony_ci return -LIBBPF_ERRNO__NLPARSE; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return dump_link_nlmsg(cookie, ifi, tb); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic int netlink_get_link(int sock, unsigned int nl_pid, 33862306a36Sopenharmony_ci dump_nlmsg_t dump_link_nlmsg, void *cookie) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct { 34162306a36Sopenharmony_ci struct nlmsghdr nlh; 34262306a36Sopenharmony_ci struct ifinfomsg ifm; 34362306a36Sopenharmony_ci } req = { 34462306a36Sopenharmony_ci .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), 34562306a36Sopenharmony_ci .nlh.nlmsg_type = RTM_GETLINK, 34662306a36Sopenharmony_ci .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, 34762306a36Sopenharmony_ci .ifm.ifi_family = AF_PACKET, 34862306a36Sopenharmony_ci }; 34962306a36Sopenharmony_ci int seq = time(NULL); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci req.nlh.nlmsg_seq = seq; 35262306a36Sopenharmony_ci if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) 35362306a36Sopenharmony_ci return -errno; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg, 35662306a36Sopenharmony_ci dump_link_nlmsg, cookie); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct bpf_netdev_t *netinfo = cookie; 36262306a36Sopenharmony_ci struct ifinfomsg *ifinfo = msg; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (netinfo->filter_idx > 0 && netinfo->filter_idx != ifinfo->ifi_index) 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (netinfo->used_len == netinfo->array_len) { 36862306a36Sopenharmony_ci netinfo->devices = realloc(netinfo->devices, 36962306a36Sopenharmony_ci (netinfo->array_len + 16) * 37062306a36Sopenharmony_ci sizeof(struct ip_devname_ifindex)); 37162306a36Sopenharmony_ci if (!netinfo->devices) 37262306a36Sopenharmony_ci return -ENOMEM; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci netinfo->array_len += 16; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci netinfo->devices[netinfo->used_len].ifindex = ifinfo->ifi_index; 37762306a36Sopenharmony_ci snprintf(netinfo->devices[netinfo->used_len].devname, 37862306a36Sopenharmony_ci sizeof(netinfo->devices[netinfo->used_len].devname), 37962306a36Sopenharmony_ci "%s", 38062306a36Sopenharmony_ci tb[IFLA_IFNAME] 38162306a36Sopenharmony_ci ? libbpf_nla_getattr_str(tb[IFLA_IFNAME]) 38262306a36Sopenharmony_ci : ""); 38362306a36Sopenharmony_ci netinfo->used_len++; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return do_xdp_dump(ifinfo, tb); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int dump_class_qdisc_nlmsg(void *cookie, void *msg, struct nlattr **tb) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct bpf_tcinfo_t *tcinfo = cookie; 39162306a36Sopenharmony_ci struct tcmsg *info = msg; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (tcinfo->is_qdisc) { 39462306a36Sopenharmony_ci /* skip clsact qdisc */ 39562306a36Sopenharmony_ci if (tb[TCA_KIND] && 39662306a36Sopenharmony_ci strcmp(libbpf_nla_data(tb[TCA_KIND]), "clsact") == 0) 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci if (info->tcm_handle == 0) 39962306a36Sopenharmony_ci return 0; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (tcinfo->used_len == tcinfo->array_len) { 40362306a36Sopenharmony_ci tcinfo->handle_array = realloc(tcinfo->handle_array, 40462306a36Sopenharmony_ci (tcinfo->array_len + 16) * sizeof(struct tc_kind_handle)); 40562306a36Sopenharmony_ci if (!tcinfo->handle_array) 40662306a36Sopenharmony_ci return -ENOMEM; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci tcinfo->array_len += 16; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci tcinfo->handle_array[tcinfo->used_len].handle = info->tcm_handle; 41162306a36Sopenharmony_ci snprintf(tcinfo->handle_array[tcinfo->used_len].kind, 41262306a36Sopenharmony_ci sizeof(tcinfo->handle_array[tcinfo->used_len].kind), 41362306a36Sopenharmony_ci "%s", 41462306a36Sopenharmony_ci tb[TCA_KIND] 41562306a36Sopenharmony_ci ? libbpf_nla_getattr_str(tb[TCA_KIND]) 41662306a36Sopenharmony_ci : "unknown"); 41762306a36Sopenharmony_ci tcinfo->used_len++; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic int dump_filter_nlmsg(void *cookie, void *msg, struct nlattr **tb) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci const struct bpf_filter_t *filter_info = cookie; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return do_filter_dump((struct tcmsg *)msg, tb, filter_info->kind, 42762306a36Sopenharmony_ci filter_info->devname, filter_info->ifindex); 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic int __show_dev_tc_bpf_name(__u32 id, char *name, size_t len) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct bpf_prog_info info = {}; 43362306a36Sopenharmony_ci __u32 ilen = sizeof(info); 43462306a36Sopenharmony_ci int fd, ret; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci fd = bpf_prog_get_fd_by_id(id); 43762306a36Sopenharmony_ci if (fd < 0) 43862306a36Sopenharmony_ci return fd; 43962306a36Sopenharmony_ci ret = bpf_obj_get_info_by_fd(fd, &info, &ilen); 44062306a36Sopenharmony_ci if (ret < 0) 44162306a36Sopenharmony_ci goto out; 44262306a36Sopenharmony_ci ret = -ENOENT; 44362306a36Sopenharmony_ci if (info.name[0]) { 44462306a36Sopenharmony_ci get_prog_full_name(&info, fd, name, len); 44562306a36Sopenharmony_ci ret = 0; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ciout: 44862306a36Sopenharmony_ci close(fd); 44962306a36Sopenharmony_ci return ret; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic void __show_dev_tc_bpf(const struct ip_devname_ifindex *dev, 45362306a36Sopenharmony_ci const enum bpf_attach_type loc) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci __u32 prog_flags[64] = {}, link_flags[64] = {}, i, j; 45662306a36Sopenharmony_ci __u32 prog_ids[64] = {}, link_ids[64] = {}; 45762306a36Sopenharmony_ci LIBBPF_OPTS(bpf_prog_query_opts, optq); 45862306a36Sopenharmony_ci char prog_name[MAX_PROG_FULL_NAME]; 45962306a36Sopenharmony_ci int ret; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci optq.prog_ids = prog_ids; 46262306a36Sopenharmony_ci optq.prog_attach_flags = prog_flags; 46362306a36Sopenharmony_ci optq.link_ids = link_ids; 46462306a36Sopenharmony_ci optq.link_attach_flags = link_flags; 46562306a36Sopenharmony_ci optq.count = ARRAY_SIZE(prog_ids); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci ret = bpf_prog_query_opts(dev->ifindex, loc, &optq); 46862306a36Sopenharmony_ci if (ret) 46962306a36Sopenharmony_ci return; 47062306a36Sopenharmony_ci for (i = 0; i < optq.count; i++) { 47162306a36Sopenharmony_ci NET_START_OBJECT; 47262306a36Sopenharmony_ci NET_DUMP_STR("devname", "%s", dev->devname); 47362306a36Sopenharmony_ci NET_DUMP_UINT("ifindex", "(%u)", dev->ifindex); 47462306a36Sopenharmony_ci NET_DUMP_STR("kind", " %s", attach_loc_strings[loc]); 47562306a36Sopenharmony_ci ret = __show_dev_tc_bpf_name(prog_ids[i], prog_name, 47662306a36Sopenharmony_ci sizeof(prog_name)); 47762306a36Sopenharmony_ci if (!ret) 47862306a36Sopenharmony_ci NET_DUMP_STR("name", " %s", prog_name); 47962306a36Sopenharmony_ci NET_DUMP_UINT("prog_id", " prog_id %u ", prog_ids[i]); 48062306a36Sopenharmony_ci if (prog_flags[i] || json_output) { 48162306a36Sopenharmony_ci NET_START_ARRAY("prog_flags", "%s "); 48262306a36Sopenharmony_ci for (j = 0; prog_flags[i] && j < 32; j++) { 48362306a36Sopenharmony_ci if (!(prog_flags[i] & (1 << j))) 48462306a36Sopenharmony_ci continue; 48562306a36Sopenharmony_ci NET_DUMP_UINT_ONLY(1 << j); 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci NET_END_ARRAY(""); 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci if (link_ids[i] || json_output) { 49062306a36Sopenharmony_ci NET_DUMP_UINT("link_id", "link_id %u ", link_ids[i]); 49162306a36Sopenharmony_ci if (link_flags[i] || json_output) { 49262306a36Sopenharmony_ci NET_START_ARRAY("link_flags", "%s "); 49362306a36Sopenharmony_ci for (j = 0; link_flags[i] && j < 32; j++) { 49462306a36Sopenharmony_ci if (!(link_flags[i] & (1 << j))) 49562306a36Sopenharmony_ci continue; 49662306a36Sopenharmony_ci NET_DUMP_UINT_ONLY(1 << j); 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci NET_END_ARRAY(""); 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci NET_END_OBJECT_FINAL; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic void show_dev_tc_bpf(struct ip_devname_ifindex *dev) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci __show_dev_tc_bpf(dev, BPF_TCX_INGRESS); 50862306a36Sopenharmony_ci __show_dev_tc_bpf(dev, BPF_TCX_EGRESS); 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic int show_dev_tc_bpf_classic(int sock, unsigned int nl_pid, 51262306a36Sopenharmony_ci struct ip_devname_ifindex *dev) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct bpf_filter_t filter_info; 51562306a36Sopenharmony_ci struct bpf_tcinfo_t tcinfo; 51662306a36Sopenharmony_ci int i, handle, ret = 0; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci tcinfo.handle_array = NULL; 51962306a36Sopenharmony_ci tcinfo.used_len = 0; 52062306a36Sopenharmony_ci tcinfo.array_len = 0; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci tcinfo.is_qdisc = false; 52362306a36Sopenharmony_ci ret = netlink_get_class(sock, nl_pid, dev->ifindex, 52462306a36Sopenharmony_ci dump_class_qdisc_nlmsg, &tcinfo); 52562306a36Sopenharmony_ci if (ret) 52662306a36Sopenharmony_ci goto out; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci tcinfo.is_qdisc = true; 52962306a36Sopenharmony_ci ret = netlink_get_qdisc(sock, nl_pid, dev->ifindex, 53062306a36Sopenharmony_ci dump_class_qdisc_nlmsg, &tcinfo); 53162306a36Sopenharmony_ci if (ret) 53262306a36Sopenharmony_ci goto out; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci filter_info.devname = dev->devname; 53562306a36Sopenharmony_ci filter_info.ifindex = dev->ifindex; 53662306a36Sopenharmony_ci for (i = 0; i < tcinfo.used_len; i++) { 53762306a36Sopenharmony_ci filter_info.kind = tcinfo.handle_array[i].kind; 53862306a36Sopenharmony_ci ret = netlink_get_filter(sock, nl_pid, dev->ifindex, 53962306a36Sopenharmony_ci tcinfo.handle_array[i].handle, 54062306a36Sopenharmony_ci dump_filter_nlmsg, &filter_info); 54162306a36Sopenharmony_ci if (ret) 54262306a36Sopenharmony_ci goto out; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /* root, ingress and egress handle */ 54662306a36Sopenharmony_ci handle = TC_H_ROOT; 54762306a36Sopenharmony_ci filter_info.kind = "root"; 54862306a36Sopenharmony_ci ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle, 54962306a36Sopenharmony_ci dump_filter_nlmsg, &filter_info); 55062306a36Sopenharmony_ci if (ret) 55162306a36Sopenharmony_ci goto out; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); 55462306a36Sopenharmony_ci filter_info.kind = "clsact/ingress"; 55562306a36Sopenharmony_ci ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle, 55662306a36Sopenharmony_ci dump_filter_nlmsg, &filter_info); 55762306a36Sopenharmony_ci if (ret) 55862306a36Sopenharmony_ci goto out; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS); 56162306a36Sopenharmony_ci filter_info.kind = "clsact/egress"; 56262306a36Sopenharmony_ci ret = netlink_get_filter(sock, nl_pid, dev->ifindex, handle, 56362306a36Sopenharmony_ci dump_filter_nlmsg, &filter_info); 56462306a36Sopenharmony_ci if (ret) 56562306a36Sopenharmony_ci goto out; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ciout: 56862306a36Sopenharmony_ci free(tcinfo.handle_array); 56962306a36Sopenharmony_ci return 0; 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic int query_flow_dissector(struct bpf_attach_info *attach_info) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci __u32 attach_flags; 57562306a36Sopenharmony_ci __u32 prog_ids[1]; 57662306a36Sopenharmony_ci __u32 prog_cnt; 57762306a36Sopenharmony_ci int err; 57862306a36Sopenharmony_ci int fd; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci fd = open("/proc/self/ns/net", O_RDONLY); 58162306a36Sopenharmony_ci if (fd < 0) { 58262306a36Sopenharmony_ci p_err("can't open /proc/self/ns/net: %s", 58362306a36Sopenharmony_ci strerror(errno)); 58462306a36Sopenharmony_ci return -1; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci prog_cnt = ARRAY_SIZE(prog_ids); 58762306a36Sopenharmony_ci err = bpf_prog_query(fd, BPF_FLOW_DISSECTOR, 0, 58862306a36Sopenharmony_ci &attach_flags, prog_ids, &prog_cnt); 58962306a36Sopenharmony_ci close(fd); 59062306a36Sopenharmony_ci if (err) { 59162306a36Sopenharmony_ci if (errno == EINVAL) { 59262306a36Sopenharmony_ci /* Older kernel's don't support querying 59362306a36Sopenharmony_ci * flow dissector programs. 59462306a36Sopenharmony_ci */ 59562306a36Sopenharmony_ci errno = 0; 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci p_err("can't query prog: %s", strerror(errno)); 59962306a36Sopenharmony_ci return -1; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (prog_cnt == 1) 60362306a36Sopenharmony_ci attach_info->flow_dissector_id = prog_ids[0]; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci return 0; 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic int net_parse_dev(int *argc, char ***argv) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci int ifindex; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (is_prefix(**argv, "dev")) { 61362306a36Sopenharmony_ci NEXT_ARGP(); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci ifindex = if_nametoindex(**argv); 61662306a36Sopenharmony_ci if (!ifindex) 61762306a36Sopenharmony_ci p_err("invalid devname %s", **argv); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci NEXT_ARGP(); 62062306a36Sopenharmony_ci } else { 62162306a36Sopenharmony_ci p_err("expected 'dev', got: '%s'?", **argv); 62262306a36Sopenharmony_ci return -1; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci return ifindex; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic int do_attach_detach_xdp(int progfd, enum net_attach_type attach_type, 62962306a36Sopenharmony_ci int ifindex, bool overwrite) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci __u32 flags = 0; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (!overwrite) 63462306a36Sopenharmony_ci flags = XDP_FLAGS_UPDATE_IF_NOEXIST; 63562306a36Sopenharmony_ci if (attach_type == NET_ATTACH_TYPE_XDP_GENERIC) 63662306a36Sopenharmony_ci flags |= XDP_FLAGS_SKB_MODE; 63762306a36Sopenharmony_ci if (attach_type == NET_ATTACH_TYPE_XDP_DRIVER) 63862306a36Sopenharmony_ci flags |= XDP_FLAGS_DRV_MODE; 63962306a36Sopenharmony_ci if (attach_type == NET_ATTACH_TYPE_XDP_OFFLOAD) 64062306a36Sopenharmony_ci flags |= XDP_FLAGS_HW_MODE; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci return bpf_xdp_attach(ifindex, progfd, flags, NULL); 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic int do_attach(int argc, char **argv) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci enum net_attach_type attach_type; 64862306a36Sopenharmony_ci int progfd, ifindex, err = 0; 64962306a36Sopenharmony_ci bool overwrite = false; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* parse attach args */ 65262306a36Sopenharmony_ci if (!REQ_ARGS(5)) 65362306a36Sopenharmony_ci return -EINVAL; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci attach_type = parse_attach_type(*argv); 65662306a36Sopenharmony_ci if (attach_type == net_attach_type_size) { 65762306a36Sopenharmony_ci p_err("invalid net attach/detach type: %s", *argv); 65862306a36Sopenharmony_ci return -EINVAL; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci NEXT_ARG(); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci progfd = prog_parse_fd(&argc, &argv); 66362306a36Sopenharmony_ci if (progfd < 0) 66462306a36Sopenharmony_ci return -EINVAL; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci ifindex = net_parse_dev(&argc, &argv); 66762306a36Sopenharmony_ci if (ifindex < 1) { 66862306a36Sopenharmony_ci err = -EINVAL; 66962306a36Sopenharmony_ci goto cleanup; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci if (argc) { 67362306a36Sopenharmony_ci if (is_prefix(*argv, "overwrite")) { 67462306a36Sopenharmony_ci overwrite = true; 67562306a36Sopenharmony_ci } else { 67662306a36Sopenharmony_ci p_err("expected 'overwrite', got: '%s'?", *argv); 67762306a36Sopenharmony_ci err = -EINVAL; 67862306a36Sopenharmony_ci goto cleanup; 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* attach xdp prog */ 68362306a36Sopenharmony_ci if (is_prefix("xdp", attach_type_strings[attach_type])) 68462306a36Sopenharmony_ci err = do_attach_detach_xdp(progfd, attach_type, ifindex, 68562306a36Sopenharmony_ci overwrite); 68662306a36Sopenharmony_ci if (err) { 68762306a36Sopenharmony_ci p_err("interface %s attach failed: %s", 68862306a36Sopenharmony_ci attach_type_strings[attach_type], strerror(-err)); 68962306a36Sopenharmony_ci goto cleanup; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (json_output) 69362306a36Sopenharmony_ci jsonw_null(json_wtr); 69462306a36Sopenharmony_cicleanup: 69562306a36Sopenharmony_ci close(progfd); 69662306a36Sopenharmony_ci return err; 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic int do_detach(int argc, char **argv) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci enum net_attach_type attach_type; 70262306a36Sopenharmony_ci int progfd, ifindex, err = 0; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* parse detach args */ 70562306a36Sopenharmony_ci if (!REQ_ARGS(3)) 70662306a36Sopenharmony_ci return -EINVAL; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci attach_type = parse_attach_type(*argv); 70962306a36Sopenharmony_ci if (attach_type == net_attach_type_size) { 71062306a36Sopenharmony_ci p_err("invalid net attach/detach type: %s", *argv); 71162306a36Sopenharmony_ci return -EINVAL; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci NEXT_ARG(); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci ifindex = net_parse_dev(&argc, &argv); 71662306a36Sopenharmony_ci if (ifindex < 1) 71762306a36Sopenharmony_ci return -EINVAL; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* detach xdp prog */ 72062306a36Sopenharmony_ci progfd = -1; 72162306a36Sopenharmony_ci if (is_prefix("xdp", attach_type_strings[attach_type])) 72262306a36Sopenharmony_ci err = do_attach_detach_xdp(progfd, attach_type, ifindex, NULL); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (err < 0) { 72562306a36Sopenharmony_ci p_err("interface %s detach failed: %s", 72662306a36Sopenharmony_ci attach_type_strings[attach_type], strerror(-err)); 72762306a36Sopenharmony_ci return err; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (json_output) 73162306a36Sopenharmony_ci jsonw_null(json_wtr); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci return 0; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic int netfilter_link_compar(const void *a, const void *b) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci const struct bpf_link_info *nfa = a; 73962306a36Sopenharmony_ci const struct bpf_link_info *nfb = b; 74062306a36Sopenharmony_ci int delta; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci delta = nfa->netfilter.pf - nfb->netfilter.pf; 74362306a36Sopenharmony_ci if (delta) 74462306a36Sopenharmony_ci return delta; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci delta = nfa->netfilter.hooknum - nfb->netfilter.hooknum; 74762306a36Sopenharmony_ci if (delta) 74862306a36Sopenharmony_ci return delta; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (nfa->netfilter.priority < nfb->netfilter.priority) 75162306a36Sopenharmony_ci return -1; 75262306a36Sopenharmony_ci if (nfa->netfilter.priority > nfb->netfilter.priority) 75362306a36Sopenharmony_ci return 1; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci return nfa->netfilter.flags - nfb->netfilter.flags; 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic void show_link_netfilter(void) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci unsigned int nf_link_len = 0, nf_link_count = 0; 76162306a36Sopenharmony_ci struct bpf_link_info *nf_link_info = NULL; 76262306a36Sopenharmony_ci __u32 id = 0; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci while (true) { 76562306a36Sopenharmony_ci struct bpf_link_info info; 76662306a36Sopenharmony_ci int fd, err; 76762306a36Sopenharmony_ci __u32 len; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci err = bpf_link_get_next_id(id, &id); 77062306a36Sopenharmony_ci if (err) { 77162306a36Sopenharmony_ci if (errno == ENOENT) 77262306a36Sopenharmony_ci break; 77362306a36Sopenharmony_ci p_err("can't get next link: %s (id %d)", strerror(errno), id); 77462306a36Sopenharmony_ci break; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci fd = bpf_link_get_fd_by_id(id); 77862306a36Sopenharmony_ci if (fd < 0) { 77962306a36Sopenharmony_ci p_err("can't get link by id (%u): %s", id, strerror(errno)); 78062306a36Sopenharmony_ci continue; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci memset(&info, 0, sizeof(info)); 78462306a36Sopenharmony_ci len = sizeof(info); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci err = bpf_link_get_info_by_fd(fd, &info, &len); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci close(fd); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (err) { 79162306a36Sopenharmony_ci p_err("can't get link info for fd %d: %s", fd, strerror(errno)); 79262306a36Sopenharmony_ci continue; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (info.type != BPF_LINK_TYPE_NETFILTER) 79662306a36Sopenharmony_ci continue; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (nf_link_count >= nf_link_len) { 79962306a36Sopenharmony_ci static const unsigned int max_link_count = INT_MAX / sizeof(info); 80062306a36Sopenharmony_ci struct bpf_link_info *expand; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (nf_link_count > max_link_count) { 80362306a36Sopenharmony_ci p_err("cannot handle more than %u links\n", max_link_count); 80462306a36Sopenharmony_ci break; 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci nf_link_len += 16; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci expand = realloc(nf_link_info, nf_link_len * sizeof(info)); 81062306a36Sopenharmony_ci if (!expand) { 81162306a36Sopenharmony_ci p_err("realloc: %s", strerror(errno)); 81262306a36Sopenharmony_ci break; 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci nf_link_info = expand; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci nf_link_info[nf_link_count] = info; 81962306a36Sopenharmony_ci nf_link_count++; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci qsort(nf_link_info, nf_link_count, sizeof(*nf_link_info), netfilter_link_compar); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci for (id = 0; id < nf_link_count; id++) { 82562306a36Sopenharmony_ci NET_START_OBJECT; 82662306a36Sopenharmony_ci if (json_output) 82762306a36Sopenharmony_ci netfilter_dump_json(&nf_link_info[id], json_wtr); 82862306a36Sopenharmony_ci else 82962306a36Sopenharmony_ci netfilter_dump_plain(&nf_link_info[id]); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci NET_DUMP_UINT("id", " prog_id %u", nf_link_info[id].prog_id); 83262306a36Sopenharmony_ci NET_END_OBJECT; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci free(nf_link_info); 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cistatic int do_show(int argc, char **argv) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci struct bpf_attach_info attach_info = {}; 84162306a36Sopenharmony_ci int i, sock, ret, filter_idx = -1; 84262306a36Sopenharmony_ci struct bpf_netdev_t dev_array; 84362306a36Sopenharmony_ci unsigned int nl_pid = 0; 84462306a36Sopenharmony_ci char err_buf[256]; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (argc == 2) { 84762306a36Sopenharmony_ci filter_idx = net_parse_dev(&argc, &argv); 84862306a36Sopenharmony_ci if (filter_idx < 1) 84962306a36Sopenharmony_ci return -1; 85062306a36Sopenharmony_ci } else if (argc != 0) { 85162306a36Sopenharmony_ci usage(); 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci ret = query_flow_dissector(&attach_info); 85562306a36Sopenharmony_ci if (ret) 85662306a36Sopenharmony_ci return -1; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci sock = netlink_open(&nl_pid); 85962306a36Sopenharmony_ci if (sock < 0) { 86062306a36Sopenharmony_ci fprintf(stderr, "failed to open netlink sock\n"); 86162306a36Sopenharmony_ci return -1; 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci dev_array.devices = NULL; 86562306a36Sopenharmony_ci dev_array.used_len = 0; 86662306a36Sopenharmony_ci dev_array.array_len = 0; 86762306a36Sopenharmony_ci dev_array.filter_idx = filter_idx; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci if (json_output) 87062306a36Sopenharmony_ci jsonw_start_array(json_wtr); 87162306a36Sopenharmony_ci NET_START_OBJECT; 87262306a36Sopenharmony_ci NET_START_ARRAY("xdp", "%s:\n"); 87362306a36Sopenharmony_ci ret = netlink_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array); 87462306a36Sopenharmony_ci NET_END_ARRAY("\n"); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (!ret) { 87762306a36Sopenharmony_ci NET_START_ARRAY("tc", "%s:\n"); 87862306a36Sopenharmony_ci for (i = 0; i < dev_array.used_len; i++) { 87962306a36Sopenharmony_ci show_dev_tc_bpf(&dev_array.devices[i]); 88062306a36Sopenharmony_ci ret = show_dev_tc_bpf_classic(sock, nl_pid, 88162306a36Sopenharmony_ci &dev_array.devices[i]); 88262306a36Sopenharmony_ci if (ret) 88362306a36Sopenharmony_ci break; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci NET_END_ARRAY("\n"); 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci NET_START_ARRAY("flow_dissector", "%s:\n"); 88962306a36Sopenharmony_ci if (attach_info.flow_dissector_id > 0) 89062306a36Sopenharmony_ci NET_DUMP_UINT("id", "id %u", attach_info.flow_dissector_id); 89162306a36Sopenharmony_ci NET_END_ARRAY("\n"); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci NET_START_ARRAY("netfilter", "%s:\n"); 89462306a36Sopenharmony_ci show_link_netfilter(); 89562306a36Sopenharmony_ci NET_END_ARRAY("\n"); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci NET_END_OBJECT; 89862306a36Sopenharmony_ci if (json_output) 89962306a36Sopenharmony_ci jsonw_end_array(json_wtr); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (ret) { 90262306a36Sopenharmony_ci if (json_output) 90362306a36Sopenharmony_ci jsonw_null(json_wtr); 90462306a36Sopenharmony_ci libbpf_strerror(ret, err_buf, sizeof(err_buf)); 90562306a36Sopenharmony_ci fprintf(stderr, "Error: %s\n", err_buf); 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci free(dev_array.devices); 90862306a36Sopenharmony_ci close(sock); 90962306a36Sopenharmony_ci return ret; 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic int do_help(int argc, char **argv) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci if (json_output) { 91562306a36Sopenharmony_ci jsonw_null(json_wtr); 91662306a36Sopenharmony_ci return 0; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci fprintf(stderr, 92062306a36Sopenharmony_ci "Usage: %1$s %2$s { show | list } [dev <devname>]\n" 92162306a36Sopenharmony_ci " %1$s %2$s attach ATTACH_TYPE PROG dev <devname> [ overwrite ]\n" 92262306a36Sopenharmony_ci " %1$s %2$s detach ATTACH_TYPE dev <devname>\n" 92362306a36Sopenharmony_ci " %1$s %2$s help\n" 92462306a36Sopenharmony_ci "\n" 92562306a36Sopenharmony_ci " " HELP_SPEC_PROGRAM "\n" 92662306a36Sopenharmony_ci " ATTACH_TYPE := { xdp | xdpgeneric | xdpdrv | xdpoffload }\n" 92762306a36Sopenharmony_ci " " HELP_SPEC_OPTIONS " }\n" 92862306a36Sopenharmony_ci "\n" 92962306a36Sopenharmony_ci "Note: Only xdp, tcx, tc, flow_dissector and netfilter attachments\n" 93062306a36Sopenharmony_ci " are currently supported.\n" 93162306a36Sopenharmony_ci " For progs attached to cgroups, use \"bpftool cgroup\"\n" 93262306a36Sopenharmony_ci " to dump program attachments. For program types\n" 93362306a36Sopenharmony_ci " sk_{filter,skb,msg,reuseport} and lwt/seg6, please\n" 93462306a36Sopenharmony_ci " consult iproute2.\n" 93562306a36Sopenharmony_ci "", 93662306a36Sopenharmony_ci bin_name, argv[-2]); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci return 0; 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cistatic const struct cmd cmds[] = { 94262306a36Sopenharmony_ci { "show", do_show }, 94362306a36Sopenharmony_ci { "list", do_show }, 94462306a36Sopenharmony_ci { "attach", do_attach }, 94562306a36Sopenharmony_ci { "detach", do_detach }, 94662306a36Sopenharmony_ci { "help", do_help }, 94762306a36Sopenharmony_ci { 0 } 94862306a36Sopenharmony_ci}; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ciint do_net(int argc, char **argv) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci return cmd_select(cmds, argc, argv, do_help); 95362306a36Sopenharmony_ci} 954