diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 03:50:40 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 03:50:40 +0000 |
commit | fc53809803cd2bc2434e312b19a18fa36776da12 (patch) | |
tree | b4b43bd6538f51965ce32856e9c053d0f90919c8 /src/network/networkd-route-metric.c | |
parent | Adding upstream version 255.5. (diff) | |
download | systemd-fc53809803cd2bc2434e312b19a18fa36776da12.tar.xz systemd-fc53809803cd2bc2434e312b19a18fa36776da12.zip |
Adding upstream version 256.upstream/256
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/network/networkd-route-metric.c')
-rw-r--r-- | src/network/networkd-route-metric.c | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/src/network/networkd-route-metric.c b/src/network/networkd-route-metric.c new file mode 100644 index 0000000..31a2bde --- /dev/null +++ b/src/network/networkd-route-metric.c @@ -0,0 +1,483 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "alloc-util.h" +#include "netlink-util.h" +#include "networkd-route.h" +#include "networkd-route-metric.h" +#include "parse-util.h" +#include "string-util.h" + +void route_metric_done(RouteMetric *metric) { + assert(metric); + + free(metric->metrics); + free(metric->metrics_set); + free(metric->tcp_congestion_control_algo); +} + +int route_metric_copy(const RouteMetric *src, RouteMetric *dest) { + assert(src); + assert(dest); + + dest->n_metrics = src->n_metrics; + if (src->n_metrics > 0) { + assert(src->n_metrics != 1); + + dest->metrics = newdup(uint32_t, src->metrics, src->n_metrics); + if (!dest->metrics) + return -ENOMEM; + } else + dest->metrics = NULL; + + dest->n_metrics_set = src->n_metrics_set; + if (src->n_metrics_set > 0) { + assert(src->n_metrics_set != 1); + + dest->metrics_set = newdup(bool, src->metrics_set, src->n_metrics_set); + if (!dest->metrics_set) + return -ENOMEM; + } else + dest->metrics_set = NULL; + + return strdup_to(&dest->tcp_congestion_control_algo, src->tcp_congestion_control_algo); +} + +void route_metric_hash_func(const RouteMetric *metric, struct siphash *state) { + assert(metric); + + siphash24_compress_typesafe(metric->n_metrics, state); + siphash24_compress_safe(metric->metrics, sizeof(uint32_t) * metric->n_metrics, state); + siphash24_compress_string(metric->tcp_congestion_control_algo, state); +} + +int route_metric_compare_func(const RouteMetric *a, const RouteMetric *b) { + int r; + + assert(a); + assert(b); + + r = memcmp_nn(a->metrics, a->n_metrics * sizeof(uint32_t), b->metrics, b->n_metrics * sizeof(uint32_t)); + if (r != 0) + return r; + + return strcmp_ptr(a->tcp_congestion_control_algo, b->tcp_congestion_control_algo); +} + +bool route_metric_can_update(const RouteMetric *a, const RouteMetric *b, bool expiration_by_kernel) { + assert(a); + assert(b); + + /* If the kernel has expiration timer for the route, then only MTU can be updated. */ + + if (!expiration_by_kernel) + return route_metric_compare_func(a, b) == 0; + + if (a->n_metrics != b->n_metrics) + return false; + + for (size_t i = 1; i < a->n_metrics; i++) { + if (i != RTAX_MTU) + continue; + if (a->metrics[i] != b->metrics[i]) + return false; + } + + return streq_ptr(a->tcp_congestion_control_algo, b->tcp_congestion_control_algo); +} + +int route_metric_set_full(RouteMetric *metric, uint16_t attr, uint32_t value, bool force) { + assert(metric); + + if (force) { + if (!GREEDY_REALLOC0(metric->metrics_set, attr + 1)) + return -ENOMEM; + + metric->metrics_set[attr] = true; + metric->n_metrics_set = MAX(metric->n_metrics_set, (size_t) (attr + 1)); + } else { + /* Do not override the values specified in conf parsers. */ + if (metric->n_metrics_set > attr && metric->metrics_set[attr]) + return 0; + } + + if (value != 0) { + if (!GREEDY_REALLOC0(metric->metrics, attr + 1)) + return -ENOMEM; + + metric->metrics[attr] = value; + metric->n_metrics = MAX(metric->n_metrics, (size_t) (attr + 1)); + return 0; + } + + if (metric->n_metrics <= attr) + return 0; + + metric->metrics[attr] = 0; + + for (size_t i = metric->n_metrics; i > 0; i--) + if (metric->metrics[i-1] != 0) { + metric->n_metrics = i; + return 0; + } + + metric->n_metrics = 0; + return 0; +} + +static void route_metric_unset(RouteMetric *metric, uint16_t attr) { + assert(metric); + + if (metric->n_metrics_set > attr) + metric->metrics_set[attr] = false; + + assert_se(route_metric_set_full(metric, attr, 0, /* force = */ false) >= 0); +} + +uint32_t route_metric_get(const RouteMetric *metric, uint16_t attr) { + assert(metric); + + if (metric->n_metrics <= attr) + return 0; + + return metric->metrics[attr]; +} + +int route_metric_set_netlink_message(const RouteMetric *metric, sd_netlink_message *m) { + int r; + + assert(metric); + assert(m); + + if (metric->n_metrics <= 0 && isempty(metric->tcp_congestion_control_algo)) + return 0; + + r = sd_netlink_message_open_container(m, RTA_METRICS); + if (r < 0) + return r; + + for (size_t i = 0; i < metric->n_metrics; i++) { + if (i == RTAX_CC_ALGO) + continue; + + if (metric->metrics[i] == 0) + continue; + + r = sd_netlink_message_append_u32(m, i, metric->metrics[i]); + if (r < 0) + return r; + } + + if (!isempty(metric->tcp_congestion_control_algo)) { + r = sd_netlink_message_append_string(m, RTAX_CC_ALGO, metric->tcp_congestion_control_algo); + if (r < 0) + return r; + } + + r = sd_netlink_message_close_container(m); + if (r < 0) + return r; + + return 0; +} + +int route_metric_read_netlink_message(RouteMetric *metric, sd_netlink_message *m) { + _cleanup_free_ void *data = NULL; + size_t len; + int r; + + assert(metric); + assert(m); + + r = sd_netlink_message_read_data(m, RTA_METRICS, &len, &data); + if (r == -ENODATA) + return 0; + if (r < 0) + return log_warning_errno(r, "rtnl: Could not read RTA_METRICS attribute, ignoring: %m"); + + for (struct rtattr *rta = data; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { + size_t rta_type = RTA_TYPE(rta); + + if (rta_type == RTAX_CC_ALGO) { + char *p = memdup_suffix0(RTA_DATA(rta), RTA_PAYLOAD(rta)); + if (!p) + return log_oom(); + + free_and_replace(metric->tcp_congestion_control_algo, p); + + } else { + if (RTA_PAYLOAD(rta) != sizeof(uint32_t)) + continue; + + r = route_metric_set(metric, rta_type, *(uint32_t*) RTA_DATA(rta)); + if (r < 0) + return log_oom(); + } + } + + return 0; +} + +static int config_parse_route_metric_advmss_impl( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + uint32_t *val = ASSERT_PTR(data); + uint64_t u; + int r; + + assert(rvalue); + + r = parse_size(rvalue, 1024, &u); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Could not parse %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + if (u == 0 || u > UINT32_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + *val = (uint32_t) u; + return 1; +} + +static int config_parse_route_metric_hop_limit_impl( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + uint32_t k, *val = ASSERT_PTR(data); + int r; + + assert(rvalue); + + r = safe_atou32(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Could not parse %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + if (k == 0 || k > 255) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + *val = k; + return 1; +} + +int config_parse_tcp_window( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + uint32_t k, *val = ASSERT_PTR(data); + int r; + + assert(rvalue); + + r = safe_atou32(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Could not parse %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + if (k == 0 || k >= 1024) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + *val = k; + return 1; +} + +static int config_parse_route_metric_tcp_rto_impl( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + uint32_t *val = ASSERT_PTR(data); + usec_t usec; + int r; + + assert(rvalue); + + r = parse_sec(rvalue, &usec); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + if (!timestamp_is_set(usec) || + DIV_ROUND_UP(usec, USEC_PER_MSEC) > UINT32_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + *val = (uint32_t) DIV_ROUND_UP(usec, USEC_PER_MSEC); + return 1; +} + +static int config_parse_route_metric_boolean_impl( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + uint32_t *val = ASSERT_PTR(data); + int r; + + assert(rvalue); + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Could not parse %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + *val = r; + return 1; +} + +#define DEFINE_CONFIG_PARSE_ROUTE_METRIC(name, parser) \ + int config_parse_route_metric_##name( \ + const char *unit, \ + const char *filename, \ + unsigned line, \ + const char *section, \ + unsigned section_line, \ + const char *lvalue, \ + int ltype, \ + const char *rvalue, \ + void *data, \ + void *userdata) { \ + \ + Network *network = ASSERT_PTR(userdata); \ + _cleanup_(route_unref_or_set_invalidp) Route *route = NULL; \ + uint16_t attr_type = ltype; \ + int r; \ + \ + assert(filename); \ + assert(section); \ + assert(lvalue); \ + assert(rvalue); \ + \ + r = route_new_static(network, filename, section_line, &route); \ + if (r == -ENOMEM) \ + return log_oom(); \ + if (r < 0) { \ + log_syntax(unit, LOG_WARNING, filename, line, r, \ + "Failed to allocate route, ignoring assignment: %m"); \ + return 0; \ + } \ + \ + if (isempty(rvalue)) { \ + route_metric_unset(&route->metric, attr_type); \ + TAKE_PTR(route); \ + return 0; \ + } \ + \ + uint32_t k; \ + r = parser(unit, filename, line, section, section_line, \ + lvalue, /* ltype = */ 0, rvalue, \ + &k, userdata); \ + if (r <= 0) \ + return r; \ + \ + if (route_metric_set_full( \ + &route->metric, \ + attr_type, \ + k, \ + /* force = */ true) < 0) \ + return log_oom(); \ + \ + TAKE_PTR(route); \ + return 0; \ + } + +DEFINE_CONFIG_PARSE_ROUTE_METRIC(mtu, config_parse_mtu); +DEFINE_CONFIG_PARSE_ROUTE_METRIC(advmss, config_parse_route_metric_advmss_impl); +DEFINE_CONFIG_PARSE_ROUTE_METRIC(hop_limit, config_parse_route_metric_hop_limit_impl); +DEFINE_CONFIG_PARSE_ROUTE_METRIC(tcp_window, config_parse_tcp_window); +DEFINE_CONFIG_PARSE_ROUTE_METRIC(tcp_rto, config_parse_route_metric_tcp_rto_impl); +DEFINE_CONFIG_PARSE_ROUTE_METRIC(boolean, config_parse_route_metric_boolean_impl); + +int config_parse_route_metric_tcp_congestion( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Network *network = ASSERT_PTR(userdata); + _cleanup_(route_unref_or_set_invalidp) Route *route = NULL; + int r; + + assert(filename); + assert(rvalue); + + r = route_new_static(network, filename, section_line, &route); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate route, ignoring assignment: %m"); + return 0; + } + + r = config_parse_string(unit, filename, line, section, section_line, lvalue, 0, + rvalue, &route->metric.tcp_congestion_control_algo, userdata); + if (r <= 0) + return r; + + TAKE_PTR(route); + return 0; +} |