]>
Commit | Line | Data |
---|---|---|
714444c0 THJ |
1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
2 | ||
3 | /* | |
4 | * Common Applications Kept Enhanced -- CAKE | |
5 | * | |
6 | * Copyright (C) 2014-2018 Jonathan Morton <chromatix99@gmail.com> | |
7 | * Copyright (C) 2017-2018 Toke Høiland-Jørgensen <toke@toke.dk> | |
8 | */ | |
9 | ||
10 | #include <stddef.h> | |
11 | #include <stdio.h> | |
12 | #include <stdlib.h> | |
13 | #include <unistd.h> | |
14 | #include <syslog.h> | |
15 | #include <fcntl.h> | |
16 | #include <sys/socket.h> | |
17 | #include <netinet/in.h> | |
18 | #include <arpa/inet.h> | |
19 | #include <string.h> | |
20 | #include <inttypes.h> | |
21 | ||
22 | #include "utils.h" | |
23 | #include "tc_util.h" | |
24 | ||
25 | struct cake_preset { | |
26 | char *name; | |
27 | unsigned int target; | |
28 | unsigned int interval; | |
29 | }; | |
30 | ||
31 | static struct cake_preset presets[] = { | |
32 | {"datacentre", 5, 100}, | |
33 | {"lan", 50, 1000}, | |
34 | {"metro", 500, 10000}, | |
35 | {"regional", 1500, 30000}, | |
36 | {"internet", 5000, 100000}, | |
37 | {"oceanic", 15000, 300000}, | |
38 | {"satellite", 50000, 1000000}, | |
39 | {"interplanetary", 50000000, 1000000000}, | |
40 | }; | |
41 | ||
42 | static const char * diffserv_names[CAKE_DIFFSERV_MAX] = { | |
43 | [CAKE_DIFFSERV_DIFFSERV3] = "diffserv3", | |
44 | [CAKE_DIFFSERV_DIFFSERV4] = "diffserv4", | |
45 | [CAKE_DIFFSERV_DIFFSERV8] = "diffserv8", | |
46 | [CAKE_DIFFSERV_BESTEFFORT] = "besteffort", | |
47 | [CAKE_DIFFSERV_PRECEDENCE] = "precedence", | |
48 | }; | |
49 | ||
50 | static const char * flowmode_names[CAKE_FLOW_MAX] = { | |
51 | [CAKE_FLOW_NONE] = "flowblind", | |
52 | [CAKE_FLOW_SRC_IP] = "srchost", | |
53 | [CAKE_FLOW_DST_IP] = "dsthost", | |
54 | [CAKE_FLOW_HOSTS] = "hosts", | |
55 | [CAKE_FLOW_FLOWS] = "flows", | |
56 | [CAKE_FLOW_DUAL_SRC] = "dual-srchost", | |
57 | [CAKE_FLOW_DUAL_DST] = "dual-dsthost", | |
58 | [CAKE_FLOW_TRIPLE] = "triple-isolate", | |
59 | }; | |
60 | ||
61 | static struct cake_preset *find_preset(char *argv) | |
62 | { | |
63 | int i; | |
64 | ||
65 | for (i = 0; i < ARRAY_SIZE(presets); i++) | |
66 | if (!strcmp(argv, presets[i].name)) | |
67 | return &presets[i]; | |
68 | return NULL; | |
69 | } | |
70 | ||
71 | static void explain(void) | |
72 | { | |
73 | fprintf(stderr, | |
77c9fbd0 | 74 | "Usage: ... cake [ bandwidth RATE | unlimited* | autorate-ingress ]\n" |
714444c0 THJ |
75 | " [ rtt TIME | datacentre | lan | metro | regional |\n" |
76 | " internet* | oceanic | satellite | interplanetary ]\n" | |
77 | " [ besteffort | diffserv8 | diffserv4 | diffserv3* ]\n" | |
78 | " [ flowblind | srchost | dsthost | hosts | flows |\n" | |
79 | " dual-srchost | dual-dsthost | triple-isolate* ]\n" | |
80 | " [ nat | nonat* ]\n" | |
81 | " [ wash | nowash* ]\n" | |
82 | " [ ack-filter | ack-filter-aggressive | no-ack-filter* ]\n" | |
83 | " [ memlimit LIMIT ]\n" | |
84 | " [ ptm | atm | noatm* ] [ overhead N | conservative | raw* ]\n" | |
85 | " [ mpu N ] [ ingress | egress* ]\n" | |
86 | " (* marks defaults)\n"); | |
87 | } | |
88 | ||
89 | static int cake_parse_opt(struct qdisc_util *qu, int argc, char **argv, | |
90 | struct nlmsghdr *n, const char *dev) | |
91 | { | |
92 | struct cake_preset *preset, *preset_set = NULL; | |
93 | bool overhead_override = false; | |
94 | bool overhead_set = false; | |
95 | unsigned int interval = 0; | |
96 | unsigned int diffserv = 0; | |
97 | unsigned int memlimit = 0; | |
98 | unsigned int target = 0; | |
99 | __u64 bandwidth = 0; | |
100 | int ack_filter = -1; | |
101 | struct rtattr *tail; | |
102 | int unlimited = 0; | |
103 | int flowmode = -1; | |
104 | int autorate = -1; | |
105 | int ingress = -1; | |
106 | int overhead = 0; | |
107 | int wash = -1; | |
108 | int nat = -1; | |
109 | int atm = -1; | |
110 | int mpu = 0; | |
111 | ||
112 | while (argc > 0) { | |
113 | if (strcmp(*argv, "bandwidth") == 0) { | |
114 | NEXT_ARG(); | |
115 | if (get_rate64(&bandwidth, *argv)) { | |
116 | fprintf(stderr, "Illegal \"bandwidth\"\n"); | |
117 | return -1; | |
118 | } | |
119 | unlimited = 0; | |
120 | autorate = 0; | |
121 | } else if (strcmp(*argv, "unlimited") == 0) { | |
122 | bandwidth = 0; | |
123 | unlimited = 1; | |
124 | autorate = 0; | |
77c9fbd0 | 125 | } else if (strcmp(*argv, "autorate-ingress") == 0) { |
714444c0 THJ |
126 | autorate = 1; |
127 | } else if (strcmp(*argv, "rtt") == 0) { | |
128 | NEXT_ARG(); | |
129 | if (get_time(&interval, *argv)) { | |
130 | fprintf(stderr, "Illegal \"rtt\"\n"); | |
131 | return -1; | |
132 | } | |
133 | target = interval / 20; | |
134 | if (!target) | |
135 | target = 1; | |
136 | } else if ((preset = find_preset(*argv))) { | |
137 | if (preset_set) | |
138 | duparg(*argv, preset_set->name); | |
139 | preset_set = preset; | |
140 | target = preset->target; | |
141 | interval = preset->interval; | |
142 | } else if (strcmp(*argv, "besteffort") == 0) { | |
143 | diffserv = CAKE_DIFFSERV_BESTEFFORT; | |
144 | } else if (strcmp(*argv, "precedence") == 0) { | |
145 | diffserv = CAKE_DIFFSERV_PRECEDENCE; | |
146 | } else if (strcmp(*argv, "diffserv8") == 0) { | |
147 | diffserv = CAKE_DIFFSERV_DIFFSERV8; | |
148 | } else if (strcmp(*argv, "diffserv4") == 0) { | |
149 | diffserv = CAKE_DIFFSERV_DIFFSERV4; | |
150 | } else if (strcmp(*argv, "diffserv") == 0) { | |
151 | diffserv = CAKE_DIFFSERV_DIFFSERV4; | |
152 | } else if (strcmp(*argv, "diffserv3") == 0) { | |
153 | diffserv = CAKE_DIFFSERV_DIFFSERV3; | |
154 | } else if (strcmp(*argv, "nowash") == 0) { | |
155 | wash = 0; | |
156 | } else if (strcmp(*argv, "wash") == 0) { | |
157 | wash = 1; | |
158 | } else if (strcmp(*argv, "flowblind") == 0) { | |
159 | flowmode = CAKE_FLOW_NONE; | |
160 | } else if (strcmp(*argv, "srchost") == 0) { | |
161 | flowmode = CAKE_FLOW_SRC_IP; | |
162 | } else if (strcmp(*argv, "dsthost") == 0) { | |
163 | flowmode = CAKE_FLOW_DST_IP; | |
164 | } else if (strcmp(*argv, "hosts") == 0) { | |
165 | flowmode = CAKE_FLOW_HOSTS; | |
166 | } else if (strcmp(*argv, "flows") == 0) { | |
167 | flowmode = CAKE_FLOW_FLOWS; | |
168 | } else if (strcmp(*argv, "dual-srchost") == 0) { | |
169 | flowmode = CAKE_FLOW_DUAL_SRC; | |
170 | } else if (strcmp(*argv, "dual-dsthost") == 0) { | |
171 | flowmode = CAKE_FLOW_DUAL_DST; | |
172 | } else if (strcmp(*argv, "triple-isolate") == 0) { | |
173 | flowmode = CAKE_FLOW_TRIPLE; | |
174 | } else if (strcmp(*argv, "nat") == 0) { | |
175 | nat = 1; | |
176 | } else if (strcmp(*argv, "nonat") == 0) { | |
177 | nat = 0; | |
178 | } else if (strcmp(*argv, "ptm") == 0) { | |
179 | atm = CAKE_ATM_PTM; | |
180 | } else if (strcmp(*argv, "atm") == 0) { | |
181 | atm = CAKE_ATM_ATM; | |
182 | } else if (strcmp(*argv, "noatm") == 0) { | |
183 | atm = CAKE_ATM_NONE; | |
184 | } else if (strcmp(*argv, "raw") == 0) { | |
185 | atm = CAKE_ATM_NONE; | |
186 | overhead = 0; | |
187 | overhead_set = true; | |
188 | overhead_override = true; | |
189 | } else if (strcmp(*argv, "conservative") == 0) { | |
190 | /* | |
191 | * Deliberately over-estimate overhead: | |
192 | * one whole ATM cell plus ATM framing. | |
193 | * A safe choice if the actual overhead is unknown. | |
194 | */ | |
195 | atm = CAKE_ATM_ATM; | |
196 | overhead = 48; | |
197 | overhead_set = true; | |
198 | ||
199 | /* Various ADSL framing schemes, all over ATM cells */ | |
200 | } else if (strcmp(*argv, "ipoa-vcmux") == 0) { | |
201 | atm = CAKE_ATM_ATM; | |
202 | overhead += 8; | |
203 | overhead_set = true; | |
204 | } else if (strcmp(*argv, "ipoa-llcsnap") == 0) { | |
205 | atm = CAKE_ATM_ATM; | |
206 | overhead += 16; | |
207 | overhead_set = true; | |
208 | } else if (strcmp(*argv, "bridged-vcmux") == 0) { | |
209 | atm = CAKE_ATM_ATM; | |
210 | overhead += 24; | |
211 | overhead_set = true; | |
212 | } else if (strcmp(*argv, "bridged-llcsnap") == 0) { | |
213 | atm = CAKE_ATM_ATM; | |
214 | overhead += 32; | |
215 | overhead_set = true; | |
216 | } else if (strcmp(*argv, "pppoa-vcmux") == 0) { | |
217 | atm = CAKE_ATM_ATM; | |
218 | overhead += 10; | |
219 | overhead_set = true; | |
220 | } else if (strcmp(*argv, "pppoa-llc") == 0) { | |
221 | atm = CAKE_ATM_ATM; | |
222 | overhead += 14; | |
223 | overhead_set = true; | |
224 | } else if (strcmp(*argv, "pppoe-vcmux") == 0) { | |
225 | atm = CAKE_ATM_ATM; | |
226 | overhead += 32; | |
227 | overhead_set = true; | |
228 | } else if (strcmp(*argv, "pppoe-llcsnap") == 0) { | |
229 | atm = CAKE_ATM_ATM; | |
230 | overhead += 40; | |
231 | overhead_set = true; | |
232 | ||
233 | /* Typical VDSL2 framing schemes, both over PTM */ | |
234 | /* PTM has 64b/65b coding which absorbs some bandwidth */ | |
235 | } else if (strcmp(*argv, "pppoe-ptm") == 0) { | |
236 | /* 2B PPP + 6B PPPoE + 6B dest MAC + 6B src MAC | |
237 | * + 2B ethertype + 4B Frame Check Sequence | |
238 | * + 1B Start of Frame (S) + 1B End of Frame (Ck) | |
239 | * + 2B TC-CRC (PTM-FCS) = 30B | |
240 | */ | |
241 | atm = CAKE_ATM_PTM; | |
242 | overhead += 30; | |
243 | overhead_set = true; | |
244 | } else if (strcmp(*argv, "bridged-ptm") == 0) { | |
245 | /* 6B dest MAC + 6B src MAC + 2B ethertype | |
246 | * + 4B Frame Check Sequence | |
247 | * + 1B Start of Frame (S) + 1B End of Frame (Ck) | |
248 | * + 2B TC-CRC (PTM-FCS) = 22B | |
249 | */ | |
250 | atm = CAKE_ATM_PTM; | |
251 | overhead += 22; | |
252 | overhead_set = true; | |
253 | } else if (strcmp(*argv, "via-ethernet") == 0) { | |
254 | /* | |
255 | * We used to use this flag to manually compensate for | |
256 | * Linux including the Ethernet header on Ethernet-type | |
257 | * interfaces, but not on IP-type interfaces. | |
258 | * | |
259 | * It is no longer needed, because Cake now adjusts for | |
260 | * that automatically, and is thus ignored. | |
261 | * | |
262 | * It would be deleted entirely, but it appears in the | |
263 | * stats output when the automatic compensation is | |
264 | * active. | |
265 | */ | |
266 | } else if (strcmp(*argv, "ethernet") == 0) { | |
267 | /* ethernet pre-amble & interframe gap & FCS | |
268 | * you may need to add vlan tag | |
269 | */ | |
270 | overhead += 38; | |
271 | overhead_set = true; | |
272 | mpu = 84; | |
273 | ||
274 | /* Additional Ethernet-related overhead used by some ISPs */ | |
275 | } else if (strcmp(*argv, "ether-vlan") == 0) { | |
276 | /* 802.1q VLAN tag - may be repeated */ | |
277 | overhead += 4; | |
278 | overhead_set = true; | |
279 | ||
280 | /* | |
281 | * DOCSIS cable shapers account for Ethernet frame with FCS, | |
282 | * but not interframe gap or preamble. | |
283 | */ | |
284 | } else if (strcmp(*argv, "docsis") == 0) { | |
285 | atm = CAKE_ATM_NONE; | |
286 | overhead += 18; | |
287 | overhead_set = true; | |
288 | mpu = 64; | |
289 | } else if (strcmp(*argv, "overhead") == 0) { | |
290 | char *p = NULL; | |
291 | ||
292 | NEXT_ARG(); | |
293 | overhead = strtol(*argv, &p, 10); | |
294 | if (!p || *p || !*argv || | |
295 | overhead < -64 || overhead > 256) { | |
296 | fprintf(stderr, | |
297 | "Illegal \"overhead\", valid range is -64 to 256\\n"); | |
298 | return -1; | |
299 | } | |
300 | overhead_set = true; | |
301 | ||
302 | } else if (strcmp(*argv, "mpu") == 0) { | |
303 | char *p = NULL; | |
304 | ||
305 | NEXT_ARG(); | |
306 | mpu = strtol(*argv, &p, 10); | |
307 | if (!p || *p || !*argv || mpu < 0 || mpu > 256) { | |
308 | fprintf(stderr, | |
309 | "Illegal \"mpu\", valid range is 0 to 256\\n"); | |
310 | return -1; | |
311 | } | |
312 | } else if (strcmp(*argv, "ingress") == 0) { | |
313 | ingress = 1; | |
314 | } else if (strcmp(*argv, "egress") == 0) { | |
315 | ingress = 0; | |
316 | } else if (strcmp(*argv, "no-ack-filter") == 0) { | |
317 | ack_filter = CAKE_ACK_NONE; | |
318 | } else if (strcmp(*argv, "ack-filter") == 0) { | |
319 | ack_filter = CAKE_ACK_FILTER; | |
320 | } else if (strcmp(*argv, "ack-filter-aggressive") == 0) { | |
321 | ack_filter = CAKE_ACK_AGGRESSIVE; | |
322 | } else if (strcmp(*argv, "memlimit") == 0) { | |
323 | NEXT_ARG(); | |
324 | if (get_size(&memlimit, *argv)) { | |
325 | fprintf(stderr, | |
326 | "Illegal value for \"memlimit\": \"%s\"\n", *argv); | |
327 | return -1; | |
328 | } | |
329 | } else if (strcmp(*argv, "help") == 0) { | |
330 | explain(); | |
331 | return -1; | |
332 | } else { | |
333 | fprintf(stderr, "What is \"%s\"?\n", *argv); | |
334 | explain(); | |
335 | return -1; | |
336 | } | |
337 | argc--; argv++; | |
338 | } | |
339 | ||
340 | tail = NLMSG_TAIL(n); | |
341 | addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); | |
342 | if (bandwidth || unlimited) | |
343 | addattr_l(n, 1024, TCA_CAKE_BASE_RATE64, &bandwidth, | |
344 | sizeof(bandwidth)); | |
345 | if (diffserv) | |
346 | addattr_l(n, 1024, TCA_CAKE_DIFFSERV_MODE, &diffserv, | |
347 | sizeof(diffserv)); | |
348 | if (atm != -1) | |
349 | addattr_l(n, 1024, TCA_CAKE_ATM, &atm, sizeof(atm)); | |
350 | if (flowmode != -1) | |
351 | addattr_l(n, 1024, TCA_CAKE_FLOW_MODE, &flowmode, | |
352 | sizeof(flowmode)); | |
353 | if (overhead_set) | |
354 | addattr_l(n, 1024, TCA_CAKE_OVERHEAD, &overhead, | |
355 | sizeof(overhead)); | |
356 | if (overhead_override) { | |
357 | unsigned int zero = 0; | |
358 | ||
359 | addattr_l(n, 1024, TCA_CAKE_RAW, &zero, sizeof(zero)); | |
360 | } | |
361 | if (mpu > 0) | |
362 | addattr_l(n, 1024, TCA_CAKE_MPU, &mpu, sizeof(mpu)); | |
363 | if (interval) | |
364 | addattr_l(n, 1024, TCA_CAKE_RTT, &interval, sizeof(interval)); | |
365 | if (target) | |
366 | addattr_l(n, 1024, TCA_CAKE_TARGET, &target, sizeof(target)); | |
367 | if (autorate != -1) | |
368 | addattr_l(n, 1024, TCA_CAKE_AUTORATE, &autorate, | |
369 | sizeof(autorate)); | |
370 | if (memlimit) | |
371 | addattr_l(n, 1024, TCA_CAKE_MEMORY, &memlimit, | |
372 | sizeof(memlimit)); | |
373 | if (nat != -1) | |
374 | addattr_l(n, 1024, TCA_CAKE_NAT, &nat, sizeof(nat)); | |
375 | if (wash != -1) | |
376 | addattr_l(n, 1024, TCA_CAKE_WASH, &wash, sizeof(wash)); | |
377 | if (ingress != -1) | |
378 | addattr_l(n, 1024, TCA_CAKE_INGRESS, &ingress, sizeof(ingress)); | |
379 | if (ack_filter != -1) | |
380 | addattr_l(n, 1024, TCA_CAKE_ACK_FILTER, &ack_filter, | |
381 | sizeof(ack_filter)); | |
382 | ||
383 | tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; | |
384 | return 0; | |
385 | } | |
386 | ||
387 | static void cake_print_mode(unsigned int value, unsigned int max, | |
388 | const char *key, const char **table) | |
389 | { | |
390 | if (value < max && table[value]) { | |
391 | print_string(PRINT_ANY, key, "%s ", table[value]); | |
392 | } else { | |
393 | print_string(PRINT_JSON, key, NULL, "unknown"); | |
394 | print_string(PRINT_FP, NULL, "(?%s?)", key); | |
395 | } | |
396 | } | |
397 | ||
398 | static int cake_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) | |
399 | { | |
400 | struct rtattr *tb[TCA_CAKE_MAX + 1]; | |
401 | unsigned int interval = 0; | |
402 | unsigned int memlimit = 0; | |
403 | __u64 bandwidth = 0; | |
404 | int ack_filter = 0; | |
405 | int split_gso = 0; | |
406 | int overhead = 0; | |
407 | int autorate = 0; | |
408 | int ingress = 0; | |
409 | int wash = 0; | |
410 | int raw = 0; | |
411 | int mpu = 0; | |
412 | int atm = 0; | |
413 | int nat = 0; | |
414 | ||
415 | SPRINT_BUF(b1); | |
416 | SPRINT_BUF(b2); | |
417 | ||
418 | if (opt == NULL) | |
419 | return 0; | |
420 | ||
421 | parse_rtattr_nested(tb, TCA_CAKE_MAX, opt); | |
422 | ||
423 | if (tb[TCA_CAKE_BASE_RATE64] && | |
424 | RTA_PAYLOAD(tb[TCA_CAKE_BASE_RATE64]) >= sizeof(bandwidth)) { | |
425 | bandwidth = rta_getattr_u64(tb[TCA_CAKE_BASE_RATE64]); | |
426 | if (bandwidth) { | |
427 | print_uint(PRINT_JSON, "bandwidth", NULL, bandwidth); | |
428 | print_string(PRINT_FP, NULL, "bandwidth %s ", | |
429 | sprint_rate(bandwidth, b1)); | |
430 | } else | |
431 | print_string(PRINT_ANY, "bandwidth", "bandwidth %s ", | |
432 | "unlimited"); | |
433 | } | |
434 | if (tb[TCA_CAKE_AUTORATE] && | |
435 | RTA_PAYLOAD(tb[TCA_CAKE_AUTORATE]) >= sizeof(__u32)) { | |
436 | autorate = rta_getattr_u32(tb[TCA_CAKE_AUTORATE]); | |
437 | if (autorate == 1) | |
77c9fbd0 THJ |
438 | print_string(PRINT_ANY, "autorate", "%s ", |
439 | "autorate-ingress"); | |
714444c0 THJ |
440 | else if (autorate) |
441 | print_string(PRINT_ANY, "autorate", "(?autorate?) ", | |
442 | "unknown"); | |
443 | } | |
444 | if (tb[TCA_CAKE_DIFFSERV_MODE] && | |
445 | RTA_PAYLOAD(tb[TCA_CAKE_DIFFSERV_MODE]) >= sizeof(__u32)) { | |
446 | cake_print_mode(rta_getattr_u32(tb[TCA_CAKE_DIFFSERV_MODE]), | |
447 | CAKE_DIFFSERV_MAX, "diffserv", diffserv_names); | |
448 | } | |
449 | if (tb[TCA_CAKE_FLOW_MODE] && | |
450 | RTA_PAYLOAD(tb[TCA_CAKE_FLOW_MODE]) >= sizeof(__u32)) { | |
451 | cake_print_mode(rta_getattr_u32(tb[TCA_CAKE_FLOW_MODE]), | |
452 | CAKE_FLOW_MAX, "flowmode", flowmode_names); | |
453 | } | |
454 | ||
455 | if (tb[TCA_CAKE_NAT] && | |
456 | RTA_PAYLOAD(tb[TCA_CAKE_NAT]) >= sizeof(__u32)) { | |
457 | nat = rta_getattr_u32(tb[TCA_CAKE_NAT]); | |
458 | } | |
459 | ||
460 | if (nat) | |
461 | print_string(PRINT_FP, NULL, "nat ", NULL); | |
462 | print_bool(PRINT_JSON, "nat", NULL, nat); | |
463 | ||
464 | if (tb[TCA_CAKE_WASH] && | |
465 | RTA_PAYLOAD(tb[TCA_CAKE_WASH]) >= sizeof(__u32)) { | |
466 | wash = rta_getattr_u32(tb[TCA_CAKE_WASH]); | |
467 | } | |
468 | if (tb[TCA_CAKE_ATM] && | |
469 | RTA_PAYLOAD(tb[TCA_CAKE_ATM]) >= sizeof(__u32)) { | |
470 | atm = rta_getattr_u32(tb[TCA_CAKE_ATM]); | |
471 | } | |
472 | if (tb[TCA_CAKE_OVERHEAD] && | |
473 | RTA_PAYLOAD(tb[TCA_CAKE_OVERHEAD]) >= sizeof(__s32)) { | |
474 | overhead = *(__s32 *) RTA_DATA(tb[TCA_CAKE_OVERHEAD]); | |
475 | } | |
476 | if (tb[TCA_CAKE_MPU] && | |
477 | RTA_PAYLOAD(tb[TCA_CAKE_MPU]) >= sizeof(__u32)) { | |
478 | mpu = rta_getattr_u32(tb[TCA_CAKE_MPU]); | |
479 | } | |
480 | if (tb[TCA_CAKE_INGRESS] && | |
481 | RTA_PAYLOAD(tb[TCA_CAKE_INGRESS]) >= sizeof(__u32)) { | |
482 | ingress = rta_getattr_u32(tb[TCA_CAKE_INGRESS]); | |
483 | } | |
484 | if (tb[TCA_CAKE_ACK_FILTER] && | |
485 | RTA_PAYLOAD(tb[TCA_CAKE_ACK_FILTER]) >= sizeof(__u32)) { | |
486 | ack_filter = rta_getattr_u32(tb[TCA_CAKE_ACK_FILTER]); | |
487 | } | |
488 | if (tb[TCA_CAKE_SPLIT_GSO] && | |
489 | RTA_PAYLOAD(tb[TCA_CAKE_SPLIT_GSO]) >= sizeof(__u32)) { | |
490 | split_gso = rta_getattr_u32(tb[TCA_CAKE_SPLIT_GSO]); | |
491 | } | |
492 | if (tb[TCA_CAKE_RAW]) { | |
493 | raw = 1; | |
494 | } | |
495 | if (tb[TCA_CAKE_RTT] && | |
496 | RTA_PAYLOAD(tb[TCA_CAKE_RTT]) >= sizeof(__u32)) { | |
497 | interval = rta_getattr_u32(tb[TCA_CAKE_RTT]); | |
498 | } | |
499 | ||
500 | if (wash) | |
501 | print_string(PRINT_FP, NULL, "wash ", NULL); | |
502 | print_bool(PRINT_JSON, "wash", NULL, wash); | |
503 | ||
504 | if (ingress) | |
505 | print_string(PRINT_FP, NULL, "ingress ", NULL); | |
506 | print_bool(PRINT_JSON, "ingress", NULL, ingress); | |
507 | ||
508 | if (ack_filter == CAKE_ACK_AGGRESSIVE) | |
509 | print_string(PRINT_ANY, "ack-filter", "ack-filter-%s ", | |
510 | "aggressive"); | |
511 | else if (ack_filter == CAKE_ACK_FILTER) | |
512 | print_string(PRINT_ANY, "ack-filter", "ack-filter ", "enabled"); | |
513 | else | |
514 | print_string(PRINT_JSON, "ack-filter", NULL, "disabled"); | |
515 | ||
516 | if (split_gso) | |
517 | print_string(PRINT_FP, NULL, "split-gso ", NULL); | |
518 | print_bool(PRINT_JSON, "split_gso", NULL, split_gso); | |
519 | ||
520 | if (interval) | |
521 | print_string(PRINT_FP, NULL, "rtt %s ", | |
522 | sprint_time(interval, b2)); | |
523 | print_uint(PRINT_JSON, "rtt", NULL, interval); | |
524 | ||
525 | if (raw) | |
526 | print_string(PRINT_FP, NULL, "raw ", NULL); | |
527 | print_bool(PRINT_JSON, "raw", NULL, raw); | |
528 | ||
529 | if (atm == CAKE_ATM_ATM) | |
530 | print_string(PRINT_ANY, "atm", "%s ", "atm"); | |
531 | else if (atm == CAKE_ATM_PTM) | |
532 | print_string(PRINT_ANY, "atm", "%s ", "ptm"); | |
533 | else if (!raw) | |
534 | print_string(PRINT_ANY, "atm", "%s ", "noatm"); | |
535 | ||
536 | print_int(PRINT_ANY, "overhead", "overhead %d ", overhead); | |
537 | ||
538 | if (mpu) | |
539 | print_uint(PRINT_ANY, "mpu", "mpu %u ", mpu); | |
540 | ||
541 | if (memlimit) { | |
542 | print_uint(PRINT_JSON, "memlimit", NULL, memlimit); | |
543 | print_string(PRINT_FP, NULL, "memlimit %s", | |
544 | sprint_size(memlimit, b1)); | |
545 | } | |
546 | ||
547 | return 0; | |
548 | } | |
549 | ||
550 | static void cake_print_json_tin(struct rtattr **tstat) | |
551 | { | |
552 | #define PRINT_TSTAT_JSON(type, name, attr) if (tstat[TCA_CAKE_TIN_STATS_ ## attr]) \ | |
553 | print_u64(PRINT_JSON, name, NULL, \ | |
554 | rta_getattr_ ## type((struct rtattr *) \ | |
555 | tstat[TCA_CAKE_TIN_STATS_ ## attr])) | |
556 | ||
557 | open_json_object(NULL); | |
558 | PRINT_TSTAT_JSON(u64, "threshold_rate", THRESHOLD_RATE64); | |
559 | PRINT_TSTAT_JSON(u64, "sent_bytes", SENT_BYTES64); | |
560 | PRINT_TSTAT_JSON(u32, "backlog_bytes", BACKLOG_BYTES); | |
561 | PRINT_TSTAT_JSON(u32, "target_us", TARGET_US); | |
562 | PRINT_TSTAT_JSON(u32, "interval_us", INTERVAL_US); | |
563 | PRINT_TSTAT_JSON(u32, "peak_delay_us", PEAK_DELAY_US); | |
564 | PRINT_TSTAT_JSON(u32, "avg_delay_us", AVG_DELAY_US); | |
565 | PRINT_TSTAT_JSON(u32, "base_delay_us", BASE_DELAY_US); | |
566 | PRINT_TSTAT_JSON(u32, "sent_packets", SENT_PACKETS); | |
567 | PRINT_TSTAT_JSON(u32, "way_indirect_hits", WAY_INDIRECT_HITS); | |
568 | PRINT_TSTAT_JSON(u32, "way_misses", WAY_MISSES); | |
569 | PRINT_TSTAT_JSON(u32, "way_collisions", WAY_COLLISIONS); | |
570 | PRINT_TSTAT_JSON(u32, "drops", DROPPED_PACKETS); | |
571 | PRINT_TSTAT_JSON(u32, "ecn_mark", ECN_MARKED_PACKETS); | |
572 | PRINT_TSTAT_JSON(u32, "ack_drops", ACKS_DROPPED_PACKETS); | |
573 | PRINT_TSTAT_JSON(u32, "sparse_flows", SPARSE_FLOWS); | |
574 | PRINT_TSTAT_JSON(u32, "bulk_flows", BULK_FLOWS); | |
575 | PRINT_TSTAT_JSON(u32, "unresponsive_flows", UNRESPONSIVE_FLOWS); | |
576 | PRINT_TSTAT_JSON(u32, "max_pkt_len", MAX_SKBLEN); | |
577 | PRINT_TSTAT_JSON(u32, "flow_quantum", FLOW_QUANTUM); | |
578 | close_json_object(); | |
579 | ||
580 | #undef PRINT_TSTAT_JSON | |
581 | } | |
582 | ||
583 | static int cake_print_xstats(struct qdisc_util *qu, FILE *f, | |
584 | struct rtattr *xstats) | |
585 | { | |
586 | struct rtattr *st[TCA_CAKE_STATS_MAX + 1]; | |
587 | SPRINT_BUF(b1); | |
588 | int i; | |
589 | ||
590 | if (xstats == NULL) | |
591 | return 0; | |
592 | ||
593 | #define GET_STAT_U32(attr) rta_getattr_u32(st[TCA_CAKE_STATS_ ## attr]) | |
594 | #define GET_STAT_S32(attr) (*(__s32 *)RTA_DATA(st[TCA_CAKE_STATS_ ## attr])) | |
595 | #define GET_STAT_U64(attr) rta_getattr_u64(st[TCA_CAKE_STATS_ ## attr]) | |
596 | ||
597 | parse_rtattr_nested(st, TCA_CAKE_STATS_MAX, xstats); | |
598 | ||
599 | if (st[TCA_CAKE_STATS_MEMORY_USED] && | |
600 | st[TCA_CAKE_STATS_MEMORY_LIMIT]) { | |
601 | print_string(PRINT_FP, NULL, " memory used: %s", | |
602 | sprint_size(GET_STAT_U32(MEMORY_USED), b1)); | |
603 | ||
604 | print_string(PRINT_FP, NULL, " of %s\n", | |
605 | sprint_size(GET_STAT_U32(MEMORY_LIMIT), b1)); | |
606 | ||
607 | print_uint(PRINT_JSON, "memory_used", NULL, | |
608 | GET_STAT_U32(MEMORY_USED)); | |
609 | print_uint(PRINT_JSON, "memory_limit", NULL, | |
610 | GET_STAT_U32(MEMORY_LIMIT)); | |
611 | } | |
612 | ||
613 | if (st[TCA_CAKE_STATS_CAPACITY_ESTIMATE64]) { | |
614 | print_string(PRINT_FP, NULL, " capacity estimate: %s\n", | |
615 | sprint_rate(GET_STAT_U64(CAPACITY_ESTIMATE64), b1)); | |
616 | print_uint(PRINT_JSON, "capacity_estimate", NULL, | |
617 | GET_STAT_U64(CAPACITY_ESTIMATE64)); | |
618 | } | |
619 | ||
620 | if (st[TCA_CAKE_STATS_MIN_NETLEN] && | |
621 | st[TCA_CAKE_STATS_MAX_NETLEN]) { | |
622 | print_uint(PRINT_ANY, "min_network_size", | |
623 | " min/max network layer size: %12u", | |
624 | GET_STAT_U32(MIN_NETLEN)); | |
625 | print_uint(PRINT_ANY, "max_network_size", | |
626 | " /%8u\n", GET_STAT_U32(MAX_NETLEN)); | |
627 | } | |
628 | ||
629 | if (st[TCA_CAKE_STATS_MIN_ADJLEN] && | |
630 | st[TCA_CAKE_STATS_MAX_ADJLEN]) { | |
631 | print_uint(PRINT_ANY, "min_adj_size", | |
632 | " min/max overhead-adjusted size: %8u", | |
633 | GET_STAT_U32(MIN_ADJLEN)); | |
634 | print_uint(PRINT_ANY, "max_adj_size", | |
635 | " /%8u\n", GET_STAT_U32(MAX_ADJLEN)); | |
636 | } | |
637 | ||
638 | if (st[TCA_CAKE_STATS_AVG_NETOFF]) | |
639 | print_uint(PRINT_ANY, "avg_hdr_offset", | |
640 | " average network hdr offset: %12u\n\n", | |
641 | GET_STAT_U32(AVG_NETOFF)); | |
642 | ||
643 | /* class stats */ | |
644 | if (st[TCA_CAKE_STATS_DEFICIT]) | |
645 | print_int(PRINT_ANY, "deficit", " deficit %u", | |
646 | GET_STAT_S32(DEFICIT)); | |
647 | if (st[TCA_CAKE_STATS_COBALT_COUNT]) | |
648 | print_uint(PRINT_ANY, "count", " count %u", | |
649 | GET_STAT_U32(COBALT_COUNT)); | |
650 | ||
651 | if (st[TCA_CAKE_STATS_DROPPING] && GET_STAT_U32(DROPPING)) { | |
652 | print_bool(PRINT_ANY, "dropping", " dropping", true); | |
653 | if (st[TCA_CAKE_STATS_DROP_NEXT_US]) { | |
654 | int drop_next = GET_STAT_S32(DROP_NEXT_US); | |
655 | ||
656 | if (drop_next < 0) { | |
657 | print_string(PRINT_FP, NULL, " drop_next -%s", | |
658 | sprint_time(drop_next, b1)); | |
659 | } else { | |
660 | print_uint(PRINT_JSON, "drop_next", NULL, | |
661 | drop_next); | |
662 | print_string(PRINT_FP, NULL, " drop_next %s", | |
663 | sprint_time(drop_next, b1)); | |
664 | } | |
665 | } | |
666 | } | |
667 | ||
668 | if (st[TCA_CAKE_STATS_P_DROP]) { | |
669 | print_uint(PRINT_ANY, "blue_prob", " blue_prob %u", | |
670 | GET_STAT_U32(P_DROP)); | |
671 | if (st[TCA_CAKE_STATS_BLUE_TIMER_US]) { | |
672 | int blue_timer = GET_STAT_S32(BLUE_TIMER_US); | |
673 | ||
674 | if (blue_timer < 0) { | |
675 | print_string(PRINT_FP, NULL, " blue_timer -%s", | |
676 | sprint_time(blue_timer, b1)); | |
677 | } else { | |
678 | print_uint(PRINT_JSON, "blue_timer", NULL, | |
679 | blue_timer); | |
680 | print_string(PRINT_FP, NULL, " blue_timer %s", | |
681 | sprint_time(blue_timer, b1)); | |
682 | } | |
683 | } | |
684 | } | |
685 | ||
686 | #undef GET_STAT_U32 | |
687 | #undef GET_STAT_S32 | |
688 | #undef GET_STAT_U64 | |
689 | ||
690 | if (st[TCA_CAKE_STATS_TIN_STATS]) { | |
691 | struct rtattr *tstat[TC_CAKE_MAX_TINS][TCA_CAKE_TIN_STATS_MAX + 1]; | |
692 | struct rtattr *tins[TC_CAKE_MAX_TINS + 1]; | |
693 | int num_tins = 0; | |
694 | ||
695 | parse_rtattr_nested(tins, TC_CAKE_MAX_TINS, | |
696 | st[TCA_CAKE_STATS_TIN_STATS]); | |
697 | ||
698 | for (i = 1; i <= TC_CAKE_MAX_TINS && tins[i]; i++) { | |
699 | parse_rtattr_nested(tstat[i-1], TCA_CAKE_TIN_STATS_MAX, | |
700 | tins[i]); | |
701 | num_tins++; | |
702 | } | |
703 | ||
704 | if (!num_tins) | |
705 | return 0; | |
706 | ||
707 | if (is_json_context()) { | |
708 | open_json_array(PRINT_JSON, "tins"); | |
709 | for (i = 0; i < num_tins; i++) | |
710 | cake_print_json_tin(tstat[i]); | |
711 | close_json_array(PRINT_JSON, NULL); | |
712 | ||
713 | return 0; | |
714 | } | |
715 | ||
716 | ||
717 | switch (num_tins) { | |
718 | case 3: | |
719 | fprintf(f, " Bulk Best Effort Voice\n"); | |
720 | break; | |
721 | ||
722 | case 4: | |
723 | fprintf(f, " Bulk Best Effort Video Voice\n"); | |
724 | break; | |
725 | ||
726 | default: | |
727 | fprintf(f, " "); | |
728 | for (i = 0; i < num_tins; i++) | |
729 | fprintf(f, " Tin %u", i); | |
730 | fprintf(f, "\n"); | |
731 | }; | |
732 | ||
733 | #define GET_TSTAT(i, attr) (tstat[i][TCA_CAKE_TIN_STATS_ ## attr]) | |
734 | #define PRINT_TSTAT(name, attr, fmts, val) do { \ | |
735 | if (GET_TSTAT(0, attr)) { \ | |
736 | fprintf(f, name); \ | |
737 | for (i = 0; i < num_tins; i++) \ | |
738 | fprintf(f, " %12" fmts, val); \ | |
739 | fprintf(f, "\n"); \ | |
740 | } \ | |
741 | } while (0) | |
742 | ||
743 | #define SPRINT_TSTAT(pfunc, type, name, attr) PRINT_TSTAT( \ | |
744 | name, attr, "s", sprint_ ## pfunc( \ | |
745 | rta_getattr_ ## type(GET_TSTAT(i, attr)), b1)) | |
746 | ||
747 | #define PRINT_TSTAT_U32(name, attr) PRINT_TSTAT( \ | |
748 | name, attr, "u", rta_getattr_u32(GET_TSTAT(i, attr))) | |
749 | ||
750 | #define PRINT_TSTAT_U64(name, attr) PRINT_TSTAT( \ | |
751 | name, attr, "llu", rta_getattr_u64(GET_TSTAT(i, attr))) | |
752 | ||
753 | SPRINT_TSTAT(rate, u64, " thresh ", THRESHOLD_RATE64); | |
754 | SPRINT_TSTAT(time, u32, " target ", TARGET_US); | |
755 | SPRINT_TSTAT(time, u32, " interval", INTERVAL_US); | |
756 | SPRINT_TSTAT(time, u32, " pk_delay", PEAK_DELAY_US); | |
757 | SPRINT_TSTAT(time, u32, " av_delay", AVG_DELAY_US); | |
758 | SPRINT_TSTAT(time, u32, " sp_delay", BASE_DELAY_US); | |
759 | SPRINT_TSTAT(size, u32, " backlog ", BACKLOG_BYTES); | |
760 | ||
761 | PRINT_TSTAT_U32(" pkts ", SENT_PACKETS); | |
762 | PRINT_TSTAT_U64(" bytes ", SENT_BYTES64); | |
763 | ||
764 | PRINT_TSTAT_U32(" way_inds", WAY_INDIRECT_HITS); | |
765 | PRINT_TSTAT_U32(" way_miss", WAY_MISSES); | |
766 | PRINT_TSTAT_U32(" way_cols", WAY_COLLISIONS); | |
767 | PRINT_TSTAT_U32(" drops ", DROPPED_PACKETS); | |
768 | PRINT_TSTAT_U32(" marks ", ECN_MARKED_PACKETS); | |
769 | PRINT_TSTAT_U32(" ack_drop", ACKS_DROPPED_PACKETS); | |
770 | PRINT_TSTAT_U32(" sp_flows", SPARSE_FLOWS); | |
771 | PRINT_TSTAT_U32(" bk_flows", BULK_FLOWS); | |
772 | PRINT_TSTAT_U32(" un_flows", UNRESPONSIVE_FLOWS); | |
773 | PRINT_TSTAT_U32(" max_len ", MAX_SKBLEN); | |
774 | PRINT_TSTAT_U32(" quantum ", FLOW_QUANTUM); | |
775 | ||
776 | #undef GET_STAT | |
777 | #undef PRINT_TSTAT | |
778 | #undef SPRINT_TSTAT | |
779 | #undef PRINT_TSTAT_U32 | |
780 | #undef PRINT_TSTAT_U64 | |
781 | } | |
782 | return 0; | |
783 | } | |
784 | ||
785 | struct qdisc_util cake_qdisc_util = { | |
786 | .id = "cake", | |
787 | .parse_qopt = cake_parse_opt, | |
788 | .print_qopt = cake_print_opt, | |
789 | .print_xstats = cake_print_xstats, | |
790 | }; |