]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-route-metric.c
b27b3c12948e81220e17e0ff980b453b6a243077
[thirdparty/systemd.git] / src / network / networkd-route-metric.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "alloc-util.h"
4 #include "netlink-util.h"
5 #include "networkd-route.h"
6 #include "networkd-route-metric.h"
7 #include "parse-util.h"
8 #include "string-util.h"
9
10 void route_metric_done(RouteMetric *metric) {
11 assert(metric);
12
13 free(metric->metrics);
14 free(metric->metrics_set);
15 free(metric->tcp_congestion_control_algo);
16 }
17
18 int route_metric_copy(const RouteMetric *src, RouteMetric *dest) {
19 assert(src);
20 assert(dest);
21
22 dest->n_metrics = src->n_metrics;
23 if (src->n_metrics > 0) {
24 assert(src->n_metrics != 1);
25
26 dest->metrics = newdup(uint32_t, src->metrics, src->n_metrics);
27 if (!dest->metrics)
28 return -ENOMEM;
29 } else
30 dest->metrics = NULL;
31
32 dest->n_metrics_set = src->n_metrics_set;
33 if (src->n_metrics_set > 0) {
34 assert(src->n_metrics_set != 1);
35
36 dest->metrics_set = newdup(bool, src->metrics_set, src->n_metrics_set);
37 if (!dest->metrics_set)
38 return -ENOMEM;
39 } else
40 dest->metrics_set = NULL;
41
42 return strdup_or_null(src->tcp_congestion_control_algo, &dest->tcp_congestion_control_algo);
43 }
44
45 void route_metric_hash_func(const RouteMetric *metric, struct siphash *state) {
46 assert(metric);
47
48 siphash24_compress_typesafe(metric->n_metrics, state);
49 siphash24_compress_safe(metric->metrics, sizeof(uint32_t) * metric->n_metrics, state);
50 siphash24_compress_string(metric->tcp_congestion_control_algo, state);
51 }
52
53 int route_metric_compare_func(const RouteMetric *a, const RouteMetric *b) {
54 int r;
55
56 assert(a);
57 assert(b);
58
59 r = memcmp_nn(a->metrics, a->n_metrics * sizeof(uint32_t), b->metrics, b->n_metrics * sizeof(uint32_t));
60 if (r != 0)
61 return r;
62
63 return strcmp_ptr(a->tcp_congestion_control_algo, b->tcp_congestion_control_algo);
64 }
65
66 int route_metric_set_full(RouteMetric *metric, uint16_t attr, uint32_t value, bool force) {
67 assert(metric);
68
69 if (force) {
70 if (!GREEDY_REALLOC0(metric->metrics_set, attr + 1))
71 return -ENOMEM;
72
73 metric->metrics_set[attr] = true;
74 metric->n_metrics_set = MAX(metric->n_metrics_set, (size_t) (attr + 1));
75 } else {
76 /* Do not override the values specified in conf parsers. */
77 if (metric->n_metrics_set > attr && metric->metrics_set[attr])
78 return 0;
79 }
80
81 if (value != 0) {
82 if (!GREEDY_REALLOC0(metric->metrics, attr + 1))
83 return -ENOMEM;
84
85 metric->metrics[attr] = value;
86 metric->n_metrics = MAX(metric->n_metrics, (size_t) (attr + 1));
87 return 0;
88 }
89
90 if (metric->n_metrics <= attr)
91 return 0;
92
93 metric->metrics[attr] = 0;
94
95 for (size_t i = metric->n_metrics; i > 0; i--)
96 if (metric->metrics[i-1] != 0) {
97 metric->n_metrics = i;
98 return 0;
99 }
100
101 metric->n_metrics = 0;
102 return 0;
103 }
104
105 static void route_metric_unset(RouteMetric *metric, uint16_t attr) {
106 assert(metric);
107
108 if (metric->n_metrics_set > attr)
109 metric->metrics_set[attr] = false;
110
111 assert_se(route_metric_set_full(metric, attr, 0, /* force = */ false) >= 0);
112 }
113
114 uint32_t route_metric_get(const RouteMetric *metric, uint16_t attr) {
115 assert(metric);
116
117 if (metric->n_metrics <= attr)
118 return 0;
119
120 return metric->metrics[attr];
121 }
122
123 int route_metric_set_netlink_message(const RouteMetric *metric, sd_netlink_message *m) {
124 int r;
125
126 assert(metric);
127 assert(m);
128
129 if (metric->n_metrics <= 0 && isempty(metric->tcp_congestion_control_algo))
130 return 0;
131
132 r = sd_netlink_message_open_container(m, RTA_METRICS);
133 if (r < 0)
134 return r;
135
136 for (size_t i = 0; i < metric->n_metrics; i++) {
137 if (i == RTAX_CC_ALGO)
138 continue;
139
140 if (metric->metrics[i] == 0)
141 continue;
142
143 r = sd_netlink_message_append_u32(m, i, metric->metrics[i]);
144 if (r < 0)
145 return r;
146 }
147
148 if (!isempty(metric->tcp_congestion_control_algo)) {
149 r = sd_netlink_message_append_string(m, RTAX_CC_ALGO, metric->tcp_congestion_control_algo);
150 if (r < 0)
151 return r;
152 }
153
154 r = sd_netlink_message_close_container(m);
155 if (r < 0)
156 return r;
157
158 return 0;
159 }
160
161 int route_metric_read_netlink_message(RouteMetric *metric, sd_netlink_message *m) {
162 _cleanup_free_ void *data = NULL;
163 size_t len;
164 int r;
165
166 assert(metric);
167 assert(m);
168
169 r = sd_netlink_message_read_data(m, RTA_METRICS, &len, &data);
170 if (r == -ENODATA)
171 return 0;
172 if (r < 0)
173 return log_warning_errno(r, "rtnl: Could not read RTA_METRICS attribute, ignoring: %m");
174
175 for (struct rtattr *rta = data; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
176 size_t rta_type = RTA_TYPE(rta);
177
178 if (rta_type == RTAX_CC_ALGO) {
179 char *p = memdup_suffix0(RTA_DATA(rta), RTA_PAYLOAD(rta));
180 if (!p)
181 return log_oom();
182
183 free_and_replace(metric->tcp_congestion_control_algo, p);
184
185 } else {
186 if (RTA_PAYLOAD(rta) != sizeof(uint32_t))
187 continue;
188
189 r = route_metric_set(metric, rta_type, *(uint32_t*) RTA_DATA(rta));
190 if (r < 0)
191 return log_oom();
192 }
193 }
194
195 return 0;
196 }
197
198 static int config_parse_route_metric_advmss_impl(
199 const char *unit,
200 const char *filename,
201 unsigned line,
202 const char *section,
203 unsigned section_line,
204 const char *lvalue,
205 int ltype,
206 const char *rvalue,
207 void *data,
208 void *userdata) {
209
210 uint32_t *val = ASSERT_PTR(data);
211 uint64_t u;
212 int r;
213
214 assert(rvalue);
215
216 r = parse_size(rvalue, 1024, &u);
217 if (r < 0) {
218 log_syntax(unit, LOG_WARNING, filename, line, r,
219 "Could not parse %s=, ignoring assignment: %s", lvalue, rvalue);
220 return 0;
221 }
222
223 if (u == 0 || u > UINT32_MAX) {
224 log_syntax(unit, LOG_WARNING, filename, line, 0,
225 "Invalid %s=, ignoring assignment: %s", lvalue, rvalue);
226 return 0;
227 }
228
229 *val = (uint32_t) u;
230 return 1;
231 }
232
233 static int config_parse_route_metric_hop_limit_impl(
234 const char *unit,
235 const char *filename,
236 unsigned line,
237 const char *section,
238 unsigned section_line,
239 const char *lvalue,
240 int ltype,
241 const char *rvalue,
242 void *data,
243 void *userdata) {
244
245 uint32_t k, *val = ASSERT_PTR(data);
246 int r;
247
248 assert(rvalue);
249
250 r = safe_atou32(rvalue, &k);
251 if (r < 0) {
252 log_syntax(unit, LOG_WARNING, filename, line, r,
253 "Could not parse %s=, ignoring assignment: %s", lvalue, rvalue);
254 return 0;
255 }
256 if (k == 0 || k > 255) {
257 log_syntax(unit, LOG_WARNING, filename, line, 0,
258 "Invalid %s=, ignoring assignment: %s", lvalue, rvalue);
259 return 0;
260 }
261
262 *val = k;
263 return 1;
264 }
265
266 int config_parse_tcp_window(
267 const char *unit,
268 const char *filename,
269 unsigned line,
270 const char *section,
271 unsigned section_line,
272 const char *lvalue,
273 int ltype,
274 const char *rvalue,
275 void *data,
276 void *userdata) {
277
278 uint32_t k, *val = ASSERT_PTR(data);
279 int r;
280
281 assert(rvalue);
282
283 r = safe_atou32(rvalue, &k);
284 if (r < 0) {
285 log_syntax(unit, LOG_WARNING, filename, line, r,
286 "Could not parse %s=, ignoring assignment: %s", lvalue, rvalue);
287 return 0;
288 }
289 if (k == 0 || k >= 1024) {
290 log_syntax(unit, LOG_WARNING, filename, line, 0,
291 "Invalid %s=, ignoring assignment: %s", lvalue, rvalue);
292 return 0;
293 }
294
295 *val = k;
296 return 1;
297 }
298
299 static int config_parse_route_metric_tcp_rto_impl(
300 const char *unit,
301 const char *filename,
302 unsigned line,
303 const char *section,
304 unsigned section_line,
305 const char *lvalue,
306 int ltype,
307 const char *rvalue,
308 void *data,
309 void *userdata) {
310
311 uint32_t *val = ASSERT_PTR(data);
312 usec_t usec;
313 int r;
314
315 assert(rvalue);
316
317 r = parse_sec(rvalue, &usec);
318 if (r < 0) {
319 log_syntax(unit, LOG_WARNING, filename, line, r,
320 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
321 return 0;
322 }
323
324 if (!timestamp_is_set(usec) ||
325 DIV_ROUND_UP(usec, USEC_PER_MSEC) > UINT32_MAX) {
326 log_syntax(unit, LOG_WARNING, filename, line, 0,
327 "Invalid %s=, ignoring assignment: %s", lvalue, rvalue);
328 return 0;
329 }
330
331 *val = (uint32_t) DIV_ROUND_UP(usec, USEC_PER_MSEC);
332 return 1;
333 }
334
335 static int config_parse_route_metric_boolean_impl(
336 const char *unit,
337 const char *filename,
338 unsigned line,
339 const char *section,
340 unsigned section_line,
341 const char *lvalue,
342 int ltype,
343 const char *rvalue,
344 void *data,
345 void *userdata) {
346
347 uint32_t *val = ASSERT_PTR(data);
348 int r;
349
350 assert(rvalue);
351
352 r = parse_boolean(rvalue);
353 if (r < 0) {
354 log_syntax(unit, LOG_WARNING, filename, line, r,
355 "Could not parse %s=, ignoring assignment: %s", lvalue, rvalue);
356 return 0;
357 }
358
359 *val = r;
360 return 1;
361 }
362
363 #define DEFINE_CONFIG_PARSE_ROUTE_METRIC(name, parser) \
364 int config_parse_route_metric_##name( \
365 const char *unit, \
366 const char *filename, \
367 unsigned line, \
368 const char *section, \
369 unsigned section_line, \
370 const char *lvalue, \
371 int ltype, \
372 const char *rvalue, \
373 void *data, \
374 void *userdata) { \
375 \
376 Network *network = ASSERT_PTR(userdata); \
377 _cleanup_(route_free_or_set_invalidp) Route *route = NULL; \
378 uint16_t attr_type = ltype; \
379 int r; \
380 \
381 assert(filename); \
382 assert(section); \
383 assert(lvalue); \
384 assert(rvalue); \
385 \
386 r = route_new_static(network, filename, section_line, &route); \
387 if (r == -ENOMEM) \
388 return log_oom(); \
389 if (r < 0) { \
390 log_syntax(unit, LOG_WARNING, filename, line, r, \
391 "Failed to allocate route, ignoring assignment: %m"); \
392 return 0; \
393 } \
394 \
395 if (isempty(rvalue)) { \
396 route_metric_unset(&route->metric, attr_type); \
397 TAKE_PTR(route); \
398 return 0; \
399 } \
400 \
401 uint32_t k; \
402 r = parser(unit, filename, line, section, section_line, \
403 lvalue, /* ltype = */ 0, rvalue, \
404 &k, userdata); \
405 if (r <= 0) \
406 return r; \
407 \
408 if (route_metric_set_full( \
409 &route->metric, \
410 attr_type, \
411 k, \
412 /* force = */ true) < 0) \
413 return log_oom(); \
414 \
415 TAKE_PTR(route); \
416 return 0; \
417 }
418
419 DEFINE_CONFIG_PARSE_ROUTE_METRIC(mtu, config_parse_mtu);
420 DEFINE_CONFIG_PARSE_ROUTE_METRIC(advmss, config_parse_route_metric_advmss_impl);
421 DEFINE_CONFIG_PARSE_ROUTE_METRIC(hop_limit, config_parse_route_metric_hop_limit_impl);
422 DEFINE_CONFIG_PARSE_ROUTE_METRIC(tcp_window, config_parse_tcp_window);
423 DEFINE_CONFIG_PARSE_ROUTE_METRIC(tcp_rto, config_parse_route_metric_tcp_rto_impl);
424 DEFINE_CONFIG_PARSE_ROUTE_METRIC(boolean, config_parse_route_metric_boolean_impl);
425
426 int config_parse_route_metric_tcp_congestion(
427 const char *unit,
428 const char *filename,
429 unsigned line,
430 const char *section,
431 unsigned section_line,
432 const char *lvalue,
433 int ltype,
434 const char *rvalue,
435 void *data,
436 void *userdata) {
437
438 Network *network = ASSERT_PTR(userdata);
439 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
440 int r;
441
442 assert(filename);
443 assert(rvalue);
444
445 r = route_new_static(network, filename, section_line, &route);
446 if (r == -ENOMEM)
447 return log_oom();
448 if (r < 0) {
449 log_syntax(unit, LOG_WARNING, filename, line, r,
450 "Failed to allocate route, ignoring assignment: %m");
451 return 0;
452 }
453
454 r = config_parse_string(unit, filename, line, section, section_line, lvalue, 0,
455 rvalue, &route->metric.tcp_congestion_control_algo, userdata);
456 if (r <= 0)
457 return r;
458
459 TAKE_PTR(route);
460 return 0;
461 }