1 /* SPDX-License-Identifier: LGPL-2.1-or-later
2  * Copyright © 2019 VMware, Inc. */
3 
4 #include <linux/pkt_sched.h>
5 
6 #include "alloc-util.h"
7 #include "conf-parser.h"
8 #include "netlink-util.h"
9 #include "parse-util.h"
10 #include "qdisc.h"
11 #include "string-util.h"
12 #include "strv.h"
13 
fair_queueing_controlled_delay_init(QDisc * qdisc)14 static int fair_queueing_controlled_delay_init(QDisc *qdisc) {
15         FairQueueingControlledDelay *fqcd;
16 
17         assert(qdisc);
18 
19         fqcd = FQ_CODEL(qdisc);
20 
21         fqcd->memory_limit = UINT32_MAX;
22         fqcd->ce_threshold_usec = USEC_INFINITY;
23         fqcd->ecn = -1;
24 
25         return 0;
26 }
27 
fair_queueing_controlled_delay_fill_message(Link * link,QDisc * qdisc,sd_netlink_message * req)28 static int fair_queueing_controlled_delay_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
29         FairQueueingControlledDelay *fqcd;
30         int r;
31 
32         assert(link);
33         assert(qdisc);
34         assert(req);
35 
36         assert_se(fqcd = FQ_CODEL(qdisc));
37 
38         r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "fq_codel");
39         if (r < 0)
40                 return r;
41 
42         if (fqcd->packet_limit > 0) {
43                 r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_LIMIT, fqcd->packet_limit);
44                 if (r < 0)
45                         return r;
46         }
47 
48         if (fqcd->flows > 0) {
49                 r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_FLOWS, fqcd->flows);
50                 if (r < 0)
51                         return r;
52         }
53 
54         if (fqcd->quantum > 0) {
55                 r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_QUANTUM, fqcd->quantum);
56                 if (r < 0)
57                         return r;
58         }
59 
60         if (fqcd->interval_usec > 0) {
61                 r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_INTERVAL, fqcd->interval_usec);
62                 if (r < 0)
63                         return r;
64         }
65 
66         if (fqcd->target_usec > 0) {
67                 r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_TARGET, fqcd->target_usec);
68                 if (r < 0)
69                         return r;
70         }
71 
72         if (fqcd->ecn >= 0) {
73                 r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_ECN, fqcd->ecn);
74                 if (r < 0)
75                         return r;
76         }
77 
78         if (fqcd->ce_threshold_usec != USEC_INFINITY) {
79                 r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_CE_THRESHOLD, fqcd->ce_threshold_usec);
80                 if (r < 0)
81                         return r;
82         }
83 
84         if (fqcd->memory_limit != UINT32_MAX) {
85                 r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_MEMORY_LIMIT, fqcd->memory_limit);
86                 if (r < 0)
87                         return r;
88         }
89 
90         r = sd_netlink_message_close_container(req);
91         if (r < 0)
92                 return r;
93 
94         return 0;
95 }
96 
config_parse_fair_queueing_controlled_delay_u32(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)97 int config_parse_fair_queueing_controlled_delay_u32(
98                 const char *unit,
99                 const char *filename,
100                 unsigned line,
101                 const char *section,
102                 unsigned section_line,
103                 const char *lvalue,
104                 int ltype,
105                 const char *rvalue,
106                 void *data,
107                 void *userdata) {
108 
109         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
110         FairQueueingControlledDelay *fqcd;
111         Network *network = data;
112         uint32_t *p;
113         int r;
114 
115         assert(filename);
116         assert(lvalue);
117         assert(rvalue);
118         assert(data);
119 
120         r = qdisc_new_static(QDISC_KIND_FQ_CODEL, network, filename, section_line, &qdisc);
121         if (r == -ENOMEM)
122                 return log_oom();
123         if (r < 0) {
124                 log_syntax(unit, LOG_WARNING, filename, line, r,
125                            "More than one kind of queueing discipline, ignoring assignment: %m");
126                 return 0;
127         }
128 
129         fqcd = FQ_CODEL(qdisc);
130 
131         if (streq(lvalue, "PacketLimit"))
132                 p = &fqcd->packet_limit;
133         else if (streq(lvalue, "Flows"))
134                 p = &fqcd->flows;
135         else
136                 assert_not_reached();
137 
138         if (isempty(rvalue)) {
139                 *p = 0;
140 
141                 TAKE_PTR(qdisc);
142                 return 0;
143         }
144 
145         r = safe_atou32(rvalue, p);
146         if (r < 0) {
147                 log_syntax(unit, LOG_WARNING, filename, line, r,
148                            "Failed to parse '%s=', ignoring assignment: %s",
149                            lvalue, rvalue);
150                 return 0;
151         }
152 
153         TAKE_PTR(qdisc);
154 
155         return 0;
156 }
157 
config_parse_fair_queueing_controlled_delay_usec(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)158 int config_parse_fair_queueing_controlled_delay_usec(
159                 const char *unit,
160                 const char *filename,
161                 unsigned line,
162                 const char *section,
163                 unsigned section_line,
164                 const char *lvalue,
165                 int ltype,
166                 const char *rvalue,
167                 void *data,
168                 void *userdata) {
169 
170         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
171         FairQueueingControlledDelay *fqcd;
172         Network *network = data;
173         usec_t *p;
174         int r;
175 
176         assert(filename);
177         assert(lvalue);
178         assert(rvalue);
179         assert(data);
180 
181         r = qdisc_new_static(QDISC_KIND_FQ_CODEL, network, filename, section_line, &qdisc);
182         if (r == -ENOMEM)
183                 return log_oom();
184         if (r < 0) {
185                 log_syntax(unit, LOG_WARNING, filename, line, r,
186                            "More than one kind of queueing discipline, ignoring assignment: %m");
187                 return 0;
188         }
189 
190         fqcd = FQ_CODEL(qdisc);
191 
192         if (streq(lvalue, "TargetSec"))
193                 p = &fqcd->target_usec;
194         else if (streq(lvalue, "IntervalSec"))
195                 p = &fqcd->interval_usec;
196         else if (streq(lvalue, "CEThresholdSec"))
197                 p = &fqcd->ce_threshold_usec;
198         else
199                 assert_not_reached();
200 
201         if (isempty(rvalue)) {
202                 if (streq(lvalue, "CEThresholdSec"))
203                         *p = USEC_INFINITY;
204                 else
205                         *p = 0;
206 
207                 TAKE_PTR(qdisc);
208                 return 0;
209         }
210 
211         r = parse_sec(rvalue, p);
212         if (r < 0) {
213                 log_syntax(unit, LOG_WARNING, filename, line, r,
214                            "Failed to parse '%s=', ignoring assignment: %s",
215                            lvalue, rvalue);
216                 return 0;
217         }
218 
219         TAKE_PTR(qdisc);
220 
221         return 0;
222 }
223 
config_parse_fair_queueing_controlled_delay_bool(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)224 int config_parse_fair_queueing_controlled_delay_bool(
225                 const char *unit,
226                 const char *filename,
227                 unsigned line,
228                 const char *section,
229                 unsigned section_line,
230                 const char *lvalue,
231                 int ltype,
232                 const char *rvalue,
233                 void *data,
234                 void *userdata) {
235 
236         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
237         FairQueueingControlledDelay *fqcd;
238         Network *network = data;
239         int r;
240 
241         assert(filename);
242         assert(lvalue);
243         assert(rvalue);
244         assert(data);
245 
246         r = qdisc_new_static(QDISC_KIND_FQ_CODEL, network, filename, section_line, &qdisc);
247         if (r == -ENOMEM)
248                 return log_oom();
249         if (r < 0) {
250                 log_syntax(unit, LOG_WARNING, filename, line, r,
251                            "More than one kind of queueing discipline, ignoring assignment: %m");
252                 return 0;
253         }
254 
255         fqcd = FQ_CODEL(qdisc);
256 
257         if (isempty(rvalue)) {
258                 fqcd->ecn = -1;
259 
260                 TAKE_PTR(qdisc);
261                 return 0;
262         }
263 
264         r = parse_boolean(rvalue);
265         if (r < 0) {
266                 log_syntax(unit, LOG_WARNING, filename, line, r,
267                            "Failed to parse '%s=', ignoring assignment: %s",
268                            lvalue, rvalue);
269                 return 0;
270         }
271 
272         fqcd->ecn = r;
273         TAKE_PTR(qdisc);
274 
275         return 0;
276 }
277 
config_parse_fair_queueing_controlled_delay_size(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)278 int config_parse_fair_queueing_controlled_delay_size(
279                 const char *unit,
280                 const char *filename,
281                 unsigned line,
282                 const char *section,
283                 unsigned section_line,
284                 const char *lvalue,
285                 int ltype,
286                 const char *rvalue,
287                 void *data,
288                 void *userdata) {
289 
290         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
291         FairQueueingControlledDelay *fqcd;
292         Network *network = data;
293         uint64_t sz;
294         uint32_t *p;
295         int r;
296 
297         assert(filename);
298         assert(lvalue);
299         assert(rvalue);
300         assert(data);
301 
302         r = qdisc_new_static(QDISC_KIND_FQ_CODEL, network, filename, section_line, &qdisc);
303         if (r == -ENOMEM)
304                 return log_oom();
305         if (r < 0) {
306                 log_syntax(unit, LOG_WARNING, filename, line, r,
307                            "More than one kind of queueing discipline, ignoring assignment: %m");
308                 return 0;
309         }
310 
311         fqcd = FQ_CODEL(qdisc);
312 
313         if (STR_IN_SET(lvalue, "MemoryLimitBytes", "MemoryLimit"))
314                 p = &fqcd->memory_limit;
315         else if (STR_IN_SET(lvalue, "QuantumBytes", "Quantum"))
316                 p = &fqcd->quantum;
317         else
318                 assert_not_reached();
319 
320         if (isempty(rvalue)) {
321                 if (STR_IN_SET(lvalue, "MemoryLimitBytes", "MemoryLimit"))
322                         *p = UINT32_MAX;
323                 else
324                         *p = 0;
325 
326                 TAKE_PTR(qdisc);
327                 return 0;
328         }
329 
330         r = parse_size(rvalue, 1024, &sz);
331         if (r < 0) {
332                 log_syntax(unit, LOG_WARNING, filename, line, r,
333                            "Failed to parse '%s=', ignoring assignment: %s",
334                            lvalue, rvalue);
335                 return 0;
336         }
337         if (sz >= UINT32_MAX) {
338                 log_syntax(unit, LOG_WARNING, filename, line, 0,
339                            "Specified '%s=' is too large, ignoring assignment: %s",
340                            lvalue, rvalue);
341                 return 0;
342         }
343 
344         *p = sz;
345         TAKE_PTR(qdisc);
346 
347         return 0;
348 }
349 
350 const QDiscVTable fq_codel_vtable = {
351         .object_size = sizeof(FairQueueingControlledDelay),
352         .tca_kind = "fq_codel",
353         .init = fair_queueing_controlled_delay_init,
354         .fill_message = fair_queueing_controlled_delay_fill_message,
355 };
356