]>
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); | |
2153e01f THJ |
471 | else |
472 | print_string(PRINT_FP, NULL, "nonat ", NULL); | |
714444c0 THJ |
473 | print_bool(PRINT_JSON, "nat", NULL, nat); |
474 | ||
475 | if (tb[TCA_CAKE_WASH] && | |
476 | RTA_PAYLOAD(tb[TCA_CAKE_WASH]) >= sizeof(__u32)) { | |
477 | wash = rta_getattr_u32(tb[TCA_CAKE_WASH]); | |
478 | } | |
479 | if (tb[TCA_CAKE_ATM] && | |
480 | RTA_PAYLOAD(tb[TCA_CAKE_ATM]) >= sizeof(__u32)) { | |
481 | atm = rta_getattr_u32(tb[TCA_CAKE_ATM]); | |
482 | } | |
483 | if (tb[TCA_CAKE_OVERHEAD] && | |
484 | RTA_PAYLOAD(tb[TCA_CAKE_OVERHEAD]) >= sizeof(__s32)) { | |
485 | overhead = *(__s32 *) RTA_DATA(tb[TCA_CAKE_OVERHEAD]); | |
486 | } | |
487 | if (tb[TCA_CAKE_MPU] && | |
488 | RTA_PAYLOAD(tb[TCA_CAKE_MPU]) >= sizeof(__u32)) { | |
489 | mpu = rta_getattr_u32(tb[TCA_CAKE_MPU]); | |
490 | } | |
491 | if (tb[TCA_CAKE_INGRESS] && | |
492 | RTA_PAYLOAD(tb[TCA_CAKE_INGRESS]) >= sizeof(__u32)) { | |
493 | ingress = rta_getattr_u32(tb[TCA_CAKE_INGRESS]); | |
494 | } | |
495 | if (tb[TCA_CAKE_ACK_FILTER] && | |
496 | RTA_PAYLOAD(tb[TCA_CAKE_ACK_FILTER]) >= sizeof(__u32)) { | |
497 | ack_filter = rta_getattr_u32(tb[TCA_CAKE_ACK_FILTER]); | |
498 | } | |
499 | if (tb[TCA_CAKE_SPLIT_GSO] && | |
500 | RTA_PAYLOAD(tb[TCA_CAKE_SPLIT_GSO]) >= sizeof(__u32)) { | |
501 | split_gso = rta_getattr_u32(tb[TCA_CAKE_SPLIT_GSO]); | |
502 | } | |
503 | if (tb[TCA_CAKE_RAW]) { | |
504 | raw = 1; | |
505 | } | |
506 | if (tb[TCA_CAKE_RTT] && | |
507 | RTA_PAYLOAD(tb[TCA_CAKE_RTT]) >= sizeof(__u32)) { | |
508 | interval = rta_getattr_u32(tb[TCA_CAKE_RTT]); | |
509 | } | |
510 | ||
511 | if (wash) | |
512 | print_string(PRINT_FP, NULL, "wash ", NULL); | |
2153e01f THJ |
513 | else |
514 | print_string(PRINT_FP, NULL, "nowash ", NULL); | |
714444c0 THJ |
515 | print_bool(PRINT_JSON, "wash", NULL, wash); |
516 | ||
517 | if (ingress) | |
518 | print_string(PRINT_FP, NULL, "ingress ", NULL); | |
519 | print_bool(PRINT_JSON, "ingress", NULL, ingress); | |
520 | ||
521 | if (ack_filter == CAKE_ACK_AGGRESSIVE) | |
522 | print_string(PRINT_ANY, "ack-filter", "ack-filter-%s ", | |
523 | "aggressive"); | |
524 | else if (ack_filter == CAKE_ACK_FILTER) | |
525 | print_string(PRINT_ANY, "ack-filter", "ack-filter ", "enabled"); | |
526 | else | |
2153e01f | 527 | print_string(PRINT_ANY, "ack-filter", "no-ack-filter ", "disabled"); |
714444c0 THJ |
528 | |
529 | if (split_gso) | |
530 | print_string(PRINT_FP, NULL, "split-gso ", NULL); | |
b914fe5f THJ |
531 | else |
532 | print_string(PRINT_FP, NULL, "no-split-gso ", NULL); | |
714444c0 THJ |
533 | print_bool(PRINT_JSON, "split_gso", NULL, split_gso); |
534 | ||
535 | if (interval) | |
536 | print_string(PRINT_FP, NULL, "rtt %s ", | |
537 | sprint_time(interval, b2)); | |
538 | print_uint(PRINT_JSON, "rtt", NULL, interval); | |
539 | ||
540 | if (raw) | |
541 | print_string(PRINT_FP, NULL, "raw ", NULL); | |
542 | print_bool(PRINT_JSON, "raw", NULL, raw); | |
543 | ||
544 | if (atm == CAKE_ATM_ATM) | |
545 | print_string(PRINT_ANY, "atm", "%s ", "atm"); | |
546 | else if (atm == CAKE_ATM_PTM) | |
547 | print_string(PRINT_ANY, "atm", "%s ", "ptm"); | |
548 | else if (!raw) | |
549 | print_string(PRINT_ANY, "atm", "%s ", "noatm"); | |
550 | ||
551 | print_int(PRINT_ANY, "overhead", "overhead %d ", overhead); | |
552 | ||
553 | if (mpu) | |
554 | print_uint(PRINT_ANY, "mpu", "mpu %u ", mpu); | |
555 | ||
556 | if (memlimit) { | |
557 | print_uint(PRINT_JSON, "memlimit", NULL, memlimit); | |
558 | print_string(PRINT_FP, NULL, "memlimit %s", | |
559 | sprint_size(memlimit, b1)); | |
560 | } | |
561 | ||
562 | return 0; | |
563 | } | |
564 | ||
565 | static void cake_print_json_tin(struct rtattr **tstat) | |
566 | { | |
567 | #define PRINT_TSTAT_JSON(type, name, attr) if (tstat[TCA_CAKE_TIN_STATS_ ## attr]) \ | |
568 | print_u64(PRINT_JSON, name, NULL, \ | |
569 | rta_getattr_ ## type((struct rtattr *) \ | |
570 | tstat[TCA_CAKE_TIN_STATS_ ## attr])) | |
571 | ||
572 | open_json_object(NULL); | |
573 | PRINT_TSTAT_JSON(u64, "threshold_rate", THRESHOLD_RATE64); | |
574 | PRINT_TSTAT_JSON(u64, "sent_bytes", SENT_BYTES64); | |
575 | PRINT_TSTAT_JSON(u32, "backlog_bytes", BACKLOG_BYTES); | |
576 | PRINT_TSTAT_JSON(u32, "target_us", TARGET_US); | |
577 | PRINT_TSTAT_JSON(u32, "interval_us", INTERVAL_US); | |
578 | PRINT_TSTAT_JSON(u32, "peak_delay_us", PEAK_DELAY_US); | |
579 | PRINT_TSTAT_JSON(u32, "avg_delay_us", AVG_DELAY_US); | |
580 | PRINT_TSTAT_JSON(u32, "base_delay_us", BASE_DELAY_US); | |
581 | PRINT_TSTAT_JSON(u32, "sent_packets", SENT_PACKETS); | |
582 | PRINT_TSTAT_JSON(u32, "way_indirect_hits", WAY_INDIRECT_HITS); | |
583 | PRINT_TSTAT_JSON(u32, "way_misses", WAY_MISSES); | |
584 | PRINT_TSTAT_JSON(u32, "way_collisions", WAY_COLLISIONS); | |
585 | PRINT_TSTAT_JSON(u32, "drops", DROPPED_PACKETS); | |
586 | PRINT_TSTAT_JSON(u32, "ecn_mark", ECN_MARKED_PACKETS); | |
587 | PRINT_TSTAT_JSON(u32, "ack_drops", ACKS_DROPPED_PACKETS); | |
588 | PRINT_TSTAT_JSON(u32, "sparse_flows", SPARSE_FLOWS); | |
589 | PRINT_TSTAT_JSON(u32, "bulk_flows", BULK_FLOWS); | |
590 | PRINT_TSTAT_JSON(u32, "unresponsive_flows", UNRESPONSIVE_FLOWS); | |
591 | PRINT_TSTAT_JSON(u32, "max_pkt_len", MAX_SKBLEN); | |
592 | PRINT_TSTAT_JSON(u32, "flow_quantum", FLOW_QUANTUM); | |
593 | close_json_object(); | |
594 | ||
595 | #undef PRINT_TSTAT_JSON | |
596 | } | |
597 | ||
598 | static int cake_print_xstats(struct qdisc_util *qu, FILE *f, | |
599 | struct rtattr *xstats) | |
600 | { | |
601 | struct rtattr *st[TCA_CAKE_STATS_MAX + 1]; | |
602 | SPRINT_BUF(b1); | |
603 | int i; | |
604 | ||
605 | if (xstats == NULL) | |
606 | return 0; | |
607 | ||
608 | #define GET_STAT_U32(attr) rta_getattr_u32(st[TCA_CAKE_STATS_ ## attr]) | |
609 | #define GET_STAT_S32(attr) (*(__s32 *)RTA_DATA(st[TCA_CAKE_STATS_ ## attr])) | |
610 | #define GET_STAT_U64(attr) rta_getattr_u64(st[TCA_CAKE_STATS_ ## attr]) | |
611 | ||
612 | parse_rtattr_nested(st, TCA_CAKE_STATS_MAX, xstats); | |
613 | ||
614 | if (st[TCA_CAKE_STATS_MEMORY_USED] && | |
615 | st[TCA_CAKE_STATS_MEMORY_LIMIT]) { | |
616 | print_string(PRINT_FP, NULL, " memory used: %s", | |
617 | sprint_size(GET_STAT_U32(MEMORY_USED), b1)); | |
618 | ||
619 | print_string(PRINT_FP, NULL, " of %s\n", | |
620 | sprint_size(GET_STAT_U32(MEMORY_LIMIT), b1)); | |
621 | ||
622 | print_uint(PRINT_JSON, "memory_used", NULL, | |
623 | GET_STAT_U32(MEMORY_USED)); | |
624 | print_uint(PRINT_JSON, "memory_limit", NULL, | |
625 | GET_STAT_U32(MEMORY_LIMIT)); | |
626 | } | |
627 | ||
628 | if (st[TCA_CAKE_STATS_CAPACITY_ESTIMATE64]) { | |
629 | print_string(PRINT_FP, NULL, " capacity estimate: %s\n", | |
630 | sprint_rate(GET_STAT_U64(CAPACITY_ESTIMATE64), b1)); | |
631 | print_uint(PRINT_JSON, "capacity_estimate", NULL, | |
632 | GET_STAT_U64(CAPACITY_ESTIMATE64)); | |
633 | } | |
634 | ||
635 | if (st[TCA_CAKE_STATS_MIN_NETLEN] && | |
636 | st[TCA_CAKE_STATS_MAX_NETLEN]) { | |
637 | print_uint(PRINT_ANY, "min_network_size", | |
638 | " min/max network layer size: %12u", | |
639 | GET_STAT_U32(MIN_NETLEN)); | |
640 | print_uint(PRINT_ANY, "max_network_size", | |
641 | " /%8u\n", GET_STAT_U32(MAX_NETLEN)); | |
642 | } | |
643 | ||
644 | if (st[TCA_CAKE_STATS_MIN_ADJLEN] && | |
645 | st[TCA_CAKE_STATS_MAX_ADJLEN]) { | |
646 | print_uint(PRINT_ANY, "min_adj_size", | |
647 | " min/max overhead-adjusted size: %8u", | |
648 | GET_STAT_U32(MIN_ADJLEN)); | |
649 | print_uint(PRINT_ANY, "max_adj_size", | |
650 | " /%8u\n", GET_STAT_U32(MAX_ADJLEN)); | |
651 | } | |
652 | ||
653 | if (st[TCA_CAKE_STATS_AVG_NETOFF]) | |
654 | print_uint(PRINT_ANY, "avg_hdr_offset", | |
655 | " average network hdr offset: %12u\n\n", | |
656 | GET_STAT_U32(AVG_NETOFF)); | |
657 | ||
658 | /* class stats */ | |
659 | if (st[TCA_CAKE_STATS_DEFICIT]) | |
660 | print_int(PRINT_ANY, "deficit", " deficit %u", | |
661 | GET_STAT_S32(DEFICIT)); | |
662 | if (st[TCA_CAKE_STATS_COBALT_COUNT]) | |
663 | print_uint(PRINT_ANY, "count", " count %u", | |
664 | GET_STAT_U32(COBALT_COUNT)); | |
665 | ||
666 | if (st[TCA_CAKE_STATS_DROPPING] && GET_STAT_U32(DROPPING)) { | |
667 | print_bool(PRINT_ANY, "dropping", " dropping", true); | |
668 | if (st[TCA_CAKE_STATS_DROP_NEXT_US]) { | |
669 | int drop_next = GET_STAT_S32(DROP_NEXT_US); | |
670 | ||
671 | if (drop_next < 0) { | |
672 | print_string(PRINT_FP, NULL, " drop_next -%s", | |
673 | sprint_time(drop_next, b1)); | |
674 | } else { | |
675 | print_uint(PRINT_JSON, "drop_next", NULL, | |
676 | drop_next); | |
677 | print_string(PRINT_FP, NULL, " drop_next %s", | |
678 | sprint_time(drop_next, b1)); | |
679 | } | |
680 | } | |
681 | } | |
682 | ||
683 | if (st[TCA_CAKE_STATS_P_DROP]) { | |
684 | print_uint(PRINT_ANY, "blue_prob", " blue_prob %u", | |
685 | GET_STAT_U32(P_DROP)); | |
686 | if (st[TCA_CAKE_STATS_BLUE_TIMER_US]) { | |
687 | int blue_timer = GET_STAT_S32(BLUE_TIMER_US); | |
688 | ||
689 | if (blue_timer < 0) { | |
690 | print_string(PRINT_FP, NULL, " blue_timer -%s", | |
691 | sprint_time(blue_timer, b1)); | |
692 | } else { | |
693 | print_uint(PRINT_JSON, "blue_timer", NULL, | |
694 | blue_timer); | |
695 | print_string(PRINT_FP, NULL, " blue_timer %s", | |
696 | sprint_time(blue_timer, b1)); | |
697 | } | |
698 | } | |
699 | } | |
700 | ||
701 | #undef GET_STAT_U32 | |
702 | #undef GET_STAT_S32 | |
703 | #undef GET_STAT_U64 | |
704 | ||
705 | if (st[TCA_CAKE_STATS_TIN_STATS]) { | |
706 | struct rtattr *tstat[TC_CAKE_MAX_TINS][TCA_CAKE_TIN_STATS_MAX + 1]; | |
707 | struct rtattr *tins[TC_CAKE_MAX_TINS + 1]; | |
708 | int num_tins = 0; | |
709 | ||
710 | parse_rtattr_nested(tins, TC_CAKE_MAX_TINS, | |
711 | st[TCA_CAKE_STATS_TIN_STATS]); | |
712 | ||
713 | for (i = 1; i <= TC_CAKE_MAX_TINS && tins[i]; i++) { | |
714 | parse_rtattr_nested(tstat[i-1], TCA_CAKE_TIN_STATS_MAX, | |
715 | tins[i]); | |
716 | num_tins++; | |
717 | } | |
718 | ||
719 | if (!num_tins) | |
720 | return 0; | |
721 | ||
722 | if (is_json_context()) { | |
723 | open_json_array(PRINT_JSON, "tins"); | |
724 | for (i = 0; i < num_tins; i++) | |
725 | cake_print_json_tin(tstat[i]); | |
726 | close_json_array(PRINT_JSON, NULL); | |
727 | ||
728 | return 0; | |
729 | } | |
730 | ||
731 | ||
732 | switch (num_tins) { | |
733 | case 3: | |
734 | fprintf(f, " Bulk Best Effort Voice\n"); | |
735 | break; | |
736 | ||
737 | case 4: | |
738 | fprintf(f, " Bulk Best Effort Video Voice\n"); | |
739 | break; | |
740 | ||
741 | default: | |
742 | fprintf(f, " "); | |
743 | for (i = 0; i < num_tins; i++) | |
744 | fprintf(f, " Tin %u", i); | |
745 | fprintf(f, "\n"); | |
746 | }; | |
747 | ||
748 | #define GET_TSTAT(i, attr) (tstat[i][TCA_CAKE_TIN_STATS_ ## attr]) | |
749 | #define PRINT_TSTAT(name, attr, fmts, val) do { \ | |
750 | if (GET_TSTAT(0, attr)) { \ | |
751 | fprintf(f, name); \ | |
752 | for (i = 0; i < num_tins; i++) \ | |
753 | fprintf(f, " %12" fmts, val); \ | |
754 | fprintf(f, "\n"); \ | |
755 | } \ | |
756 | } while (0) | |
757 | ||
758 | #define SPRINT_TSTAT(pfunc, type, name, attr) PRINT_TSTAT( \ | |
759 | name, attr, "s", sprint_ ## pfunc( \ | |
760 | rta_getattr_ ## type(GET_TSTAT(i, attr)), b1)) | |
761 | ||
762 | #define PRINT_TSTAT_U32(name, attr) PRINT_TSTAT( \ | |
763 | name, attr, "u", rta_getattr_u32(GET_TSTAT(i, attr))) | |
764 | ||
765 | #define PRINT_TSTAT_U64(name, attr) PRINT_TSTAT( \ | |
766 | name, attr, "llu", rta_getattr_u64(GET_TSTAT(i, attr))) | |
767 | ||
768 | SPRINT_TSTAT(rate, u64, " thresh ", THRESHOLD_RATE64); | |
769 | SPRINT_TSTAT(time, u32, " target ", TARGET_US); | |
770 | SPRINT_TSTAT(time, u32, " interval", INTERVAL_US); | |
771 | SPRINT_TSTAT(time, u32, " pk_delay", PEAK_DELAY_US); | |
772 | SPRINT_TSTAT(time, u32, " av_delay", AVG_DELAY_US); | |
773 | SPRINT_TSTAT(time, u32, " sp_delay", BASE_DELAY_US); | |
774 | SPRINT_TSTAT(size, u32, " backlog ", BACKLOG_BYTES); | |
775 | ||
776 | PRINT_TSTAT_U32(" pkts ", SENT_PACKETS); | |
777 | PRINT_TSTAT_U64(" bytes ", SENT_BYTES64); | |
778 | ||
779 | PRINT_TSTAT_U32(" way_inds", WAY_INDIRECT_HITS); | |
780 | PRINT_TSTAT_U32(" way_miss", WAY_MISSES); | |
781 | PRINT_TSTAT_U32(" way_cols", WAY_COLLISIONS); | |
782 | PRINT_TSTAT_U32(" drops ", DROPPED_PACKETS); | |
783 | PRINT_TSTAT_U32(" marks ", ECN_MARKED_PACKETS); | |
784 | PRINT_TSTAT_U32(" ack_drop", ACKS_DROPPED_PACKETS); | |
785 | PRINT_TSTAT_U32(" sp_flows", SPARSE_FLOWS); | |
786 | PRINT_TSTAT_U32(" bk_flows", BULK_FLOWS); | |
787 | PRINT_TSTAT_U32(" un_flows", UNRESPONSIVE_FLOWS); | |
788 | PRINT_TSTAT_U32(" max_len ", MAX_SKBLEN); | |
789 | PRINT_TSTAT_U32(" quantum ", FLOW_QUANTUM); | |
790 | ||
791 | #undef GET_STAT | |
792 | #undef PRINT_TSTAT | |
793 | #undef SPRINT_TSTAT | |
794 | #undef PRINT_TSTAT_U32 | |
795 | #undef PRINT_TSTAT_U64 | |
796 | } | |
797 | return 0; | |
798 | } | |
799 | ||
800 | struct qdisc_util cake_qdisc_util = { | |
801 | .id = "cake", | |
802 | .parse_qopt = cake_parse_opt, | |
803 | .print_qopt = cake_print_opt, | |
804 | .print_xstats = cake_print_xstats, | |
805 | }; |