]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network/route: remove existing route if some property conflict with requested ones
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 2 Feb 2024 03:18:33 +0000 (12:18 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 14 Feb 2024 15:43:12 +0000 (00:43 +0900)
Fixes #28853.

src/network/networkd-route-metric.c
src/network/networkd-route-metric.h
src/network/networkd-route.c
src/network/networkd-route.h

index 9c417a4fc4f26ed76b96ff8e9bb72eaefce01752..1bae89393467a3bc339fd9cf7754d074d98aa7ef 100644 (file)
@@ -63,6 +63,28 @@ int route_metric_compare_func(const RouteMetric *a, const RouteMetric *b) {
         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);
 
index 1a3aaf2ae59bbfca5b5d10673247cbf6df54a74e..212f9079cba0964f88298818d306e1edb800d92b 100644 (file)
@@ -26,6 +26,7 @@ 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);
+bool route_metric_can_update(const RouteMetric *a, const RouteMetric *b, bool expiration_by_kernel);
 
 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) {
index b0bc73205459fb85577fc101cdf74540b3e8bb52..743b4f552001234784c5a52e771c76e5b76c7b3b 100644 (file)
@@ -1305,6 +1305,43 @@ static bool route_by_kernel(const Route *route) {
         return false;
 }
 
+bool route_can_update(const Route *existing, const Route *requesting) {
+        assert(existing);
+        assert(requesting);
+
+        if (route_compare_func(existing, requesting) != 0)
+                return false;
+
+        switch (existing->family) {
+        case AF_INET:
+                if (existing->nexthop.weight != requesting->nexthop.weight)
+                        return false;
+                return true;
+
+        case AF_INET6:
+                if (existing->protocol != requesting->protocol)
+                        return false;
+                if (existing->type != requesting->type)
+                        return false;
+                if (existing->flags != requesting->flags)
+                        return false;
+                if (!in6_addr_equal(&existing->prefsrc.in6, &requesting->prefsrc.in6))
+                        return false;
+                if (existing->pref != requesting->pref)
+                        return false;
+                if (existing->expiration_managed_by_kernel && requesting->lifetime_usec != USEC_INFINITY)
+                        return false; /* We cannot disable expiration timer in the kernel. */
+                if (!route_metric_can_update(&existing->metric, &requesting->metric, existing->expiration_managed_by_kernel))
+                        return false;
+                if (existing->nexthop.weight != requesting->nexthop.weight)
+                        return false;
+                return true;
+
+        default:
+                assert_not_reached();
+        }
+}
+
 static int link_unmark_route(Link *link, const Route *route, const RouteNextHop *nh) {
         _cleanup_(route_unrefp) Route *tmp = NULL;
         Route *existing;
@@ -1324,6 +1361,9 @@ static int link_unmark_route(Link *link, const Route *route, const RouteNextHop
         if (route_get(link->manager, tmp, &existing) < 0)
                 return 0;
 
+        if (!route_can_update(existing, tmp))
+                return 0;
+
         route_unmark(existing);
         return 1;
 }
index a5019db65cbb487c6bab1a40861675b5447dc6d8..d292716b1d6d1cc5eae93d5e407eed5529708d52 100644 (file)
@@ -97,6 +97,8 @@ int route_remove_and_cancel(Route *route, Manager *manager);
 
 int route_get(Manager *manager, const Route *route, Route **ret);
 
+bool route_can_update(const Route *existing, const Route *requesting);
+
 int link_drop_routes(Link *link, bool foreign);
 static inline int link_drop_static_routes(Link *link) {
         return link_drop_routes(link, false);