]>
Commit | Line | Data |
---|---|---|
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 |
10 | void 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 | ||
18 | int 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 | ||
45 | void 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 | ||
53 | int 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 |
66 | bool 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 |
88 | int 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 | ||
127 | static 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 | ||
136 | uint32_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 | ||
145 | int 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 | ||
183 | int 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 | 220 | static 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 | 255 | static 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 | ||
288 | int 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 | 321 | static 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 | 357 | static 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 | ||
441 | DEFINE_CONFIG_PARSE_ROUTE_METRIC(mtu, config_parse_mtu); | |
5e124de7 YW |
442 | DEFINE_CONFIG_PARSE_ROUTE_METRIC(advmss, config_parse_route_metric_advmss_impl); |
443 | DEFINE_CONFIG_PARSE_ROUTE_METRIC(hop_limit, config_parse_route_metric_hop_limit_impl); | |
be6c9d56 | 444 | DEFINE_CONFIG_PARSE_ROUTE_METRIC(tcp_window, config_parse_tcp_window); |
5e124de7 YW |
445 | DEFINE_CONFIG_PARSE_ROUTE_METRIC(tcp_rto, config_parse_route_metric_tcp_rto_impl); |
446 | DEFINE_CONFIG_PARSE_ROUTE_METRIC(boolean, config_parse_route_metric_boolean_impl); | |
be6c9d56 | 447 | |
e2126e23 | 448 | int 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 | } |