1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <linux/pkt_sched.h>
4 
5 #include "alloc-util.h"
6 #include "conf-parser.h"
7 #include "ets.h"
8 #include "extract-word.h"
9 #include "memory-util.h"
10 #include "netlink-util.h"
11 #include "parse-util.h"
12 #include "qdisc.h"
13 #include "string-util.h"
14 #include "tc-util.h"
15 
enhanced_transmission_selection_fill_message(Link * link,QDisc * qdisc,sd_netlink_message * req)16 static int enhanced_transmission_selection_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
17         EnhancedTransmissionSelection *ets;
18         int r;
19 
20         assert(link);
21         assert(qdisc);
22         assert(req);
23 
24         assert_se(ets = ETS(qdisc));
25 
26         r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "ets");
27         if (r < 0)
28                 return r;
29 
30         r = sd_netlink_message_append_u8(req, TCA_ETS_NBANDS, ets->n_bands);
31         if (r < 0)
32                 return r;
33 
34         if (ets->n_strict > 0) {
35                 r = sd_netlink_message_append_u8(req, TCA_ETS_NSTRICT, ets->n_strict);
36                 if (r < 0)
37                         return r;
38         }
39 
40         if (ets->n_quanta > 0) {
41                 r = sd_netlink_message_open_container(req, TCA_ETS_QUANTA);
42                 if (r < 0)
43                         return r;
44 
45                 for (unsigned i = 0; i < ets->n_quanta; i++) {
46                         r = sd_netlink_message_append_u32(req, TCA_ETS_QUANTA_BAND, ets->quanta[i]);
47                         if (r < 0)
48                                 return r;
49                 }
50 
51                 r = sd_netlink_message_close_container(req);
52                 if (r < 0)
53                         return r;
54         }
55 
56         if (ets->n_prio > 0) {
57                 r = sd_netlink_message_open_container(req, TCA_ETS_PRIOMAP);
58                 if (r < 0)
59                         return r;
60 
61                 for (unsigned i = 0; i < ets->n_prio; i++) {
62                         r = sd_netlink_message_append_u8(req, TCA_ETS_PRIOMAP_BAND, ets->prio[i]);
63                         if (r < 0)
64                                 return r;
65                 }
66 
67                 r = sd_netlink_message_close_container(req);
68                 if (r < 0)
69                         return r;
70         }
71 
72         r = sd_netlink_message_close_container(req);
73         if (r < 0)
74                 return r;
75 
76         return 0;
77 }
78 
config_parse_ets_u8(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)79 int config_parse_ets_u8(
80                 const char *unit,
81                 const char *filename,
82                 unsigned line,
83                 const char *section,
84                 unsigned section_line,
85                 const char *lvalue,
86                 int ltype,
87                 const char *rvalue,
88                 void *data,
89                 void *userdata) {
90 
91         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
92         EnhancedTransmissionSelection *ets;
93         Network *network = data;
94         uint8_t v, *p;
95         int r;
96 
97         assert(filename);
98         assert(lvalue);
99         assert(rvalue);
100         assert(data);
101 
102         r = qdisc_new_static(QDISC_KIND_ETS, network, filename, section_line, &qdisc);
103         if (r == -ENOMEM)
104                 return log_oom();
105         if (r < 0) {
106                 log_syntax(unit, LOG_WARNING, filename, line, r,
107                            "More than one kind of queueing discipline, ignoring assignment: %m");
108                 return 0;
109         }
110 
111         ets = ETS(qdisc);
112         if (streq(lvalue, "Bands"))
113                 p = &ets->n_bands;
114         else if (streq(lvalue, "StrictBands"))
115                 p = &ets->n_strict;
116         else
117                 assert_not_reached();
118 
119         if (isempty(rvalue)) {
120                 *p = 0;
121 
122                 qdisc = NULL;
123                 return 0;
124         }
125 
126         r = safe_atou8(rvalue, &v);
127         if (r < 0) {
128                 log_syntax(unit, LOG_WARNING, filename, line, r,
129                            "Failed to parse '%s=', ignoring assignment: %s",
130                            lvalue, rvalue);
131                 return 0;
132         }
133         if (v > TCQ_ETS_MAX_BANDS) {
134                 log_syntax(unit, LOG_WARNING, filename, line, 0,
135                            "Invalid '%s='. The value must be <= %d, ignoring assignment: %s",
136                            lvalue, TCQ_ETS_MAX_BANDS, rvalue);
137                 return 0;
138         }
139 
140         *p = v;
141         qdisc = NULL;
142 
143         return 0;
144 }
145 
config_parse_ets_quanta(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)146 int config_parse_ets_quanta(
147                 const char *unit,
148                 const char *filename,
149                 unsigned line,
150                 const char *section,
151                 unsigned section_line,
152                 const char *lvalue,
153                 int ltype,
154                 const char *rvalue,
155                 void *data,
156                 void *userdata) {
157 
158         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
159         EnhancedTransmissionSelection *ets;
160         Network *network = data;
161         int r;
162 
163         assert(filename);
164         assert(lvalue);
165         assert(rvalue);
166         assert(data);
167 
168         r = qdisc_new_static(QDISC_KIND_ETS, network, filename, section_line, &qdisc);
169         if (r == -ENOMEM)
170                 return log_oom();
171         if (r < 0) {
172                 log_syntax(unit, LOG_WARNING, filename, line, r,
173                            "More than one kind of queueing discipline, ignoring assignment: %m");
174                 return 0;
175         }
176 
177         ets = ETS(qdisc);
178 
179         if (isempty(rvalue)) {
180                 memzero(ets->quanta, sizeof(uint32_t) * TCQ_ETS_MAX_BANDS);
181                 ets->n_quanta = 0;
182 
183                 qdisc = NULL;
184                 return 0;
185         }
186 
187         for (const char *p = rvalue;;) {
188                 _cleanup_free_ char *word = NULL;
189                 uint64_t v;
190 
191                 r = extract_first_word(&p, &word, NULL, 0);
192                 if (r == -ENOMEM)
193                         return log_oom();
194                 if (r < 0) {
195                         log_syntax(unit, LOG_WARNING, filename, line, r,
196                                    "Failed to extract next value, ignoring: %m");
197                         break;
198                 }
199                 if (r == 0)
200                         break;
201 
202                 r = parse_size(word, 1024, &v);
203                 if (r < 0) {
204                         log_syntax(unit, LOG_WARNING, filename, line, r,
205                                    "Failed to parse '%s=', ignoring assignment: %s",
206                                    lvalue, word);
207                         continue;
208                 }
209                 if (v == 0 || v > UINT32_MAX) {
210                         log_syntax(unit, LOG_WARNING, filename, line, 0,
211                                    "Invalid '%s=', ignoring assignment: %s",
212                                    lvalue, word);
213                         continue;
214                 }
215                 if (ets->n_quanta >= TCQ_ETS_MAX_BANDS) {
216                         log_syntax(unit, LOG_WARNING, filename, line, 0,
217                                    "Too many quanta in '%s=', ignoring assignment: %s",
218                                    lvalue, word);
219                         continue;
220                 }
221 
222                 ets->quanta[ets->n_quanta++] = v;
223         }
224 
225         qdisc = NULL;
226 
227         return 0;
228 }
229 
config_parse_ets_prio(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)230 int config_parse_ets_prio(
231                 const char *unit,
232                 const char *filename,
233                 unsigned line,
234                 const char *section,
235                 unsigned section_line,
236                 const char *lvalue,
237                 int ltype,
238                 const char *rvalue,
239                 void *data,
240                 void *userdata) {
241 
242         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
243         EnhancedTransmissionSelection *ets;
244         Network *network = data;
245         int r;
246 
247         assert(filename);
248         assert(lvalue);
249         assert(rvalue);
250         assert(data);
251 
252         r = qdisc_new_static(QDISC_KIND_ETS, network, filename, section_line, &qdisc);
253         if (r == -ENOMEM)
254                 return log_oom();
255         if (r < 0) {
256                 log_syntax(unit, LOG_WARNING, filename, line, r,
257                            "More than one kind of queueing discipline, ignoring assignment: %m");
258                 return 0;
259         }
260 
261         ets = ETS(qdisc);
262 
263         if (isempty(rvalue)) {
264                 memzero(ets->prio, sizeof(uint8_t) * (TC_PRIO_MAX + 1));
265                 ets->n_prio = 0;
266 
267                 qdisc = NULL;
268                 return 0;
269         }
270 
271         for (const char *p = rvalue;;) {
272                 _cleanup_free_ char *word = NULL;
273                 uint8_t v;
274 
275                 r = extract_first_word(&p, &word, NULL, 0);
276                 if (r == -ENOMEM)
277                         return log_oom();
278                 if (r < 0) {
279                         log_syntax(unit, LOG_WARNING, filename, line, r,
280                                    "Failed to extract next value, ignoring: %m");
281                         break;
282                 }
283                 if (r == 0)
284                         break;
285 
286                 r = safe_atou8(word, &v);
287                 if (r < 0) {
288                         log_syntax(unit, LOG_WARNING, filename, line, r,
289                                    "Failed to parse '%s=', ignoring assignment: %s",
290                                    lvalue, word);
291                         continue;
292                 }
293                 if (ets->n_prio > TC_PRIO_MAX) {
294                         log_syntax(unit, LOG_WARNING, filename, line, 0,
295                                    "Too many priomap in '%s=', ignoring assignment: %s",
296                                    lvalue, word);
297                         continue;
298                 }
299 
300                 ets->prio[ets->n_prio++] = v;
301         }
302 
303         qdisc = NULL;
304 
305         return 0;
306 }
307 
enhanced_transmission_selection_verify(QDisc * qdisc)308 static int enhanced_transmission_selection_verify(QDisc *qdisc) {
309         EnhancedTransmissionSelection *ets;
310 
311         assert(qdisc);
312 
313         ets = ETS(qdisc);
314 
315         if (ets->n_bands == 0)
316                 ets->n_bands = ets->n_strict + ets->n_quanta;
317 
318         if (ets->n_bands == 0)
319                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
320                                          "%s: At least one of Band=, Strict=, or Quanta= must be specified. "
321                                          "Ignoring [EnhancedTransmissionSelection] section from line %u.",
322                                          qdisc->section->filename, qdisc->section->line);
323 
324         if (ets->n_bands < ets->n_strict + ets->n_quanta)
325                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
326                                          "%s: Not enough total bands to cover all the strict bands and quanta. "
327                                          "Ignoring [EnhancedTransmissionSelection] section from line %u.",
328                                          qdisc->section->filename, qdisc->section->line);
329 
330         for (unsigned i = 0; i < ets->n_prio; i++)
331                 if (ets->prio[i] >= ets->n_bands)
332                         return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
333                                                  "%s: PriorityMap= element is out of bands. "
334                                                  "Ignoring [EnhancedTransmissionSelection] section from line %u.",
335                                                  qdisc->section->filename, qdisc->section->line);
336 
337         return 0;
338 }
339 
340 const QDiscVTable ets_vtable = {
341         .object_size = sizeof(EnhancedTransmissionSelection),
342         .tca_kind = "ets",
343         .fill_message = enhanced_transmission_selection_fill_message,
344         .verify = enhanced_transmission_selection_verify,
345 };
346