1 /* SPDX-License-Identifier: LGPL-2.1+
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 struct tc_tbf_qopt opt
= {};
21 TokenBucketFilter
*tbf
;
30 opt
.rate
.rate
= tbf
->rate
>= (1ULL << 32) ? ~0U : tbf
->rate
;
31 opt
.peakrate
.rate
= tbf
->peak_rate
>= (1ULL << 32) ? ~0U : tbf
->peak_rate
;
34 opt
.limit
= tbf
->limit
;
38 lim
= tbf
->rate
* (double) tbf
->latency
/ USEC_PER_SEC
+ tbf
->burst
;
39 if (tbf
->peak_rate
> 0) {
40 lim2
= tbf
->peak_rate
* (double) tbf
->latency
/ USEC_PER_SEC
+ tbf
->mtu
;
46 opt
.rate
.mpu
= tbf
->mpu
;
48 r
= tc_fill_ratespec_and_table(&opt
.rate
, rtab
, tbf
->mtu
);
50 return log_link_error_errno(link
, r
, "Failed to calculate ratespec: %m");
52 r
= tc_transmit_time(opt
.rate
.rate
, tbf
->burst
, &opt
.buffer
);
54 return log_link_error_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_error_errno(link
, r
, "Failed to calculate ratespec: %m");
63 r
= tc_transmit_time(opt
.peakrate
.rate
, tbf
->mtu
, &opt
.mtu
);
65 return log_link_error_errno(link
, r
, "Failed to calculate mtu size: %m");
68 r
= sd_netlink_message_open_container_union(req
, TCA_OPTIONS
, "tbf");
70 return log_link_error_errno(link
, r
, "Could not open container TCA_OPTIONS: %m");
72 r
= sd_netlink_message_append_data(req
, TCA_TBF_PARMS
, &opt
, sizeof(struct tc_tbf_qopt
));
74 return log_link_error_errno(link
, r
, "Could not append TCA_TBF_PARMS attribute: %m");
76 r
= sd_netlink_message_append_data(req
, TCA_TBF_BURST
, &tbf
->burst
, sizeof(tbf
->burst
));
78 return log_link_error_errno(link
, r
, "Could not append TCA_TBF_BURST attribute: %m");
80 if (tbf
->rate
>= (1ULL << 32)) {
81 r
= sd_netlink_message_append_u64(req
, TCA_TBF_RATE64
, tbf
->rate
);
83 return log_link_error_errno(link
, r
, "Could not append TCA_TBF_RATE64 attribute: %m");
86 r
= sd_netlink_message_append_data(req
, TCA_TBF_RTAB
, rtab
, sizeof(rtab
));
88 return log_link_error_errno(link
, r
, "Could not append TCA_TBF_RTAB attribute: %m");
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
);
94 return log_link_error_errno(link
, r
, "Could not append TCA_TBF_PRATE64 attribute: %m");
97 r
= sd_netlink_message_append_u32(req
, TCA_TBF_PBURST
, tbf
->mtu
);
99 return log_link_error_errno(link
, r
, "Could not append TCA_TBF_PBURST attribute: %m");
101 r
= sd_netlink_message_append_data(req
, TCA_TBF_PTAB
, ptab
, sizeof(ptab
));
103 return log_link_error_errno(link
, r
, "Could not append TCA_TBF_PTAB attribute: %m");
106 r
= sd_netlink_message_close_container(req
);
108 return log_link_error_errno(link
, r
, "Could not close container TCA_OPTIONS: %m");
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
= data
;
127 TokenBucketFilter
*tbf
;
136 r
= qdisc_new_static(QDISC_KIND_TBF
, network
, filename
, section_line
, &qdisc
);
140 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
141 "More than one kind of queueing discipline, ignoring assignment: %m");
145 if (isempty(rvalue
)) {
146 if (STR_IN_SET(lvalue
, "BurstBytes", "Burst"))
148 else if (STR_IN_SET(lvalue
, "LimitBytes", "LimitSize"))
150 else if (streq(lvalue
, "MTUBytes"))
152 else if (streq(lvalue
, "MPUBytes"))
155 assert_not_reached("unknown lvalue");
161 r
= parse_size(rvalue
, 1024, &k
);
163 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
164 "Failed to parse '%s=', ignoring assignment: %s",
169 if (STR_IN_SET(lvalue
, "BurstBytes", "Burst"))
171 else if (STR_IN_SET(lvalue
, "LimitBytes", "LimitSize"))
173 else if (streq(lvalue
, "MPUBytes"))
175 else if (streq(lvalue
, "MTUBytes"))
178 assert_not_reached("unknown lvalue");
185 int config_parse_token_bucket_filter_rate(
187 const char *filename
,
190 unsigned section_line
,
197 _cleanup_(qdisc_free_or_set_invalidp
) QDisc
*qdisc
= NULL
;
198 Network
*network
= data
;
199 TokenBucketFilter
*tbf
;
208 r
= qdisc_new_static(QDISC_KIND_TBF
, network
, filename
, section_line
, &qdisc
);
212 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
213 "More than one kind of queueing discipline, ignoring assignment: %m");
216 if (streq(lvalue
, "Rate"))
218 else if (streq(lvalue
, "PeakRate"))
221 assert_not_reached("unknown lvalue");
223 if (isempty(rvalue
)) {
230 r
= parse_size(rvalue
, 1000, &k
);
232 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
233 "Failed to parse '%s=', ignoring assignment: %s",
245 int config_parse_token_bucket_filter_latency(
247 const char *filename
,
250 unsigned section_line
,
257 _cleanup_(qdisc_free_or_set_invalidp
) QDisc
*qdisc
= NULL
;
258 Network
*network
= data
;
259 TokenBucketFilter
*tbf
;
268 r
= qdisc_new_static(QDISC_KIND_TBF
, network
, filename
, section_line
, &qdisc
);
272 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
273 "More than one kind of queueing discipline, ignoring assignment: %m");
277 if (isempty(rvalue
)) {
284 r
= parse_sec(rvalue
, &u
);
286 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
287 "Failed to parse '%s=', ignoring assignment: %s",
299 static int token_bucket_filter_verify(QDisc
*qdisc
) {
300 TokenBucketFilter
*tbf
= TBF(qdisc
);
302 if (tbf
->limit
> 0 && tbf
->latency
> 0)
303 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
304 "%s: Specifying both LimitSize= and LatencySec= is not allowed. "
305 "Ignoring [TokenBucketFilter] section from line %u.",
306 qdisc
->section
->filename
, qdisc
->section
->line
);
308 if (tbf
->limit
== 0 && tbf
->latency
== 0)
309 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
310 "%s: Either LimitSize= or LatencySec= is required. "
311 "Ignoring [TokenBucketFilter] section from line %u.",
312 qdisc
->section
->filename
, qdisc
->section
->line
);
315 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
316 "%s: Rate= is mandatory. "
317 "Ignoring [TokenBucketFilter] section from line %u.",
318 qdisc
->section
->filename
, qdisc
->section
->line
);
321 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
322 "%s: Burst= is mandatory. "
323 "Ignoring [TokenBucketFilter] section from line %u.",
324 qdisc
->section
->filename
, qdisc
->section
->line
);
326 if (tbf
->peak_rate
> 0 && tbf
->mtu
== 0)
327 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
328 "%s: MTUBytes= is mandatory when PeakRate= is specified. "
329 "Ignoring [TokenBucketFilter] section from line %u.",
330 qdisc
->section
->filename
, qdisc
->section
->line
);
335 const QDiscVTable tbf_vtable
= {
336 .object_size
= sizeof(TokenBucketFilter
),
338 .fill_message
= token_bucket_filter_fill_message
,
339 .verify
= token_bucket_filter_verify