]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/tc/tbf.c
network: TokenBufferFilter→TokenBucketFilter
[thirdparty/systemd.git] / src / network / tc / tbf.c
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"
15 #include "tc-util.h"
16 #include "util.h"
17
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;
22 int r;
23
24 assert(link);
25 assert(qdisc);
26 assert(req);
27
28 tbf = TBF(qdisc);
29
30 opt.rate.rate = tbf->rate >= (1ULL << 32) ? ~0U : tbf->rate;
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 }
67
68 r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "tbf");
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)) {
81 r = sd_netlink_message_append_u64(req, TCA_TBF_RATE64, tbf->rate);
82 if (r < 0)
83 return log_link_error_errno(link, r, "Could not append TCA_TBF_RATE64 attribute: %m");
84 }
85
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)) {
92 r = sd_netlink_message_append_u64(req, TCA_TBF_PRATE64, tbf->peak_rate);
93 if (r < 0)
94 return log_link_error_errno(link, r, "Could not append TCA_TBF_PRATE64 attribute: %m");
95 }
96
97 r = sd_netlink_message_append_u32(req, TCA_TBF_PBURST, tbf->mtu);
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
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
113 int config_parse_token_bucket_filter_size(
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
125 _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
126 Network *network = data;
127 TokenBucketFilter *tbf;
128 uint64_t k;
129 int r;
130
131 assert(filename);
132 assert(lvalue);
133 assert(rvalue);
134 assert(data);
135
136 r = qdisc_new_static(QDISC_KIND_TBF, network, filename, section_line, &qdisc);
137 if (r == -ENOMEM)
138 return log_oom();
139 if (r < 0)
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);
144
145 if (isempty(rvalue)) {
146 if (streq(lvalue, "Rate"))
147 tbf->rate = 0;
148 else if (streq(lvalue, "Burst"))
149 tbf->burst = 0;
150 else if (streq(lvalue, "LimitSize"))
151 tbf->limit = 0;
152 else if (streq(lvalue, "MTUBytes"))
153 tbf->mtu = 0;
154 else if (streq(lvalue, "MPUBytes"))
155 tbf->mpu = 0;
156 else if (streq(lvalue, "PeakRate"))
157 tbf->peak_rate = 0;
158
159 qdisc = NULL;
160 return 0;
161 }
162
163 r = parse_size(rvalue, 1000, &k);
164 if (r < 0) {
165 log_syntax(unit, LOG_ERR, filename, line, r,
166 "Failed to parse '%s=', ignoring assignment: %s",
167 lvalue, rvalue);
168 return 0;
169 }
170
171 if (streq(lvalue, "Rate"))
172 tbf->rate = k / 8;
173 else if (streq(lvalue, "Burst"))
174 tbf->burst = k;
175 else if (streq(lvalue, "LimitSize"))
176 tbf->limit = k;
177 else if (streq(lvalue, "MPUBytes"))
178 tbf->mpu = k;
179 else if (streq(lvalue, "MTUBytes"))
180 tbf->mtu = k;
181 else if (streq(lvalue, "PeakRate"))
182 tbf->peak_rate = k / 8;
183
184 qdisc = NULL;
185
186 return 0;
187 }
188
189 int config_parse_token_bucket_filter_latency(
190 const char *unit,
191 const char *filename,
192 unsigned line,
193 const char *section,
194 unsigned section_line,
195 const char *lvalue,
196 int ltype,
197 const char *rvalue,
198 void *data,
199 void *userdata) {
200
201 _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
202 Network *network = data;
203 TokenBucketFilter *tbf;
204 usec_t u;
205 int r;
206
207 assert(filename);
208 assert(lvalue);
209 assert(rvalue);
210 assert(data);
211
212 r = qdisc_new_static(QDISC_KIND_TBF, network, filename, section_line, &qdisc);
213 if (r == -ENOMEM)
214 return log_oom();
215 if (r < 0)
216 return log_syntax(unit, LOG_ERR, filename, line, r,
217 "More than one kind of queueing discipline, ignoring assignment: %m");
218
219 tbf = TBF(qdisc);
220
221 if (isempty(rvalue)) {
222 tbf->latency = 0;
223
224 qdisc = NULL;
225 return 0;
226 }
227
228 r = parse_sec(rvalue, &u);
229 if (r < 0) {
230 log_syntax(unit, LOG_ERR, filename, line, r,
231 "Failed to parse '%s=', ignoring assignment: %s",
232 lvalue, rvalue);
233 return 0;
234 }
235
236 tbf->latency = u;
237
238 qdisc = NULL;
239
240 return 0;
241 }
242
243 static int token_bucket_filter_verify(QDisc *qdisc) {
244 TokenBucketFilter *tbf = TBF(qdisc);
245
246 if (tbf->limit > 0 && tbf->latency > 0)
247 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
248 "%s: Specifying both LimitSize= and LatencySec= is not allowed. "
249 "Ignoring [TokenBucketFilter] section from line %u.",
250 qdisc->section->filename, qdisc->section->line);
251
252 if (tbf->limit == 0 && tbf->latency == 0)
253 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
254 "%s: Either LimitSize= or LatencySec= is required. "
255 "Ignoring [TokenBucketFilter] section from line %u.",
256 qdisc->section->filename, qdisc->section->line);
257
258 if (tbf->rate == 0)
259 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
260 "%s: Rate= is mandatory. "
261 "Ignoring [TokenBucketFilter] section from line %u.",
262 qdisc->section->filename, qdisc->section->line);
263
264 if (tbf->burst == 0)
265 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
266 "%s: Burst= is mandatory. "
267 "Ignoring [TokenBucketFilter] section from line %u.",
268 qdisc->section->filename, qdisc->section->line);
269
270 if (tbf->peak_rate > 0 && tbf->mtu == 0)
271 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
272 "%s: MTUBytes= is mandatory when PeakRate= is specified. "
273 "Ignoring [TokenBucketFilter] section from line %u.",
274 qdisc->section->filename, qdisc->section->line);
275
276 return 0;
277 }
278
279 const QDiscVTable tbf_vtable = {
280 .object_size = sizeof(TokenBucketFilter),
281 .tca_kind = "tbf",
282 .fill_message = token_bucket_filter_fill_message,
283 .verify = token_bucket_filter_verify
284 };