Warning: That file was not part of the compilation database. It may have many parsing errors.
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 <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] |
73 | ? libbpf_nla_getattr_str(tb[IFLA_IFNAME]) |
74 | : ""); |
75 | netinfo->used_len++; |
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] && |
88 | strcmp(libbpf_nla_data(tb[TCA_KIND]), "clsact") == 0) |
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)); |
97 | if (!tcinfo->handle_array) |
98 | return -ENOMEM; |
99 | |
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), |
105 | "%s", |
106 | tb[TCA_KIND] |
107 | ? libbpf_nla_getattr_str(tb[TCA_KIND]) |
108 | : "unknown"); |
109 | tcinfo->used_len++; |
110 | |
111 | return 0; |
112 | } |
113 | |
114 | static int dump_filter_nlmsg(void *cookie, void *msg, struct nlattr **tb) |
115 | { |
116 | const struct bpf_filter_t *filter_info = cookie; |
117 | |
118 | return do_filter_dump((struct tcmsg *)msg, tb, filter_info->kind, |
119 | filter_info->devname, filter_info->ifindex); |
120 | } |
121 | |
122 | static int show_dev_tc_bpf(int sock, unsigned int nl_pid, |
123 | struct ip_devname_ifindex *dev) |
124 | { |
125 | struct bpf_filter_t filter_info; |
126 | struct bpf_tcinfo_t tcinfo; |
127 | int i, handle, ret = 0; |
128 | |
129 | tcinfo.handle_array = NULL; |
130 | tcinfo.used_len = 0; |
131 | tcinfo.array_len = 0; |
132 | |
133 | tcinfo.is_qdisc = false; |
134 | ret = libbpf_nl_get_class(sock, nl_pid, dev->ifindex, |
135 | dump_class_qdisc_nlmsg, &tcinfo); |
136 | if (ret) |
137 | goto out; |
138 | |
139 | tcinfo.is_qdisc = true; |
140 | ret = libbpf_nl_get_qdisc(sock, nl_pid, dev->ifindex, |
141 | dump_class_qdisc_nlmsg, &tcinfo); |
142 | if (ret) |
143 | goto out; |
144 | |
145 | filter_info.devname = dev->devname; |
146 | filter_info.ifindex = dev->ifindex; |
147 | for (i = 0; i < tcinfo.used_len; i++) { |
148 | filter_info.kind = tcinfo.handle_array[i].kind; |
149 | ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, |
150 | tcinfo.handle_array[i].handle, |
151 | dump_filter_nlmsg, &filter_info); |
152 | if (ret) |
153 | goto out; |
154 | } |
155 | |
156 | /* root, ingress and egress handle */ |
157 | handle = TC_H_ROOT; |
158 | filter_info.kind = "root"; |
159 | ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, handle, |
160 | dump_filter_nlmsg, &filter_info); |
161 | if (ret) |
162 | goto out; |
163 | |
164 | handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); |
165 | filter_info.kind = "clsact/ingress"; |
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_EGRESS); |
172 | filter_info.kind = "clsact/egress"; |
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 | out: |
179 | free(tcinfo.handle_array); |
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 | |
202 | sock = libbpf_netlink_open(&nl_pid); |
203 | if (sock < 0) { |
204 | fprintf(stderr, "failed to open netlink sock\n"); |
205 | return -1; |
206 | } |
207 | |
208 | dev_array.devices = NULL; |
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; |
216 | NET_START_ARRAY("xdp", "%s:\n"); |
217 | ret = libbpf_nl_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array); |
218 | NET_END_ARRAY("\n"); |
219 | |
220 | if (!ret) { |
221 | NET_START_ARRAY("tc", "%s:\n"); |
222 | for (i = 0; i < dev_array.used_len; i++) { |
223 | ret = show_dev_tc_bpf(sock, nl_pid, |
224 | &dev_array.devices[i]); |
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 | } |
240 | free(dev_array.devices); |
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" |
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", |
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 | } |
276 |
Warning: That file was not part of the compilation database. It may have many parsing errors.