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