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