Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 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> |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 21 | struct ip_devname_ifindex { |
| 22 | char devname[64]; |
| 23 | int ifindex; |
| 24 | }; |
| 25 | |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 26 | struct bpf_netdev_t { |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 27 | struct ip_devname_ifindex *devices; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 45 | struct bpf_filter_t { |
| 46 | const char *kind; |
| 47 | const char *devname; |
| 48 | int ifindex; |
| 49 | }; |
| 50 | |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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) { |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 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 | |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 66 | netinfo->array_len += 16; |
| 67 | } |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 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", |
Andrey Ignatov | f04bc8a4 | 2018-10-03 15:26:40 -0700 | [diff] [blame^] | 72 | tb[IFLA_IFNAME] |
| 73 | ? libbpf_nla_getattr_str(tb[IFLA_IFNAME]) |
| 74 | : ""); |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 75 | netinfo->used_len++; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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] && |
Andrey Ignatov | f04bc8a4 | 2018-10-03 15:26:40 -0700 | [diff] [blame^] | 88 | strcmp(libbpf_nla_data(tb[TCA_KIND]), "clsact") == 0) |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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)); |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 97 | if (!tcinfo->handle_array) |
| 98 | return -ENOMEM; |
| 99 | |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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), |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 105 | "%s", |
Andrey Ignatov | f04bc8a4 | 2018-10-03 15:26:40 -0700 | [diff] [blame^] | 106 | tb[TCA_KIND] |
| 107 | ? libbpf_nla_getattr_str(tb[TCA_KIND]) |
| 108 | : "unknown"); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 109 | tcinfo->used_len++; |
| 110 | |
| 111 | return 0; |
| 112 | } |
| 113 | |
| 114 | static int dump_filter_nlmsg(void *cookie, void *msg, struct nlattr **tb) |
| 115 | { |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 116 | const struct bpf_filter_t *filter_info = cookie; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 117 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 118 | return do_filter_dump((struct tcmsg *)msg, tb, filter_info->kind, |
| 119 | filter_info->devname, filter_info->ifindex); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 120 | } |
| 121 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 122 | static int show_dev_tc_bpf(int sock, unsigned int nl_pid, |
| 123 | struct ip_devname_ifindex *dev) |
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 | struct bpf_filter_t filter_info; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 126 | struct bpf_tcinfo_t tcinfo; |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 127 | int i, handle, ret = 0; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 128 | |
| 129 | tcinfo.handle_array = NULL; |
| 130 | tcinfo.used_len = 0; |
| 131 | tcinfo.array_len = 0; |
| 132 | |
| 133 | tcinfo.is_qdisc = false; |
Andrey Ignatov | aae5778 | 2018-10-03 15:26:39 -0700 | [diff] [blame] | 134 | ret = libbpf_nl_get_class(sock, nl_pid, dev->ifindex, |
| 135 | dump_class_qdisc_nlmsg, &tcinfo); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 136 | if (ret) |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 137 | goto out; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 138 | |
| 139 | tcinfo.is_qdisc = true; |
Andrey Ignatov | aae5778 | 2018-10-03 15:26:39 -0700 | [diff] [blame] | 140 | ret = libbpf_nl_get_qdisc(sock, nl_pid, dev->ifindex, |
| 141 | dump_class_qdisc_nlmsg, &tcinfo); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 142 | if (ret) |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 143 | goto out; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 144 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 145 | filter_info.devname = dev->devname; |
| 146 | filter_info.ifindex = dev->ifindex; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 147 | for (i = 0; i < tcinfo.used_len; i++) { |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 148 | filter_info.kind = tcinfo.handle_array[i].kind; |
Andrey Ignatov | aae5778 | 2018-10-03 15:26:39 -0700 | [diff] [blame] | 149 | ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, |
| 150 | tcinfo.handle_array[i].handle, |
| 151 | dump_filter_nlmsg, &filter_info); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 152 | if (ret) |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 153 | goto out; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 154 | } |
| 155 | |
| 156 | /* root, ingress and egress handle */ |
| 157 | handle = TC_H_ROOT; |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 158 | filter_info.kind = "root"; |
Andrey Ignatov | aae5778 | 2018-10-03 15:26:39 -0700 | [diff] [blame] | 159 | ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, handle, |
| 160 | dump_filter_nlmsg, &filter_info); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 161 | if (ret) |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 162 | goto out; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 163 | |
| 164 | handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 165 | filter_info.kind = "clsact/ingress"; |
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_EGRESS); |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 172 | filter_info.kind = "clsact/egress"; |
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 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 178 | out: |
| 179 | free(tcinfo.handle_array); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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 | |
Andrey Ignatov | aae5778 | 2018-10-03 15:26:39 -0700 | [diff] [blame] | 202 | sock = libbpf_netlink_open(&nl_pid); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 203 | if (sock < 0) { |
| 204 | fprintf(stderr, "failed to open netlink sock\n"); |
| 205 | return -1; |
| 206 | } |
| 207 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 208 | dev_array.devices = NULL; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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; |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 216 | NET_START_ARRAY("xdp", "%s:\n"); |
Andrey Ignatov | aae5778 | 2018-10-03 15:26:39 -0700 | [diff] [blame] | 217 | 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] | 218 | NET_END_ARRAY("\n"); |
| 219 | |
| 220 | if (!ret) { |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 221 | NET_START_ARRAY("tc", "%s:\n"); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 222 | for (i = 0; i < dev_array.used_len; i++) { |
| 223 | ret = show_dev_tc_bpf(sock, nl_pid, |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 224 | &dev_array.devices[i]); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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 | } |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 240 | free(dev_array.devices); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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" |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame] | 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", |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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 | } |