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