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