1 /* SPDX-License-Identifier: LGPL-2.1-or-later
2 * Copyright © 2020 VMware, Inc. */
3
4 #include <linux/pkt_sched.h>
5
6 #include "alloc-util.h"
7 #include "cake.h"
8 #include "conf-parser.h"
9 #include "netlink-util.h"
10 #include "parse-util.h"
11 #include "qdisc.h"
12 #include "string-table.h"
13 #include "string-util.h"
14
cake_init(QDisc * qdisc)15 static int cake_init(QDisc *qdisc) {
16 CommonApplicationsKeptEnhanced *c;
17
18 assert(qdisc);
19
20 c = CAKE(qdisc);
21
22 c->autorate = -1;
23 c->compensation_mode = _CAKE_COMPENSATION_MODE_INVALID;
24 c->raw = -1;
25 c->flow_isolation_mode = _CAKE_FLOW_ISOLATION_MODE_INVALID;
26 c->nat = -1;
27 c->preset = _CAKE_PRESET_INVALID;
28 c->wash = -1;
29 c->split_gso = -1;
30
31 return 0;
32 }
33
cake_fill_message(Link * link,QDisc * qdisc,sd_netlink_message * req)34 static int cake_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
35 CommonApplicationsKeptEnhanced *c;
36 int r;
37
38 assert(link);
39 assert(qdisc);
40 assert(req);
41
42 assert_se(c = CAKE(qdisc));
43
44 r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "cake");
45 if (r < 0)
46 return r;
47
48 if (c->bandwidth > 0) {
49 r = sd_netlink_message_append_u64(req, TCA_CAKE_BASE_RATE64, c->bandwidth);
50 if (r < 0)
51 return r;
52 }
53
54 if (c->autorate >= 0) {
55 r = sd_netlink_message_append_u32(req, TCA_CAKE_AUTORATE, c->autorate);
56 if (r < 0)
57 return r;
58 }
59
60 if (c->overhead_set) {
61 r = sd_netlink_message_append_s32(req, TCA_CAKE_OVERHEAD, c->overhead);
62 if (r < 0)
63 return r;
64 }
65
66 if (c->mpu > 0) {
67 r = sd_netlink_message_append_u32(req, TCA_CAKE_MPU, c->mpu);
68 if (r < 0)
69 return r;
70 }
71
72 if (c->compensation_mode >= 0) {
73 r = sd_netlink_message_append_u32(req, TCA_CAKE_ATM, c->compensation_mode);
74 if (r < 0)
75 return r;
76 }
77
78 if (c->raw > 0) {
79 /* TCA_CAKE_RAW attribute is mostly a flag, not boolean. */
80 r = sd_netlink_message_append_u32(req, TCA_CAKE_RAW, 0);
81 if (r < 0)
82 return r;
83 }
84
85 if (c->flow_isolation_mode >= 0) {
86 r = sd_netlink_message_append_u32(req, TCA_CAKE_FLOW_MODE, c->flow_isolation_mode);
87 if (r < 0)
88 return r;
89 }
90
91 if (c->nat >= 0) {
92 r = sd_netlink_message_append_u32(req, TCA_CAKE_NAT, c->nat);
93 if (r < 0)
94 return r;
95 }
96
97 if (c->preset >= 0) {
98 r = sd_netlink_message_append_u32(req, TCA_CAKE_DIFFSERV_MODE, c->preset);
99 if (r < 0)
100 return r;
101 }
102
103 if (c->fwmark > 0) {
104 r = sd_netlink_message_append_u32(req, TCA_CAKE_FWMARK, c->fwmark);
105 if (r < 0)
106 return r;
107 }
108
109 if (c->wash >= 0) {
110 r = sd_netlink_message_append_u32(req, TCA_CAKE_WASH, c->wash);
111 if (r < 0)
112 return r;
113 }
114
115 if (c->split_gso >= 0) {
116 r = sd_netlink_message_append_u32(req, TCA_CAKE_SPLIT_GSO, c->split_gso);
117 if (r < 0)
118 return r;
119 }
120
121 r = sd_netlink_message_close_container(req);
122 if (r < 0)
123 return r;
124
125 return 0;
126 }
127
config_parse_cake_bandwidth(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)128 int config_parse_cake_bandwidth(
129 const char *unit,
130 const char *filename,
131 unsigned line,
132 const char *section,
133 unsigned section_line,
134 const char *lvalue,
135 int ltype,
136 const char *rvalue,
137 void *data,
138 void *userdata) {
139
140 _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
141 CommonApplicationsKeptEnhanced *c;
142 Network *network = data;
143 uint64_t k;
144 int r;
145
146 assert(filename);
147 assert(lvalue);
148 assert(rvalue);
149 assert(data);
150
151 r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
152 if (r == -ENOMEM)
153 return log_oom();
154 if (r < 0) {
155 log_syntax(unit, LOG_WARNING, filename, line, r,
156 "More than one kind of queueing discipline, ignoring assignment: %m");
157 return 0;
158 }
159
160 c = CAKE(qdisc);
161
162 if (isempty(rvalue)) {
163 c->bandwidth = 0;
164
165 TAKE_PTR(qdisc);
166 return 0;
167 }
168
169 r = parse_size(rvalue, 1000, &k);
170 if (r < 0) {
171 log_syntax(unit, LOG_WARNING, filename, line, r,
172 "Failed to parse '%s=', ignoring assignment: %s",
173 lvalue, rvalue);
174 return 0;
175 }
176
177 c->bandwidth = k/8;
178 TAKE_PTR(qdisc);
179
180 return 0;
181 }
182
config_parse_cake_overhead(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)183 int config_parse_cake_overhead(
184 const char *unit,
185 const char *filename,
186 unsigned line,
187 const char *section,
188 unsigned section_line,
189 const char *lvalue,
190 int ltype,
191 const char *rvalue,
192 void *data,
193 void *userdata) {
194
195 _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
196 CommonApplicationsKeptEnhanced *c;
197 Network *network = data;
198 int32_t v;
199 int r;
200
201 assert(filename);
202 assert(lvalue);
203 assert(rvalue);
204 assert(data);
205
206 r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
207 if (r == -ENOMEM)
208 return log_oom();
209 if (r < 0) {
210 log_syntax(unit, LOG_WARNING, filename, line, r,
211 "More than one kind of queueing discipline, ignoring assignment: %m");
212 return 0;
213 }
214
215 c = CAKE(qdisc);
216
217 if (isempty(rvalue)) {
218 c->overhead_set = false;
219 TAKE_PTR(qdisc);
220 return 0;
221 }
222
223 r = safe_atoi32(rvalue, &v);
224 if (r < 0) {
225 log_syntax(unit, LOG_WARNING, filename, line, r,
226 "Failed to parse '%s=', ignoring assignment: %s",
227 lvalue, rvalue);
228 return 0;
229 }
230 if (v < -64 || v > 256) {
231 log_syntax(unit, LOG_WARNING, filename, line, 0,
232 "Invalid '%s=', ignoring assignment: %s",
233 lvalue, rvalue);
234 return 0;
235 }
236
237 c->overhead = v;
238 c->overhead_set = true;
239 TAKE_PTR(qdisc);
240 return 0;
241 }
242
config_parse_cake_mpu(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)243 int config_parse_cake_mpu(
244 const char *unit,
245 const char *filename,
246 unsigned line,
247 const char *section,
248 unsigned section_line,
249 const char *lvalue,
250 int ltype,
251 const char *rvalue,
252 void *data,
253 void *userdata) {
254
255 _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
256 CommonApplicationsKeptEnhanced *c;
257 Network *network = data;
258 uint32_t v;
259 int r;
260
261 assert(filename);
262 assert(lvalue);
263 assert(rvalue);
264 assert(data);
265
266 r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
267 if (r == -ENOMEM)
268 return log_oom();
269 if (r < 0) {
270 log_syntax(unit, LOG_WARNING, filename, line, r,
271 "More than one kind of queueing discipline, ignoring assignment: %m");
272 return 0;
273 }
274
275 c = CAKE(qdisc);
276
277 if (isempty(rvalue)) {
278 c->mpu = 0;
279 TAKE_PTR(qdisc);
280 return 0;
281 }
282
283 r = safe_atou32(rvalue, &v);
284 if (r < 0) {
285 log_syntax(unit, LOG_WARNING, filename, line, r,
286 "Failed to parse '%s=', ignoring assignment: %s",
287 lvalue, rvalue);
288 return 0;
289 }
290 if (v <= 0 || v > 256) {
291 log_syntax(unit, LOG_WARNING, filename, line, 0,
292 "Invalid '%s=', ignoring assignment: %s",
293 lvalue, rvalue);
294 return 0;
295 }
296
297 c->mpu = v;
298 TAKE_PTR(qdisc);
299 return 0;
300 }
301
config_parse_cake_tristate(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)302 int config_parse_cake_tristate(
303 const char *unit,
304 const char *filename,
305 unsigned line,
306 const char *section,
307 unsigned section_line,
308 const char *lvalue,
309 int ltype,
310 const char *rvalue,
311 void *data,
312 void *userdata) {
313
314 _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
315 CommonApplicationsKeptEnhanced *c;
316 Network *network = data;
317 int *dest, r;
318
319 assert(filename);
320 assert(lvalue);
321 assert(rvalue);
322 assert(data);
323
324 r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
325 if (r == -ENOMEM)
326 return log_oom();
327 if (r < 0) {
328 log_syntax(unit, LOG_WARNING, filename, line, r,
329 "More than one kind of queueing discipline, ignoring assignment: %m");
330 return 0;
331 }
332
333 c = CAKE(qdisc);
334
335 if (streq(lvalue, "AutoRateIngress"))
336 dest = &c->autorate;
337 else if (streq(lvalue, "UseRawPacketSize"))
338 dest = &c->raw;
339 else if (streq(lvalue, "NAT"))
340 dest = &c->nat;
341 else if (streq(lvalue, "Wash"))
342 dest = &c->wash;
343 else if (streq(lvalue, "SplitGSO"))
344 dest = &c->split_gso;
345 else
346 assert_not_reached();
347
348 if (isempty(rvalue)) {
349 *dest = -1;
350 TAKE_PTR(qdisc);
351 return 0;
352 }
353
354 r = parse_boolean(rvalue);
355 if (r < 0) {
356 log_syntax(unit, LOG_WARNING, filename, line, r,
357 "Failed to parse '%s=', ignoring assignment: %s",
358 lvalue, rvalue);
359 return 0;
360 }
361
362 *dest = r;
363 TAKE_PTR(qdisc);
364 return 0;
365 }
366
367 static const char * const cake_compensation_mode_table[_CAKE_COMPENSATION_MODE_MAX] = {
368 [CAKE_COMPENSATION_MODE_NONE] = "none",
369 [CAKE_COMPENSATION_MODE_ATM] = "atm",
370 [CAKE_COMPENSATION_MODE_PTM] = "ptm",
371 };
372
373 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(cake_compensation_mode, CakeCompensationMode);
374
config_parse_cake_compensation_mode(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)375 int config_parse_cake_compensation_mode(
376 const char *unit,
377 const char *filename,
378 unsigned line,
379 const char *section,
380 unsigned section_line,
381 const char *lvalue,
382 int ltype,
383 const char *rvalue,
384 void *data,
385 void *userdata) {
386
387 _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
388 CommonApplicationsKeptEnhanced *c;
389 Network *network = data;
390 CakeCompensationMode mode;
391 int r;
392
393 assert(filename);
394 assert(lvalue);
395 assert(rvalue);
396 assert(data);
397
398 r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
399 if (r == -ENOMEM)
400 return log_oom();
401 if (r < 0) {
402 log_syntax(unit, LOG_WARNING, filename, line, r,
403 "More than one kind of queueing discipline, ignoring assignment: %m");
404 return 0;
405 }
406
407 c = CAKE(qdisc);
408
409 if (isempty(rvalue)) {
410 c->compensation_mode = _CAKE_COMPENSATION_MODE_INVALID;
411 TAKE_PTR(qdisc);
412 return 0;
413 }
414
415 mode = cake_compensation_mode_from_string(rvalue);
416 if (mode < 0) {
417 log_syntax(unit, LOG_WARNING, filename, line, mode,
418 "Failed to parse '%s=', ignoring assignment: %s",
419 lvalue, rvalue);
420 return 0;
421 }
422
423 c->compensation_mode = mode;
424 TAKE_PTR(qdisc);
425 return 0;
426 }
427
428 static const char * const cake_flow_isolation_mode_table[_CAKE_FLOW_ISOLATION_MODE_MAX] = {
429 [CAKE_FLOW_ISOLATION_MODE_NONE] = "none",
430 [CAKE_FLOW_ISOLATION_MODE_SRC_IP] = "src-host",
431 [CAKE_FLOW_ISOLATION_MODE_DST_IP] = "dst-host",
432 [CAKE_FLOW_ISOLATION_MODE_HOSTS] = "hosts",
433 [CAKE_FLOW_ISOLATION_MODE_FLOWS] = "flows",
434 [CAKE_FLOW_ISOLATION_MODE_DUAL_SRC] = "dual-src-host",
435 [CAKE_FLOW_ISOLATION_MODE_DUAL_DST] = "dual-dst-host",
436 [CAKE_FLOW_ISOLATION_MODE_TRIPLE] = "triple",
437 };
438
439 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(cake_flow_isolation_mode, CakeFlowIsolationMode);
440
config_parse_cake_flow_isolation_mode(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)441 int config_parse_cake_flow_isolation_mode(
442 const char *unit,
443 const char *filename,
444 unsigned line,
445 const char *section,
446 unsigned section_line,
447 const char *lvalue,
448 int ltype,
449 const char *rvalue,
450 void *data,
451 void *userdata) {
452
453 _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
454 CommonApplicationsKeptEnhanced *c;
455 Network *network = data;
456 CakeFlowIsolationMode mode;
457 int r;
458
459 assert(filename);
460 assert(lvalue);
461 assert(rvalue);
462 assert(data);
463
464 r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
465 if (r == -ENOMEM)
466 return log_oom();
467 if (r < 0) {
468 log_syntax(unit, LOG_WARNING, filename, line, r,
469 "More than one kind of queueing discipline, ignoring assignment: %m");
470 return 0;
471 }
472
473 c = CAKE(qdisc);
474
475 if (isempty(rvalue)) {
476 c->flow_isolation_mode = _CAKE_FLOW_ISOLATION_MODE_INVALID;
477 TAKE_PTR(qdisc);
478 return 0;
479 }
480
481 mode = cake_flow_isolation_mode_from_string(rvalue);
482 if (mode < 0) {
483 log_syntax(unit, LOG_WARNING, filename, line, mode,
484 "Failed to parse '%s=', ignoring assignment: %s",
485 lvalue, rvalue);
486 return 0;
487 }
488
489 c->flow_isolation_mode = mode;
490 TAKE_PTR(qdisc);
491 return 0;
492 }
493
494 static const char * const cake_priority_queueing_preset_table[_CAKE_PRESET_MAX] = {
495 [CAKE_PRESET_DIFFSERV3] = "diffserv3",
496 [CAKE_PRESET_DIFFSERV4] = "diffserv4",
497 [CAKE_PRESET_DIFFSERV8] = "diffserv8",
498 [CAKE_PRESET_BESTEFFORT] = "besteffort",
499 [CAKE_PRESET_PRECEDENCE] = "precedence",
500 };
501
502 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(cake_priority_queueing_preset, CakePriorityQueueingPreset);
503
config_parse_cake_priority_queueing_preset(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)504 int config_parse_cake_priority_queueing_preset(
505 const char *unit,
506 const char *filename,
507 unsigned line,
508 const char *section,
509 unsigned section_line,
510 const char *lvalue,
511 int ltype,
512 const char *rvalue,
513 void *data,
514 void *userdata) {
515
516 _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
517 CommonApplicationsKeptEnhanced *c;
518 CakePriorityQueueingPreset preset;
519 Network *network = data;
520 int r;
521
522 assert(filename);
523 assert(lvalue);
524 assert(rvalue);
525 assert(data);
526
527 r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
528 if (r == -ENOMEM)
529 return log_oom();
530 if (r < 0) {
531 log_syntax(unit, LOG_WARNING, filename, line, r,
532 "More than one kind of queueing discipline, ignoring assignment: %m");
533 return 0;
534 }
535
536 c = CAKE(qdisc);
537
538 if (isempty(rvalue)) {
539 c->preset = _CAKE_PRESET_INVALID;
540 TAKE_PTR(qdisc);
541 return 0;
542 }
543
544 preset = cake_priority_queueing_preset_from_string(rvalue);
545 if (preset < 0) {
546 log_syntax(unit, LOG_WARNING, filename, line, preset,
547 "Failed to parse '%s=', ignoring assignment: %s",
548 lvalue, rvalue);
549 return 0;
550 }
551
552 c->preset = preset;
553 TAKE_PTR(qdisc);
554 return 0;
555 }
556
config_parse_cake_fwmark(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)557 int config_parse_cake_fwmark(
558 const char *unit,
559 const char *filename,
560 unsigned line,
561 const char *section,
562 unsigned section_line,
563 const char *lvalue,
564 int ltype,
565 const char *rvalue,
566 void *data,
567 void *userdata) {
568
569 _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
570 CommonApplicationsKeptEnhanced *c;
571 Network *network = data;
572 uint32_t fwmark;
573 int r;
574
575 assert(filename);
576 assert(lvalue);
577 assert(rvalue);
578 assert(data);
579
580 r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
581 if (r == -ENOMEM)
582 return log_oom();
583 if (r < 0) {
584 log_syntax(unit, LOG_WARNING, filename, line, r,
585 "More than one kind of queueing discipline, ignoring assignment: %m");
586 return 0;
587 }
588
589 c = CAKE(qdisc);
590
591 if (isempty(rvalue)) {
592 c->fwmark = 0;
593 TAKE_PTR(qdisc);
594 return 0;
595 }
596
597 r = safe_atou32(rvalue, &fwmark);
598 if (r < 0) {
599 log_syntax(unit, LOG_WARNING, filename, line, r,
600 "Failed to parse '%s=', ignoring assignment: %s",
601 lvalue, rvalue);
602 return 0;
603 }
604 if (fwmark <= 0) {
605 log_syntax(unit, LOG_WARNING, filename, line, 0,
606 "Invalid '%s=', ignoring assignment: %s",
607 lvalue, rvalue);
608 return 0;
609 }
610
611 c->fwmark = fwmark;
612 TAKE_PTR(qdisc);
613 return 0;
614 }
615
616 const QDiscVTable cake_vtable = {
617 .object_size = sizeof(CommonApplicationsKeptEnhanced),
618 .tca_kind = "cake",
619 .init = cake_init,
620 .fill_message = cake_fill_message,
621 };
622