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