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