summary refs log tree commit diff
path: root/src/core/platform
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/platform')
-rw-r--r--src/core/platform/nm-platform.c124
-rw-r--r--src/core/platform/tests/test-common.c5
2 files changed, 116 insertions, 13 deletions
diff --git a/src/core/platform/nm-platform.c b/src/core/platform/nm-platform.c
index 0e5f8ab5..459a3307 100644
--- a/src/core/platform/nm-platform.c
+++ b/src/core/platform/nm-platform.c
@@ -4357,34 +4357,134 @@ nm_platform_ip_route_get_prune_list(NMPlatform *           self,
     GPtrArray *                  routes_prune;
     const NMDedupMultiHeadEntry *head_entry;
     CList *                      iter;
+    NMPlatformIP4Route           rt_local4;
+    NMPlatformIP6Route           rt_local6;
+    const NMPlatformLink *       pllink;
+    const NMPlatformLnkVrf *     lnk_vrf;
+    guint32                      local_table;
 
     nm_assert(NM_IS_PLATFORM(self));
     nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6));
     nm_assert(NM_IN_SET(route_table_sync,
                         NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN,
                         NM_IP_ROUTE_TABLE_SYNC_MODE_FULL,
-                        NM_IP_ROUTE_TABLE_SYNC_MODE_ALL));
+                        NM_IP_ROUTE_TABLE_SYNC_MODE_ALL,
+                        NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE));
 
     nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ROUTE(NM_IS_IPv4(addr_family)), ifindex);
     head_entry = nm_platform_lookup(self, &lookup);
     if (!head_entry)
         return NULL;
 
+    lnk_vrf = nm_platform_link_get_lnk_vrf(self, ifindex, &pllink);
+    if (!lnk_vrf && pllink && pllink->master > 0)
+        lnk_vrf = nm_platform_link_get_lnk_vrf(self, pllink->master, NULL);
+    local_table = lnk_vrf ? lnk_vrf->table : RT_TABLE_LOCAL;
+
+    rt_local4.plen = 0;
+    rt_local6.plen = 0;
+
     routes_prune = g_ptr_array_new_full(head_entry->len, (GDestroyNotify) nm_dedup_multi_obj_unref);
 
     c_list_for_each (iter, &head_entry->lst_entries_head) {
-        const NMPObject *obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj;
+        const NMPObject *         obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj;
+        const NMPlatformIPXRoute *rt  = NMP_OBJECT_CAST_IPX_ROUTE(obj);
 
-        if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_FULL) {
-            if (nm_platform_ip_route_get_effective_table(NMP_OBJECT_CAST_IP_ROUTE(obj))
-                == RT_TABLE_LOCAL)
+        switch (route_table_sync) {
+        case NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN:
+            if (!nm_platform_route_table_is_main(nm_platform_ip_route_get_effective_table(&rt->rx)))
                 continue;
-        } else if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN) {
-            if (!nm_platform_route_table_is_main(
-                    nm_platform_ip_route_get_effective_table(NMP_OBJECT_CAST_IP_ROUTE(obj))))
+            break;
+        case NM_IP_ROUTE_TABLE_SYNC_MODE_FULL:
+            if (nm_platform_ip_route_get_effective_table(&rt->rx) == RT_TABLE_LOCAL)
                 continue;
-        } else
-            nm_assert(route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_ALL);
+            break;
+        case NM_IP_ROUTE_TABLE_SYNC_MODE_ALL:
+
+            /* FIXME: we should better handle routes that are automatically added by kernel.
+             *
+             * For now, make a good guess which are those routes and exclude them from
+             * pruning them. */
+
+            if (NM_IS_IPv4(addr_family)) {
+                /* for each IPv4 address kernel adds a route like
+                 *
+                 *  local $ADDR dev $IFACE table local proto kernel scope host src $PRIMARY_ADDR
+                 *
+                 * Check whether route could be of that kind. */
+                if (nm_platform_ip_route_get_effective_table(&rt->rx) == local_table
+                    && rt->rx.plen == 32 && rt->rx.rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL
+                    && rt->rx.metric == 0
+                    && rt->r4.scope_inv == nm_platform_route_scope_inv(RT_SCOPE_HOST)
+                    && rt->r4.gateway == INADDR_ANY) {
+                    if (rt_local4.plen == 0) {
+                        rt_local4 = (NMPlatformIP4Route){
+                            .ifindex       = ifindex,
+                            .type_coerced  = nm_platform_route_type_coerce(RTN_LOCAL),
+                            .plen          = 32,
+                            .rt_source     = NM_IP_CONFIG_SOURCE_RTPROT_KERNEL,
+                            .metric        = 0,
+                            .table_coerced = nm_platform_route_table_coerce(local_table),
+                            .scope_inv     = nm_platform_route_scope_inv(RT_SCOPE_HOST),
+                            .gateway       = INADDR_ANY,
+                        };
+                    }
+
+                    /* the possible "network" depends on the addresses we have. We don't check that
+                     * carefully. If the other parameters match, we assume that this route is the one
+                     * generated by kernel. */
+                    rt_local4.network  = rt->r4.network;
+                    rt_local4.pref_src = rt->r4.pref_src;
+
+                    /* to be more confident about comparing the value, use our nm_platform_ip4_route_cmp()
+                     * implementation. That will also consider parameters that we leave unspecified here. */
+                    if (nm_platform_ip4_route_cmp(&rt->r4,
+                                                  &rt_local4,
+                                                  NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
+                        == 0)
+                        continue;
+                }
+            } else {
+                /* for each IPv6 address (that is no longer tentative) kernel adds a route like
+                 *
+                 *  local $ADDR dev $IFACE table local proto kernel metric 0 pref medium
+                 *
+                 * Same as for the IPv4 case. */
+                if (nm_platform_ip_route_get_effective_table(&rt->rx) == local_table
+                    && rt->rx.plen == 128 && rt->rx.rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL
+                    && rt->rx.metric == 0 && rt->r6.rt_pref == NM_ICMPV6_ROUTER_PREF_MEDIUM
+                    && IN6_IS_ADDR_UNSPECIFIED(&rt->r6.gateway)) {
+                    if (rt_local6.plen == 0) {
+                        rt_local6 = (NMPlatformIP6Route){
+                            .ifindex       = ifindex,
+                            .type_coerced  = nm_platform_route_type_coerce(RTN_LOCAL),
+                            .plen          = 128,
+                            .rt_source     = NM_IP_CONFIG_SOURCE_RTPROT_KERNEL,
+                            .metric        = 0,
+                            .table_coerced = nm_platform_route_table_coerce(local_table),
+                            .rt_pref       = NM_ICMPV6_ROUTER_PREF_MEDIUM,
+                            .gateway       = IN6ADDR_ANY_INIT,
+                        };
+                    }
+
+                    rt_local6.network = rt->r6.network;
+
+                    if (nm_platform_ip6_route_cmp(&rt->r6,
+                                                  &rt_local6,
+                                                  NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
+                        == 0)
+                        continue;
+                }
+            }
+            break;
+
+        case NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE:
+            break;
+
+        default:
+            nm_assert_not_reached();
+            break;
+        }
 
         g_ptr_array_add(routes_prune, (gpointer) nmp_object_ref(obj));
     }
@@ -4679,7 +4779,7 @@ nm_platform_ip_route_flush(NMPlatform *self, int addr_family, int ifindex)
         routes_prune = nm_platform_ip_route_get_prune_list(self,
                                                            AF_INET,
                                                            ifindex,
-                                                           NM_IP_ROUTE_TABLE_SYNC_MODE_ALL);
+                                                           NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE);
         success &= nm_platform_ip_route_sync(self, AF_INET, ifindex, NULL, routes_prune, NULL);
     }
     if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET6)) {
@@ -4688,7 +4788,7 @@ nm_platform_ip_route_flush(NMPlatform *self, int addr_family, int ifindex)
         routes_prune = nm_platform_ip_route_get_prune_list(self,
                                                            AF_INET6,
                                                            ifindex,
-                                                           NM_IP_ROUTE_TABLE_SYNC_MODE_ALL);
+                                                           NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE);
         success &= nm_platform_ip_route_sync(self, AF_INET6, ifindex, NULL, routes_prune, NULL);
     }
     return success;
diff --git a/src/core/platform/tests/test-common.c b/src/core/platform/tests/test-common.c
index 4a117d59..1b977145 100644
--- a/src/core/platform/tests/test-common.c
+++ b/src/core/platform/tests/test-common.c
@@ -1542,7 +1542,10 @@ nmtstp_link_bridge_add(NMPlatform *               platform,
 
     ll = NMP_OBJECT_CAST_LNK_BRIDGE(NMP_OBJECT_UP_CAST(pllink)->_link.netlink.lnk);
 
-    g_assert_cmpint(lnk->forward_delay, ==, ll->forward_delay);
+    /* account for roundtrip rounding error with clock_t_to_jiffies()/jiffies_to_clock_t(). */
+    g_assert_cmpint(lnk->forward_delay, >=, ll->forward_delay - 1);
+    g_assert_cmpint(lnk->forward_delay, <=, ll->forward_delay);
+
     g_assert_cmpint(lnk->hello_time, ==, ll->hello_time);
     g_assert_cmpint(lnk->max_age, ==, ll->max_age);
     g_assert_cmpint(lnk->ageing_time, ==, ll->ageing_time);