]>
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> | |
19 | #include <netinet/in.h> | |
20 | #include <arpa/inet.h> | |
21 | #include <string.h> | |
22 | #include <math.h> | |
23 | ||
24 | #include "utils.h" | |
25 | #include "tc_util.h" | |
26 | ||
dbd90dc2 | 27 | int get_qdisc_handle(__u32 *h, const char *str) |
aba5acdf SH |
28 | { |
29 | __u32 maj; | |
30 | char *p; | |
31 | ||
32 | maj = TC_H_UNSPEC; | |
33 | if (strcmp(str, "none") == 0) | |
34 | goto ok; | |
35 | maj = strtoul(str, &p, 16); | |
36 | if (p == str) | |
37 | return -1; | |
38 | maj <<= 16; | |
39 | if (*p != ':' && *p!=0) | |
40 | return -1; | |
41 | ok: | |
42 | *h = maj; | |
43 | return 0; | |
44 | } | |
45 | ||
dbd90dc2 | 46 | int get_tc_classid(__u32 *h, const char *str) |
aba5acdf SH |
47 | { |
48 | __u32 maj, min; | |
49 | char *p; | |
50 | ||
51 | maj = TC_H_ROOT; | |
52 | if (strcmp(str, "root") == 0) | |
53 | goto ok; | |
54 | maj = TC_H_UNSPEC; | |
55 | if (strcmp(str, "none") == 0) | |
56 | goto ok; | |
57 | maj = strtoul(str, &p, 16); | |
58 | if (p == str) { | |
59 | maj = 0; | |
60 | if (*p != ':') | |
61 | return -1; | |
62 | } | |
63 | if (*p == ':') { | |
64 | maj <<= 16; | |
65 | str = p+1; | |
66 | min = strtoul(str, &p, 16); | |
67 | if (*p != 0) | |
68 | return -1; | |
69 | maj |= min; | |
70 | } else if (*p != 0) | |
71 | return -1; | |
72 | ||
73 | ok: | |
74 | *h = maj; | |
75 | return 0; | |
76 | } | |
77 | ||
78 | int print_tc_classid(char *buf, int len, __u32 h) | |
79 | { | |
80 | if (h == TC_H_ROOT) | |
81 | sprintf(buf, "root"); | |
82 | else if (h == TC_H_UNSPEC) | |
83 | snprintf(buf, len, "none"); | |
84 | else if (TC_H_MAJ(h) == 0) | |
85 | snprintf(buf, len, ":%x", TC_H_MIN(h)); | |
86 | else if (TC_H_MIN(h) == 0) | |
87 | snprintf(buf, len, "%x:", TC_H_MAJ(h)>>16); | |
88 | else | |
89 | snprintf(buf, len, "%x:%x", TC_H_MAJ(h)>>16, TC_H_MIN(h)); | |
90 | return 0; | |
91 | } | |
92 | ||
93 | char * sprint_tc_classid(__u32 h, char *buf) | |
94 | { | |
95 | if (print_tc_classid(buf, SPRINT_BSIZE-1, h)) | |
96 | strcpy(buf, "???"); | |
97 | return buf; | |
98 | } | |
99 | ||
26ab0b10 SH |
100 | /* See http://physics.nist.gov/cuu/Units/binary.html */ |
101 | static const struct rate_suffix { | |
102 | const char *name; | |
103 | double scale; | |
104 | } suffixes[] = { | |
105 | { "bit", 1. }, | |
106 | { "Kibit", 1024. }, | |
107 | { "kbit", 1000. }, | |
108 | { "mibit", 1024.*1024. }, | |
109 | { "mbit", 1000000. }, | |
110 | { "gibit", 1024.*1024.*1024. }, | |
111 | { "gbit", 1000000000. }, | |
112 | { "tibit", 1024.*1024.*1024.*1024. }, | |
113 | { "tbit", 1000000000000. }, | |
114 | { "Bps", 8. }, | |
115 | { "KiBps", 8.*1024. }, | |
116 | { "KBps", 8000. }, | |
117 | { "MiBps", 8.*1024*1024. }, | |
118 | { "MBps", 8000000. }, | |
119 | { "GiBps", 8.*1024.*1024.*1024. }, | |
120 | { "GBps", 8000000000. }, | |
121 | { "TiBps", 8.*1024.*1024.*1024.*1024. }, | |
122 | { "TBps", 8000000000000. }, | |
123 | { NULL } | |
124 | }; | |
125 | ||
126 | ||
dbd90dc2 | 127 | int get_rate(unsigned *rate, const char *str) |
aba5acdf SH |
128 | { |
129 | char *p; | |
130 | double bps = strtod(str, &p); | |
26ab0b10 | 131 | const struct rate_suffix *s; |
aba5acdf SH |
132 | |
133 | if (p == str) | |
134 | return -1; | |
135 | ||
26ab0b10 SH |
136 | if (*p == '\0') { |
137 | *rate = bps / 8.; /* assume bytes/sec */ | |
138 | return 0; | |
139 | } | |
aba5acdf | 140 | |
26ab0b10 SH |
141 | for (s = suffixes; s->name; ++s) { |
142 | if (strcasecmp(s->name, p) == 0) { | |
143 | *rate = (bps * s->scale) / 8.; | |
144 | return 0; | |
145 | } | |
146 | } | |
147 | ||
148 | return -1; | |
aba5acdf SH |
149 | } |
150 | ||
151 | int get_rate_and_cell(unsigned *rate, int *cell_log, char *str) | |
152 | { | |
153 | char * slash = strchr(str, '/'); | |
154 | ||
155 | if (slash) | |
156 | *slash = 0; | |
157 | ||
158 | if (get_rate(rate, str)) | |
159 | return -1; | |
160 | ||
161 | if (slash) { | |
162 | int cell; | |
163 | int i; | |
164 | ||
165 | if (get_integer(&cell, slash+1, 0)) | |
166 | return -1; | |
167 | *slash = '/'; | |
168 | ||
169 | for (i=0; i<32; i++) { | |
170 | if ((1<<i) == cell) { | |
171 | *cell_log = i; | |
172 | return 0; | |
173 | } | |
174 | } | |
175 | return -1; | |
176 | } | |
177 | return 0; | |
178 | } | |
179 | ||
d40b38b4 | 180 | void print_rate(char *buf, int len, __u32 rate) |
aba5acdf SH |
181 | { |
182 | double tmp = (double)rate*8; | |
d40b38b4 SH |
183 | extern int use_iec; |
184 | ||
185 | if (use_iec) { | |
abf1d0b0 SH |
186 | if (tmp >= 1000.0*1024.0*1024.0) |
187 | snprintf(buf, len, "%.0fMibit", tmp/1024.0*1024.0); | |
188 | else if (tmp >= 1000.0*1024) | |
189 | snprintf(buf, len, "%.0fKibit", tmp/1024); | |
d40b38b4 | 190 | else |
abf1d0b0 SH |
191 | snprintf(buf, len, "%.0fbit", tmp); |
192 | } else { | |
193 | if (tmp >= 1000.0*1000000.0) | |
194 | snprintf(buf, len, "%.0fMbit", tmp/1000000.0); | |
195 | else if (tmp >= 1000.0 * 1000.0) | |
196 | snprintf(buf, len, "%.0fKbit", tmp/1000.0); | |
d40b38b4 | 197 | else |
abf1d0b0 | 198 | snprintf(buf, len, "%.0fbit", tmp); |
d40b38b4 | 199 | } |
aba5acdf SH |
200 | } |
201 | ||
202 | char * sprint_rate(__u32 rate, char *buf) | |
203 | { | |
d40b38b4 | 204 | print_rate(buf, SPRINT_BSIZE-1, rate); |
aba5acdf SH |
205 | return buf; |
206 | } | |
207 | ||
dbd90dc2 | 208 | int get_usecs(unsigned *usecs, const char *str) |
aba5acdf SH |
209 | { |
210 | double t; | |
211 | char *p; | |
212 | ||
213 | t = strtod(str, &p); | |
214 | if (p == str) | |
215 | return -1; | |
216 | ||
217 | if (*p) { | |
218 | if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec")==0 || | |
219 | strcasecmp(p, "secs")==0) | |
220 | t *= 1000000; | |
221 | else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec")==0 || | |
222 | strcasecmp(p, "msecs") == 0) | |
223 | t *= 1000; | |
224 | else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec")==0 || | |
225 | strcasecmp(p, "usecs") == 0) | |
226 | t *= 1; | |
227 | else | |
228 | return -1; | |
229 | } | |
230 | ||
231 | *usecs = t; | |
232 | return 0; | |
233 | } | |
234 | ||
235 | ||
d40b38b4 | 236 | void print_usecs(char *buf, int len, __u32 usec) |
aba5acdf SH |
237 | { |
238 | double tmp = usec; | |
239 | ||
240 | if (tmp >= 1000000) | |
241 | snprintf(buf, len, "%.1fs", tmp/1000000); | |
242 | else if (tmp >= 1000) | |
243 | snprintf(buf, len, "%.1fms", tmp/1000); | |
244 | else | |
245 | snprintf(buf, len, "%uus", usec); | |
aba5acdf SH |
246 | } |
247 | ||
248 | char * sprint_usecs(__u32 usecs, char *buf) | |
249 | { | |
d40b38b4 | 250 | print_usecs(buf, SPRINT_BSIZE-1, usecs); |
aba5acdf SH |
251 | return buf; |
252 | } | |
253 | ||
dbd90dc2 | 254 | int get_size(unsigned *size, const char *str) |
aba5acdf SH |
255 | { |
256 | double sz; | |
257 | char *p; | |
258 | ||
259 | sz = strtod(str, &p); | |
260 | if (p == str) | |
261 | return -1; | |
262 | ||
263 | if (*p) { | |
264 | if (strcasecmp(p, "kb") == 0 || strcasecmp(p, "k")==0) | |
265 | sz *= 1024; | |
dbd90dc2 SH |
266 | else if (strcasecmp(p, "gb") == 0 || strcasecmp(p, "g")==0) |
267 | sz *= 1024*1024*1024; | |
268 | else if (strcasecmp(p, "gbit") == 0) | |
269 | sz *= 1024*1024*1024/8; | |
aba5acdf SH |
270 | else if (strcasecmp(p, "mb") == 0 || strcasecmp(p, "m")==0) |
271 | sz *= 1024*1024; | |
272 | else if (strcasecmp(p, "mbit") == 0) | |
273 | sz *= 1024*1024/8; | |
274 | else if (strcasecmp(p, "kbit") == 0) | |
275 | sz *= 1024/8; | |
276 | else if (strcasecmp(p, "b") != 0) | |
277 | return -1; | |
278 | } | |
279 | ||
280 | *size = sz; | |
281 | return 0; | |
282 | } | |
283 | ||
284 | int get_size_and_cell(unsigned *size, int *cell_log, char *str) | |
285 | { | |
286 | char * slash = strchr(str, '/'); | |
287 | ||
288 | if (slash) | |
289 | *slash = 0; | |
290 | ||
291 | if (get_size(size, str)) | |
292 | return -1; | |
293 | ||
294 | if (slash) { | |
295 | int cell; | |
296 | int i; | |
297 | ||
298 | if (get_integer(&cell, slash+1, 0)) | |
299 | return -1; | |
300 | *slash = '/'; | |
301 | ||
302 | for (i=0; i<32; i++) { | |
303 | if ((1<<i) == cell) { | |
304 | *cell_log = i; | |
305 | return 0; | |
306 | } | |
307 | } | |
308 | return -1; | |
309 | } | |
310 | return 0; | |
311 | } | |
312 | ||
d40b38b4 | 313 | void print_size(char *buf, int len, __u32 sz) |
aba5acdf SH |
314 | { |
315 | double tmp = sz; | |
316 | ||
317 | if (sz >= 1024*1024 && fabs(1024*1024*rint(tmp/(1024*1024)) - sz) < 1024) | |
318 | snprintf(buf, len, "%gMb", rint(tmp/(1024*1024))); | |
319 | else if (sz >= 1024 && fabs(1024*rint(tmp/1024) - sz) < 16) | |
320 | snprintf(buf, len, "%gKb", rint(tmp/1024)); | |
321 | else | |
322 | snprintf(buf, len, "%ub", sz); | |
aba5acdf SH |
323 | } |
324 | ||
325 | char * sprint_size(__u32 size, char *buf) | |
326 | { | |
d40b38b4 | 327 | print_size(buf, SPRINT_BSIZE-1, size); |
aba5acdf SH |
328 | return buf; |
329 | } | |
330 | ||
25f86909 | 331 | static const double max_percent_value = 0xffffffff; |
63e989f5 SH |
332 | |
333 | int get_percent(__u32 *percent, const char *str) | |
334 | { | |
335 | char *p; | |
25f86909 | 336 | double per = strtod(str, &p) / 100.; |
63e989f5 | 337 | |
25f86909 | 338 | if (per > 1. || per < 0) |
63e989f5 SH |
339 | return -1; |
340 | if (*p && strcmp(p, "%")) | |
341 | return -1; | |
342 | ||
25f86909 | 343 | *percent = (unsigned) rint(per * max_percent_value); |
63e989f5 SH |
344 | return 0; |
345 | } | |
346 | ||
d40b38b4 | 347 | void print_percent(char *buf, int len, __u32 per) |
63e989f5 | 348 | { |
25f86909 | 349 | snprintf(buf, len, "%g%%", 100. * (double) per / max_percent_value); |
63e989f5 SH |
350 | } |
351 | ||
352 | char * sprint_percent(__u32 per, char *buf) | |
353 | { | |
d40b38b4 | 354 | print_percent(buf, SPRINT_BSIZE-1, per); |
63e989f5 SH |
355 | return buf; |
356 | } | |
357 | ||
d40b38b4 | 358 | void print_qdisc_handle(char *buf, int len, __u32 h) |
aba5acdf SH |
359 | { |
360 | snprintf(buf, len, "%x:", TC_H_MAJ(h)>>16); | |
aba5acdf SH |
361 | } |
362 | ||
363 | char * sprint_qdisc_handle(__u32 h, char *buf) | |
364 | { | |
d40b38b4 | 365 | print_qdisc_handle(buf, SPRINT_BSIZE-1, h); |
aba5acdf SH |
366 | return buf; |
367 | } | |
368 | ||
2373fde9 SH |
369 | char * action_n2a(int action, char *buf, int len) |
370 | { | |
371 | switch (action) { | |
372 | case -1: | |
373 | return "continue"; | |
374 | break; | |
375 | case TC_ACT_OK: | |
376 | return "pass"; | |
377 | break; | |
378 | case TC_ACT_SHOT: | |
379 | return "drop"; | |
380 | break; | |
381 | case TC_ACT_RECLASSIFY: | |
382 | return "reclassify"; | |
383 | case TC_ACT_PIPE: | |
384 | return "pipe"; | |
385 | case TC_ACT_STOLEN: | |
386 | return "stolen"; | |
387 | default: | |
388 | snprintf(buf, len, "%d", action); | |
389 | return buf; | |
390 | } | |
391 | } | |
aba5acdf | 392 | |
2373fde9 SH |
393 | int action_a2n(char *arg, int *result) |
394 | { | |
395 | int res; | |
396 | ||
397 | if (matches(arg, "continue") == 0) | |
398 | res = -1; | |
399 | else if (matches(arg, "drop") == 0) | |
400 | res = TC_ACT_SHOT; | |
401 | else if (matches(arg, "shot") == 0) | |
402 | res = TC_ACT_SHOT; | |
403 | else if (matches(arg, "pass") == 0) | |
404 | res = TC_ACT_OK; | |
405 | else if (strcmp(arg, "ok") == 0) | |
406 | res = TC_ACT_OK; | |
407 | else if (matches(arg, "reclassify") == 0) | |
408 | res = TC_ACT_RECLASSIFY; | |
409 | else { | |
410 | char dummy; | |
411 | if (sscanf(arg, "%d%c", &res, &dummy) != 1) | |
412 | return -1; | |
413 | } | |
414 | *result = res; | |
415 | return 0; | |
416 | } | |
417 | ||
6dc9f016 | 418 | void print_tm(FILE * f, const struct tcf_t *tm) |
2373fde9 SH |
419 | { |
420 | int hz = get_hz(); | |
421 | if (tm->install != 0) | |
422 | fprintf(f, " installed %d sec", tm->install/hz); | |
423 | if (tm->lastuse != 0) | |
424 | fprintf(f, " used %d sec", tm->lastuse/hz); | |
425 | if (tm->expires != 0) | |
426 | fprintf(f, " expires %d sec", tm->expires/hz); | |
427 | } | |
e5879dc6 | 428 | |
429 | void print_tcstats2_attr(FILE *fp, struct rtattr *rta, char *prefix, struct rtattr **xstats) | |
430 | { | |
431 | SPRINT_BUF(b1); | |
432 | struct rtattr *tbs[TCA_STATS_MAX + 1] = {0}; | |
433 | ||
434 | parse_rtattr(tbs, TCA_STATS_MAX, RTA_DATA(rta), RTA_PAYLOAD(rta)); | |
435 | ||
436 | if (tbs[TCA_STATS_BASIC]) { | |
437 | struct gnet_stats_basic bs = {0}; | |
438 | memcpy(&bs, RTA_DATA(tbs[TCA_STATS_BASIC]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC]), sizeof(bs))); | |
439 | fprintf(fp, "%sSent %llu bytes %u pkt", | |
b906243b | 440 | prefix, (unsigned long long) bs.bytes, bs.packets); |
e5879dc6 | 441 | } |
442 | ||
443 | if (tbs[TCA_STATS_QUEUE]) { | |
444 | struct gnet_stats_queue q = {0}; | |
445 | memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q))); | |
446 | fprintf(fp, " (dropped %u, overlimits %u requeues %u) ", | |
447 | q.drops, q.overlimits, q.requeues); | |
448 | } | |
449 | ||
450 | if (tbs[TCA_STATS_RATE_EST]) { | |
451 | struct gnet_stats_rate_est re = {0}; | |
452 | memcpy(&re, RTA_DATA(tbs[TCA_STATS_RATE_EST]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_RATE_EST]), sizeof(re))); | |
453 | fprintf(fp, "\n%srate %s %upps ", | |
454 | prefix, sprint_rate(re.bps, b1), re.pps); | |
455 | } | |
456 | ||
457 | if (tbs[TCA_STATS_QUEUE]) { | |
458 | struct gnet_stats_queue q = {0}; | |
459 | memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q))); | |
460 | if (!tbs[TCA_STATS_RATE_EST]) | |
461 | fprintf(fp, "\n%s", prefix); | |
462 | fprintf(fp, "backlog %s %up requeues %u ", | |
463 | sprint_size(q.backlog, b1), q.qlen, q.requeues); | |
464 | } | |
465 | ||
466 | if (xstats) | |
467 | *xstats = tbs[TCA_STATS_APP] ? : NULL; | |
468 | } | |
469 | ||
470 | void print_tcstats_attr(FILE *fp, struct rtattr *tb[], char *prefix, struct rtattr **xstats) | |
471 | { | |
472 | SPRINT_BUF(b1); | |
473 | ||
474 | if (tb[TCA_STATS2]) { | |
475 | print_tcstats2_attr(fp, tb[TCA_STATS2], prefix, xstats); | |
476 | if (xstats && NULL == *xstats) | |
477 | goto compat_xstats; | |
478 | return; | |
479 | } | |
480 | /* backward compatibility */ | |
481 | if (tb[TCA_STATS]) { | |
482 | struct tc_stats st; | |
483 | ||
484 | /* handle case where kernel returns more/less than we know about */ | |
485 | memset(&st, 0, sizeof(st)); | |
486 | memcpy(&st, RTA_DATA(tb[TCA_STATS]), MIN(RTA_PAYLOAD(tb[TCA_STATS]), sizeof(st))); | |
487 | ||
488 | fprintf(fp, "%sSent %llu bytes %u pkts (dropped %u, overlimits %u) ", | |
489 | prefix, (unsigned long long)st.bytes, st.packets, st.drops, | |
490 | st.overlimits); | |
491 | ||
492 | if (st.bps || st.pps || st.qlen || st.backlog) { | |
493 | fprintf(fp, "\n%s", prefix); | |
494 | if (st.bps || st.pps) { | |
495 | fprintf(fp, "rate "); | |
496 | if (st.bps) | |
497 | fprintf(fp, "%s ", sprint_rate(st.bps, b1)); | |
498 | if (st.pps) | |
499 | fprintf(fp, "%upps ", st.pps); | |
500 | } | |
501 | if (st.qlen || st.backlog) { | |
502 | fprintf(fp, "backlog "); | |
503 | if (st.backlog) | |
504 | fprintf(fp, "%s ", sprint_size(st.backlog, b1)); | |
505 | if (st.qlen) | |
506 | fprintf(fp, "%up ", st.qlen); | |
507 | } | |
508 | } | |
509 | } | |
510 | ||
511 | compat_xstats: | |
512 | if (tb[TCA_XSTATS] && xstats) | |
513 | *xstats = tb[TCA_XSTATS]; | |
514 | } | |
515 |