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", |
| 72 | tb[IFLA_IFNAME] ? nla_getattr_str(tb[IFLA_IFNAME]) : ""); |
| 73 | netinfo->used_len++; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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)); |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 95 | if (!tcinfo->handle_array) |
| 96 | return -ENOMEM; |
| 97 | |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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), |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 103 | "%s", |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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 | { |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 112 | const struct bpf_filter_t *filter_info = cookie; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 113 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 114 | return do_filter_dump((struct tcmsg *)msg, tb, filter_info->kind, |
| 115 | filter_info->devname, filter_info->ifindex); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 116 | } |
| 117 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 118 | static int show_dev_tc_bpf(int sock, unsigned int nl_pid, |
| 119 | struct ip_devname_ifindex *dev) |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 120 | { |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 121 | struct bpf_filter_t filter_info; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 122 | struct bpf_tcinfo_t tcinfo; |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 123 | int i, handle, ret = 0; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 124 | |
| 125 | tcinfo.handle_array = NULL; |
| 126 | tcinfo.used_len = 0; |
| 127 | tcinfo.array_len = 0; |
| 128 | |
| 129 | tcinfo.is_qdisc = false; |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 130 | ret = nl_get_class(sock, nl_pid, dev->ifindex, dump_class_qdisc_nlmsg, |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 131 | &tcinfo); |
| 132 | if (ret) |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 133 | goto out; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 134 | |
| 135 | tcinfo.is_qdisc = true; |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 136 | ret = nl_get_qdisc(sock, nl_pid, dev->ifindex, dump_class_qdisc_nlmsg, |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 137 | &tcinfo); |
| 138 | if (ret) |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 139 | goto out; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 140 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 141 | filter_info.devname = dev->devname; |
| 142 | filter_info.ifindex = dev->ifindex; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 143 | for (i = 0; i < tcinfo.used_len; i++) { |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 144 | filter_info.kind = tcinfo.handle_array[i].kind; |
| 145 | ret = nl_get_filter(sock, nl_pid, dev->ifindex, |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 146 | tcinfo.handle_array[i].handle, |
| 147 | dump_filter_nlmsg, |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 148 | &filter_info); |
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 | } |
| 152 | |
| 153 | /* root, ingress and egress handle */ |
| 154 | handle = TC_H_ROOT; |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 155 | filter_info.kind = "root"; |
| 156 | ret = nl_get_filter(sock, nl_pid, dev->ifindex, handle, |
| 157 | dump_filter_nlmsg, &filter_info); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 158 | if (ret) |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 159 | goto out; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 160 | |
| 161 | handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 162 | filter_info.kind = "clsact/ingress"; |
| 163 | ret = nl_get_filter(sock, nl_pid, dev->ifindex, handle, |
| 164 | dump_filter_nlmsg, &filter_info); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 165 | if (ret) |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 166 | goto out; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 167 | |
| 168 | handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS); |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 169 | filter_info.kind = "clsact/egress"; |
| 170 | ret = nl_get_filter(sock, nl_pid, dev->ifindex, handle, |
| 171 | dump_filter_nlmsg, &filter_info); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 172 | if (ret) |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 173 | goto out; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 174 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 175 | out: |
| 176 | free(tcinfo.handle_array); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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 | |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 205 | dev_array.devices = NULL; |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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; |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 213 | NET_START_ARRAY("xdp", "%s:\n"); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 214 | ret = nl_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array); |
| 215 | NET_END_ARRAY("\n"); |
| 216 | |
| 217 | if (!ret) { |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 218 | NET_START_ARRAY("tc", "%s:\n"); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 219 | for (i = 0; i < dev_array.used_len; i++) { |
| 220 | ret = show_dev_tc_bpf(sock, nl_pid, |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 221 | &dev_array.devices[i]); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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 | } |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 237 | free(dev_array.devices); |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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" |
Yonghong Song | 7900efc | 2018-09-17 16:13:00 -0700 | [diff] [blame^] | 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", |
Yonghong Song | f6f3bac | 2018-09-05 16:58:06 -0700 | [diff] [blame] | 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 | } |