]>
Commit | Line | Data |
---|---|---|
aba5acdf SH |
1 | /* |
2 | * tc.c "tc" utility frontend. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public License | |
6 | * as published by the Free Software Foundation; either version | |
7 | * 2 of the License, or (at your option) any later version. | |
8 | * | |
9 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | |
10 | * | |
11 | * Fixes: | |
12 | * | |
13 | * Petri Mattila <petri@prihateam.fi> 990308: wrong memset's resulted in faults | |
14 | */ | |
15 | ||
16 | #include <stdio.h> | |
17 | #include <stdlib.h> | |
18 | #include <unistd.h> | |
aba5acdf SH |
19 | #include <fcntl.h> |
20 | #include <dlfcn.h> | |
21 | #include <sys/socket.h> | |
22 | #include <netinet/in.h> | |
23 | #include <arpa/inet.h> | |
24 | #include <string.h> | |
25 | #include <errno.h> | |
26 | ||
fbef6555 | 27 | #include "version.h" |
aba5acdf SH |
28 | #include "utils.h" |
29 | #include "tc_util.h" | |
30 | #include "tc_common.h" | |
67e1d73b | 31 | #include "namespace.h" |
31f45088 | 32 | #include "rt_names.h" |
503e9229 | 33 | #include "bpf_util.h" |
aba5acdf | 34 | |
32a121cb SH |
35 | int show_stats; |
36 | int show_details; | |
37 | int show_raw; | |
32a121cb | 38 | int show_graph; |
32a6fbe5 | 39 | int timestamp; |
44dcfe82 | 40 | |
32a121cb | 41 | int batch_mode; |
32a121cb SH |
42 | int use_iec; |
43 | int force; | |
44 | bool use_names; | |
c91d262f | 45 | int json; |
2d165c08 | 46 | int color; |
6e8634eb | 47 | int oneline; |
477ca0df | 48 | int brief; |
4612d04d VK |
49 | |
50 | static char *conf_file; | |
51 | ||
7901660a | 52 | struct rtnl_handle rth; |
aba5acdf | 53 | |
32a121cb SH |
54 | static void *BODY; /* cached handle dlopen(NULL) */ |
55 | static struct qdisc_util *qdisc_list; | |
56 | static struct filter_util *filter_list; | |
aba5acdf | 57 | |
ae665a52 | 58 | static int print_noqopt(struct qdisc_util *qu, FILE *f, |
1798c9d5 | 59 | struct rtattr *opt) |
aba5acdf SH |
60 | { |
61 | if (opt && RTA_PAYLOAD(opt)) | |
ae665a52 | 62 | fprintf(f, "[Unknown qdisc, optlen=%u] ", |
32a121cb | 63 | (unsigned int) RTA_PAYLOAD(opt)); |
aba5acdf SH |
64 | return 0; |
65 | } | |
66 | ||
fec62c0e SH |
67 | static int parse_noqopt(struct qdisc_util *qu, int argc, char **argv, |
68 | struct nlmsghdr *n, const char *dev) | |
aba5acdf SH |
69 | { |
70 | if (argc) { | |
fec62c0e SH |
71 | fprintf(stderr, |
72 | "Unknown qdisc \"%s\", hence option \"%s\" is unparsable\n", | |
73 | qu->id, *argv); | |
aba5acdf SH |
74 | return -1; |
75 | } | |
76 | return 0; | |
77 | } | |
78 | ||
79 | static int print_nofopt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 fhandle) | |
80 | { | |
81 | if (opt && RTA_PAYLOAD(opt)) | |
ae665a52 | 82 | fprintf(f, "fh %08x [Unknown filter, optlen=%u] ", |
32a121cb | 83 | fhandle, (unsigned int) RTA_PAYLOAD(opt)); |
aba5acdf SH |
84 | else if (fhandle) |
85 | fprintf(f, "fh %08x ", fhandle); | |
86 | return 0; | |
87 | } | |
88 | ||
fec62c0e SH |
89 | static int parse_nofopt(struct filter_util *qu, char *fhandle, |
90 | int argc, char **argv, struct nlmsghdr *n) | |
aba5acdf SH |
91 | { |
92 | __u32 handle; | |
93 | ||
94 | if (argc) { | |
fec62c0e SH |
95 | fprintf(stderr, |
96 | "Unknown filter \"%s\", hence option \"%s\" is unparsable\n", | |
97 | qu->id, *argv); | |
aba5acdf SH |
98 | return -1; |
99 | } | |
100 | if (fhandle) { | |
101 | struct tcmsg *t = NLMSG_DATA(n); | |
32a121cb | 102 | |
aba5acdf SH |
103 | if (get_u32(&handle, fhandle, 16)) { |
104 | fprintf(stderr, "Unparsable filter ID \"%s\"\n", fhandle); | |
105 | return -1; | |
106 | } | |
107 | t->tcm_handle = handle; | |
108 | } | |
109 | return 0; | |
110 | } | |
111 | ||
4094db72 | 112 | struct qdisc_util *get_qdisc_kind(const char *str) |
aba5acdf SH |
113 | { |
114 | void *dlh; | |
115 | char buf[256]; | |
116 | struct qdisc_util *q; | |
117 | ||
118 | for (q = qdisc_list; q; q = q->next) | |
119 | if (strcmp(q->id, str) == 0) | |
120 | return q; | |
121 | ||
aa27f88c | 122 | snprintf(buf, sizeof(buf), "%s/q_%s.so", get_tc_lib(), str); |
aba5acdf | 123 | dlh = dlopen(buf, RTLD_LAZY); |
b7a45150 SH |
124 | if (!dlh) { |
125 | /* look in current binary, only open once */ | |
aba5acdf SH |
126 | dlh = BODY; |
127 | if (dlh == NULL) { | |
128 | dlh = BODY = dlopen(NULL, RTLD_LAZY); | |
129 | if (dlh == NULL) | |
130 | goto noexist; | |
131 | } | |
132 | } | |
133 | ||
95812b56 | 134 | snprintf(buf, sizeof(buf), "%s_qdisc_util", str); |
aba5acdf SH |
135 | q = dlsym(dlh, buf); |
136 | if (q == NULL) | |
137 | goto noexist; | |
138 | ||
139 | reg: | |
140 | q->next = qdisc_list; | |
141 | qdisc_list = q; | |
142 | return q; | |
143 | ||
144 | noexist: | |
f89bb021 | 145 | q = calloc(1, sizeof(*q)); |
aba5acdf | 146 | if (q) { |
f89bb021 | 147 | q->id = strdup(str); |
aba5acdf SH |
148 | q->parse_qopt = parse_noqopt; |
149 | q->print_qopt = print_noqopt; | |
150 | goto reg; | |
151 | } | |
152 | return q; | |
153 | } | |
154 | ||
155 | ||
4094db72 | 156 | struct filter_util *get_filter_kind(const char *str) |
aba5acdf SH |
157 | { |
158 | void *dlh; | |
159 | char buf[256]; | |
160 | struct filter_util *q; | |
161 | ||
162 | for (q = filter_list; q; q = q->next) | |
163 | if (strcmp(q->id, str) == 0) | |
164 | return q; | |
165 | ||
aa27f88c | 166 | snprintf(buf, sizeof(buf), "%s/f_%s.so", get_tc_lib(), str); |
aba5acdf SH |
167 | dlh = dlopen(buf, RTLD_LAZY); |
168 | if (dlh == NULL) { | |
169 | dlh = BODY; | |
170 | if (dlh == NULL) { | |
171 | dlh = BODY = dlopen(NULL, RTLD_LAZY); | |
172 | if (dlh == NULL) | |
173 | goto noexist; | |
174 | } | |
175 | } | |
176 | ||
95812b56 | 177 | snprintf(buf, sizeof(buf), "%s_filter_util", str); |
aba5acdf SH |
178 | q = dlsym(dlh, buf); |
179 | if (q == NULL) | |
180 | goto noexist; | |
181 | ||
182 | reg: | |
183 | q->next = filter_list; | |
184 | filter_list = q; | |
185 | return q; | |
aba5acdf | 186 | noexist: |
f89bb021 | 187 | q = calloc(1, sizeof(*q)); |
aba5acdf | 188 | if (q) { |
aba5acdf SH |
189 | strncpy(q->id, str, 15); |
190 | q->parse_fopt = parse_nofopt; | |
191 | q->print_fopt = print_nofopt; | |
192 | goto reg; | |
193 | } | |
194 | return q; | |
195 | } | |
196 | ||
c2f3a0f9 | 197 | static void usage(void) |
aba5acdf | 198 | { |
fec62c0e | 199 | fprintf(stderr, |
8589eb4e MC |
200 | "Usage: tc [ OPTIONS ] OBJECT { COMMAND | help }\n" |
201 | " tc [-force] -batch filename\n" | |
afcd0699 | 202 | "where OBJECT := { qdisc | class | filter | chain |\n" |
8589eb4e | 203 | " action | monitor | exec }\n" |
fec62c0e | 204 | " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[aw] |\n" |
8589eb4e | 205 | " -o[neline] | -j[son] | -p[retty] | -c[olor]\n" |
ca697cee | 206 | " -b[atch] [filename] | -n[etns] name | -N[umeric] |\n" |
477ca0df VB |
207 | " -nm | -nam[es] | { -cf | -conf } path\n" |
208 | " -br[ief] }\n"); | |
c2f3a0f9 | 209 | } |
210 | ||
e991c04d | 211 | static int do_cmd(int argc, char **argv) |
c2f3a0f9 | 212 | { |
213 | if (matches(*argv, "qdisc") == 0) | |
214 | return do_qdisc(argc-1, argv+1); | |
c2f3a0f9 | 215 | if (matches(*argv, "class") == 0) |
216 | return do_class(argc-1, argv+1); | |
c2f3a0f9 | 217 | if (matches(*argv, "filter") == 0) |
e991c04d | 218 | return do_filter(argc-1, argv+1); |
afcd0699 | 219 | if (matches(*argv, "chain") == 0) |
e991c04d | 220 | return do_chain(argc-1, argv+1); |
c2f3a0f9 | 221 | if (matches(*argv, "actions") == 0) |
e991c04d | 222 | return do_action(argc-1, argv+1); |
5bec3484 JHS |
223 | if (matches(*argv, "monitor") == 0) |
224 | return do_tcmonitor(argc-1, argv+1); | |
4bd62446 DB |
225 | if (matches(*argv, "exec") == 0) |
226 | return do_exec(argc-1, argv+1); | |
c2f3a0f9 | 227 | if (matches(*argv, "help") == 0) { |
228 | usage(); | |
229 | return 0; | |
230 | } | |
ae665a52 SH |
231 | |
232 | fprintf(stderr, "Object \"%s\" is unknown, try \"tc help\".\n", | |
c2f3a0f9 | 233 | *argv); |
044ebf35 | 234 | return -1; |
aba5acdf SH |
235 | } |
236 | ||
1d9a81b8 PM |
237 | static int tc_batch_cmd(int argc, char *argv[], void *data) |
238 | { | |
239 | return do_cmd(argc, argv); | |
240 | } | |
241 | ||
c2f3a0f9 | 242 | static int batch(const char *name) |
aba5acdf | 243 | { |
1d9a81b8 | 244 | int ret; |
c2f3a0f9 | 245 | |
a3aa47a5 | 246 | batch_mode = 1; |
c2f3a0f9 | 247 | tc_core_init(); |
248 | ||
249 | if (rtnl_open(&rth, 0) < 0) { | |
250 | fprintf(stderr, "Cannot open rtnetlink\n"); | |
251 | return -1; | |
252 | } | |
253 | ||
1d9a81b8 | 254 | ret = do_batch(name, force, tc_batch_cmd, NULL); |
7901660a | 255 | |
c2f3a0f9 | 256 | rtnl_close(&rth); |
257 | return ret; | |
258 | } | |
7901660a | 259 | |
c2f3a0f9 | 260 | |
261 | int main(int argc, char **argv) | |
262 | { | |
503e9229 | 263 | const char *libbpf_version; |
a3aa47a5 | 264 | char *batch_file = NULL; |
503e9229 | 265 | int ret; |
aba5acdf SH |
266 | |
267 | while (argc > 1) { | |
268 | if (argv[1][0] != '-') | |
269 | break; | |
270 | if (matches(argv[1], "-stats") == 0 || | |
3ea2bf45 | 271 | matches(argv[1], "-statistics") == 0) { |
aba5acdf SH |
272 | ++show_stats; |
273 | } else if (matches(argv[1], "-details") == 0) { | |
274 | ++show_details; | |
275 | } else if (matches(argv[1], "-raw") == 0) { | |
276 | ++show_raw; | |
44dcfe82 | 277 | } else if (matches(argv[1], "-pretty") == 0) { |
54336567 | 278 | ++pretty; |
d954b34a VK |
279 | } else if (matches(argv[1], "-graph") == 0) { |
280 | show_graph = 1; | |
aba5acdf | 281 | } else if (matches(argv[1], "-Version") == 0) { |
503e9229 HL |
282 | printf("tc utility, iproute2-%s", version); |
283 | libbpf_version = get_libbpf_version(); | |
284 | if (libbpf_version) | |
285 | printf(", libbpf %s", libbpf_version); | |
286 | printf("\n"); | |
044ebf35 | 287 | return 0; |
3ea2bf45 SH |
288 | } else if (matches(argv[1], "-iec") == 0) { |
289 | ++use_iec; | |
aba5acdf SH |
290 | } else if (matches(argv[1], "-help") == 0) { |
291 | usage(); | |
c2f3a0f9 | 292 | return 0; |
08856f02 SH |
293 | } else if (matches(argv[1], "-force") == 0) { |
294 | ++force; | |
4612d04d | 295 | } else if (matches(argv[1], "-batch") == 0) { |
08856f02 | 296 | argc--; argv++; |
a3aa47a5 SH |
297 | if (argc <= 1) |
298 | usage(); | |
299 | batch_file = argv[1]; | |
67e1d73b VK |
300 | } else if (matches(argv[1], "-netns") == 0) { |
301 | NEXT_ARG(); | |
302 | if (netns_switch(argv[1])) | |
303 | return -1; | |
ca697cee HL |
304 | } else if (matches(argv[1], "-Numeric") == 0) { |
305 | ++numeric; | |
4612d04d VK |
306 | } else if (matches(argv[1], "-names") == 0 || |
307 | matches(argv[1], "-nm") == 0) { | |
308 | use_names = true; | |
309 | } else if (matches(argv[1], "-cf") == 0 || | |
310 | matches(argv[1], "-conf") == 0) { | |
311 | NEXT_ARG(); | |
312 | conf_file = argv[1]; | |
ff1ab8ed | 313 | } else if (matches_color(argv[1], &color)) { |
32a6fbe5 ED |
314 | } else if (matches(argv[1], "-timestamp") == 0) { |
315 | timestamp++; | |
316 | } else if (matches(argv[1], "-tshort") == 0) { | |
317 | ++timestamp; | |
318 | ++timestamp_short; | |
c91d262f JP |
319 | } else if (matches(argv[1], "-json") == 0) { |
320 | ++json; | |
6e8634eb RM |
321 | } else if (matches(argv[1], "-oneline") == 0) { |
322 | ++oneline; | |
477ca0df VB |
323 | }else if (matches(argv[1], "-brief") == 0) { |
324 | ++brief; | |
aba5acdf | 325 | } else { |
fec62c0e SH |
326 | fprintf(stderr, |
327 | "Option \"%s\" is unknown, try \"tc -help\".\n", | |
328 | argv[1]); | |
044ebf35 | 329 | return -1; |
aba5acdf SH |
330 | } |
331 | argc--; argv++; | |
332 | } | |
333 | ||
6e8634eb RM |
334 | _SL_ = oneline ? "\\" : "\n"; |
335 | ||
4d82962c | 336 | check_enable_color(color, json); |
2d165c08 | 337 | |
a3aa47a5 SH |
338 | if (batch_file) |
339 | return batch(batch_file); | |
08856f02 | 340 | |
c2f3a0f9 | 341 | if (argc <= 1) { |
342 | usage(); | |
343 | return 0; | |
344 | } | |
345 | ||
aba5acdf | 346 | tc_core_init(); |
7901660a SH |
347 | if (rtnl_open(&rth, 0) < 0) { |
348 | fprintf(stderr, "Cannot open rtnetlink\n"); | |
349 | exit(1); | |
350 | } | |
aba5acdf | 351 | |
4612d04d VK |
352 | if (use_names && cls_names_init(conf_file)) { |
353 | ret = -1; | |
354 | goto Exit; | |
355 | } | |
356 | ||
e991c04d | 357 | ret = do_cmd(argc-1, argv+1); |
4612d04d | 358 | Exit: |
7901660a | 359 | rtnl_close(&rth); |
c2f3a0f9 | 360 | |
4612d04d VK |
361 | if (use_names) |
362 | cls_names_uninit(); | |
363 | ||
c2f3a0f9 | 364 | return ret; |
aba5acdf | 365 | } |