]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/tc/htb.c
Merge pull request #34499 from YHNdnzj/sd-path-trivial-cleanup
[thirdparty/systemd.git] / src / network / tc / htb.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
b934ac3d
YW
2
3#include <linux/pkt_sched.h>
4
5#include "alloc-util.h"
6#include "conf-parser.h"
7#include "netlink-util.h"
3a67b8bb 8#include "networkd-link.h"
b934ac3d
YW
9#include "parse-util.h"
10#include "qdisc.h"
11#include "htb.h"
12#include "string-util.h"
19f86a63 13#include "tc-util.h"
b934ac3d 14
d9eacc1c
YW
15#define HTB_DEFAULT_RATE_TO_QUANTUM 10
16#define HTB_DEFAULT_MTU 1600 /* Ethernet packet length */
17
b934ac3d
YW
18static int hierarchy_token_bucket_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
19 HierarchyTokenBucket *htb;
b934ac3d
YW
20 int r;
21
22 assert(link);
23 assert(qdisc);
24 assert(req);
25
16924f54 26 assert_se(htb = HTB(qdisc));
b934ac3d 27
16924f54
ZJS
28 struct tc_htb_glob opt = {
29 .version = 3,
30 .rate2quantum = htb->rate_to_quantum,
31 .defcls = htb->default_class,
32 };
b934ac3d
YW
33
34 r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "htb");
35 if (r < 0)
16924f54 36 return r;
b934ac3d
YW
37
38 r = sd_netlink_message_append_data(req, TCA_HTB_INIT, &opt, sizeof(opt));
39 if (r < 0)
16924f54 40 return r;
b934ac3d
YW
41
42 r = sd_netlink_message_close_container(req);
43 if (r < 0)
16924f54 44 return r;
b934ac3d
YW
45 return 0;
46}
47
48int config_parse_hierarchy_token_bucket_default_class(
49 const char *unit,
50 const char *filename,
51 unsigned line,
52 const char *section,
53 unsigned section_line,
54 const char *lvalue,
55 int ltype,
56 const char *rvalue,
57 void *data,
58 void *userdata) {
59
9b294afa 60 _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b934ac3d 61 HierarchyTokenBucket *htb;
99534007 62 Network *network = ASSERT_PTR(data);
b934ac3d
YW
63 int r;
64
65 assert(filename);
66 assert(lvalue);
67 assert(rvalue);
b934ac3d
YW
68
69 r = qdisc_new_static(QDISC_KIND_HTB, network, filename, section_line, &qdisc);
70 if (r == -ENOMEM)
71 return log_oom();
d96edb2c
YW
72 if (r < 0) {
73 log_syntax(unit, LOG_WARNING, filename, line, r,
74 "More than one kind of queueing discipline, ignoring assignment: %m");
75 return 0;
76 }
b934ac3d
YW
77
78 htb = HTB(qdisc);
79
80 if (isempty(rvalue)) {
81 htb->default_class = 0;
82
0132453c 83 TAKE_PTR(qdisc);
b934ac3d
YW
84 return 0;
85 }
86
87 r = safe_atou32_full(rvalue, 16, &htb->default_class);
88 if (r < 0) {
d96edb2c 89 log_syntax(unit, LOG_WARNING, filename, line, r,
b934ac3d
YW
90 "Failed to parse '%s=', ignoring assignment: %s",
91 lvalue, rvalue);
92 return 0;
93 }
94
0132453c 95 TAKE_PTR(qdisc);
b934ac3d
YW
96
97 return 0;
98}
99
d9eacc1c
YW
100int config_parse_hierarchy_token_bucket_u32(
101 const char *unit,
102 const char *filename,
103 unsigned line,
104 const char *section,
105 unsigned section_line,
106 const char *lvalue,
107 int ltype,
108 const char *rvalue,
109 void *data,
110 void *userdata) {
111
9b294afa 112 _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
d9eacc1c 113 HierarchyTokenBucket *htb;
99534007 114 Network *network = ASSERT_PTR(data);
d9eacc1c
YW
115 int r;
116
117 assert(filename);
118 assert(lvalue);
119 assert(rvalue);
d9eacc1c
YW
120
121 r = qdisc_new_static(QDISC_KIND_HTB, network, filename, section_line, &qdisc);
122 if (r == -ENOMEM)
123 return log_oom();
d96edb2c
YW
124 if (r < 0) {
125 log_syntax(unit, LOG_WARNING, filename, line, r,
126 "More than one kind of queueing discipline, ignoring assignment: %m");
127 return 0;
128 }
d9eacc1c
YW
129
130 htb = HTB(qdisc);
131
132 if (isempty(rvalue)) {
133 htb->rate_to_quantum = HTB_DEFAULT_RATE_TO_QUANTUM;
134
0132453c 135 TAKE_PTR(qdisc);
d9eacc1c
YW
136 return 0;
137 }
138
139 r = safe_atou32(rvalue, &htb->rate_to_quantum);
140 if (r < 0) {
d96edb2c 141 log_syntax(unit, LOG_WARNING, filename, line, r,
d9eacc1c
YW
142 "Failed to parse '%s=', ignoring assignment: %s",
143 lvalue, rvalue);
144 return 0;
145 }
146
0132453c 147 TAKE_PTR(qdisc);
d9eacc1c
YW
148
149 return 0;
150}
151
152static int hierarchy_token_bucket_init(QDisc *qdisc) {
153 HierarchyTokenBucket *htb;
154
155 assert(qdisc);
156
157 htb = HTB(qdisc);
158
159 htb->rate_to_quantum = HTB_DEFAULT_RATE_TO_QUANTUM;
160
161 return 0;
162}
163
b934ac3d
YW
164const QDiscVTable htb_vtable = {
165 .object_size = sizeof(HierarchyTokenBucket),
166 .tca_kind = "htb",
167 .fill_message = hierarchy_token_bucket_fill_message,
d9eacc1c 168 .init = hierarchy_token_bucket_init,
b934ac3d 169};
19f86a63
YW
170
171static int hierarchy_token_bucket_class_fill_message(Link *link, TClass *tclass, sd_netlink_message *req) {
172 HierarchyTokenBucketClass *htb;
d9eacc1c 173 uint32_t rtab[256], ctab[256];
19f86a63
YW
174 int r;
175
176 assert(link);
177 assert(tclass);
178 assert(req);
179
16924f54 180 assert_se(htb = TCLASS_TO_HTB(tclass));
19f86a63 181
16924f54
ZJS
182 struct tc_htb_opt opt = {
183 .prio = htb->priority,
184 .quantum = htb->quantum,
185 .rate.rate = (htb->rate >= (1ULL << 32)) ? ~0U : htb->rate,
186 .ceil.rate = (htb->ceil_rate >= (1ULL << 32)) ? ~0U : htb->ceil_rate,
187 .rate.overhead = htb->overhead,
188 .ceil.overhead = htb->overhead,
189 };
d9eacc1c
YW
190
191 r = tc_transmit_time(htb->rate, htb->buffer, &opt.buffer);
19f86a63 192 if (r < 0)
16924f54 193 return log_link_debug_errno(link, r, "Failed to calculate buffer size: %m");
19f86a63 194
d9eacc1c 195 r = tc_transmit_time(htb->ceil_rate, htb->ceil_buffer, &opt.cbuffer);
19f86a63 196 if (r < 0)
16924f54 197 return log_link_debug_errno(link, r, "Failed to calculate ceil buffer size: %m");
19f86a63 198
d9eacc1c 199 r = tc_fill_ratespec_and_table(&opt.rate, rtab, htb->mtu);
19f86a63 200 if (r < 0)
16924f54 201 return log_link_debug_errno(link, r, "Failed to calculate rate table: %m");
19f86a63 202
d9eacc1c 203 r = tc_fill_ratespec_and_table(&opt.ceil, ctab, htb->mtu);
19f86a63 204 if (r < 0)
16924f54 205 return log_link_debug_errno(link, r, "Failed to calculate ceil rate table: %m");
19f86a63
YW
206
207 r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "htb");
208 if (r < 0)
16924f54 209 return r;
19f86a63
YW
210
211 r = sd_netlink_message_append_data(req, TCA_HTB_PARMS, &opt, sizeof(opt));
212 if (r < 0)
16924f54 213 return r;
19f86a63
YW
214
215 if (htb->rate >= (1ULL << 32)) {
216 r = sd_netlink_message_append_u64(req, TCA_HTB_RATE64, htb->rate);
217 if (r < 0)
16924f54 218 return r;
19f86a63
YW
219 }
220
221 if (htb->ceil_rate >= (1ULL << 32)) {
222 r = sd_netlink_message_append_u64(req, TCA_HTB_CEIL64, htb->ceil_rate);
223 if (r < 0)
16924f54 224 return r;
19f86a63
YW
225 }
226
227 r = sd_netlink_message_append_data(req, TCA_HTB_RTAB, rtab, sizeof(rtab));
228 if (r < 0)
16924f54 229 return r;
19f86a63
YW
230
231 r = sd_netlink_message_append_data(req, TCA_HTB_CTAB, ctab, sizeof(ctab));
232 if (r < 0)
16924f54 233 return r;
19f86a63
YW
234
235 r = sd_netlink_message_close_container(req);
236 if (r < 0)
16924f54
ZJS
237 return r;
238
19f86a63
YW
239 return 0;
240}
241
d9eacc1c 242int config_parse_hierarchy_token_bucket_class_u32(
19f86a63
YW
243 const char *unit,
244 const char *filename,
245 unsigned line,
246 const char *section,
247 unsigned section_line,
248 const char *lvalue,
249 int ltype,
250 const char *rvalue,
251 void *data,
252 void *userdata) {
253
541d0ed2 254 _cleanup_(tclass_unref_or_set_invalidp) TClass *tclass = NULL;
19f86a63 255 HierarchyTokenBucketClass *htb;
99534007 256 Network *network = ASSERT_PTR(data);
d9eacc1c 257 uint32_t v;
19f86a63
YW
258 int r;
259
260 assert(filename);
261 assert(lvalue);
262 assert(rvalue);
19f86a63
YW
263
264 r = tclass_new_static(TCLASS_KIND_HTB, network, filename, section_line, &tclass);
d96edb2c
YW
265 if (r == -ENOMEM)
266 return log_oom();
267 if (r < 0) {
268 log_syntax(unit, LOG_WARNING, filename, line, r,
269 "Failed to create traffic control class, ignoring assignment: %m");
270 return 0;
271 }
19f86a63
YW
272
273 htb = TCLASS_TO_HTB(tclass);
274
275 if (isempty(rvalue)) {
276 htb->priority = 0;
d9eacc1c
YW
277 tclass = NULL;
278 return 0;
279 }
280
281 r = safe_atou32(rvalue, &v);
282 if (r < 0) {
d96edb2c 283 log_syntax(unit, LOG_WARNING, filename, line, r,
d9eacc1c
YW
284 "Failed to parse '%s=', ignoring assignment: %s",
285 lvalue, rvalue);
286 return 0;
287 }
288
289 htb->priority = v;
290 tclass = NULL;
291
292 return 0;
293}
294
295int config_parse_hierarchy_token_bucket_class_size(
296 const char *unit,
297 const char *filename,
298 unsigned line,
299 const char *section,
300 unsigned section_line,
301 const char *lvalue,
302 int ltype,
303 const char *rvalue,
304 void *data,
305 void *userdata) {
306
541d0ed2 307 _cleanup_(tclass_unref_or_set_invalidp) TClass *tclass = NULL;
d9eacc1c 308 HierarchyTokenBucketClass *htb;
99534007 309 Network *network = ASSERT_PTR(data);
d9eacc1c
YW
310 uint64_t v;
311 int r;
312
313 assert(filename);
314 assert(lvalue);
315 assert(rvalue);
d9eacc1c
YW
316
317 r = tclass_new_static(TCLASS_KIND_HTB, network, filename, section_line, &tclass);
d96edb2c
YW
318 if (r == -ENOMEM)
319 return log_oom();
320 if (r < 0) {
321 log_syntax(unit, LOG_WARNING, filename, line, r,
322 "Failed to create traffic control class, ignoring assignment: %m");
323 return 0;
324 }
d9eacc1c
YW
325
326 htb = TCLASS_TO_HTB(tclass);
327
328 if (isempty(rvalue)) {
329 if (streq(lvalue, "QuantumBytes"))
330 htb->quantum = 0;
331 else if (streq(lvalue, "MTUBytes"))
332 htb->mtu = HTB_DEFAULT_MTU;
333 else if (streq(lvalue, "OverheadBytes"))
334 htb->overhead = 0;
335 else if (streq(lvalue, "BufferBytes"))
336 htb->buffer = 0;
337 else if (streq(lvalue, "CeilBufferBytes"))
338 htb->ceil_buffer = 0;
339 else
04499a70 340 assert_not_reached();
19f86a63
YW
341
342 tclass = NULL;
343 return 0;
344 }
345
d9eacc1c 346 r = parse_size(rvalue, 1024, &v);
19f86a63 347 if (r < 0) {
d96edb2c 348 log_syntax(unit, LOG_WARNING, filename, line, r,
19f86a63
YW
349 "Failed to parse '%s=', ignoring assignment: %s",
350 lvalue, rvalue);
351 return 0;
352 }
d9eacc1c 353 if ((streq(lvalue, "OverheadBytes") && v > UINT16_MAX) || v > UINT32_MAX) {
d96edb2c 354 log_syntax(unit, LOG_WARNING, filename, line, 0,
d9eacc1c
YW
355 "Invalid '%s=', ignoring assignment: %s",
356 lvalue, rvalue);
357 return 0;
358 }
359
360 if (streq(lvalue, "QuantumBytes"))
361 htb->quantum = v;
362 else if (streq(lvalue, "OverheadBytes"))
363 htb->overhead = v;
364 else if (streq(lvalue, "MTUBytes"))
365 htb->mtu = v;
366 else if (streq(lvalue, "BufferBytes"))
367 htb->buffer = v;
368 else if (streq(lvalue, "CeilBufferBytes"))
369 htb->ceil_buffer = v;
370 else
04499a70 371 assert_not_reached();
19f86a63
YW
372
373 tclass = NULL;
374
375 return 0;
376}
377
d9eacc1c 378int config_parse_hierarchy_token_bucket_class_rate(
19f86a63
YW
379 const char *unit,
380 const char *filename,
381 unsigned line,
382 const char *section,
383 unsigned section_line,
384 const char *lvalue,
385 int ltype,
386 const char *rvalue,
387 void *data,
388 void *userdata) {
389
541d0ed2 390 _cleanup_(tclass_unref_or_set_invalidp) TClass *tclass = NULL;
19f86a63 391 HierarchyTokenBucketClass *htb;
99534007 392 Network *network = ASSERT_PTR(data);
19f86a63
YW
393 uint64_t *v;
394 int r;
395
396 assert(filename);
397 assert(lvalue);
398 assert(rvalue);
19f86a63
YW
399
400 r = tclass_new_static(TCLASS_KIND_HTB, network, filename, section_line, &tclass);
d96edb2c
YW
401 if (r == -ENOMEM)
402 return log_oom();
403 if (r < 0) {
404 log_syntax(unit, LOG_WARNING, filename, line, r,
405 "Failed to create traffic control class, ignoring assignment: %m");
406 return 0;
407 }
19f86a63
YW
408
409 htb = TCLASS_TO_HTB(tclass);
410 if (streq(lvalue, "Rate"))
411 v = &htb->rate;
412 else if (streq(lvalue, "CeilRate"))
413 v = &htb->ceil_rate;
414 else
04499a70 415 assert_not_reached();
19f86a63
YW
416
417 if (isempty(rvalue)) {
418 *v = 0;
419
420 tclass = NULL;
421 return 0;
422 }
423
424 r = parse_size(rvalue, 1000, v);
425 if (r < 0) {
d96edb2c 426 log_syntax(unit, LOG_WARNING, filename, line, r,
19f86a63
YW
427 "Failed to parse '%s=', ignoring assignment: %s",
428 lvalue, rvalue);
429 return 0;
430 }
431
432 *v /= 8;
433 tclass = NULL;
434
435 return 0;
436}
437
d9eacc1c
YW
438static int hierarchy_token_bucket_class_init(TClass *tclass) {
439 HierarchyTokenBucketClass *htb;
440
441 assert(tclass);
442
443 htb = TCLASS_TO_HTB(tclass);
444
445 htb->mtu = HTB_DEFAULT_MTU;
446
447 return 0;
448}
449
450static int hierarchy_token_bucket_class_verify(TClass *tclass) {
451 HierarchyTokenBucketClass *htb;
452 uint32_t hz;
453 int r;
454
455 assert(tclass);
456
457 htb = TCLASS_TO_HTB(tclass);
458
459 if (htb->rate == 0)
460 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
461 "%s: Rate= is mandatory. "
462 "Ignoring [HierarchyTokenBucketClass] section from line %u.",
463 tclass->section->filename, tclass->section->line);
464
465 /* if CeilRate= setting is missing, use the same as Rate= */
466 if (htb->ceil_rate == 0)
467 htb->ceil_rate = htb->rate;
468
469 r = tc_init(NULL, &hz);
470 if (r < 0)
471 return log_error_errno(r, "Failed to read /proc/net/psched: %m");
472
473 if (htb->buffer == 0)
474 htb->buffer = htb->rate / hz + htb->mtu;
475 if (htb->ceil_buffer == 0)
476 htb->ceil_buffer = htb->ceil_rate / hz + htb->mtu;
477
478 return 0;
479}
480
19f86a63
YW
481const TClassVTable htb_tclass_vtable = {
482 .object_size = sizeof(HierarchyTokenBucketClass),
483 .tca_kind = "htb",
484 .fill_message = hierarchy_token_bucket_class_fill_message,
d9eacc1c
YW
485 .init = hierarchy_token_bucket_class_init,
486 .verify = hierarchy_token_bucket_class_verify,
19f86a63 487};