1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
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"
10 void route_metric_done(RouteMetric
*metric
) {
13 free(metric
->metrics
);
14 free(metric
->metrics_set
);
15 free(metric
->tcp_congestion_control_algo
);
18 int route_metric_copy(const RouteMetric
*src
, RouteMetric
*dest
) {
22 dest
->n_metrics
= src
->n_metrics
;
23 if (src
->n_metrics
> 0) {
24 assert(src
->n_metrics
!= 1);
26 dest
->metrics
= newdup(uint32_t, src
->metrics
, src
->n_metrics
);
32 dest
->n_metrics_set
= src
->n_metrics_set
;
33 if (src
->n_metrics_set
> 0) {
34 assert(src
->n_metrics_set
!= 1);
36 dest
->metrics_set
= newdup(bool, src
->metrics_set
, src
->n_metrics_set
);
37 if (!dest
->metrics_set
)
40 dest
->metrics_set
= NULL
;
42 return strdup_to(&dest
->tcp_congestion_control_algo
, src
->tcp_congestion_control_algo
);
45 void route_metric_hash_func(const RouteMetric
*metric
, struct siphash
*state
) {
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
);
53 int route_metric_compare_func(const RouteMetric
*a
, const RouteMetric
*b
) {
59 r
= memcmp_nn(a
->metrics
, a
->n_metrics
* sizeof(uint32_t), b
->metrics
, b
->n_metrics
* sizeof(uint32_t));
63 return strcmp_ptr(a
->tcp_congestion_control_algo
, b
->tcp_congestion_control_algo
);
66 bool route_metric_can_update(const RouteMetric
*a
, const RouteMetric
*b
, bool expiration_by_kernel
) {
70 /* If the kernel has expiration timer for the route, then only MTU can be updated. */
72 if (!expiration_by_kernel
)
73 return route_metric_compare_func(a
, b
) == 0;
75 if (a
->n_metrics
!= b
->n_metrics
)
78 for (size_t i
= 1; i
< a
->n_metrics
; i
++) {
81 if (a
->metrics
[i
] != b
->metrics
[i
])
85 return streq_ptr(a
->tcp_congestion_control_algo
, b
->tcp_congestion_control_algo
);
88 int route_metric_set_full(RouteMetric
*metric
, uint16_t attr
, uint32_t value
, bool force
) {
92 if (!GREEDY_REALLOC0(metric
->metrics_set
, attr
+ 1))
95 metric
->metrics_set
[attr
] = true;
96 metric
->n_metrics_set
= MAX(metric
->n_metrics_set
, (size_t) (attr
+ 1));
98 /* Do not override the values specified in conf parsers. */
99 if (metric
->n_metrics_set
> attr
&& metric
->metrics_set
[attr
])
104 if (!GREEDY_REALLOC0(metric
->metrics
, attr
+ 1))
107 metric
->metrics
[attr
] = value
;
108 metric
->n_metrics
= MAX(metric
->n_metrics
, (size_t) (attr
+ 1));
112 if (metric
->n_metrics
<= attr
)
115 metric
->metrics
[attr
] = 0;
117 for (size_t i
= metric
->n_metrics
; i
> 0; i
--)
118 if (metric
->metrics
[i
-1] != 0) {
119 metric
->n_metrics
= i
;
123 metric
->n_metrics
= 0;
127 static void route_metric_unset(RouteMetric
*metric
, uint16_t attr
) {
130 if (metric
->n_metrics_set
> attr
)
131 metric
->metrics_set
[attr
] = false;
133 assert_se(route_metric_set_full(metric
, attr
, 0, /* force = */ false) >= 0);
136 uint32_t route_metric_get(const RouteMetric
*metric
, uint16_t attr
) {
139 if (metric
->n_metrics
<= attr
)
142 return metric
->metrics
[attr
];
145 int route_metric_set_netlink_message(const RouteMetric
*metric
, sd_netlink_message
*m
) {
151 if (metric
->n_metrics
<= 0 && isempty(metric
->tcp_congestion_control_algo
))
154 r
= sd_netlink_message_open_container(m
, RTA_METRICS
);
158 for (size_t i
= 0; i
< metric
->n_metrics
; i
++) {
159 if (i
== RTAX_CC_ALGO
)
162 if (metric
->metrics
[i
] == 0)
165 r
= sd_netlink_message_append_u32(m
, i
, metric
->metrics
[i
]);
170 if (!isempty(metric
->tcp_congestion_control_algo
)) {
171 r
= sd_netlink_message_append_string(m
, RTAX_CC_ALGO
, metric
->tcp_congestion_control_algo
);
176 r
= sd_netlink_message_close_container(m
);
183 int route_metric_read_netlink_message(RouteMetric
*metric
, sd_netlink_message
*m
) {
184 _cleanup_free_
void *data
= NULL
;
191 r
= sd_netlink_message_read_data(m
, RTA_METRICS
, &len
, &data
);
195 return log_warning_errno(r
, "rtnl: Could not read RTA_METRICS attribute, ignoring: %m");
197 for (struct rtattr
*rta
= data
; RTA_OK(rta
, len
); rta
= RTA_NEXT(rta
, len
)) {
198 size_t rta_type
= RTA_TYPE(rta
);
200 if (rta_type
== RTAX_CC_ALGO
) {
201 char *p
= memdup_suffix0(RTA_DATA(rta
), RTA_PAYLOAD(rta
));
205 free_and_replace(metric
->tcp_congestion_control_algo
, p
);
208 if (RTA_PAYLOAD(rta
) != sizeof(uint32_t))
211 r
= route_metric_set(metric
, rta_type
, *(uint32_t*) RTA_DATA(rta
));
220 static int config_parse_route_metric_advmss_impl(
222 const char *filename
,
225 unsigned section_line
,
232 uint32_t *val
= ASSERT_PTR(data
);
238 r
= parse_size(rvalue
, 1024, &u
);
240 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
241 "Could not parse %s=, ignoring assignment: %s", lvalue
, rvalue
);
245 if (u
== 0 || u
> UINT32_MAX
) {
246 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
247 "Invalid %s=, ignoring assignment: %s", lvalue
, rvalue
);
255 static int config_parse_route_metric_hop_limit_impl(
257 const char *filename
,
260 unsigned section_line
,
267 uint32_t k
, *val
= ASSERT_PTR(data
);
272 r
= safe_atou32(rvalue
, &k
);
274 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
275 "Could not parse %s=, ignoring assignment: %s", lvalue
, rvalue
);
278 if (k
== 0 || k
> 255) {
279 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
280 "Invalid %s=, ignoring assignment: %s", lvalue
, rvalue
);
288 int config_parse_tcp_window(
290 const char *filename
,
293 unsigned section_line
,
300 uint32_t k
, *val
= ASSERT_PTR(data
);
305 r
= safe_atou32(rvalue
, &k
);
307 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
308 "Could not parse %s=, ignoring assignment: %s", lvalue
, rvalue
);
311 if (k
== 0 || k
>= 1024) {
312 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
313 "Invalid %s=, ignoring assignment: %s", lvalue
, rvalue
);
321 static int config_parse_route_metric_tcp_rto_impl(
323 const char *filename
,
326 unsigned section_line
,
333 uint32_t *val
= ASSERT_PTR(data
);
339 r
= parse_sec(rvalue
, &usec
);
341 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
342 "Failed to parse %s=, ignoring assignment: %s", lvalue
, rvalue
);
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,
349 "Invalid %s=, ignoring assignment: %s", lvalue
, rvalue
);
353 *val
= (uint32_t) DIV_ROUND_UP(usec
, USEC_PER_MSEC
);
357 static int config_parse_route_metric_boolean_impl(
359 const char *filename
,
362 unsigned section_line
,
369 uint32_t *val
= ASSERT_PTR(data
);
374 r
= parse_boolean(rvalue
);
376 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
377 "Could not parse %s=, ignoring assignment: %s", lvalue
, rvalue
);
385 #define DEFINE_CONFIG_PARSE_ROUTE_METRIC(name, parser) \
386 int config_parse_route_metric_##name( \
388 const char *filename, \
390 const char *section, \
391 unsigned section_line, \
392 const char *lvalue, \
394 const char *rvalue, \
398 Network *network = ASSERT_PTR(userdata); \
399 _cleanup_(route_unref_or_set_invalidp) Route *route = NULL; \
400 uint16_t attr_type = ltype; \
408 r = route_new_static(network, filename, section_line, &route); \
412 log_syntax(unit, LOG_WARNING, filename, line, r, \
413 "Failed to allocate route, ignoring assignment: %m"); \
417 if (isempty(rvalue)) { \
418 route_metric_unset(&route->metric, attr_type); \
424 r = parser(unit, filename, line, section, section_line, \
425 lvalue, /* ltype = */ 0, rvalue, \
430 if (route_metric_set_full( \
434 /* force = */ true) < 0) \
441 DEFINE_CONFIG_PARSE_ROUTE_METRIC(mtu
, config_parse_mtu
);
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
);
444 DEFINE_CONFIG_PARSE_ROUTE_METRIC(tcp_window
, config_parse_tcp_window
);
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
);
448 int config_parse_route_metric_tcp_congestion(
450 const char *filename
,
453 unsigned section_line
,
460 Network
*network
= ASSERT_PTR(userdata
);
461 _cleanup_(route_unref_or_set_invalidp
) Route
*route
= NULL
;
467 r
= route_new_static(network
, filename
, section_line
, &route
);
471 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
472 "Failed to allocate route, ignoring assignment: %m");
476 r
= config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, 0,
477 rvalue
, &route
->metric
.tcp_congestion_control_algo
, userdata
);