1 /* SPDX-License-Identifier: LGPL-2.1-or-later
2 * Copyright © 2019 VMware, Inc. */
4 #include <linux/pkt_sched.h>
7 #include "alloc-util.h"
8 #include "conf-parser.h"
10 #include "netlink-util.h"
11 #include "networkd-manager.h"
12 #include "parse-util.h"
14 #include "string-util.h"
18 static int token_bucket_filter_fill_message(Link
*link
, QDisc
*qdisc
, sd_netlink_message
*req
) {
19 uint32_t rtab
[256], ptab
[256];
20 TokenBucketFilter
*tbf
;
27 assert_se(tbf
= TBF(qdisc
));
29 struct tc_tbf_qopt opt
= {
30 .rate
.rate
= tbf
->rate
>= (1ULL << 32) ? ~0U : tbf
->rate
,
31 .peakrate
.rate
= tbf
->peak_rate
>= (1ULL << 32) ? ~0U : tbf
->peak_rate
,
36 opt
.limit
= tbf
->limit
;
40 lim
= tbf
->rate
* (double) tbf
->latency
/ USEC_PER_SEC
+ tbf
->burst
;
41 if (tbf
->peak_rate
> 0) {
42 lim2
= tbf
->peak_rate
* (double) tbf
->latency
/ USEC_PER_SEC
+ tbf
->mtu
;
48 r
= tc_fill_ratespec_and_table(&opt
.rate
, rtab
, tbf
->mtu
);
50 return log_link_debug_errno(link
, r
, "Failed to calculate ratespec: %m");
52 r
= tc_transmit_time(opt
.rate
.rate
, tbf
->burst
, &opt
.buffer
);
54 return log_link_debug_errno(link
, r
, "Failed to calculate buffer size: %m");
56 if (opt
.peakrate
.rate
> 0) {
57 opt
.peakrate
.mpu
= tbf
->mpu
;
59 r
= tc_fill_ratespec_and_table(&opt
.peakrate
, ptab
, tbf
->mtu
);
61 return log_link_debug_errno(link
, r
, "Failed to calculate ratespec: %m");
63 r
= tc_transmit_time(opt
.peakrate
.rate
, tbf
->mtu
, &opt
.mtu
);
65 return log_link_debug_errno(link
, r
, "Failed to calculate mtu size: %m");
68 r
= sd_netlink_message_open_container_union(req
, TCA_OPTIONS
, "tbf");
72 r
= sd_netlink_message_append_data(req
, TCA_TBF_PARMS
, &opt
, sizeof(opt
));
76 r
= sd_netlink_message_append_data(req
, TCA_TBF_BURST
, &tbf
->burst
, sizeof(tbf
->burst
));
80 if (tbf
->rate
>= (1ULL << 32)) {
81 r
= sd_netlink_message_append_u64(req
, TCA_TBF_RATE64
, tbf
->rate
);
86 r
= sd_netlink_message_append_data(req
, TCA_TBF_RTAB
, rtab
, sizeof(rtab
));
90 if (opt
.peakrate
.rate
> 0) {
91 if (tbf
->peak_rate
>= (1ULL << 32)) {
92 r
= sd_netlink_message_append_u64(req
, TCA_TBF_PRATE64
, tbf
->peak_rate
);
97 r
= sd_netlink_message_append_u32(req
, TCA_TBF_PBURST
, tbf
->mtu
);
101 r
= sd_netlink_message_append_data(req
, TCA_TBF_PTAB
, ptab
, sizeof(ptab
));
106 r
= sd_netlink_message_close_container(req
);
113 int config_parse_token_bucket_filter_size(
115 const char *filename
,
118 unsigned section_line
,
125 _cleanup_(qdisc_free_or_set_invalidp
) QDisc
*qdisc
= NULL
;
126 Network
*network
= ASSERT_PTR(data
);
127 TokenBucketFilter
*tbf
;
135 r
= qdisc_new_static(QDISC_KIND_TBF
, network
, filename
, section_line
, &qdisc
);
139 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
140 "More than one kind of queueing discipline, ignoring assignment: %m");
146 if (isempty(rvalue
)) {
147 if (STR_IN_SET(lvalue
, "BurstBytes", "Burst"))
149 else if (STR_IN_SET(lvalue
, "LimitBytes", "LimitSize"))
151 else if (streq(lvalue
, "MTUBytes"))
153 else if (streq(lvalue
, "MPUBytes"))
156 assert_not_reached();
162 r
= parse_size(rvalue
, 1024, &k
);
164 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
165 "Failed to parse '%s=', ignoring assignment: %s",
170 if (STR_IN_SET(lvalue
, "BurstBytes", "Burst"))
172 else if (STR_IN_SET(lvalue
, "LimitBytes", "LimitSize"))
174 else if (streq(lvalue
, "MPUBytes"))
176 else if (streq(lvalue
, "MTUBytes"))
179 assert_not_reached();
186 int config_parse_token_bucket_filter_rate(
188 const char *filename
,
191 unsigned section_line
,
198 _cleanup_(qdisc_free_or_set_invalidp
) QDisc
*qdisc
= NULL
;
199 Network
*network
= ASSERT_PTR(data
);
200 TokenBucketFilter
*tbf
;
208 r
= qdisc_new_static(QDISC_KIND_TBF
, network
, filename
, section_line
, &qdisc
);
212 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
213 "More than one kind of queueing discipline, ignoring assignment: %m");
218 if (streq(lvalue
, "Rate"))
220 else if (streq(lvalue
, "PeakRate"))
223 assert_not_reached();
225 if (isempty(rvalue
)) {
232 r
= parse_size(rvalue
, 1000, &k
);
234 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
235 "Failed to parse '%s=', ignoring assignment: %s",
247 int config_parse_token_bucket_filter_latency(
249 const char *filename
,
252 unsigned section_line
,
259 _cleanup_(qdisc_free_or_set_invalidp
) QDisc
*qdisc
= NULL
;
260 Network
*network
= ASSERT_PTR(data
);
261 TokenBucketFilter
*tbf
;
269 r
= qdisc_new_static(QDISC_KIND_TBF
, network
, filename
, section_line
, &qdisc
);
273 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
274 "More than one kind of queueing discipline, ignoring assignment: %m");
280 if (isempty(rvalue
)) {
287 r
= parse_sec(rvalue
, &u
);
289 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
290 "Failed to parse '%s=', ignoring assignment: %s",
302 static int token_bucket_filter_verify(QDisc
*qdisc
) {
303 TokenBucketFilter
*tbf
= TBF(qdisc
);
305 if (tbf
->limit
> 0 && tbf
->latency
> 0)
306 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
307 "%s: Specifying both LimitSize= and LatencySec= is not allowed. "
308 "Ignoring [TokenBucketFilter] section from line %u.",
309 qdisc
->section
->filename
, qdisc
->section
->line
);
311 if (tbf
->limit
== 0 && tbf
->latency
== 0)
312 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
313 "%s: Either LimitSize= or LatencySec= is required. "
314 "Ignoring [TokenBucketFilter] section from line %u.",
315 qdisc
->section
->filename
, qdisc
->section
->line
);
318 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
319 "%s: Rate= is mandatory. "
320 "Ignoring [TokenBucketFilter] section from line %u.",
321 qdisc
->section
->filename
, qdisc
->section
->line
);
324 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
325 "%s: Burst= is mandatory. "
326 "Ignoring [TokenBucketFilter] section from line %u.",
327 qdisc
->section
->filename
, qdisc
->section
->line
);
329 if (tbf
->peak_rate
> 0 && tbf
->mtu
== 0)
330 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
331 "%s: MTUBytes= is mandatory when PeakRate= is specified. "
332 "Ignoring [TokenBucketFilter] section from line %u.",
333 qdisc
->section
->filename
, qdisc
->section
->line
);
338 const QDiscVTable tbf_vtable
= {
339 .object_size
= sizeof(TokenBucketFilter
),
341 .fill_message
= token_bucket_filter_fill_message
,
342 .verify
= token_bucket_filter_verify