1 // SPDX-License-Identifier: GPL-2.0+ 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] ? nla_getattr_str(tb[IFLA_IFNAME]) : ""); 73 netinfo->used_len++; 74 75 return do_xdp_dump(ifinfo, tb); 76 } 77 78 static int dump_class_qdisc_nlmsg(void *cookie, void *msg, struct nlattr **tb) 79 { 80 struct bpf_tcinfo_t *tcinfo = cookie; 81 struct tcmsg *info = msg; 82 83 if (tcinfo->is_qdisc) { 84 /* skip clsact qdisc */ 85 if (tb[TCA_KIND] && 86 strcmp(nla_data(tb[TCA_KIND]), "clsact") == 0) 87 return 0; 88 if (info->tcm_handle == 0) 89 return 0; 90 } 91 92 if (tcinfo->used_len == tcinfo->array_len) { 93 tcinfo->handle_array = realloc(tcinfo->handle_array, 94 (tcinfo->array_len + 16) * sizeof(struct tc_kind_handle)); 95 if (!tcinfo->handle_array) 96 return -ENOMEM; 97 98 tcinfo->array_len += 16; 99 } 100 tcinfo->handle_array[tcinfo->used_len].handle = info->tcm_handle; 101 snprintf(tcinfo->handle_array[tcinfo->used_len].kind, 102 sizeof(tcinfo->handle_array[tcinfo->used_len].kind), 103 "%s", 104 tb[TCA_KIND] ? nla_getattr_str(tb[TCA_KIND]) : "unknown"); 105 tcinfo->used_len++; 106 107 return 0; 108 } 109 110 static int dump_filter_nlmsg(void *cookie, void *msg, struct nlattr **tb) 111 { 112 const struct bpf_filter_t *filter_info = cookie; 113 114 return do_filter_dump((struct tcmsg *)msg, tb, filter_info->kind, 115 filter_info->devname, filter_info->ifindex); 116 } 117 118 static int show_dev_tc_bpf(int sock, unsigned int nl_pid, 119 struct ip_devname_ifindex *dev) 120 { 121 struct bpf_filter_t filter_info; 122 struct bpf_tcinfo_t tcinfo; 123 int i, handle, ret = 0; 124 125 tcinfo.handle_array = NULL; 126 tcinfo.used_len = 0; 127 tcinfo.array_len = 0; 128 129 tcinfo.is_qdisc = false; 130 ret = nl_get_class(sock, nl_pid, dev->ifindex, dump_class_qdisc_nlmsg, 131 &tcinfo); 132 if (ret) 133 goto out; 134 135 tcinfo.is_qdisc = true; 136 ret = nl_get_qdisc(sock, nl_pid, dev->ifindex, dump_class_qdisc_nlmsg, 137 &tcinfo); 138 if (ret) 139 goto out; 140 141 filter_info.devname = dev->devname; 142 filter_info.ifindex = dev->ifindex; 143 for (i = 0; i < tcinfo.used_len; i++) { 144 filter_info.kind = tcinfo.handle_array[i].kind; 145 ret = nl_get_filter(sock, nl_pid, dev->ifindex, 146 tcinfo.handle_array[i].handle, 147 dump_filter_nlmsg, 148 &filter_info); 149 if (ret) 150 goto out; 151 } 152 153 /* root, ingress and egress handle */ 154 handle = TC_H_ROOT; 155 filter_info.kind = "root"; 156 ret = nl_get_filter(sock, nl_pid, dev->ifindex, handle, 157 dump_filter_nlmsg, &filter_info); 158 if (ret) 159 goto out; 160 161 handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); 162 filter_info.kind = "clsact/ingress"; 163 ret = nl_get_filter(sock, nl_pid, dev->ifindex, handle, 164 dump_filter_nlmsg, &filter_info); 165 if (ret) 166 goto out; 167 168 handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS); 169 filter_info.kind = "clsact/egress"; 170 ret = nl_get_filter(sock, nl_pid, dev->ifindex, handle, 171 dump_filter_nlmsg, &filter_info); 172 if (ret) 173 goto out; 174 175 out: 176 free(tcinfo.handle_array); 177 return 0; 178 } 179 180 static int do_show(int argc, char **argv) 181 { 182 int i, sock, ret, filter_idx = -1; 183 struct bpf_netdev_t dev_array; 184 unsigned int nl_pid; 185 char err_buf[256]; 186 187 if (argc == 2) { 188 if (strcmp(argv[0], "dev") != 0) 189 usage(); 190 filter_idx = if_nametoindex(argv[1]); 191 if (filter_idx == 0) { 192 fprintf(stderr, "invalid dev name %s\n", argv[1]); 193 return -1; 194 } 195 } else if (argc != 0) { 196 usage(); 197 } 198 199 sock = bpf_netlink_open(&nl_pid); 200 if (sock < 0) { 201 fprintf(stderr, "failed to open netlink sock\n"); 202 return -1; 203 } 204 205 dev_array.devices = NULL; 206 dev_array.used_len = 0; 207 dev_array.array_len = 0; 208 dev_array.filter_idx = filter_idx; 209 210 if (json_output) 211 jsonw_start_array(json_wtr); 212 NET_START_OBJECT; 213 NET_START_ARRAY("xdp", "%s:\n"); 214 ret = nl_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array); 215 NET_END_ARRAY("\n"); 216 217 if (!ret) { 218 NET_START_ARRAY("tc", "%s:\n"); 219 for (i = 0; i < dev_array.used_len; i++) { 220 ret = show_dev_tc_bpf(sock, nl_pid, 221 &dev_array.devices[i]); 222 if (ret) 223 break; 224 } 225 NET_END_ARRAY("\n"); 226 } 227 NET_END_OBJECT; 228 if (json_output) 229 jsonw_end_array(json_wtr); 230 231 if (ret) { 232 if (json_output) 233 jsonw_null(json_wtr); 234 libbpf_strerror(ret, err_buf, sizeof(err_buf)); 235 fprintf(stderr, "Error: %s\n", err_buf); 236 } 237 free(dev_array.devices); 238 close(sock); 239 return ret; 240 } 241 242 static int do_help(int argc, char **argv) 243 { 244 if (json_output) { 245 jsonw_null(json_wtr); 246 return 0; 247 } 248 249 fprintf(stderr, 250 "Usage: %s %s { show | list } [dev <devname>]\n" 251 " %s %s help\n" 252 "Note: Only xdp and tc attachments are supported now.\n" 253 " For progs attached to cgroups, use \"bpftool cgroup\"\n" 254 " to dump program attachments. For program types\n" 255 " sk_{filter,skb,msg,reuseport} and lwt/seg6, please\n" 256 " consult iproute2.\n", 257 bin_name, argv[-2], bin_name, argv[-2]); 258 259 return 0; 260 } 261 262 static const struct cmd cmds[] = { 263 { "show", do_show }, 264 { "list", do_show }, 265 { "help", do_help }, 266 { 0 } 267 }; 268 269 int do_net(int argc, char **argv) 270 { 271 return cmd_select(cmds, argc, argv, do_help); 272 } 273