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 <stdlib.h> 7 #include <string.h> 8 #include <unistd.h> 9 #include <libbpf.h> 10 #include <net/if.h> 11 #include <linux/if.h> 12 #include <linux/rtnetlink.h> 13 #include <linux/tc_act/tc_bpf.h> 14 #include <sys/socket.h> 15 16 #include <bpf.h> 17 #include <nlattr.h> 18 #include "main.h" 19 #include "netlink_dumper.h" 20 21 struct ip_devname_ifindex { 22 char devname[64]; 23 int ifindex; 24 }; 25 26 struct bpf_netdev_t { 27 struct ip_devname_ifindex *devices; 28 int used_len; 29 int array_len; 30 int filter_idx; 31 }; 32 33 struct tc_kind_handle { 34 char kind[64]; 35 int handle; 36 }; 37 38 struct bpf_tcinfo_t { 39 struct tc_kind_handle *handle_array; 40 int used_len; 41 int array_len; 42 bool is_qdisc; 43 }; 44 45 struct bpf_filter_t { 46 const char *kind; 47 const char *devname; 48 int ifindex; 49 }; 50 51 static int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb) 52 { 53 struct bpf_netdev_t *netinfo = cookie; 54 struct ifinfomsg *ifinfo = msg; 55 56 if (netinfo->filter_idx > 0 && netinfo->filter_idx != ifinfo->ifi_index) 57 return 0; 58 59 if (netinfo->used_len == netinfo->array_len) { 60 netinfo->devices = realloc(netinfo->devices, 61 (netinfo->array_len + 16) * 62 sizeof(struct ip_devname_ifindex)); 63 if (!netinfo->devices) 64 return -ENOMEM; 65 66 netinfo->array_len += 16; 67 } 68 netinfo->devices[netinfo->used_len].ifindex = ifinfo->ifi_index; 69 snprintf(netinfo->devices[netinfo->used_len].devname, 70 sizeof(netinfo->devices[netinfo->used_len].devname), 71 "%s", 72 tb[IFLA_IFNAME] 73 ? libbpf_nla_getattr_str(tb[IFLA_IFNAME]) 74 : ""); 75 netinfo->used_len++; 76 77 return do_xdp_dump(ifinfo, tb); 78 } 79 80 static int dump_class_qdisc_nlmsg(void *cookie, void *msg, struct nlattr **tb) 81 { 82 struct bpf_tcinfo_t *tcinfo = cookie; 83 struct tcmsg *info = msg; 84 85 if (tcinfo->is_qdisc) { 86 /* skip clsact qdisc */ 87 if (tb[TCA_KIND] && 88 strcmp(libbpf_nla_data(tb[TCA_KIND]), "clsact") == 0) 89 return 0; 90 if (info->tcm_handle == 0) 91 return 0; 92 } 93 94 if (tcinfo->used_len == tcinfo->array_len) { 95 tcinfo->handle_array = realloc(tcinfo->handle_array, 96 (tcinfo->array_len + 16) * sizeof(struct tc_kind_handle)); 97 if (!tcinfo->handle_array) 98 return -ENOMEM; 99 100 tcinfo->array_len += 16; 101 } 102 tcinfo->handle_array[tcinfo->used_len].handle = info->tcm_handle; 103 snprintf(tcinfo->handle_array[tcinfo->used_len].kind, 104 sizeof(tcinfo->handle_array[tcinfo->used_len].kind), 105 "%s", 106 tb[TCA_KIND] 107 ? libbpf_nla_getattr_str(tb[TCA_KIND]) 108 : "unknown"); 109 tcinfo->used_len++; 110 111 return 0; 112 } 113 114 static int dump_filter_nlmsg(void *cookie, void *msg, struct nlattr **tb) 115 { 116 const struct bpf_filter_t *filter_info = cookie; 117 118 return do_filter_dump((struct tcmsg *)msg, tb, filter_info->kind, 119 filter_info->devname, filter_info->ifindex); 120 } 121 122 static int show_dev_tc_bpf(int sock, unsigned int nl_pid, 123 struct ip_devname_ifindex *dev) 124 { 125 struct bpf_filter_t filter_info; 126 struct bpf_tcinfo_t tcinfo; 127 int i, handle, ret = 0; 128 129 tcinfo.handle_array = NULL; 130 tcinfo.used_len = 0; 131 tcinfo.array_len = 0; 132 133 tcinfo.is_qdisc = false; 134 ret = libbpf_nl_get_class(sock, nl_pid, dev->ifindex, 135 dump_class_qdisc_nlmsg, &tcinfo); 136 if (ret) 137 goto out; 138 139 tcinfo.is_qdisc = true; 140 ret = libbpf_nl_get_qdisc(sock, nl_pid, dev->ifindex, 141 dump_class_qdisc_nlmsg, &tcinfo); 142 if (ret) 143 goto out; 144 145 filter_info.devname = dev->devname; 146 filter_info.ifindex = dev->ifindex; 147 for (i = 0; i < tcinfo.used_len; i++) { 148 filter_info.kind = tcinfo.handle_array[i].kind; 149 ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, 150 tcinfo.handle_array[i].handle, 151 dump_filter_nlmsg, &filter_info); 152 if (ret) 153 goto out; 154 } 155 156 /* root, ingress and egress handle */ 157 handle = TC_H_ROOT; 158 filter_info.kind = "root"; 159 ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, handle, 160 dump_filter_nlmsg, &filter_info); 161 if (ret) 162 goto out; 163 164 handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); 165 filter_info.kind = "clsact/ingress"; 166 ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, handle, 167 dump_filter_nlmsg, &filter_info); 168 if (ret) 169 goto out; 170 171 handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS); 172 filter_info.kind = "clsact/egress"; 173 ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, handle, 174 dump_filter_nlmsg, &filter_info); 175 if (ret) 176 goto out; 177 178 out: 179 free(tcinfo.handle_array); 180 return 0; 181 } 182 183 static int do_show(int argc, char **argv) 184 { 185 int i, sock, ret, filter_idx = -1; 186 struct bpf_netdev_t dev_array; 187 unsigned int nl_pid; 188 char err_buf[256]; 189 190 if (argc == 2) { 191 if (strcmp(argv[0], "dev") != 0) 192 usage(); 193 filter_idx = if_nametoindex(argv[1]); 194 if (filter_idx == 0) { 195 fprintf(stderr, "invalid dev name %s\n", argv[1]); 196 return -1; 197 } 198 } else if (argc != 0) { 199 usage(); 200 } 201 202 sock = libbpf_netlink_open(&nl_pid); 203 if (sock < 0) { 204 fprintf(stderr, "failed to open netlink sock\n"); 205 return -1; 206 } 207 208 dev_array.devices = NULL; 209 dev_array.used_len = 0; 210 dev_array.array_len = 0; 211 dev_array.filter_idx = filter_idx; 212 213 if (json_output) 214 jsonw_start_array(json_wtr); 215 NET_START_OBJECT; 216 NET_START_ARRAY("xdp", "%s:\n"); 217 ret = libbpf_nl_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array); 218 NET_END_ARRAY("\n"); 219 220 if (!ret) { 221 NET_START_ARRAY("tc", "%s:\n"); 222 for (i = 0; i < dev_array.used_len; i++) { 223 ret = show_dev_tc_bpf(sock, nl_pid, 224 &dev_array.devices[i]); 225 if (ret) 226 break; 227 } 228 NET_END_ARRAY("\n"); 229 } 230 NET_END_OBJECT; 231 if (json_output) 232 jsonw_end_array(json_wtr); 233 234 if (ret) { 235 if (json_output) 236 jsonw_null(json_wtr); 237 libbpf_strerror(ret, err_buf, sizeof(err_buf)); 238 fprintf(stderr, "Error: %s\n", err_buf); 239 } 240 free(dev_array.devices); 241 close(sock); 242 return ret; 243 } 244 245 static int do_help(int argc, char **argv) 246 { 247 if (json_output) { 248 jsonw_null(json_wtr); 249 return 0; 250 } 251 252 fprintf(stderr, 253 "Usage: %s %s { show | list } [dev <devname>]\n" 254 " %s %s help\n" 255 "Note: Only xdp and tc attachments are supported now.\n" 256 " For progs attached to cgroups, use \"bpftool cgroup\"\n" 257 " to dump program attachments. For program types\n" 258 " sk_{filter,skb,msg,reuseport} and lwt/seg6, please\n" 259 " consult iproute2.\n", 260 bin_name, argv[-2], bin_name, argv[-2]); 261 262 return 0; 263 } 264 265 static const struct cmd cmds[] = { 266 { "show", do_show }, 267 { "list", do_show }, 268 { "help", do_help }, 269 { 0 } 270 }; 271 272 int do_net(int argc, char **argv) 273 { 274 return cmd_select(cmds, argc, argv, do_help); 275 } 276