]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/tc/tbf.c
tree-wide: use ASSERT_PTR more
[thirdparty/systemd.git] / src / network / tc / tbf.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later
ba5841b5
SS
2 * Copyright © 2019 VMware, Inc. */
3
4#include <linux/pkt_sched.h>
5#include <math.h>
6
7#include "alloc-util.h"
8#include "conf-parser.h"
9#include "netem.h"
10#include "netlink-util.h"
11#include "networkd-manager.h"
12#include "parse-util.h"
13#include "qdisc.h"
14#include "string-util.h"
c03ef420 15#include "strv.h"
dcfc23ae 16#include "tc-util.h"
ba5841b5 17
60ed2dcf 18static int token_bucket_filter_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
dcfc23ae 19 uint32_t rtab[256], ptab[256];
60ed2dcf 20 TokenBucketFilter *tbf;
ba5841b5
SS
21 int r;
22
23 assert(link);
e8c17dc0 24 assert(qdisc);
ba5841b5
SS
25 assert(req);
26
16924f54 27 assert_se(tbf = TBF(qdisc));
e8c17dc0 28
16924f54
ZJS
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,
32 .rate.mpu = tbf->mpu,
33 };
dcfc23ae
YW
34
35 if (tbf->limit > 0)
36 opt.limit = tbf->limit;
37 else {
38 double lim, lim2;
39
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;
43 lim = MIN(lim, lim2);
44 }
45 opt.limit = lim;
46 }
47
dcfc23ae
YW
48 r = tc_fill_ratespec_and_table(&opt.rate, rtab, tbf->mtu);
49 if (r < 0)
16924f54 50 return log_link_debug_errno(link, r, "Failed to calculate ratespec: %m");
dcfc23ae
YW
51
52 r = tc_transmit_time(opt.rate.rate, tbf->burst, &opt.buffer);
53 if (r < 0)
16924f54 54 return log_link_debug_errno(link, r, "Failed to calculate buffer size: %m");
dcfc23ae
YW
55
56 if (opt.peakrate.rate > 0) {
57 opt.peakrate.mpu = tbf->mpu;
58
59 r = tc_fill_ratespec_and_table(&opt.peakrate, ptab, tbf->mtu);
60 if (r < 0)
16924f54 61 return log_link_debug_errno(link, r, "Failed to calculate ratespec: %m");
dcfc23ae
YW
62
63 r = tc_transmit_time(opt.peakrate.rate, tbf->mtu, &opt.mtu);
64 if (r < 0)
16924f54 65 return log_link_debug_errno(link, r, "Failed to calculate mtu size: %m");
dcfc23ae 66 }
ba5841b5 67
92c7593f 68 r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "tbf");
ba5841b5 69 if (r < 0)
16924f54 70 return r;
ba5841b5 71
16924f54 72 r = sd_netlink_message_append_data(req, TCA_TBF_PARMS, &opt, sizeof(opt));
ba5841b5 73 if (r < 0)
16924f54 74 return r;
ba5841b5
SS
75
76 r = sd_netlink_message_append_data(req, TCA_TBF_BURST, &tbf->burst, sizeof(tbf->burst));
77 if (r < 0)
16924f54 78 return r;
ba5841b5
SS
79
80 if (tbf->rate >= (1ULL << 32)) {
92c7593f 81 r = sd_netlink_message_append_u64(req, TCA_TBF_RATE64, tbf->rate);
ba5841b5 82 if (r < 0)
16924f54 83 return r;
ba5841b5
SS
84 }
85
dcfc23ae
YW
86 r = sd_netlink_message_append_data(req, TCA_TBF_RTAB, rtab, sizeof(rtab));
87 if (r < 0)
16924f54 88 return r;
dcfc23ae
YW
89
90 if (opt.peakrate.rate > 0) {
91 if (tbf->peak_rate >= (1ULL << 32)) {
92c7593f 92 r = sd_netlink_message_append_u64(req, TCA_TBF_PRATE64, tbf->peak_rate);
dcfc23ae 93 if (r < 0)
16924f54 94 return r;
dcfc23ae
YW
95 }
96
92c7593f 97 r = sd_netlink_message_append_u32(req, TCA_TBF_PBURST, tbf->mtu);
dcfc23ae 98 if (r < 0)
16924f54 99 return r;
dcfc23ae
YW
100
101 r = sd_netlink_message_append_data(req, TCA_TBF_PTAB, ptab, sizeof(ptab));
102 if (r < 0)
16924f54 103 return r;
dcfc23ae
YW
104 }
105
ba5841b5
SS
106 r = sd_netlink_message_close_container(req);
107 if (r < 0)
16924f54 108 return r;
ba5841b5
SS
109
110 return 0;
111}
112
60ed2dcf 113int config_parse_token_bucket_filter_size(
ba5841b5
SS
114 const char *unit,
115 const char *filename,
116 unsigned line,
117 const char *section,
118 unsigned section_line,
119 const char *lvalue,
120 int ltype,
121 const char *rvalue,
122 void *data,
123 void *userdata) {
124
edc54f2f 125 _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
99534007 126 Network *network = ASSERT_PTR(data);
60ed2dcf 127 TokenBucketFilter *tbf;
ba5841b5
SS
128 uint64_t k;
129 int r;
130
131 assert(filename);
132 assert(lvalue);
133 assert(rvalue);
ba5841b5 134
e8c17dc0
YW
135 r = qdisc_new_static(QDISC_KIND_TBF, network, filename, section_line, &qdisc);
136 if (r == -ENOMEM)
137 return log_oom();
d96edb2c
YW
138 if (r < 0) {
139 log_syntax(unit, LOG_WARNING, filename, line, r,
140 "More than one kind of queueing discipline, ignoring assignment: %m");
141 return 0;
142 }
e8c17dc0
YW
143
144 tbf = TBF(qdisc);
ba5841b5
SS
145
146 if (isempty(rvalue)) {
c03ef420 147 if (STR_IN_SET(lvalue, "BurstBytes", "Burst"))
e8c17dc0 148 tbf->burst = 0;
c03ef420 149 else if (STR_IN_SET(lvalue, "LimitBytes", "LimitSize"))
e8c17dc0 150 tbf->limit = 0;
18de0969 151 else if (streq(lvalue, "MTUBytes"))
e8c17dc0 152 tbf->mtu = 0;
18de0969 153 else if (streq(lvalue, "MPUBytes"))
e8c17dc0 154 tbf->mpu = 0;
c03ef420 155 else
04499a70 156 assert_not_reached();
ba5841b5 157
5a3e1cb9 158 TAKE_PTR(qdisc);
ba5841b5
SS
159 return 0;
160 }
161
c03ef420 162 r = parse_size(rvalue, 1024, &k);
ba5841b5 163 if (r < 0) {
d96edb2c 164 log_syntax(unit, LOG_WARNING, filename, line, r,
ba5841b5
SS
165 "Failed to parse '%s=', ignoring assignment: %s",
166 lvalue, rvalue);
167 return 0;
168 }
169
c03ef420 170 if (STR_IN_SET(lvalue, "BurstBytes", "Burst"))
e8c17dc0 171 tbf->burst = k;
c03ef420 172 else if (STR_IN_SET(lvalue, "LimitBytes", "LimitSize"))
e8c17dc0 173 tbf->limit = k;
18de0969 174 else if (streq(lvalue, "MPUBytes"))
e8c17dc0 175 tbf->mpu = k;
18de0969 176 else if (streq(lvalue, "MTUBytes"))
e8c17dc0 177 tbf->mtu = k;
c03ef420 178 else
04499a70 179 assert_not_reached();
c03ef420 180
5a3e1cb9 181 TAKE_PTR(qdisc);
c03ef420
YW
182
183 return 0;
184}
185
186int config_parse_token_bucket_filter_rate(
187 const char *unit,
188 const char *filename,
189 unsigned line,
190 const char *section,
191 unsigned section_line,
192 const char *lvalue,
193 int ltype,
194 const char *rvalue,
195 void *data,
196 void *userdata) {
197
198 _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
99534007 199 Network *network = ASSERT_PTR(data);
c03ef420
YW
200 TokenBucketFilter *tbf;
201 uint64_t k, *p;
202 int r;
203
204 assert(filename);
205 assert(lvalue);
206 assert(rvalue);
c03ef420
YW
207
208 r = qdisc_new_static(QDISC_KIND_TBF, network, filename, section_line, &qdisc);
209 if (r == -ENOMEM)
210 return log_oom();
d96edb2c
YW
211 if (r < 0) {
212 log_syntax(unit, LOG_WARNING, filename, line, r,
213 "More than one kind of queueing discipline, ignoring assignment: %m");
214 return 0;
215 }
c03ef420
YW
216
217 tbf = TBF(qdisc);
218 if (streq(lvalue, "Rate"))
219 p = &tbf->rate;
18de0969 220 else if (streq(lvalue, "PeakRate"))
c03ef420
YW
221 p = &tbf->peak_rate;
222 else
04499a70 223 assert_not_reached();
c03ef420
YW
224
225 if (isempty(rvalue)) {
226 *p = 0;
227
5a3e1cb9 228 TAKE_PTR(qdisc);
c03ef420
YW
229 return 0;
230 }
231
232 r = parse_size(rvalue, 1000, &k);
233 if (r < 0) {
d96edb2c 234 log_syntax(unit, LOG_WARNING, filename, line, r,
c03ef420
YW
235 "Failed to parse '%s=', ignoring assignment: %s",
236 lvalue, rvalue);
237 return 0;
238 }
239
240 *p = k / 8;
ba5841b5 241
ba5841b5
SS
242 qdisc = NULL;
243
244 return 0;
245}
246
60ed2dcf 247int config_parse_token_bucket_filter_latency(
ba5841b5
SS
248 const char *unit,
249 const char *filename,
250 unsigned line,
251 const char *section,
252 unsigned section_line,
253 const char *lvalue,
254 int ltype,
255 const char *rvalue,
256 void *data,
257 void *userdata) {
258
edc54f2f 259 _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
99534007 260 Network *network = ASSERT_PTR(data);
60ed2dcf 261 TokenBucketFilter *tbf;
ba5841b5
SS
262 usec_t u;
263 int r;
264
265 assert(filename);
266 assert(lvalue);
267 assert(rvalue);
ba5841b5 268
e8c17dc0
YW
269 r = qdisc_new_static(QDISC_KIND_TBF, network, filename, section_line, &qdisc);
270 if (r == -ENOMEM)
271 return log_oom();
d96edb2c
YW
272 if (r < 0) {
273 log_syntax(unit, LOG_WARNING, filename, line, r,
274 "More than one kind of queueing discipline, ignoring assignment: %m");
275 return 0;
276 }
e8c17dc0
YW
277
278 tbf = TBF(qdisc);
ba5841b5
SS
279
280 if (isempty(rvalue)) {
e8c17dc0 281 tbf->latency = 0;
ba5841b5
SS
282
283 qdisc = NULL;
284 return 0;
285 }
286
287 r = parse_sec(rvalue, &u);
288 if (r < 0) {
d96edb2c 289 log_syntax(unit, LOG_WARNING, filename, line, r,
ba5841b5
SS
290 "Failed to parse '%s=', ignoring assignment: %s",
291 lvalue, rvalue);
292 return 0;
293 }
294
e8c17dc0 295 tbf->latency = u;
ba5841b5 296
ba5841b5
SS
297 qdisc = NULL;
298
299 return 0;
300}
dcfc23ae 301
60ed2dcf
ZJS
302static int token_bucket_filter_verify(QDisc *qdisc) {
303 TokenBucketFilter *tbf = TBF(qdisc);
e8c17dc0 304
dcfc23ae
YW
305 if (tbf->limit > 0 && tbf->latency > 0)
306 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
18de0969 307 "%s: Specifying both LimitSize= and LatencySec= is not allowed. "
60ed2dcf 308 "Ignoring [TokenBucketFilter] section from line %u.",
e8c17dc0 309 qdisc->section->filename, qdisc->section->line);
dcfc23ae
YW
310
311 if (tbf->limit == 0 && tbf->latency == 0)
312 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
18de0969 313 "%s: Either LimitSize= or LatencySec= is required. "
60ed2dcf 314 "Ignoring [TokenBucketFilter] section from line %u.",
e8c17dc0 315 qdisc->section->filename, qdisc->section->line);
dcfc23ae
YW
316
317 if (tbf->rate == 0)
318 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
18de0969 319 "%s: Rate= is mandatory. "
60ed2dcf 320 "Ignoring [TokenBucketFilter] section from line %u.",
e8c17dc0 321 qdisc->section->filename, qdisc->section->line);
dcfc23ae
YW
322
323 if (tbf->burst == 0)
324 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
18de0969 325 "%s: Burst= is mandatory. "
60ed2dcf 326 "Ignoring [TokenBucketFilter] section from line %u.",
e8c17dc0 327 qdisc->section->filename, qdisc->section->line);
dcfc23ae
YW
328
329 if (tbf->peak_rate > 0 && tbf->mtu == 0)
330 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
18de0969 331 "%s: MTUBytes= is mandatory when PeakRate= is specified. "
60ed2dcf 332 "Ignoring [TokenBucketFilter] section from line %u.",
e8c17dc0 333 qdisc->section->filename, qdisc->section->line);
dcfc23ae
YW
334
335 return 0;
336}
e8c17dc0
YW
337
338const QDiscVTable tbf_vtable = {
60ed2dcf 339 .object_size = sizeof(TokenBucketFilter),
e8c17dc0 340 .tca_kind = "tbf",
60ed2dcf
ZJS
341 .fill_message = token_bucket_filter_fill_message,
342 .verify = token_bucket_filter_verify
e8c17dc0 343};