From 06032aa758091e3a8bf5b9236eefc834fba1e293 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 8 Jul 2024 10:46:08 +0200 Subject: [PATCH] ndisc: preserve router preferences If we add multiple default routes with the same metric and different preferences, kernel merges them into a single ECMP route, with overall preference equal to the preference of the first route added. Therefore, the preference of individual routes is not respected. To avoid that, add routes with different metrics if they have different preferences, so that they are not merged together. We could configure only the route(s) with highest preference ignoring the others, and the effect would be the same. However, it is better to add all routes so that users can easily see from "ip route" that there are multiple routers available. https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1468 https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1983 Fixes: 032b4e4371ab ('core: use router preference for IPv6 routes') (cherry picked from commit c437625a765e937c6f72efe8052c5fec1ab29890) (cherry picked from commit 8445076d55258acde56349eddc58ab8f8afd055d) --- src/core/ndisc/nm-ndisc.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/core/ndisc/nm-ndisc.c b/src/core/ndisc/nm-ndisc.c index e6b1a94ea0..3a1210a309 100644 --- a/src/core/ndisc/nm-ndisc.c +++ b/src/core/ndisc/nm-ndisc.c @@ -170,6 +170,9 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex *multi_idx, } if (rdata->gateways_n > 0) { + guint metric_offset = 0; + NMIcmpv6RouterPref prev_pref = NM_ICMPV6_ROUTER_PREF_INVALID; + NMPlatformIP6Route r = { .rt_source = NM_IP_CONFIG_SOURCE_NDISC, .ifindex = ifindex, @@ -180,6 +183,20 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex *multi_idx, }; for (i = 0; i < rdata->gateways_n; i++) { + /* If we add multiple default routes with the same metric and + * different preferences, kernel merges them into a single ECMP + * route, with overall preference equal to the preference of the + * first route added. Therefore, the preference of individual routes + * is not respected. + * To avoid that, add routes with different metrics if they have + * different preferences, so that they are not merged together. Here + * the gateways are already ordered by increasing preference. */ + if (i != 0 && rdata->gateways[i].preference != prev_pref) { + metric_offset++; + } + + prev_pref = rdata->gateways[i].preference; + r.metric = metric_offset; r.gateway = rdata->gateways[i].address; r.rt_pref = rdata->gateways[i].preference; nm_assert((NMIcmpv6RouterPref) r.rt_pref == rdata->gateways[i].preference);