]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network/route-metric: manage uint32_t RTAX_XYZ attributes in the same way
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 7 Jan 2024 01:36:41 +0000 (10:36 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 7 Jan 2024 05:04:00 +0000 (14:04 +0900)
Except for RTAX_CC_ALGO, all RTAX_XYZ attributes take uint32_t and
the kernel's default value is zero. So, let's unify handling of the
attributes.

This should not change any effective behavior. Just refactoring.

src/network/networkd-dhcp4.c
src/network/networkd-json.c
src/network/networkd-ndisc.c
src/network/networkd-route-metric.c
src/network/networkd-route-metric.h
src/network/networkd-route.c

index 4fada25a9c2bbabde3fda4e047ac9943068f62b0..64c84f42625a04b49d5ed812b6671b0e7737ba34 100644 (file)
@@ -380,14 +380,18 @@ static int dhcp4_request_route(Route *in, Link *link) {
                 route->priority = link->network->dhcp_route_metric;
         if (!route->table_set)
                 route->table = link_get_dhcp4_route_table(link);
-        if (route->metric.mtu == 0)
-                route->metric.mtu = link->network->dhcp_route_mtu;
-        if (route->metric.quickack < 0)
-                route->metric.quickack = link->network->dhcp_quickack;
-        if (route->metric.initcwnd == 0)
-                route->metric.initcwnd = link->network->dhcp_initial_congestion_window;
-        if (route->metric.initrwnd == 0)
-                route->metric.initrwnd = link->network->dhcp_advertised_receive_window;
+        r = route_metric_set(&route->metric, RTAX_MTU, link->network->dhcp_route_mtu);
+        if (r < 0)
+                return r;
+        r = route_metric_set(&route->metric, RTAX_INITCWND, link->network->dhcp_initial_congestion_window);
+        if (r < 0)
+                return r;
+        r = route_metric_set(&route->metric, RTAX_INITRWND, link->network->dhcp_advertised_receive_window);
+        if (r < 0)
+                return r;
+        r = route_metric_set(&route->metric, RTAX_QUICKACK, link->network->dhcp_quickack);
+        if (r < 0)
+                return r;
 
         if (route_get(NULL, link, route, &existing) < 0) /* This is a new route. */
                 link->dhcp4_configured = false;
index 7b703bdfef39a9d9569a4736dede6f24fd286f40..e62e57b7652d4587a8119f132f1d2bddef7312a0 100644 (file)
@@ -251,7 +251,7 @@ static int route_append_json(Route *route, JsonVariant **array) {
                                 JSON_BUILD_PAIR_UNSIGNED("Priority", route->priority),
                                 JSON_BUILD_PAIR_UNSIGNED("Table", route->table),
                                 JSON_BUILD_PAIR_STRING("TableString", table),
-                                JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("MTU", route->metric.mtu),
+                                JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("MTU", route_metric_get(&route->metric, RTAX_MTU)),
                                 JSON_BUILD_PAIR_UNSIGNED("Preference", route->pref),
                                 JSON_BUILD_PAIR_UNSIGNED("Flags", route->flags),
                                 JSON_BUILD_PAIR_STRING("FlagsString", strempty(flags)),
index ee9e2448f35aa6cdd9f3e87efdd90e02515ae6ec..43c0cd3ad4aa355162711d1f2526e3c22483d528 100644 (file)
@@ -208,12 +208,15 @@ static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
         ndisc_set_route_priority(link, route);
         if (!route->protocol_set)
                 route->protocol = RTPROT_RA;
-        if (route->metric.quickack < 0)
-                route->metric.quickack = link->network->ipv6_accept_ra_quickack;
-        if (route->metric.mtu == 0)
-                route->metric.mtu = mtu;
-        if (route->metric.hop_limit == 0)
-                route->metric.hop_limit = hop_limit;
+        r = route_metric_set(&route->metric, RTAX_MTU, mtu);
+        if (r < 0)
+                return r;
+        r = route_metric_set(&route->metric, RTAX_HOPLIMIT, hop_limit);
+        if (r < 0)
+                return r;
+        r = route_metric_set(&route->metric, RTAX_QUICKACK, link->network->ipv6_accept_ra_quickack);
+        if (r < 0)
+                return r;
 
         is_new = route_get(NULL, link, route, NULL) < 0;
 
index 4c74a5d792b9a7c6fab6fed96bf5957e75e37f2c..01ebbbc68d49e6d7b81631e167e31c206ec68c85 100644 (file)
@@ -10,6 +10,8 @@
 void route_metric_done(RouteMetric *metric) {
         assert(metric);
 
+        free(metric->metrics);
+        free(metric->metrics_set);
         free(metric->tcp_congestion_control_algo);
 }
 
@@ -17,14 +19,25 @@ int route_metric_copy(const RouteMetric *src, RouteMetric *dest) {
         assert(src);
         assert(dest);
 
-        dest->quickack = src->quickack;
-        dest->fast_open_no_cookie = src->fast_open_no_cookie;
-        dest->mtu = src->mtu;
-        dest->initcwnd = src->initcwnd;
-        dest->initrwnd = src->initrwnd;
-        dest->advmss = src->advmss;
-        dest->hop_limit = src->hop_limit;
-        dest->tcp_rto_usec = src->tcp_rto_usec;
+        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 free_and_strdup(&dest->tcp_congestion_control_algo, src->tcp_congestion_control_algo);
 }
@@ -32,9 +45,9 @@ int route_metric_copy(const RouteMetric *src, RouteMetric *dest) {
 void route_metric_hash_func(const RouteMetric *metric, struct siphash *state) {
         assert(metric);
 
-        siphash24_compress_typesafe(metric->initcwnd, state);
-        siphash24_compress_typesafe(metric->initrwnd, state);
-        siphash24_compress_typesafe(metric->advmss, state);
+        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) {
@@ -43,15 +56,68 @@ int route_metric_compare_func(const RouteMetric *a, const RouteMetric *b) {
         assert(a);
         assert(b);
 
-        r = CMP(a->initcwnd, b->initcwnd);
+        r = memcmp_nn(a->metrics, a->n_metrics * sizeof(uint32_t), b->metrics, b->n_metrics * sizeof(uint32_t));
         if (r != 0)
                 return r;
 
-        r = CMP(a->initrwnd, b->initrwnd);
-        if (r != 0)
-                return r;
+        return strcmp_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;
+                }
 
-        return CMP(a->advmss, b->advmss);
+        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) {
@@ -60,42 +126,21 @@ int route_metric_set_netlink_message(const RouteMetric *metric, sd_netlink_messa
         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;
 
-        if (metric->mtu > 0) {
-                r = sd_netlink_message_append_u32(m, RTAX_MTU, metric->mtu);
-                if (r < 0)
-                        return r;
-        }
-
-        if (metric->initcwnd > 0) {
-                r = sd_netlink_message_append_u32(m, RTAX_INITCWND, metric->initcwnd);
-                if (r < 0)
-                        return r;
-        }
-
-        if (metric->initrwnd > 0) {
-                r = sd_netlink_message_append_u32(m, RTAX_INITRWND, metric->initrwnd);
-                if (r < 0)
-                        return r;
-        }
+        for (size_t i = 0; i < metric->n_metrics; i++) {
+                if (i == RTAX_CC_ALGO)
+                        continue;
 
-        if (metric->quickack >= 0) {
-                r = sd_netlink_message_append_u32(m, RTAX_QUICKACK, metric->quickack);
-                if (r < 0)
-                        return r;
-        }
+                if (metric->metrics[i] == 0)
+                        continue;
 
-        if (metric->fast_open_no_cookie >= 0) {
-                r = sd_netlink_message_append_u32(m, RTAX_FASTOPEN_NO_COOKIE, metric->fast_open_no_cookie);
-                if (r < 0)
-                        return r;
-        }
-
-        if (metric->advmss > 0) {
-                r = sd_netlink_message_append_u32(m, RTAX_ADVMSS, metric->advmss);
+                r = sd_netlink_message_append_u32(m, i, metric->metrics[i]);
                 if (r < 0)
                         return r;
         }
@@ -106,18 +151,6 @@ int route_metric_set_netlink_message(const RouteMetric *metric, sd_netlink_messa
                         return r;
         }
 
-        if (metric->hop_limit > 0) {
-                r = sd_netlink_message_append_u32(m, RTAX_HOPLIMIT, metric->hop_limit);
-                if (r < 0)
-                        return r;
-        }
-
-        if (metric->tcp_rto_usec > 0) {
-                r = sd_netlink_message_append_u32(m, RTAX_RTO_MIN, DIV_ROUND_UP(metric->tcp_rto_usec, USEC_PER_MSEC));
-                if (r < 0)
-                        return r;
-        }
-
         r = sd_netlink_message_close_container(m);
         if (r < 0)
                 return r;
@@ -126,32 +159,38 @@ int route_metric_set_netlink_message(const RouteMetric *metric, sd_netlink_messa
 }
 
 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_enter_container(m, RTA_METRICS);
+        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 enter RTA_METRICS container, ignoring: %m");
+                return log_warning_errno(r, "rtnl: Could not read RTA_METRICS attribute, ignoring: %m");
 
-        r = sd_netlink_message_read_u32(m, RTAX_INITCWND, &metric->initcwnd);
-        if (r < 0 && r != -ENODATA)
-                return log_warning_errno(r, "rtnl: received route message with invalid initcwnd, ignoring: %m");
+        for (struct rtattr *rta = data; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
+                size_t rta_type = RTA_TYPE(rta);
 
-        r = sd_netlink_message_read_u32(m, RTAX_INITRWND, &metric->initrwnd);
-        if (r < 0 && r != -ENODATA)
-                return log_warning_errno(r, "rtnl: received route message with invalid initrwnd, ignoring: %m");
+                if (rta_type == RTAX_CC_ALGO) {
+                        char *p = memdup_suffix0(RTA_DATA(rta), RTA_PAYLOAD(rta));
+                        if (!p)
+                                return log_oom();
 
-        r = sd_netlink_message_read_u32(m, RTAX_ADVMSS, &metric->advmss);
-        if (r < 0 && r != -ENODATA)
-                return log_warning_errno(r, "rtnl: received route message with invalid advmss, ignoring: %m");
+                        free_and_replace(metric->tcp_congestion_control_algo, p);
 
-        r = sd_netlink_message_exit_container(m);
-        if (r < 0)
-                return log_warning_errno(r, "rtnl: Could not exit from RTA_METRICS container, ignoring: %m");
+                } 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;
 }
@@ -187,10 +226,14 @@ int config_parse_route_metric_mtu(
                 return 0;
         }
 
-        r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, AF_UNSPEC, rvalue, &route->metric.mtu, userdata);
+        uint32_t k;
+        r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, AF_UNSPEC, rvalue, &k, userdata);
         if (r <= 0)
                 return r;
 
+        if (route_metric_set_full(&route->metric, ltype, k, /* force = */ true) < 0)
+                return log_oom();
+
         TAKE_PTR(route);
         return 0;
 }
@@ -228,7 +271,7 @@ int config_parse_route_metric_advmss(
         }
 
         if (isempty(rvalue)) {
-                route->metric.advmss = 0;
+                route_metric_unset(&route->metric, ltype);
                 TAKE_PTR(route);
                 return 0;
         }
@@ -246,7 +289,8 @@ int config_parse_route_metric_advmss(
                 return 0;
         }
 
-        route->metric.advmss = u;
+        if (route_metric_set_full(&route->metric, ltype, u, /* force = */ true) < 0)
+                return log_oom();
 
         TAKE_PTR(route);
         return 0;
@@ -285,7 +329,7 @@ int config_parse_route_metric_hop_limit(
         }
 
         if (isempty(rvalue)) {
-                route->metric.hop_limit = 0;
+                route_metric_unset(&route->metric, ltype);
                 TAKE_PTR(route);
                 return 0;
         }
@@ -302,7 +346,8 @@ int config_parse_route_metric_hop_limit(
                 return 0;
         }
 
-        route->metric.hop_limit = k;
+        if (route_metric_set_full(&route->metric, ltype, k, /* force = */ true) < 0)
+                return log_oom();
 
         TAKE_PTR(route);
         return 0;
@@ -343,7 +388,7 @@ int config_parse_tcp_window(
         }
 
         *window = k;
-        return 0;
+        return 1;
 }
 
 int config_parse_route_metric_tcp_window(
@@ -360,7 +405,6 @@ int config_parse_route_metric_tcp_window(
 
         _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
         Network *network = userdata;
-        uint32_t *d;
         int r;
 
         assert(filename);
@@ -378,17 +422,14 @@ int config_parse_route_metric_tcp_window(
                 return 0;
         }
 
-        if (streq(lvalue, "InitialCongestionWindow"))
-                d = &route->metric.initcwnd;
-        else if (streq(lvalue, "InitialAdvertisedReceiveWindow"))
-                d = &route->metric.initrwnd;
-        else
-                assert_not_reached();
-
-        r = config_parse_tcp_window(unit, filename, line, section, section_line, lvalue, ltype, rvalue, d, userdata);
-        if (r < 0)
+        uint32_t k;
+        r = config_parse_tcp_window(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &k, userdata);
+        if (r <= 0)
                 return r;
 
+        if (route_metric_set_full(&route->metric, ltype, k, /* force = */ true) < 0)
+                return log_oom();
+
         TAKE_PTR(route);
         return 0;
 }
@@ -439,7 +480,8 @@ int config_parse_route_metric_tcp_rto(
                 return 0;
         }
 
-        route->metric.tcp_rto_usec = usec;
+        if (route_metric_set_full(&route->metric, ltype, DIV_ROUND_UP(usec, USEC_PER_MSEC), /* force = */ true) < 0)
+                return log_oom();
 
         TAKE_PTR(route);
         return 0;
@@ -483,12 +525,8 @@ int config_parse_route_metric_boolean(
                 return 0;
         }
 
-        if (streq(lvalue, "QuickAck"))
-                route->metric.quickack = r;
-        else if (streq(lvalue, "FastOpenNoCookie"))
-                route->metric.fast_open_no_cookie = r;
-        else
-                assert_not_reached();
+        if (route_metric_set_full(&route->metric, ltype, r, /* force = */ true) < 0)
+                return log_oom();
 
         TAKE_PTR(route);
         return 0;
index 8f447f6e811e92cdbc89b84d6e41c63181693468..1a3aaf2ae59bbfca5b5d10673247cbf6df54a74e 100644 (file)
 #include "hash-funcs.h"
 
 typedef struct RouteMetric {
-        int quickack;
-        int fast_open_no_cookie;
-        uint32_t mtu;
-        uint32_t initcwnd;
-        uint32_t initrwnd;
-        uint32_t advmss;
-        uint32_t hop_limit;
-        char *tcp_congestion_control_algo;
-        usec_t tcp_rto_usec;
+        size_t n_metrics; /* maximum metric attr type with non-zero value */
+        uint32_t *metrics; /* RTAX_*, except for RTAX_CC_ALGO */
+
+        size_t n_metrics_set;
+        bool *metrics_set; /* used by conf parsers */
+
+        char *tcp_congestion_control_algo; /* RTAX_CC_ALGO */
 } RouteMetric;
 
-#define ROUTE_METRIC_NULL                       \
-        ((const RouteMetric) {                  \
-                .quickack = -1,                 \
-                .fast_open_no_cookie = -1,      \
-        })
+#define ROUTE_METRIC_NULL ((const RouteMetric) {})
 
 void route_metric_done(RouteMetric *metric);
 int route_metric_copy(const RouteMetric *src, RouteMetric *dest);
@@ -33,6 +27,12 @@ int route_metric_copy(const RouteMetric *src, RouteMetric *dest);
 void route_metric_hash_func(const RouteMetric *metric, struct siphash *state);
 int route_metric_compare_func(const RouteMetric *a, const RouteMetric *b);
 
+int route_metric_set_full(RouteMetric *metric, uint16_t attr, uint32_t value, bool force);
+static inline int route_metric_set(RouteMetric *metric, uint16_t attr, uint32_t value) {
+        return route_metric_set_full(metric, attr, value, false);
+}
+uint32_t route_metric_get(const RouteMetric *metric, uint16_t attr);
+
 int route_metric_set_netlink_message(const RouteMetric *metric, sd_netlink_message *m);
 int route_metric_read_netlink_message(RouteMetric *metric, sd_netlink_message *message);
 
index 4fe2bd49bd71d8a6196995e5c86e19dc1ce203de..0553a6634381f0e34b183b9d1ae3083ffc2e7925 100644 (file)
@@ -35,7 +35,6 @@ int route_new(Route **ret) {
                 .type = RTN_UNICAST,
                 .table = RT_TABLE_MAIN,
                 .lifetime_usec = USEC_INFINITY,
-                .metric = ROUTE_METRIC_NULL,
                 .gateway_onlink = -1,
         };