1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <linux/pkt_sched.h>
5 #include "sd-netlink.h"
9 #include "networkd-link.h"
10 #include "parse-util.h"
12 #include "string-util.h"
15 #define HTB_DEFAULT_RATE_TO_QUANTUM 10
16 #define HTB_DEFAULT_MTU 1600 /* Ethernet packet length */
18 static int hierarchy_token_bucket_fill_message(Link
*link
, QDisc
*qdisc
, sd_netlink_message
*req
) {
19 HierarchyTokenBucket
*htb
;
26 assert_se(htb
= HTB(qdisc
));
28 struct tc_htb_glob opt
= {
30 .rate2quantum
= htb
->rate_to_quantum
,
31 .defcls
= htb
->default_class
,
34 r
= sd_netlink_message_open_container_union(req
, TCA_OPTIONS
, "htb");
38 r
= sd_netlink_message_append_data(req
, TCA_HTB_INIT
, &opt
, sizeof(opt
));
42 r
= sd_netlink_message_close_container(req
);
48 int config_parse_hierarchy_token_bucket_default_class(
53 unsigned section_line
,
60 _cleanup_(qdisc_unref_or_set_invalidp
) QDisc
*qdisc
= NULL
;
61 HierarchyTokenBucket
*htb
;
62 Network
*network
= ASSERT_PTR(data
);
69 r
= qdisc_new_static(QDISC_KIND_HTB
, network
, filename
, section_line
, &qdisc
);
73 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
74 "More than one kind of queueing discipline, ignoring assignment: %m");
80 if (isempty(rvalue
)) {
81 htb
->default_class
= 0;
87 r
= safe_atou32_full(rvalue
, 16, &htb
->default_class
);
89 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
90 "Failed to parse '%s=', ignoring assignment: %s",
100 int config_parse_hierarchy_token_bucket_u32(
102 const char *filename
,
105 unsigned section_line
,
112 _cleanup_(qdisc_unref_or_set_invalidp
) QDisc
*qdisc
= NULL
;
113 HierarchyTokenBucket
*htb
;
114 Network
*network
= ASSERT_PTR(data
);
121 r
= qdisc_new_static(QDISC_KIND_HTB
, network
, filename
, section_line
, &qdisc
);
125 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
126 "More than one kind of queueing discipline, ignoring assignment: %m");
132 if (isempty(rvalue
)) {
133 htb
->rate_to_quantum
= HTB_DEFAULT_RATE_TO_QUANTUM
;
139 r
= safe_atou32(rvalue
, &htb
->rate_to_quantum
);
141 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
142 "Failed to parse '%s=', ignoring assignment: %s",
152 static int hierarchy_token_bucket_init(QDisc
*qdisc
) {
153 HierarchyTokenBucket
*htb
;
159 htb
->rate_to_quantum
= HTB_DEFAULT_RATE_TO_QUANTUM
;
164 const QDiscVTable htb_vtable
= {
165 .object_size
= sizeof(HierarchyTokenBucket
),
167 .fill_message
= hierarchy_token_bucket_fill_message
,
168 .init
= hierarchy_token_bucket_init
,
171 static int hierarchy_token_bucket_class_fill_message(Link
*link
, TClass
*tclass
, sd_netlink_message
*req
) {
172 HierarchyTokenBucketClass
*htb
;
173 uint32_t rtab
[256], ctab
[256];
180 assert_se(htb
= TCLASS_TO_HTB(tclass
));
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
,
191 r
= tc_transmit_time(htb
->rate
, htb
->buffer
, &opt
.buffer
);
193 return log_link_debug_errno(link
, r
, "Failed to calculate buffer size: %m");
195 r
= tc_transmit_time(htb
->ceil_rate
, htb
->ceil_buffer
, &opt
.cbuffer
);
197 return log_link_debug_errno(link
, r
, "Failed to calculate ceil buffer size: %m");
199 r
= tc_fill_ratespec_and_table(&opt
.rate
, rtab
, htb
->mtu
);
201 return log_link_debug_errno(link
, r
, "Failed to calculate rate table: %m");
203 r
= tc_fill_ratespec_and_table(&opt
.ceil
, ctab
, htb
->mtu
);
205 return log_link_debug_errno(link
, r
, "Failed to calculate ceil rate table: %m");
207 r
= sd_netlink_message_open_container_union(req
, TCA_OPTIONS
, "htb");
211 r
= sd_netlink_message_append_data(req
, TCA_HTB_PARMS
, &opt
, sizeof(opt
));
215 if (htb
->rate
>= (1ULL << 32)) {
216 r
= sd_netlink_message_append_u64(req
, TCA_HTB_RATE64
, htb
->rate
);
221 if (htb
->ceil_rate
>= (1ULL << 32)) {
222 r
= sd_netlink_message_append_u64(req
, TCA_HTB_CEIL64
, htb
->ceil_rate
);
227 r
= sd_netlink_message_append_data(req
, TCA_HTB_RTAB
, rtab
, sizeof(rtab
));
231 r
= sd_netlink_message_append_data(req
, TCA_HTB_CTAB
, ctab
, sizeof(ctab
));
235 r
= sd_netlink_message_close_container(req
);
242 int config_parse_hierarchy_token_bucket_class_u32(
244 const char *filename
,
247 unsigned section_line
,
254 _cleanup_(tclass_unref_or_set_invalidp
) TClass
*tclass
= NULL
;
255 HierarchyTokenBucketClass
*htb
;
256 Network
*network
= ASSERT_PTR(data
);
264 r
= tclass_new_static(TCLASS_KIND_HTB
, network
, filename
, section_line
, &tclass
);
268 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
269 "Failed to create traffic control class, ignoring assignment: %m");
273 htb
= TCLASS_TO_HTB(tclass
);
275 if (isempty(rvalue
)) {
281 r
= safe_atou32(rvalue
, &v
);
283 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
284 "Failed to parse '%s=', ignoring assignment: %s",
295 int config_parse_hierarchy_token_bucket_class_size(
297 const char *filename
,
300 unsigned section_line
,
307 _cleanup_(tclass_unref_or_set_invalidp
) TClass
*tclass
= NULL
;
308 HierarchyTokenBucketClass
*htb
;
309 Network
*network
= ASSERT_PTR(data
);
317 r
= tclass_new_static(TCLASS_KIND_HTB
, network
, filename
, section_line
, &tclass
);
321 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
322 "Failed to create traffic control class, ignoring assignment: %m");
326 htb
= TCLASS_TO_HTB(tclass
);
328 if (isempty(rvalue
)) {
329 if (streq(lvalue
, "QuantumBytes"))
331 else if (streq(lvalue
, "MTUBytes"))
332 htb
->mtu
= HTB_DEFAULT_MTU
;
333 else if (streq(lvalue
, "OverheadBytes"))
335 else if (streq(lvalue
, "BufferBytes"))
337 else if (streq(lvalue
, "CeilBufferBytes"))
338 htb
->ceil_buffer
= 0;
340 assert_not_reached();
346 r
= parse_size(rvalue
, 1024, &v
);
348 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
349 "Failed to parse '%s=', ignoring assignment: %s",
353 if ((streq(lvalue
, "OverheadBytes") && v
> UINT16_MAX
) || v
> UINT32_MAX
) {
354 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
355 "Invalid '%s=', ignoring assignment: %s",
360 if (streq(lvalue
, "QuantumBytes"))
362 else if (streq(lvalue
, "OverheadBytes"))
364 else if (streq(lvalue
, "MTUBytes"))
366 else if (streq(lvalue
, "BufferBytes"))
368 else if (streq(lvalue
, "CeilBufferBytes"))
369 htb
->ceil_buffer
= v
;
371 assert_not_reached();
378 int config_parse_hierarchy_token_bucket_class_rate(
380 const char *filename
,
383 unsigned section_line
,
390 _cleanup_(tclass_unref_or_set_invalidp
) TClass
*tclass
= NULL
;
391 HierarchyTokenBucketClass
*htb
;
392 Network
*network
= ASSERT_PTR(data
);
400 r
= tclass_new_static(TCLASS_KIND_HTB
, network
, filename
, section_line
, &tclass
);
404 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
405 "Failed to create traffic control class, ignoring assignment: %m");
409 htb
= TCLASS_TO_HTB(tclass
);
410 if (streq(lvalue
, "Rate"))
412 else if (streq(lvalue
, "CeilRate"))
415 assert_not_reached();
417 if (isempty(rvalue
)) {
424 r
= parse_size(rvalue
, 1000, v
);
426 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
427 "Failed to parse '%s=', ignoring assignment: %s",
438 static int hierarchy_token_bucket_class_init(TClass
*tclass
) {
439 HierarchyTokenBucketClass
*htb
;
443 htb
= TCLASS_TO_HTB(tclass
);
445 htb
->mtu
= HTB_DEFAULT_MTU
;
450 static int hierarchy_token_bucket_class_verify(TClass
*tclass
) {
451 HierarchyTokenBucketClass
*htb
;
457 htb
= TCLASS_TO_HTB(tclass
);
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
);
465 /* if CeilRate= setting is missing, use the same as Rate= */
466 if (htb
->ceil_rate
== 0)
467 htb
->ceil_rate
= htb
->rate
;
469 r
= tc_init(NULL
, &hz
);
471 return log_error_errno(r
, "Failed to read /proc/net/psched: %m");
473 /* Kernel would never hand us 0 Hz. */
475 if (htb
->buffer
== 0)
476 htb
->buffer
= htb
->rate
/ hz
+ htb
->mtu
;
477 if (htb
->ceil_buffer
== 0)
478 htb
->ceil_buffer
= htb
->ceil_rate
/ hz
+ htb
->mtu
;
483 const TClassVTable htb_tclass_vtable
= {
484 .object_size
= sizeof(HierarchyTokenBucketClass
),
486 .fill_message
= hierarchy_token_bucket_class_fill_message
,
487 .init
= hierarchy_token_bucket_class_init
,
488 .verify
= hierarchy_token_bucket_class_verify
,