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