Blame SOURCES/1003-dhcp-decline-IPv6-lease-if-all-adresses-fail-DAD-rh2132281.patch

56f330
From 67f7ee1e7a5c4cfc2d0b67c56a8655b3d9f4781d Mon Sep 17 00:00:00 2001
56f330
From: Beniamino Galvani <bgalvani@redhat.com>
56f330
Date: Thu, 1 Sep 2022 10:21:20 +0200
56f330
Subject: [PATCH] dhcp: decline IPv6 lease if all adresses fail DAD
56f330
56f330
Currently we accept the DHCPv6 just after addresses are configured on
56f330
kernel, without waiting DAD result. Instead, wait that DAD completes
56f330
and decline the lease if all addresses are detected as duplicate.
56f330
56f330
Note that when an address has non-infinite lifetime and fails DAD,
56f330
kernel removes it automatically. With iproute2 we see something like:
56f330
56f330
602: testX6    inet6 2620::1234:5678/128 scope global tentative dynamic noprefixroute
56f330
       valid_lft 7500sec preferred_lft 7200sec
56f330
Deleted 602: testX6    inet6 2620::1234:5678/128 scope global dadfailed tentative dynamic noprefixroute
56f330
       valid_lft 7500sec preferred_lft 7200sec
56f330
56f330
Since the address gets removed from the platform cache, at the moment
56f330
we don't have a way to check the flags of the removal
56f330
message. Therefore, we assume that any address that goes away in
56f330
tentative state was detected as duplicate.
56f330
56f330
https://bugzilla.redhat.com/show_bug.cgi?id=2096386
56f330
(cherry picked from commit a7eb77260ae6cfc56313e99f6178daa0b8283226)
56f330
(cherry picked from commit b671c36189bcddef058bcdce6c9bfeaddeb6c340)
56f330
---
56f330
 src/core/dhcp/nm-dhcp-client.c | 128 +++++++++++++++++++++++----------
56f330
 1 file changed, 92 insertions(+), 36 deletions(-)
56f330
56f330
diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c
56f330
index 77cfeecf81..a22fe65b6d 100644
56f330
--- a/src/core/dhcp/nm-dhcp-client.c
56f330
+++ b/src/core/dhcp/nm-dhcp-client.c
56f330
@@ -1043,8 +1043,11 @@ ipv6_lladdr_find(NMDhcpClient *self)
56f330
     return NULL;
56f330
 }
56f330
 
56f330
-static const NMPlatformIP6Address *
56f330
-ipv6_tentative_addr_find(NMDhcpClient *self)
56f330
+static void
56f330
+ipv6_tentative_addr_check(NMDhcpClient                *self,
56f330
+                          GPtrArray                  **tentative,
56f330
+                          GPtrArray                  **missing,
56f330
+                          const NMPlatformIP6Address **valid)
56f330
 {
56f330
     NMDhcpClientPrivate        *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
56f330
     NMDedupMultiIter            iter;
56f330
@@ -1062,16 +1065,26 @@ ipv6_tentative_addr_find(NMDhcpClient *self)
56f330
                                                                     NMP_CACHE_ID_TYPE_OBJECT_TYPE,
56f330
                                                                     &needle));
56f330
         if (!pladdr) {
56f330
-            /* Address was removed from platform */
56f330
+            /* address removed: we assume that's because DAD failed */
56f330
+            if (missing) {
56f330
+                if (!*missing)
56f330
+                    *missing = g_ptr_array_new();
56f330
+                g_ptr_array_add(*missing, (gpointer) addr);
56f330
+            }
56f330
             continue;
56f330
         }
56f330
 
56f330
         if (NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_TENTATIVE)
56f330
-            && !NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_OPTIMISTIC))
56f330
-            return pladdr;
56f330
-    }
56f330
+            && !NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_OPTIMISTIC)) {
56f330
+            if (tentative) {
56f330
+                if (!*tentative)
56f330
+                    *tentative = g_ptr_array_new();
56f330
+                g_ptr_array_add(*tentative, (gpointer) addr);
56f330
+            }
56f330
+        }
56f330
 
56f330
-    return NULL;
56f330
+        NM_SET_OUT(valid, addr);
56f330
+    }
56f330
 }
56f330
 
56f330
 static void
56f330
@@ -1108,21 +1121,61 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp
56f330
 
56f330
     if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE
56f330
         && priv->l3cfg_notify.wait_ipv6_dad) {
56f330
-        const NMPlatformIP6Address *tentative;
56f330
+        gs_unref_ptrarray GPtrArray *tentative = NULL;
56f330
+        gs_unref_ptrarray GPtrArray *missing   = NULL;
56f330
+        const NMPlatformIP6Address  *valid     = NULL;
56f330
+        char                         str[NM_UTILS_TO_STRING_BUFFER_SIZE];
56f330
+        guint                        i;
56f330
+        gs_free_error GError        *error = NULL;
56f330
+
56f330
+        ipv6_tentative_addr_check(self, &tentative, &missing, &valid);
56f330
+        if (tentative) {
56f330
+            for (i = 0; i < tentative->len; i++) {
56f330
+                _LOGD("still waiting DAD for address: %s",
56f330
+                      nm_platform_ip6_address_to_string(tentative->pdata[i], str, sizeof(str)));
56f330
+            }
56f330
+        } else {
56f330
+            /* done */
56f330
 
56f330
-        tentative = ipv6_tentative_addr_find(self);
56f330
-        if (!tentative) {
56f330
-            _LOGD("addresses in the lease completed DAD");
56f330
             priv->l3cfg_notify.wait_ipv6_dad = FALSE;
56f330
             nm_clear_g_source_inst(&priv->v6.dad_timeout_source);
56f330
             l3_cfg_notify_check_connected(self);
56f330
-            _emit_notify(
56f330
-                self,
56f330
-                &((NMDhcpClientNotifyData){.notify_type  = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE,
56f330
-                                           .lease_update = {
56f330
-                                               .l3cd     = priv->l3cd_curr,
56f330
-                                               .accepted = TRUE,
56f330
-                                           }}));
56f330
+
56f330
+            if (missing) {
56f330
+                for (i = 0; i < missing->len; i++) {
56f330
+                    _LOGE("DAD failed for address: %s",
56f330
+                          nm_platform_ip6_address_to_string(missing->pdata[i], str, sizeof(str)));
56f330
+                }
56f330
+            }
56f330
+
56f330
+            if (valid) {
56f330
+                /* at least one non-duplicate address */
56f330
+                _LOGD("addresses in the lease completed DAD: accept the lease");
56f330
+
56f330
+                if (_dhcp_client_accept(self, priv->l3cd_curr, &error)) {
56f330
+                    _emit_notify(self,
56f330
+                                 &((NMDhcpClientNotifyData){
56f330
+                                     .notify_type  = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE,
56f330
+                                     .lease_update = {
56f330
+                                         .l3cd     = priv->l3cd_curr,
56f330
+                                         .accepted = TRUE,
56f330
+                                     }}));
56f330
+                } else {
56f330
+                    gs_free char *reason =
56f330
+                        g_strdup_printf("error accepting lease: %s", error->message);
56f330
+
56f330
+                    _LOGD("accept failed: %s", error->message);
56f330
+                    _emit_notify(self,
56f330
+                                 &((NMDhcpClientNotifyData){
56f330
+                                     .notify_type         = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
56f330
+                                     .it_looks_bad.reason = reason,
56f330
+                                 }));
56f330
+                }
56f330
+            } else {
56f330
+                _LOGD("decline the lease");
56f330
+                if (!_dhcp_client_decline(self, priv->l3cd_curr, "DAD failed", &error))
56f330
+                    _LOGD("decline failed: %s", error->message);
56f330
+            }
56f330
         }
56f330
     }
56f330
 
56f330
@@ -1155,20 +1208,23 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp
56f330
                                                     address4->peer_address))
56f330
                 goto wait_dhcp_commit_done;
56f330
         } else {
56f330
-            const NMPlatformIP6Address *address6 = (const NMPlatformIP6Address *) lease_address;
56f330
-            const NMPlatformIP6Address *tentative;
56f330
-            char                        str[NM_UTILS_TO_STRING_BUFFER_SIZE];
56f330
+            const NMPlatformIP6Address  *address6  = (const NMPlatformIP6Address *) lease_address;
56f330
+            gs_unref_ptrarray GPtrArray *tentative = NULL;
56f330
+            char                         str[NM_UTILS_TO_STRING_BUFFER_SIZE];
56f330
+            guint                        i;
56f330
 
56f330
             if (!nm_l3_config_data_lookup_address_6(committed_l3cd, &address6->address))
56f330
                 goto wait_dhcp_commit_done;
56f330
 
56f330
-            tentative = ipv6_tentative_addr_find(self);
56f330
+            ipv6_tentative_addr_check(self, &tentative, NULL, NULL);
56f330
             if (tentative) {
56f330
                 priv->l3cfg_notify.wait_ipv6_dad = TRUE;
56f330
                 priv->v6.dad_timeout_source =
56f330
                     nm_g_timeout_add_seconds_source(30, ipv6_dad_timeout, self);
56f330
-                _LOGD("wait DAD for address %s",
56f330
-                      nm_platform_ip6_address_to_string(tentative, str, sizeof(str)));
56f330
+                for (i = 0; i < tentative->len; i++) {
56f330
+                    _LOGD("wait DAD for address %s",
56f330
+                          nm_platform_ip6_address_to_string(tentative->pdata[i], str, sizeof(str)));
56f330
+                }
56f330
             } else {
56f330
                 priv->l3cfg_notify.wait_ipv6_dad = FALSE;
56f330
                 nm_clear_g_source_inst(&priv->v6.dad_timeout_source);
56f330
@@ -1179,22 +1235,22 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp
56f330
 
56f330
         l3_cfg_notify_check_connected(self);
56f330
 
56f330
-        _LOGD("accept lease");
56f330
+        if (priv->config.addr_family == AF_INET || !priv->l3cfg_notify.wait_ipv6_dad) {
56f330
+            _LOGD("accept lease");
56f330
 
56f330
-        if (!_dhcp_client_accept(self, priv->l3cd_curr, &error)) {
56f330
-            gs_free char *reason = g_strdup_printf("error accepting lease: %s", error->message);
56f330
+            if (!_dhcp_client_accept(self, priv->l3cd_curr, &error)) {
56f330
+                gs_free char *reason = g_strdup_printf("error accepting lease: %s", error->message);
56f330
 
56f330
-            _LOGD("accept failed: %s", error->message);
56f330
+                _LOGD("accept failed: %s", error->message);
56f330
 
56f330
-            _emit_notify(self,
56f330
-                         &((NMDhcpClientNotifyData){
56f330
-                             .notify_type         = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
56f330
-                             .it_looks_bad.reason = reason,
56f330
-                         }));
56f330
-            goto wait_dhcp_commit_done;
56f330
-        }
56f330
+                _emit_notify(self,
56f330
+                             &((NMDhcpClientNotifyData){
56f330
+                                 .notify_type         = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
56f330
+                                 .it_looks_bad.reason = reason,
56f330
+                             }));
56f330
+                goto wait_dhcp_commit_done;
56f330
+            }
56f330
 
56f330
-        if (priv->config.addr_family == AF_INET || !priv->l3cfg_notify.wait_ipv6_dad) {
56f330
             _emit_notify(
56f330
                 self,
56f330
                 &((NMDhcpClientNotifyData){.notify_type  = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE,
56f330
-- 
56f330
2.38.1
56f330