summary refs log tree commit diff
path: root/src/core/ndisc/nm-ndisc.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2026-07-03 19:53:18 +0200
committerMichael Biebl <biebl@debian.org>2026-07-03 19:53:18 +0200
commit537bfce2bda471c92caabd388589230200891509 (patch)
treeaedeccfaf0ba52c238ecf51fc009c0db5d4b60f0 /src/core/ndisc/nm-ndisc.c
parent869e9027026cdbb15d4e4a6327ff41d2697858eb (diff)
New upstream version 1.58~rc1 upstream/1.58_rc1
Diffstat (limited to 'src/core/ndisc/nm-ndisc.c')
-rw-r--r--src/core/ndisc/nm-ndisc.c152
1 files changed, 134 insertions, 18 deletions
diff --git a/src/core/ndisc/nm-ndisc.c b/src/core/ndisc/nm-ndisc.c
index 1a2bf480..2056b5f8 100644
--- a/src/core/ndisc/nm-ndisc.c
+++ b/src/core/ndisc/nm-ndisc.c
@@ -34,6 +34,7 @@
 #define _SIZE_MAX_ROUTES      1000u
 #define _SIZE_MAX_DNS_SERVERS 64u
 #define _SIZE_MAX_DNS_DOMAINS 64u
+#define _SIZE_MAX_PREF64      8u
 
 /*****************************************************************************/
 
@@ -109,7 +110,8 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex        *multi_idx,
                       int                       ifindex,
                       const NMNDiscData        *rdata,
                       NMSettingIP6ConfigPrivacy ip6_privacy,
-                      NMUtilsIPv6IfaceId       *token)
+                      NMUtilsIPv6IfaceId       *token,
+                      const char               *network_id)
 {
     nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL;
     guint32                                 ifa_flags;
@@ -211,13 +213,21 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex        *multi_idx,
     for (i = 0; i < rdata->dns_domains_n; i++)
         nm_l3_config_data_add_search(l3cd, AF_INET6, rdata->dns_domains[i].domain);
 
+    if (rdata->pref64_n > 0) {
+        nm_l3_config_data_set_pref64(l3cd, rdata->pref64[0].prefix, rdata->pref64[0].plen);
+    } else {
+        nm_l3_config_data_set_pref64_valid(l3cd, FALSE);
+    }
+
     nm_l3_config_data_set_ndisc_hop_limit(l3cd, rdata->hop_limit);
     nm_l3_config_data_set_ndisc_reachable_time_msec(l3cd, rdata->reachable_time_ms);
     nm_l3_config_data_set_ndisc_retrans_timer_msec(l3cd, rdata->retrans_timer_ms);
 
-    nm_l3_config_data_set_ip6_mtu(l3cd, rdata->mtu);
+    nm_l3_config_data_set_ip6_mtu_ra(l3cd, rdata->mtu);
     if (token)
         nm_l3_config_data_set_ip6_token(l3cd, *token);
+    if (network_id)
+        nm_l3_config_data_set_network_id(l3cd, network_id);
 
     return g_steal_pointer(&l3cd);
 }
@@ -416,6 +426,7 @@ _data_complete(NMNDiscDataInternal *data)
     _SET(data, gateways);
     _SET(data, addresses);
     _SET(data, routes);
+    _SET(data, pref64);
     _SET(data, dns_servers);
     _SET(data, dns_domains);
 #undef _SET
@@ -437,7 +448,8 @@ nm_ndisc_emit_config_change(NMNDisc *self, NMNDiscConfigMap changed)
                                  nm_l3cfg_get_ifindex(priv->config.l3cfg),
                                  rdata,
                                  priv->config.ip6_privacy,
-                                 priv->iid_is_token ? &priv->iid : NULL);
+                                 priv->iid_is_token ? &priv->iid : NULL,
+                                 priv->config.network_id);
     l3cd = nm_l3_config_data_seal(l3cd);
 
     if (!nm_l3_config_data_equal(priv->l3cd, l3cd))
@@ -761,6 +773,59 @@ nm_ndisc_add_route(NMNDisc *ndisc, const NMNDiscRoute *new_item, gint64 now_msec
 }
 
 gboolean
+nm_ndisc_add_pref64(NMNDisc *ndisc, const NMNDiscPref64 *new_item, gint64 now_msec)
+{
+    NMNDiscDataInternal *rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata;
+    guint                i;
+    guint                insert_idx = G_MAXUINT;
+
+    for (i = 0; i < rdata->pref64->len;) {
+        NMNDiscPref64 *item = &nm_g_array_index(rdata->pref64, NMNDiscPref64, i);
+
+        if (item->plen == new_item->plen && IN6_ARE_ADDR_EQUAL(&item->prefix, &new_item->prefix)
+            && IN6_ARE_ADDR_EQUAL(&item->gateway, &new_item->gateway)) {
+            if (new_item->expiry_msec <= now_msec) {
+                g_array_remove_index(rdata->pref64, i);
+                return TRUE;
+            }
+
+            if (item->gateway_preference != new_item->gateway_preference) {
+                g_array_remove_index(rdata->pref64, i);
+                continue;
+            }
+
+            item->gateway_expiry_msec = new_item->gateway_expiry_msec;
+
+            if (item->expiry_msec == new_item->expiry_msec)
+                return FALSE;
+
+            item->expiry_msec = new_item->expiry_msec;
+            return TRUE;
+        }
+
+        /* Put before less preferable gateways. */
+        if (_preference_to_priority(item->gateway_preference)
+                < _preference_to_priority(new_item->gateway_preference)
+            && insert_idx == G_MAXUINT)
+            insert_idx = i;
+
+        i++;
+    }
+
+    if (rdata->pref64->len >= _SIZE_MAX_PREF64)
+        return FALSE;
+
+    if (new_item->expiry_msec <= now_msec)
+        return FALSE;
+
+    g_array_insert_val(rdata->pref64,
+                       insert_idx == G_MAXUINT ? rdata->pref64->len : insert_idx,
+                       *new_item);
+
+    return TRUE;
+}
+
+gboolean
 nm_ndisc_add_dns_server(NMNDisc *ndisc, const NMNDiscDNSServer *new_item, gint64 now_msec)
 {
     NMNDiscPrivate      *priv;
@@ -1059,7 +1124,8 @@ nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd)
     const NMPObject   *obj;
     guint              len;
     guint              i;
-    gint32             fake_now = NM_NDISC_EXPIRY_BASE_TIMESTAMP / 1000;
+    gint64             now_msec = nm_utils_get_monotonic_timestamp_msec();
+    gint32             now      = now_msec / 1000;
 
     nm_assert(NM_IS_NDISC(ndisc));
     nm_assert(nm_ndisc_get_node_type(ndisc) == NM_NDISC_NODE_TYPE_ROUTER);
@@ -1082,16 +1148,15 @@ nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd)
         lifetime = nmp_utils_lifetime_get(addr->timestamp,
                                           addr->lifetime,
                                           addr->preferred,
-                                          &fake_now,
+                                          &now,
                                           &preferred);
         if (!lifetime)
             continue;
 
         a = (NMNDiscAddress) {
-            .address     = addr->address,
-            .expiry_msec = _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, lifetime),
-            .expiry_preferred_msec =
-                _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, preferred),
+            .address               = addr->address,
+            .expiry_msec           = _nm_ndisc_lifetime_to_expiry(now_msec, lifetime),
+            .expiry_preferred_msec = _nm_ndisc_lifetime_to_expiry(now_msec, preferred),
         };
 
         if (nm_ndisc_add_address(ndisc, &a, 0, FALSE))
@@ -1111,8 +1176,7 @@ nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd)
 
         n = (NMNDiscDNSServer) {
             .address     = a.addr6,
-            .expiry_msec = _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP,
-                                                        NM_NDISC_ROUTER_LIFETIME),
+            .expiry_msec = _nm_ndisc_lifetime_to_expiry(now_msec, NM_NDISC_ROUTER_LIFETIME),
         };
         if (nm_ndisc_add_dns_server(ndisc, &n, G_MININT64))
             changed = TRUE;
@@ -1127,8 +1191,7 @@ nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd)
 
         n = (NMNDiscDNSDomain) {
             .domain      = (char *) strvarr[i],
-            .expiry_msec = _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP,
-                                                        NM_NDISC_ROUTER_LIFETIME),
+            .expiry_msec = _nm_ndisc_lifetime_to_expiry(now_msec, NM_NDISC_ROUTER_LIFETIME),
         };
         if (nm_ndisc_add_dns_domain(ndisc, &n, G_MININT64))
             changed = TRUE;
@@ -1400,6 +1463,17 @@ _config_changed_log(NMNDisc *ndisc, NMNDiscConfigMap changed)
               nm_icmpv6_router_pref_to_string(route->preference, str_pref, sizeof(str_pref)),
               get_exp(str_exp, now_msec, route));
     }
+    for (i = 0; i < rdata->pref64->len; i++) {
+        const NMNDiscPref64 *pref64 = &nm_g_array_index(rdata->pref64, NMNDiscPref64, i);
+        char                 addrstr2[NM_INET_ADDRSTRLEN];
+
+        _LOGD("  pref64 %s/%u via %s exp %s",
+              nm_inet6_ntop(&pref64->prefix, addrstr),
+              pref64->plen,
+              nm_inet6_ntop(&pref64->gateway, addrstr2),
+              get_exp(str_exp, now_msec, pref64));
+    }
+
     for (i = 0; i < rdata->dns_servers->len; i++) {
         const NMNDiscDNSServer *dns_server =
             &nm_g_array_index(rdata->dns_servers, NMNDiscDNSServer, i);
@@ -1486,7 +1560,7 @@ clean_addresses(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint
     }
 
     if (i != j) {
-        *changed = NM_NDISC_CONFIG_ADDRESSES;
+        *changed |= NM_NDISC_CONFIG_ADDRESSES;
         g_array_set_size(rdata->addresses, j);
     }
 
@@ -1520,11 +1594,50 @@ clean_routes(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint64
         g_array_set_size(rdata->routes, j);
     }
 
-    if (_array_set_size_max(rdata->gateways, _SIZE_MAX_ROUTES))
+    if (_array_set_size_max(rdata->routes, _SIZE_MAX_ROUTES))
         *changed |= NM_NDISC_CONFIG_ROUTES;
 }
 
 static void
+clean_pref64(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint64 *next_msec)
+{
+    NMNDiscDataInternal *rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata;
+    NMNDiscPref64       *arr;
+    guint                i;
+    guint                j;
+
+    if (rdata->pref64->len == 0)
+        return;
+
+    arr = &nm_g_array_first(rdata->pref64, NMNDiscPref64);
+
+    for (i = 0, j = 0; i < rdata->pref64->len; i++) {
+        if (!expiry_next(now_msec, arr[i].expiry_msec, next_msec)
+            || !expiry_next(now_msec,
+                            arr[i].gateway_expiry_msec,
+                            next_msec)) { /* no gateway no party */
+            if (i == 0) {
+                /* Emit the changed signal only when the first PREF64 expires,
+                 * because only the first item is exported into the l3cd. Changes
+                 * in other PREF64s are not relevant. */
+                *changed |= NM_NDISC_CONFIG_PREF64;
+            }
+            continue;
+        }
+
+        if (i != j)
+            arr[j] = arr[i];
+        j++;
+    }
+
+    if (i != j) {
+        g_array_set_size(rdata->pref64, j);
+    }
+
+    _array_set_size_max(rdata->pref64, _SIZE_MAX_PREF64);
+}
+
+static void
 clean_dns_servers(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint64 *next_msec)
 {
     NMNDiscDataInternal *rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata;
@@ -1550,7 +1663,7 @@ clean_dns_servers(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gi
         g_array_set_size(rdata->dns_servers, j);
     }
 
-    if (_array_set_size_max(rdata->gateways, _SIZE_MAX_DNS_SERVERS))
+    if (_array_set_size_max(rdata->dns_servers, _SIZE_MAX_DNS_SERVERS))
         *changed |= NM_NDISC_CONFIG_DNS_SERVERS;
 }
 
@@ -1580,12 +1693,12 @@ clean_dns_domains(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gi
         j++;
     }
 
-    if (i != 0) {
+    if (i != j) {
         *changed |= NM_NDISC_CONFIG_DNS_DOMAINS;
         g_array_set_size(rdata->dns_domains, j);
     }
 
-    if (_array_set_size_max(rdata->gateways, _SIZE_MAX_DNS_DOMAINS))
+    if (_array_set_size_max(rdata->dns_domains, _SIZE_MAX_DNS_DOMAINS))
         *changed |= NM_NDISC_CONFIG_DNS_DOMAINS;
 }
 
@@ -1600,6 +1713,7 @@ check_timestamps(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap changed)
     clean_gateways(ndisc, now_msec, &changed, &next_msec);
     clean_addresses(ndisc, now_msec, &changed, &next_msec);
     clean_routes(ndisc, now_msec, &changed, &next_msec);
+    clean_pref64(ndisc, now_msec, &changed, &next_msec);
     clean_dns_servers(ndisc, now_msec, &changed, &next_msec);
     clean_dns_domains(ndisc, now_msec, &changed, &next_msec);
 
@@ -1919,6 +2033,7 @@ nm_ndisc_init(NMNDisc *ndisc)
     rdata->gateways    = g_array_new(FALSE, FALSE, sizeof(NMNDiscGateway));
     rdata->addresses   = g_array_new(FALSE, FALSE, sizeof(NMNDiscAddress));
     rdata->routes      = g_array_new(FALSE, FALSE, sizeof(NMNDiscRoute));
+    rdata->pref64      = g_array_new(FALSE, FALSE, sizeof(NMNDiscPref64));
     rdata->dns_servers = g_array_new(FALSE, FALSE, sizeof(NMNDiscDNSServer));
     rdata->dns_domains = g_array_new(FALSE, FALSE, sizeof(NMNDiscDNSDomain));
     g_array_set_clear_func(rdata->dns_domains, dns_domain_free);
@@ -1951,6 +2066,7 @@ finalize(GObject *object)
     g_array_unref(rdata->gateways);
     g_array_unref(rdata->addresses);
     g_array_unref(rdata->routes);
+    g_array_unref(rdata->pref64);
     g_array_unref(rdata->dns_servers);
     g_array_unref(rdata->dns_domains);