]>
Commit | Line | Data |
---|---|---|
aba5acdf SH |
1 | /* |
2 | * tc_util.c Misc TC utility functions. | |
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 | */ | |
12 | ||
13 | #include <stdio.h> | |
14 | #include <stdlib.h> | |
15 | #include <unistd.h> | |
16 | #include <syslog.h> | |
17 | #include <fcntl.h> | |
18 | #include <sys/socket.h> | |
dd9cc0ee | 19 | #include <sys/param.h> |
aba5acdf SH |
20 | #include <netinet/in.h> |
21 | #include <arpa/inet.h> | |
22 | #include <string.h> | |
23 | #include <math.h> | |
8b90a990 | 24 | #include <errno.h> |
aba5acdf SH |
25 | |
26 | #include "utils.h" | |
4612d04d | 27 | #include "names.h" |
aba5acdf | 28 | #include "tc_util.h" |
4612d04d | 29 | #include "tc_common.h" |
aba5acdf | 30 | |
5e3bb534 | 31 | #ifndef LIBDIR |
5c434a9e | 32 | #define LIBDIR "/usr/lib" |
b514b358 RA |
33 | #endif |
34 | ||
32a121cb | 35 | static struct db_names *cls_names; |
4612d04d | 36 | |
8b90a990 | 37 | #define NAMES_DB "/etc/iproute2/tc_cls" |
4612d04d VK |
38 | |
39 | int cls_names_init(char *path) | |
40 | { | |
8b90a990 VK |
41 | int ret; |
42 | ||
43 | cls_names = db_names_alloc(); | |
44 | if (!cls_names) | |
45 | return -1; | |
46 | ||
47 | ret = db_names_load(cls_names, path ?: NAMES_DB); | |
48 | if (ret == -ENOENT && path) { | |
49 | fprintf(stderr, "Can't open class names file: %s\n", path); | |
4612d04d VK |
50 | return -1; |
51 | } | |
8b90a990 VK |
52 | if (ret) { |
53 | db_names_free(cls_names); | |
54 | cls_names = NULL; | |
55 | } | |
4612d04d VK |
56 | |
57 | return 0; | |
58 | } | |
59 | ||
60 | void cls_names_uninit(void) | |
61 | { | |
62 | db_names_free(cls_names); | |
63 | } | |
64 | ||
aa27f88c SH |
65 | const char *get_tc_lib(void) |
66 | { | |
67 | const char *lib_dir; | |
68 | ||
69 | lib_dir = getenv("TC_LIB_DIR"); | |
70 | if (!lib_dir) | |
5e3bb534 | 71 | lib_dir = LIBDIR "/tc/"; |
aa27f88c SH |
72 | |
73 | return lib_dir; | |
74 | } | |
75 | ||
dbd90dc2 | 76 | int get_qdisc_handle(__u32 *h, const char *str) |
aba5acdf SH |
77 | { |
78 | __u32 maj; | |
79 | char *p; | |
80 | ||
81 | maj = TC_H_UNSPEC; | |
82 | if (strcmp(str, "none") == 0) | |
83 | goto ok; | |
84 | maj = strtoul(str, &p, 16); | |
087dec7f | 85 | if (p == str || maj >= (1 << 16)) |
aba5acdf SH |
86 | return -1; |
87 | maj <<= 16; | |
32a121cb | 88 | if (*p != ':' && *p != 0) |
aba5acdf SH |
89 | return -1; |
90 | ok: | |
91 | *h = maj; | |
92 | return 0; | |
93 | } | |
94 | ||
dbd90dc2 | 95 | int get_tc_classid(__u32 *h, const char *str) |
aba5acdf SH |
96 | { |
97 | __u32 maj, min; | |
98 | char *p; | |
99 | ||
100 | maj = TC_H_ROOT; | |
101 | if (strcmp(str, "root") == 0) | |
102 | goto ok; | |
103 | maj = TC_H_UNSPEC; | |
104 | if (strcmp(str, "none") == 0) | |
105 | goto ok; | |
106 | maj = strtoul(str, &p, 16); | |
107 | if (p == str) { | |
108 | maj = 0; | |
109 | if (*p != ':') | |
110 | return -1; | |
111 | } | |
112 | if (*p == ':') { | |
a8b303cc SH |
113 | if (maj >= (1<<16)) |
114 | return -1; | |
aba5acdf SH |
115 | maj <<= 16; |
116 | str = p+1; | |
117 | min = strtoul(str, &p, 16); | |
118 | if (*p != 0) | |
119 | return -1; | |
a8b303cc SH |
120 | if (min >= (1<<16)) |
121 | return -1; | |
aba5acdf SH |
122 | maj |= min; |
123 | } else if (*p != 0) | |
124 | return -1; | |
125 | ||
126 | ok: | |
127 | *h = maj; | |
128 | return 0; | |
129 | } | |
130 | ||
46679bbb | 131 | int print_tc_classid(char *buf, int blen, __u32 h) |
aba5acdf | 132 | { |
46679bbb VK |
133 | SPRINT_BUF(handle) = {}; |
134 | int hlen = SPRINT_BSIZE - 1; | |
4612d04d | 135 | |
aba5acdf | 136 | if (h == TC_H_ROOT) |
4612d04d | 137 | sprintf(handle, "root"); |
aba5acdf | 138 | else if (h == TC_H_UNSPEC) |
46679bbb | 139 | snprintf(handle, hlen, "none"); |
aba5acdf | 140 | else if (TC_H_MAJ(h) == 0) |
46679bbb | 141 | snprintf(handle, hlen, ":%x", TC_H_MIN(h)); |
aba5acdf | 142 | else if (TC_H_MIN(h) == 0) |
46679bbb | 143 | snprintf(handle, hlen, "%x:", TC_H_MAJ(h) >> 16); |
aba5acdf | 144 | else |
46679bbb | 145 | snprintf(handle, hlen, "%x:%x", TC_H_MAJ(h) >> 16, TC_H_MIN(h)); |
4612d04d VK |
146 | |
147 | if (use_names) { | |
148 | char clname[IDNAME_MAX] = {}; | |
149 | ||
150 | if (id_to_name(cls_names, h, clname)) | |
46679bbb | 151 | snprintf(buf, blen, "%s#%s", clname, handle); |
4612d04d | 152 | else |
46679bbb | 153 | snprintf(buf, blen, "%s", handle); |
4612d04d | 154 | } else { |
46679bbb | 155 | snprintf(buf, blen, "%s", handle); |
4612d04d VK |
156 | } |
157 | ||
aba5acdf SH |
158 | return 0; |
159 | } | |
160 | ||
4612d04d | 161 | char *sprint_tc_classid(__u32 h, char *buf) |
aba5acdf SH |
162 | { |
163 | if (print_tc_classid(buf, SPRINT_BSIZE-1, h)) | |
164 | strcpy(buf, "???"); | |
165 | return buf; | |
166 | } | |
167 | ||
26ab0b10 SH |
168 | /* See http://physics.nist.gov/cuu/Units/binary.html */ |
169 | static const struct rate_suffix { | |
170 | const char *name; | |
171 | double scale; | |
172 | } suffixes[] = { | |
173 | { "bit", 1. }, | |
174 | { "Kibit", 1024. }, | |
175 | { "kbit", 1000. }, | |
176 | { "mibit", 1024.*1024. }, | |
177 | { "mbit", 1000000. }, | |
178 | { "gibit", 1024.*1024.*1024. }, | |
179 | { "gbit", 1000000000. }, | |
180 | { "tibit", 1024.*1024.*1024.*1024. }, | |
181 | { "tbit", 1000000000000. }, | |
182 | { "Bps", 8. }, | |
183 | { "KiBps", 8.*1024. }, | |
184 | { "KBps", 8000. }, | |
185 | { "MiBps", 8.*1024*1024. }, | |
186 | { "MBps", 8000000. }, | |
187 | { "GiBps", 8.*1024.*1024.*1024. }, | |
188 | { "GBps", 8000000000. }, | |
189 | { "TiBps", 8.*1024.*1024.*1024.*1024. }, | |
190 | { "TBps", 8000000000000. }, | |
191 | { NULL } | |
192 | }; | |
193 | ||
194 | ||
32a121cb | 195 | int get_rate(unsigned int *rate, const char *str) |
aba5acdf SH |
196 | { |
197 | char *p; | |
198 | double bps = strtod(str, &p); | |
26ab0b10 | 199 | const struct rate_suffix *s; |
aba5acdf SH |
200 | |
201 | if (p == str) | |
202 | return -1; | |
203 | ||
26ab0b10 SH |
204 | for (s = suffixes; s->name; ++s) { |
205 | if (strcasecmp(s->name, p) == 0) { | |
a303853e ED |
206 | bps *= s->scale; |
207 | p += strlen(p); | |
208 | break; | |
26ab0b10 SH |
209 | } |
210 | } | |
211 | ||
a303853e ED |
212 | if (*p) |
213 | return -1; /* unknown suffix */ | |
214 | ||
215 | bps /= 8; /* -> bytes per second */ | |
216 | *rate = bps; | |
217 | /* detect if an overflow happened */ | |
218 | if (*rate != floor(bps)) | |
219 | return -1; | |
220 | return 0; | |
aba5acdf SH |
221 | } |
222 | ||
8334bb32 ED |
223 | int get_rate64(__u64 *rate, const char *str) |
224 | { | |
225 | char *p; | |
226 | double bps = strtod(str, &p); | |
227 | const struct rate_suffix *s; | |
228 | ||
229 | if (p == str) | |
230 | return -1; | |
231 | ||
232 | for (s = suffixes; s->name; ++s) { | |
233 | if (strcasecmp(s->name, p) == 0) { | |
234 | bps *= s->scale; | |
235 | p += strlen(p); | |
236 | break; | |
237 | } | |
238 | } | |
239 | ||
240 | if (*p) | |
241 | return -1; /* unknown suffix */ | |
242 | ||
243 | bps /= 8; /* -> bytes per second */ | |
244 | *rate = bps; | |
245 | return 0; | |
246 | } | |
247 | ||
8f7574ed | 248 | void print_rate(char *buf, int len, __u64 rate) |
aba5acdf | 249 | { |
d40b38b4 | 250 | extern int use_iec; |
8cecdc28 ED |
251 | unsigned long kilo = use_iec ? 1024 : 1000; |
252 | const char *str = use_iec ? "i" : ""; | |
8cecdc28 | 253 | static char *units[5] = {"", "K", "M", "G", "T"}; |
ad1fe0d8 | 254 | int i; |
d40b38b4 | 255 | |
8cecdc28 ED |
256 | rate <<= 3; /* bytes/sec -> bits/sec */ |
257 | ||
ad1fe0d8 | 258 | for (i = 0; i < ARRAY_SIZE(units) - 1; i++) { |
8cecdc28 ED |
259 | if (rate < kilo) |
260 | break; | |
261 | if (((rate % kilo) != 0) && rate < 1000*kilo) | |
262 | break; | |
263 | rate /= kilo; | |
d40b38b4 | 264 | } |
ad1fe0d8 | 265 | |
8cecdc28 | 266 | snprintf(buf, len, "%.0f%s%sbit", (double)rate, units[i], str); |
aba5acdf SH |
267 | } |
268 | ||
32a121cb | 269 | char *sprint_rate(__u64 rate, char *buf) |
aba5acdf | 270 | { |
d40b38b4 | 271 | print_rate(buf, SPRINT_BSIZE-1, rate); |
aba5acdf SH |
272 | return buf; |
273 | } | |
274 | ||
32a121cb | 275 | int get_time(unsigned int *time, const char *str) |
aba5acdf SH |
276 | { |
277 | double t; | |
278 | char *p; | |
279 | ||
280 | t = strtod(str, &p); | |
281 | if (p == str) | |
282 | return -1; | |
283 | ||
284 | if (*p) { | |
32a121cb SH |
285 | if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec") == 0 || |
286 | strcasecmp(p, "secs") == 0) | |
f0bda7e5 | 287 | t *= TIME_UNITS_PER_SEC; |
32a121cb | 288 | else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec") == 0 || |
aba5acdf | 289 | strcasecmp(p, "msecs") == 0) |
f0bda7e5 | 290 | t *= TIME_UNITS_PER_SEC/1000; |
32a121cb | 291 | else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec") == 0 || |
aba5acdf | 292 | strcasecmp(p, "usecs") == 0) |
f0bda7e5 | 293 | t *= TIME_UNITS_PER_SEC/1000000; |
aba5acdf SH |
294 | else |
295 | return -1; | |
296 | } | |
297 | ||
8f34caaf | 298 | *time = t; |
aba5acdf SH |
299 | return 0; |
300 | } | |
301 | ||
302 | ||
8f34caaf | 303 | void print_time(char *buf, int len, __u32 time) |
aba5acdf | 304 | { |
8f34caaf | 305 | double tmp = time; |
aba5acdf | 306 | |
f0bda7e5 PM |
307 | if (tmp >= TIME_UNITS_PER_SEC) |
308 | snprintf(buf, len, "%.1fs", tmp/TIME_UNITS_PER_SEC); | |
309 | else if (tmp >= TIME_UNITS_PER_SEC/1000) | |
310 | snprintf(buf, len, "%.1fms", tmp/(TIME_UNITS_PER_SEC/1000)); | |
aba5acdf | 311 | else |
89151447 | 312 | snprintf(buf, len, "%uus", time); |
aba5acdf SH |
313 | } |
314 | ||
32a121cb | 315 | char *sprint_time(__u32 time, char *buf) |
aba5acdf | 316 | { |
8f34caaf | 317 | print_time(buf, SPRINT_BSIZE-1, time); |
aba5acdf SH |
318 | return buf; |
319 | } | |
320 | ||
32a121cb | 321 | char *sprint_ticks(__u32 ticks, char *buf) |
bd29e35d PM |
322 | { |
323 | return sprint_time(tc_core_tick2time(ticks), buf); | |
324 | } | |
325 | ||
32a121cb | 326 | int get_size(unsigned int *size, const char *str) |
aba5acdf SH |
327 | { |
328 | double sz; | |
329 | char *p; | |
330 | ||
331 | sz = strtod(str, &p); | |
332 | if (p == str) | |
333 | return -1; | |
334 | ||
335 | if (*p) { | |
32a121cb | 336 | if (strcasecmp(p, "kb") == 0 || strcasecmp(p, "k") == 0) |
aba5acdf | 337 | sz *= 1024; |
32a121cb | 338 | else if (strcasecmp(p, "gb") == 0 || strcasecmp(p, "g") == 0) |
dbd90dc2 SH |
339 | sz *= 1024*1024*1024; |
340 | else if (strcasecmp(p, "gbit") == 0) | |
341 | sz *= 1024*1024*1024/8; | |
32a121cb | 342 | else if (strcasecmp(p, "mb") == 0 || strcasecmp(p, "m") == 0) |
aba5acdf SH |
343 | sz *= 1024*1024; |
344 | else if (strcasecmp(p, "mbit") == 0) | |
345 | sz *= 1024*1024/8; | |
346 | else if (strcasecmp(p, "kbit") == 0) | |
347 | sz *= 1024/8; | |
348 | else if (strcasecmp(p, "b") != 0) | |
349 | return -1; | |
350 | } | |
351 | ||
352 | *size = sz; | |
353 | return 0; | |
354 | } | |
355 | ||
32a121cb | 356 | int get_size_and_cell(unsigned int *size, int *cell_log, char *str) |
aba5acdf | 357 | { |
32a121cb | 358 | char *slash = strchr(str, '/'); |
aba5acdf SH |
359 | |
360 | if (slash) | |
361 | *slash = 0; | |
362 | ||
363 | if (get_size(size, str)) | |
364 | return -1; | |
365 | ||
366 | if (slash) { | |
367 | int cell; | |
368 | int i; | |
369 | ||
370 | if (get_integer(&cell, slash+1, 0)) | |
371 | return -1; | |
372 | *slash = '/'; | |
373 | ||
32a121cb | 374 | for (i = 0; i < 32; i++) { |
aba5acdf SH |
375 | if ((1<<i) == cell) { |
376 | *cell_log = i; | |
377 | return 0; | |
378 | } | |
379 | } | |
380 | return -1; | |
381 | } | |
382 | return 0; | |
383 | } | |
384 | ||
d40b38b4 | 385 | void print_size(char *buf, int len, __u32 sz) |
aba5acdf SH |
386 | { |
387 | double tmp = sz; | |
388 | ||
389 | if (sz >= 1024*1024 && fabs(1024*1024*rint(tmp/(1024*1024)) - sz) < 1024) | |
390 | snprintf(buf, len, "%gMb", rint(tmp/(1024*1024))); | |
391 | else if (sz >= 1024 && fabs(1024*rint(tmp/1024) - sz) < 16) | |
392 | snprintf(buf, len, "%gKb", rint(tmp/1024)); | |
393 | else | |
394 | snprintf(buf, len, "%ub", sz); | |
aba5acdf SH |
395 | } |
396 | ||
32a121cb | 397 | char *sprint_size(__u32 size, char *buf) |
aba5acdf | 398 | { |
d40b38b4 | 399 | print_size(buf, SPRINT_BSIZE-1, size); |
aba5acdf SH |
400 | return buf; |
401 | } | |
402 | ||
d40b38b4 | 403 | void print_qdisc_handle(char *buf, int len, __u32 h) |
aba5acdf SH |
404 | { |
405 | snprintf(buf, len, "%x:", TC_H_MAJ(h)>>16); | |
aba5acdf SH |
406 | } |
407 | ||
32a121cb | 408 | char *sprint_qdisc_handle(__u32 h, char *buf) |
aba5acdf | 409 | { |
d40b38b4 | 410 | print_qdisc_handle(buf, SPRINT_BSIZE-1, h); |
aba5acdf SH |
411 | return buf; |
412 | } | |
413 | ||
e67aba55 | 414 | static const char *action_n2a(int action) |
2373fde9 | 415 | { |
70932006 PS |
416 | static char buf[64]; |
417 | ||
d19f72f7 JP |
418 | if (TC_ACT_EXT_CMP(action, TC_ACT_GOTO_CHAIN)) |
419 | return "goto"; | |
2373fde9 | 420 | switch (action) { |
70932006 | 421 | case TC_ACT_UNSPEC: |
2373fde9 | 422 | return "continue"; |
2373fde9 SH |
423 | case TC_ACT_OK: |
424 | return "pass"; | |
2373fde9 SH |
425 | case TC_ACT_SHOT: |
426 | return "drop"; | |
2373fde9 SH |
427 | case TC_ACT_RECLASSIFY: |
428 | return "reclassify"; | |
429 | case TC_ACT_PIPE: | |
430 | return "pipe"; | |
431 | case TC_ACT_STOLEN: | |
432 | return "stolen"; | |
d5ebd6fd JP |
433 | case TC_ACT_TRAP: |
434 | return "trap"; | |
2373fde9 | 435 | default: |
70932006 | 436 | snprintf(buf, 64, "%d", action); |
2373fde9 SH |
437 | return buf; |
438 | } | |
439 | } | |
aba5acdf | 440 | |
53aadc52 PS |
441 | /* Convert action branch name into numeric format. |
442 | * | |
443 | * Parameters: | |
444 | * @arg - string to parse | |
445 | * @result - pointer to output variable | |
446 | * @allow_num - whether @arg may be in numeric format already | |
447 | * | |
448 | * In error case, returns -1 and does not touch @result. Otherwise returns 0. | |
449 | */ | |
e67aba55 | 450 | static int action_a2n(char *arg, int *result, bool allow_num) |
2373fde9 | 451 | { |
53aadc52 PS |
452 | int n; |
453 | char dummy; | |
454 | struct { | |
455 | const char *a; | |
456 | int n; | |
457 | } a2n[] = { | |
458 | {"continue", TC_ACT_UNSPEC}, | |
459 | {"drop", TC_ACT_SHOT}, | |
460 | {"shot", TC_ACT_SHOT}, | |
461 | {"pass", TC_ACT_OK}, | |
462 | {"ok", TC_ACT_OK}, | |
463 | {"reclassify", TC_ACT_RECLASSIFY}, | |
464 | {"pipe", TC_ACT_PIPE}, | |
d19f72f7 | 465 | {"goto", TC_ACT_GOTO_CHAIN}, |
d5ebd6fd | 466 | {"trap", TC_ACT_TRAP}, |
53aadc52 PS |
467 | { NULL }, |
468 | }, *iter; | |
469 | ||
470 | for (iter = a2n; iter->a; iter++) { | |
471 | if (matches(arg, iter->a) != 0) | |
472 | continue; | |
473 | *result = iter->n; | |
474 | return 0; | |
2373fde9 | 475 | } |
53aadc52 PS |
476 | if (!allow_num || sscanf(arg, "%d%c", &n, &dummy) != 1) |
477 | return -1; | |
478 | ||
479 | *result = n; | |
2373fde9 SH |
480 | return 0; |
481 | } | |
482 | ||
c794b7b1 JP |
483 | static int __parse_action_control(int *argc_p, char ***argv_p, int *result_p, |
484 | bool allow_num, bool ignore_a2n_miss) | |
e67aba55 JP |
485 | { |
486 | int argc = *argc_p; | |
487 | char **argv = *argv_p; | |
488 | int result; | |
489 | ||
490 | if (!argc) | |
491 | return -1; | |
492 | if (action_a2n(*argv, &result, allow_num) == -1) { | |
c794b7b1 JP |
493 | if (!ignore_a2n_miss) |
494 | fprintf(stderr, "Bad action type %s\n", *argv); | |
e67aba55 JP |
495 | return -1; |
496 | } | |
d19f72f7 JP |
497 | if (result == TC_ACT_GOTO_CHAIN) { |
498 | __u32 chain_index; | |
499 | ||
500 | NEXT_ARG(); | |
501 | if (matches(*argv, "chain") != 0) { | |
502 | fprintf(stderr, "\"chain index\" expected\n"); | |
503 | return -1; | |
504 | } | |
505 | NEXT_ARG(); | |
506 | if (get_u32(&chain_index, *argv, 10) || | |
507 | chain_index > TC_ACT_EXT_VAL_MASK) { | |
508 | fprintf(stderr, "Illegal \"chain index\"\n"); | |
509 | return -1; | |
510 | } | |
511 | result |= chain_index; | |
512 | } | |
e67aba55 JP |
513 | NEXT_ARG_FWD(); |
514 | *argc_p = argc; | |
515 | *argv_p = argv; | |
516 | *result_p = result; | |
517 | return 0; | |
518 | } | |
519 | ||
c794b7b1 JP |
520 | /* Parse action control including possible options. |
521 | * | |
522 | * Parameters: | |
523 | * @argc_p - pointer to argc to parse | |
524 | * @argv_p - pointer to argv to parse | |
525 | * @result_p - pointer to output variable | |
526 | * @allow_num - whether action may be in numeric format already | |
527 | * | |
528 | * In error case, returns -1 and does not touch @result_1p. Otherwise returns 0. | |
529 | */ | |
530 | int parse_action_control(int *argc_p, char ***argv_p, | |
531 | int *result_p, bool allow_num) | |
532 | { | |
533 | return __parse_action_control(argc_p, argv_p, result_p, | |
534 | allow_num, false); | |
535 | } | |
536 | ||
e67aba55 JP |
537 | /* Parse action control including possible options. |
538 | * | |
539 | * Parameters: | |
540 | * @argc_p - pointer to argc to parse | |
541 | * @argv_p - pointer to argv to parse | |
542 | * @result_p - pointer to output variable | |
543 | * @allow_num - whether action may be in numeric format already | |
544 | * @default_result - set as a result in case of parsing error | |
545 | * | |
546 | * In case there is an error during parsing, the default result is used. | |
547 | */ | |
548 | void parse_action_control_dflt(int *argc_p, char ***argv_p, | |
549 | int *result_p, bool allow_num, | |
550 | int default_result) | |
551 | { | |
c794b7b1 | 552 | if (__parse_action_control(argc_p, argv_p, result_p, allow_num, true)) |
e67aba55 JP |
553 | *result_p = default_result; |
554 | } | |
555 | ||
556 | static int parse_action_control_slash_spaces(int *argc_p, char ***argv_p, | |
557 | int *result1_p, int *result2_p, | |
558 | bool allow_num) | |
559 | { | |
560 | int argc = *argc_p; | |
561 | char **argv = *argv_p; | |
562 | int result1, result2; | |
563 | int *result_p = &result1; | |
564 | int ok = 0; | |
565 | int ret; | |
566 | ||
567 | while (argc > 0) { | |
568 | switch (ok) { | |
569 | case 1: | |
570 | if (strcmp(*argv, "/") != 0) | |
571 | goto out; | |
572 | result_p = &result2; | |
573 | NEXT_ARG(); | |
574 | /* fall-through */ | |
575 | case 0: /* fall-through */ | |
576 | case 2: | |
577 | ret = parse_action_control(&argc, &argv, | |
578 | result_p, allow_num); | |
579 | if (ret) | |
580 | return ret; | |
581 | ok++; | |
582 | break; | |
583 | default: | |
584 | goto out; | |
585 | } | |
586 | } | |
587 | out: | |
588 | *result1_p = result1; | |
589 | if (ok == 2) | |
590 | *result2_p = result2; | |
591 | *argc_p = argc; | |
592 | *argv_p = argv; | |
593 | return 0; | |
594 | } | |
595 | ||
596 | /* Parse action control with slash including possible options. | |
597 | * | |
598 | * Parameters: | |
599 | * @argc_p - pointer to argc to parse | |
600 | * @argv_p - pointer to argv to parse | |
601 | * @result1_p - pointer to the first (before slash) output variable | |
602 | * @result2_p - pointer to the second (after slash) output variable | |
603 | * @allow_num - whether action may be in numeric format already | |
604 | * | |
605 | * In error case, returns -1 and does not touch @result*. Otherwise returns 0. | |
606 | */ | |
607 | int parse_action_control_slash(int *argc_p, char ***argv_p, | |
608 | int *result1_p, int *result2_p, bool allow_num) | |
609 | { | |
610 | char **argv = *argv_p; | |
611 | int result1, result2; | |
612 | char *p = strchr(*argv, '/'); | |
613 | ||
614 | if (!p) | |
615 | return parse_action_control_slash_spaces(argc_p, argv_p, | |
616 | result1_p, result2_p, | |
617 | allow_num); | |
618 | *p = 0; | |
619 | if (action_a2n(*argv, &result1, allow_num)) { | |
620 | if (p) | |
621 | *p = '/'; | |
622 | return -1; | |
623 | } | |
624 | ||
625 | *p = '/'; | |
626 | if (action_a2n(p + 1, &result2, allow_num)) | |
627 | return -1; | |
628 | ||
629 | *result1_p = result1; | |
630 | *result2_p = result2; | |
631 | return 0; | |
632 | } | |
633 | ||
634 | void print_action_control(FILE *f, const char *prefix, | |
635 | int action, const char *suffix) | |
636 | { | |
d19f72f7 JP |
637 | fprintf(f, "%s%s", prefix, action_n2a(action)); |
638 | if (TC_ACT_EXT_CMP(action, TC_ACT_GOTO_CHAIN)) | |
639 | fprintf(f, " chain %u", action & TC_ACT_EXT_VAL_MASK); | |
640 | fprintf(f, "%s", suffix); | |
e67aba55 JP |
641 | } |
642 | ||
32a121cb | 643 | int get_linklayer(unsigned int *val, const char *arg) |
292f29b4 JDB |
644 | { |
645 | int res; | |
646 | ||
647 | if (matches(arg, "ethernet") == 0) | |
648 | res = LINKLAYER_ETHERNET; | |
649 | else if (matches(arg, "atm") == 0) | |
650 | res = LINKLAYER_ATM; | |
651 | else if (matches(arg, "adsl") == 0) | |
652 | res = LINKLAYER_ATM; | |
653 | else | |
654 | return -1; /* Indicate error */ | |
655 | ||
656 | *val = res; | |
657 | return 0; | |
658 | } | |
659 | ||
32a121cb | 660 | void print_linklayer(char *buf, int len, unsigned int linklayer) |
839c8456 JK |
661 | { |
662 | switch (linklayer) { | |
663 | case LINKLAYER_UNSPEC: | |
664 | snprintf(buf, len, "%s", "unspec"); | |
665 | return; | |
666 | case LINKLAYER_ETHERNET: | |
667 | snprintf(buf, len, "%s", "ethernet"); | |
668 | return; | |
669 | case LINKLAYER_ATM: | |
670 | snprintf(buf, len, "%s", "atm"); | |
671 | return; | |
672 | default: | |
673 | snprintf(buf, len, "%s", "unknown"); | |
674 | return; | |
675 | } | |
676 | } | |
677 | ||
32a121cb | 678 | char *sprint_linklayer(unsigned int linklayer, char *buf) |
839c8456 JK |
679 | { |
680 | print_linklayer(buf, SPRINT_BSIZE-1, linklayer); | |
681 | return buf; | |
682 | } | |
683 | ||
32a121cb | 684 | void print_tm(FILE *f, const struct tcf_t *tm) |
2373fde9 | 685 | { |
5e8bc631 | 686 | int hz = get_user_hz(); |
32a121cb | 687 | |
2373fde9 | 688 | if (tm->install != 0) |
32a121cb | 689 | fprintf(f, " installed %u sec", (unsigned int)(tm->install/hz)); |
2373fde9 | 690 | if (tm->lastuse != 0) |
32a121cb | 691 | fprintf(f, " used %u sec", (unsigned int)(tm->lastuse/hz)); |
2373fde9 | 692 | if (tm->expires != 0) |
32a121cb | 693 | fprintf(f, " expires %u sec", (unsigned int)(tm->expires/hz)); |
2373fde9 | 694 | } |
e5879dc6 | 695 | |
696 | void print_tcstats2_attr(FILE *fp, struct rtattr *rta, char *prefix, struct rtattr **xstats) | |
697 | { | |
698 | SPRINT_BUF(b1); | |
7d69fd97 | 699 | struct rtattr *tbs[TCA_STATS_MAX + 1]; |
e5879dc6 | 700 | |
7d69fd97 | 701 | parse_rtattr_nested(tbs, TCA_STATS_MAX, rta); |
e5879dc6 | 702 | |
703 | if (tbs[TCA_STATS_BASIC]) { | |
704 | struct gnet_stats_basic bs = {0}; | |
32a121cb | 705 | |
e5879dc6 | 706 | memcpy(&bs, RTA_DATA(tbs[TCA_STATS_BASIC]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC]), sizeof(bs))); |
707 | fprintf(fp, "%sSent %llu bytes %u pkt", | |
b906243b | 708 | prefix, (unsigned long long) bs.bytes, bs.packets); |
e5879dc6 | 709 | } |
710 | ||
711 | if (tbs[TCA_STATS_QUEUE]) { | |
712 | struct gnet_stats_queue q = {0}; | |
32a121cb | 713 | |
e5879dc6 | 714 | memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q))); |
715 | fprintf(fp, " (dropped %u, overlimits %u requeues %u) ", | |
716 | q.drops, q.overlimits, q.requeues); | |
717 | } | |
ae665a52 | 718 | |
8f7574ed ED |
719 | if (tbs[TCA_STATS_RATE_EST64]) { |
720 | struct gnet_stats_rate_est64 re = {0}; | |
721 | ||
722 | memcpy(&re, RTA_DATA(tbs[TCA_STATS_RATE_EST64]), | |
723 | MIN(RTA_PAYLOAD(tbs[TCA_STATS_RATE_EST64]), | |
724 | sizeof(re))); | |
725 | fprintf(fp, "\n%srate %s %llupps ", | |
726 | prefix, sprint_rate(re.bps, b1), re.pps); | |
727 | } else if (tbs[TCA_STATS_RATE_EST]) { | |
e5879dc6 | 728 | struct gnet_stats_rate_est re = {0}; |
8f7574ed ED |
729 | |
730 | memcpy(&re, RTA_DATA(tbs[TCA_STATS_RATE_EST]), | |
731 | MIN(RTA_PAYLOAD(tbs[TCA_STATS_RATE_EST]), sizeof(re))); | |
e5879dc6 | 732 | fprintf(fp, "\n%srate %s %upps ", |
733 | prefix, sprint_rate(re.bps, b1), re.pps); | |
734 | } | |
735 | ||
736 | if (tbs[TCA_STATS_QUEUE]) { | |
737 | struct gnet_stats_queue q = {0}; | |
32a121cb | 738 | |
e5879dc6 | 739 | memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q))); |
740 | if (!tbs[TCA_STATS_RATE_EST]) | |
741 | fprintf(fp, "\n%s", prefix); | |
742 | fprintf(fp, "backlog %s %up requeues %u ", | |
743 | sprint_size(q.backlog, b1), q.qlen, q.requeues); | |
744 | } | |
745 | ||
746 | if (xstats) | |
747 | *xstats = tbs[TCA_STATS_APP] ? : NULL; | |
748 | } | |
749 | ||
750 | void print_tcstats_attr(FILE *fp, struct rtattr *tb[], char *prefix, struct rtattr **xstats) | |
751 | { | |
752 | SPRINT_BUF(b1); | |
753 | ||
754 | if (tb[TCA_STATS2]) { | |
755 | print_tcstats2_attr(fp, tb[TCA_STATS2], prefix, xstats); | |
756 | if (xstats && NULL == *xstats) | |
757 | goto compat_xstats; | |
758 | return; | |
759 | } | |
760 | /* backward compatibility */ | |
761 | if (tb[TCA_STATS]) { | |
d17b136f | 762 | struct tc_stats st = {}; |
e5879dc6 | 763 | |
764 | /* handle case where kernel returns more/less than we know about */ | |
e5879dc6 | 765 | memcpy(&st, RTA_DATA(tb[TCA_STATS]), MIN(RTA_PAYLOAD(tb[TCA_STATS]), sizeof(st))); |
766 | ||
767 | fprintf(fp, "%sSent %llu bytes %u pkts (dropped %u, overlimits %u) ", | |
ae665a52 | 768 | prefix, (unsigned long long)st.bytes, st.packets, st.drops, |
e5879dc6 | 769 | st.overlimits); |
770 | ||
771 | if (st.bps || st.pps || st.qlen || st.backlog) { | |
772 | fprintf(fp, "\n%s", prefix); | |
773 | if (st.bps || st.pps) { | |
774 | fprintf(fp, "rate "); | |
775 | if (st.bps) | |
776 | fprintf(fp, "%s ", sprint_rate(st.bps, b1)); | |
777 | if (st.pps) | |
778 | fprintf(fp, "%upps ", st.pps); | |
779 | } | |
780 | if (st.qlen || st.backlog) { | |
781 | fprintf(fp, "backlog "); | |
782 | if (st.backlog) | |
783 | fprintf(fp, "%s ", sprint_size(st.backlog, b1)); | |
784 | if (st.qlen) | |
785 | fprintf(fp, "%up ", st.qlen); | |
786 | } | |
787 | } | |
788 | } | |
789 | ||
790 | compat_xstats: | |
791 | if (tb[TCA_XSTATS] && xstats) | |
792 | *xstats = tb[TCA_XSTATS]; | |
793 | } |