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