Jakub Kicinski | 907b223 | 2018-12-12 19:59:26 -0800 | [diff] [blame] | 1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 2 | // Copyright (C) 2018 Facebook |
| 3 | |
| 4 | #define _GNU_SOURCE |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 5 | #include <errno.h> |
Stanislav Fomichev | 7f0c57f | 2019-04-25 14:37:24 -0700 | [diff] [blame] | 6 | #include <fcntl.h> |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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> |
Stanislav Fomichev | 7f0c57f | 2019-04-25 14:37:24 -0700 | [diff] [blame] | 16 | #include <sys/stat.h> |
| 17 | #include <sys/types.h> |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 18 | |
| 19 | #include <bpf.h> |
| 20 | #include <nlattr.h> |
| 21 | #include "main.h" |
| 22 | #include "netlink_dumper.h" |
| 23 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 24 | struct ip_devname_ifindex { |
| 25 | char devname[64]; |
| 26 | int ifindex; |
| 27 | }; |
| 28 | |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 29 | struct bpf_netdev_t { |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 30 | struct ip_devname_ifindex *devices; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 48 | struct bpf_filter_t { |
| 49 | const char *kind; |
| 50 | const char *devname; |
| 51 | int ifindex; |
| 52 | }; |
| 53 | |
Stanislav Fomichev | 7f0c57f | 2019-04-25 14:37:24 -0700 | [diff] [blame] | 54 | struct bpf_attach_info { |
| 55 | __u32 flow_dissector_id; |
| 56 | }; |
| 57 | |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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) { |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 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 | |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 73 | netinfo->array_len += 16; |
| 74 | } |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 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", |
Andrey Ignatov | f04bc8a4 | 2018-10-03 15:26:40 -0700 | [diff] [blame] | 79 | tb[IFLA_IFNAME] |
| 80 | ? libbpf_nla_getattr_str(tb[IFLA_IFNAME]) |
| 81 | : ""); |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 82 | netinfo->used_len++; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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] && |
Andrey Ignatov | f04bc8a4 | 2018-10-03 15:26:40 -0700 | [diff] [blame] | 95 | strcmp(libbpf_nla_data(tb[TCA_KIND]), "clsact") == 0) |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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)); |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 104 | if (!tcinfo->handle_array) |
| 105 | return -ENOMEM; |
| 106 | |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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), |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 112 | "%s", |
Andrey Ignatov | f04bc8a4 | 2018-10-03 15:26:40 -0700 | [diff] [blame] | 113 | tb[TCA_KIND] |
| 114 | ? libbpf_nla_getattr_str(tb[TCA_KIND]) |
| 115 | : "unknown"); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 116 | tcinfo->used_len++; |
| 117 | |
| 118 | return 0; |
| 119 | } |
| 120 | |
| 121 | static int dump_filter_nlmsg(void *cookie, void *msg, struct nlattr **tb) |
| 122 | { |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 123 | const struct bpf_filter_t *filter_info = cookie; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 124 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 125 | return do_filter_dump((struct tcmsg *)msg, tb, filter_info->kind, |
| 126 | filter_info->devname, filter_info->ifindex); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 127 | } |
| 128 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 129 | static int show_dev_tc_bpf(int sock, unsigned int nl_pid, |
| 130 | struct ip_devname_ifindex *dev) |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 131 | { |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 132 | struct bpf_filter_t filter_info; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 133 | struct bpf_tcinfo_t tcinfo; |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 134 | int i, handle, ret = 0; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 135 | |
| 136 | tcinfo.handle_array = NULL; |
| 137 | tcinfo.used_len = 0; |
| 138 | tcinfo.array_len = 0; |
| 139 | |
| 140 | tcinfo.is_qdisc = false; |
Andrey Ignatov | aae5778 | 2018-10-03 15:26:39 -0700 | [diff] [blame] | 141 | ret = libbpf_nl_get_class(sock, nl_pid, dev->ifindex, |
| 142 | dump_class_qdisc_nlmsg, &tcinfo); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 143 | if (ret) |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 144 | goto out; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 145 | |
| 146 | tcinfo.is_qdisc = true; |
Andrey Ignatov | aae5778 | 2018-10-03 15:26:39 -0700 | [diff] [blame] | 147 | ret = libbpf_nl_get_qdisc(sock, nl_pid, dev->ifindex, |
| 148 | dump_class_qdisc_nlmsg, &tcinfo); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 149 | if (ret) |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 150 | goto out; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 151 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 152 | filter_info.devname = dev->devname; |
| 153 | filter_info.ifindex = dev->ifindex; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 154 | for (i = 0; i < tcinfo.used_len; i++) { |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 155 | filter_info.kind = tcinfo.handle_array[i].kind; |
Andrey Ignatov | aae5778 | 2018-10-03 15:26:39 -0700 | [diff] [blame] | 156 | ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, |
| 157 | tcinfo.handle_array[i].handle, |
| 158 | dump_filter_nlmsg, &filter_info); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 159 | if (ret) |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 160 | goto out; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | /* root, ingress and egress handle */ |
| 164 | handle = TC_H_ROOT; |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 165 | filter_info.kind = "root"; |
Andrey Ignatov | aae5778 | 2018-10-03 15:26:39 -0700 | [diff] [blame] | 166 | ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, handle, |
| 167 | dump_filter_nlmsg, &filter_info); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 168 | if (ret) |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 169 | goto out; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 170 | |
| 171 | handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 172 | filter_info.kind = "clsact/ingress"; |
Andrey Ignatov | aae5778 | 2018-10-03 15:26:39 -0700 | [diff] [blame] | 173 | ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, handle, |
| 174 | dump_filter_nlmsg, &filter_info); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 175 | if (ret) |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 176 | goto out; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 177 | |
| 178 | handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS); |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 179 | filter_info.kind = "clsact/egress"; |
Andrey Ignatov | aae5778 | 2018-10-03 15:26:39 -0700 | [diff] [blame] | 180 | ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, handle, |
| 181 | dump_filter_nlmsg, &filter_info); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 182 | if (ret) |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 183 | goto out; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 184 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 185 | out: |
| 186 | free(tcinfo.handle_array); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 187 | return 0; |
| 188 | } |
| 189 | |
Stanislav Fomichev | 7f0c57f | 2019-04-25 14:37:24 -0700 | [diff] [blame] | 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 | |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 226 | static int do_show(int argc, char **argv) |
| 227 | { |
Stanislav Fomichev | 7f0c57f | 2019-04-25 14:37:24 -0700 | [diff] [blame] | 228 | struct bpf_attach_info attach_info = {}; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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 | |
Stanislav Fomichev | 7f0c57f | 2019-04-25 14:37:24 -0700 | [diff] [blame] | 246 | ret = query_flow_dissector(&attach_info); |
| 247 | if (ret) |
| 248 | return -1; |
| 249 | |
Andrey Ignatov | aae5778 | 2018-10-03 15:26:39 -0700 | [diff] [blame] | 250 | sock = libbpf_netlink_open(&nl_pid); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 251 | if (sock < 0) { |
| 252 | fprintf(stderr, "failed to open netlink sock\n"); |
| 253 | return -1; |
| 254 | } |
| 255 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 256 | dev_array.devices = NULL; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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; |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 264 | NET_START_ARRAY("xdp", "%s:\n"); |
Andrey Ignatov | aae5778 | 2018-10-03 15:26:39 -0700 | [diff] [blame] | 265 | ret = libbpf_nl_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 266 | NET_END_ARRAY("\n"); |
| 267 | |
| 268 | if (!ret) { |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 269 | NET_START_ARRAY("tc", "%s:\n"); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 270 | for (i = 0; i < dev_array.used_len; i++) { |
| 271 | ret = show_dev_tc_bpf(sock, nl_pid, |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 272 | &dev_array.devices[i]); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 273 | if (ret) |
| 274 | break; |
| 275 | } |
| 276 | NET_END_ARRAY("\n"); |
| 277 | } |
Stanislav Fomichev | 7f0c57f | 2019-04-25 14:37:24 -0700 | [diff] [blame] | 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 | |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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 | } |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 294 | free(dev_array.devices); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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" |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 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", |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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 | } |