summary refs log tree commit diff
path: root/src/core/dhcp/nm-dhcp-client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/dhcp/nm-dhcp-client.c')
-rw-r--r--src/core/dhcp/nm-dhcp-client.c229
1 files changed, 127 insertions, 102 deletions
diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c
index 805b42d2..600cb930 100644
--- a/src/core/dhcp/nm-dhcp-client.c
+++ b/src/core/dhcp/nm-dhcp-client.c
@@ -84,6 +84,7 @@ typedef struct _NMDhcpClientPrivate {
      * and is set from l3cd_next. */
     const NML3ConfigData *l3cd_curr;
 
+    GSource *previous_lease_timeout_source;
     GSource *no_lease_timeout_source;
     GSource *watch_source;
     GBytes  *effective_client_id;
@@ -269,6 +270,12 @@ nm_dhcp_client_create_options_dict(NMDhcpClient *self, gboolean static_keys)
     return options;
 }
 
+const NML3ConfigData *
+nm_dhcp_client_get_lease(NMDhcpClient *self)
+{
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->l3cd_curr;
+}
+
 /*****************************************************************************/
 
 gboolean
@@ -301,11 +308,16 @@ nm_dhcp_client_set_effective_client_id(NMDhcpClient *self, GBytes *client_id)
 /*****************************************************************************/
 
 static void
-_emit_notify(NMDhcpClient *self, const NMDhcpClientNotifyData *notify_data)
+_emit_notify_data(NMDhcpClient *self, const NMDhcpClientNotifyData *notify_data)
 {
     g_signal_emit(G_OBJECT(self), signals[SIGNAL_NOTIFY], 0, notify_data);
 }
 
+#define _emit_notify(self, _notify_type, ...) \
+    _emit_notify_data(                        \
+        (self),                               \
+        &((const NMDhcpClientNotifyData){.notify_type = (_notify_type), __VA_ARGS__}))
+
 /*****************************************************************************/
 
 static void
@@ -392,12 +404,7 @@ _no_lease_timeout(gpointer user_data)
     NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
 
     nm_clear_g_source_inst(&priv->no_lease_timeout_source);
-
-    _emit_notify(self,
-                 &((NMDhcpClientNotifyData){
-                     .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT,
-                 }));
-
+    _emit_notify(self, NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT);
     return G_SOURCE_CONTINUE;
 }
 
@@ -473,7 +480,7 @@ _acd_complete_on_idle_cb(gpointer user_data)
 }
 
 #define _acd_reglist_data_get(priv, idx) \
-    nm_g_array_index_p((priv)->v4.acd.reglist, AcdRegListData, (idx))
+    (&nm_g_array_index((priv)->v4.acd.reglist, AcdRegListData, (idx)))
 
 static guint
 _acd_reglist_data_find(NMDhcpClientPrivate *priv, in_addr_t addr_needle)
@@ -503,10 +510,10 @@ _acd_reglist_data_remove(NMDhcpClient *self, guint idx, gboolean do_log)
     reglist_data = _acd_reglist_data_get(priv, idx);
 
     if (do_log) {
-        char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN];
+        char sbuf_addr[NM_INET_ADDRSTRLEN];
 
         _LOGD("acd: drop check for address %s (l3cd " NM_HASH_OBFUSCATE_PTR_FMT ")",
-              _nm_utils_inet4_ntop(reglist_data->addr, sbuf_addr),
+              nm_inet4_ntop(reglist_data->addr, sbuf_addr),
               NM_HASH_OBFUSCATE_PTR(reglist_data->l3cd));
     }
 
@@ -585,7 +592,7 @@ static void
 _acd_check_lease(NMDhcpClient *self, NMOptionBool *out_acd_state)
 {
     NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
-    char                 sbuf_addr[NM_UTILS_INET_ADDRSTRLEN];
+    char                 sbuf_addr[NM_INET_ADDRSTRLEN];
     in_addr_t            addr;
     gboolean             addr_changed = FALSE;
     guint                idx;
@@ -624,7 +631,7 @@ _acd_check_lease(NMDhcpClient *self, NMOptionBool *out_acd_state)
 
     _LOGD("acd: %s check for address %s (timeout %u msec, l3cd " NM_HASH_OBFUSCATE_PTR_FMT ")",
           addr_changed ? "add" : "update",
-          _nm_utils_inet4_ntop(addr, sbuf_addr),
+          nm_inet4_ntop(addr, sbuf_addr),
           priv->config.v4.acd_timeout_msec,
           NM_HASH_OBFUSCATE_PTR(priv->l3cd_next));
 
@@ -711,7 +718,7 @@ gboolean
 _nm_dhcp_client_accept_offer(NMDhcpClient *self, gconstpointer p_yiaddr)
 {
     NMDhcpClientPrivate   *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
-    char                   sbuf_addr[NM_UTILS_INET_ADDRSTRLEN];
+    char                   sbuf_addr[NM_INET_ADDRSTRLEN];
     NMIPAddr               yiaddr;
     const NML3AcdAddrInfo *acd_info;
 
@@ -744,7 +751,7 @@ _nm_dhcp_client_accept_offer(NMDhcpClient *self, gconstpointer p_yiaddr)
         return TRUE;
 
     _LOGD("offered lease rejected: address %s failed ACD check",
-          _nm_utils_inet4_ntop(yiaddr.addr4, sbuf_addr));
+          nm_inet4_ntop(yiaddr.addr4, sbuf_addr));
 
     return FALSE;
 }
@@ -863,6 +870,9 @@ _nm_dhcp_client_notify(NMDhcpClient         *self,
         return;
     }
 
+    if (priv->l3cd_next)
+        nm_clear_g_source_inst(&priv->previous_lease_timeout_source);
+
     nm_l3_config_data_reset(&priv->l3cd_curr, priv->l3cd_next);
 
     if (client_event_type == NM_DHCP_CLIENT_EVENT_TYPE_BOUND && priv->l3cd_curr
@@ -883,18 +893,12 @@ _nm_dhcp_client_notify(NMDhcpClient         *self,
 
     l3_cfg_notify_check_connected(self);
 
-    {
-        const NMDhcpClientNotifyData notify_data = {
-            .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE,
-            .lease_update =
-                {
-                    .l3cd     = priv->l3cd_curr,
-                    .accepted = !priv->l3cfg_notify.wait_dhcp_commit,
-                },
-        };
-
-        _emit_notify(self, &notify_data);
-    }
+    _emit_notify(self,
+                 NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE,
+                 .lease_update = {
+                     .l3cd     = priv->l3cd_curr,
+                     .accepted = !priv->l3cfg_notify.wait_dhcp_commit,
+                 });
 }
 
 static void
@@ -1010,12 +1014,10 @@ ipv6_lladdr_timeout(gpointer user_data)
 
     nm_clear_g_source_inst(&priv->v6.lladdr_timeout_source);
 
-    _emit_notify(
-        self,
-        &((NMDhcpClientNotifyData){
-            .notify_type         = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
-            .it_looks_bad.reason = "timeout reached while waiting for an IPv6 link-local address",
-        }));
+    _emit_notify(self,
+                 NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
+                 .it_looks_bad.reason =
+                     "timeout reached while waiting for an IPv6 link-local address");
     return G_SOURCE_CONTINUE;
 }
 
@@ -1027,12 +1029,9 @@ ipv6_dad_timeout(gpointer user_data)
 
     nm_clear_g_source_inst(&priv->v6.dad_timeout_source);
 
-    _emit_notify(
-        self,
-        &((NMDhcpClientNotifyData){
-            .notify_type         = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
-            .it_looks_bad.reason = "timeout reached while waiting for IPv6 DAD to complete",
-        }));
+    _emit_notify(self,
+                 NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
+                 .it_looks_bad.reason = "timeout reached while waiting for IPv6 DAD to complete");
     return G_SOURCE_CONTINUE;
 }
 
@@ -1068,35 +1067,33 @@ ipv6_lladdr_find(NMDhcpClient *self)
 static void
 ipv6_tentative_addr_check(NMDhcpClient                *self,
                           GPtrArray                  **tentative,
-                          GPtrArray                  **missing,
+                          GPtrArray                  **dadfailed,
                           const NMPlatformIP6Address **valid)
 {
     NMDhcpClientPrivate        *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
     NMDedupMultiIter            iter;
     const NMPlatformIP6Address *addr;
-    NML3Cfg                    *l3cfg = priv->config.l3cfg;
+    NML3Cfg                    *l3cfg    = priv->config.l3cfg;
+    NMPlatform                 *platform = nm_l3cfg_get_platform(l3cfg);
+    int                         ifindex  = nm_l3cfg_get_ifindex(l3cfg);
 
     /* For each address in the lease, check whether it's tentative
-     * in platform. */
+     * or dad-failed in platform. */
     nm_l3_config_data_iter_ip6_address_for_each (&iter, priv->l3cd_curr, &addr) {
         const NMPlatformIP6Address *pladdr;
-        NMPObject                   needle;
-
-        nmp_object_stackinit_id_ip6_address(&needle, nm_l3cfg_get_ifindex(l3cfg), &addr->address);
-        pladdr = NMP_OBJECT_CAST_IP6_ADDRESS(nm_platform_lookup_obj(nm_l3cfg_get_platform(l3cfg),
-                                                                    NMP_CACHE_ID_TYPE_OBJECT_TYPE,
-                                                                    &needle));
-        if (!pladdr) {
-            /* address removed: we assume that's because DAD failed */
-            if (missing) {
-                if (!*missing)
-                    *missing = g_ptr_array_new();
-                g_ptr_array_add(*missing, (gpointer) addr);
+
+        pladdr = nm_platform_ip6_address_get(platform, ifindex, &addr->address);
+        if ((pladdr && NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_DADFAILED))
+            || (!pladdr && nm_platform_ip6_dadfailed_check(platform, ifindex, &addr->address))) {
+            if (dadfailed) {
+                if (!*dadfailed)
+                    *dadfailed = g_ptr_array_new();
+                g_ptr_array_add(*dadfailed, (gpointer) addr);
             }
             continue;
         }
 
-        if (NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_TENTATIVE)
+        if (pladdr && NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_TENTATIVE)
             && !NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_OPTIMISTIC)) {
             if (tentative) {
                 if (!*tentative)
@@ -1105,6 +1102,9 @@ ipv6_tentative_addr_check(NMDhcpClient                *self,
             }
         }
 
+        /* Here the address is non-tentative or it was removed externally by the user.
+         * In both cases it has completed DAD.
+         */
         NM_SET_OUT(valid, addr);
     }
 }
@@ -1113,7 +1113,7 @@ static void
 l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcpClient *self)
 {
     NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
-    char                 sbuf_addr[NM_UTILS_INET_ADDRSTRLEN];
+    char                 sbuf_addr[NM_INET_ADDRSTRLEN];
 
     nm_assert(l3cfg == priv->config.l3cfg);
 
@@ -1133,10 +1133,8 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp
 
             if (!NM_DHCP_CLIENT_GET_CLASS(self)->ip6_start(self, &addr->address, &error)) {
                 _emit_notify(self,
-                             &((NMDhcpClientNotifyData){
-                                 .notify_type         = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
-                                 .it_looks_bad.reason = error->message,
-                             }));
+                             NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
+                             .it_looks_bad.reason = error->message);
             }
         }
     }
@@ -1144,13 +1142,13 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp
     if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE
         && priv->l3cfg_notify.wait_ipv6_dad) {
         gs_unref_ptrarray GPtrArray *tentative = NULL;
-        gs_unref_ptrarray GPtrArray *missing   = NULL;
+        gs_unref_ptrarray GPtrArray *dadfailed = NULL;
         const NMPlatformIP6Address  *valid     = NULL;
         char                         str[NM_UTILS_TO_STRING_BUFFER_SIZE];
         guint                        i;
         gs_free_error GError        *error = NULL;
 
-        ipv6_tentative_addr_check(self, &tentative, &missing, &valid);
+        ipv6_tentative_addr_check(self, &tentative, &dadfailed, &valid);
         if (tentative) {
             for (i = 0; i < tentative->len; i++) {
                 _LOGD("still waiting DAD for address: %s",
@@ -1163,10 +1161,10 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp
             nm_clear_g_source_inst(&priv->v6.dad_timeout_source);
             l3_cfg_notify_check_connected(self);
 
-            if (missing) {
-                for (i = 0; i < missing->len; i++) {
+            if (dadfailed) {
+                for (i = 0; i < dadfailed->len; i++) {
                     _LOGE("DAD failed for address: %s",
-                          nm_platform_ip6_address_to_string(missing->pdata[i], str, sizeof(str)));
+                          nm_platform_ip6_address_to_string(dadfailed->pdata[i], str, sizeof(str)));
                 }
             }
 
@@ -1176,22 +1174,19 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp
 
                 if (_dhcp_client_accept(self, priv->l3cd_curr, &error)) {
                     _emit_notify(self,
-                                 &((NMDhcpClientNotifyData){
-                                     .notify_type  = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE,
-                                     .lease_update = {
-                                         .l3cd     = priv->l3cd_curr,
-                                         .accepted = TRUE,
-                                     }}));
+                                 NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE,
+                                 .lease_update = {
+                                     .l3cd     = priv->l3cd_curr,
+                                     .accepted = TRUE,
+                                 });
                 } else {
                     gs_free char *reason =
                         g_strdup_printf("error accepting lease: %s", error->message);
 
                     _LOGD("accept failed: %s", error->message);
                     _emit_notify(self,
-                                 &((NMDhcpClientNotifyData){
-                                     .notify_type         = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
-                                     .it_looks_bad.reason = reason,
-                                 }));
+                                 NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
+                                 .it_looks_bad.reason = reason);
                 }
             } else {
                 _LOGD("decline the lease");
@@ -1266,20 +1261,17 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp
                 _LOGD("accept failed: %s", error->message);
 
                 _emit_notify(self,
-                             &((NMDhcpClientNotifyData){
-                                 .notify_type         = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
-                                 .it_looks_bad.reason = reason,
-                             }));
+                             NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
+                             .it_looks_bad.reason = reason, );
                 goto wait_dhcp_commit_done;
             }
 
-            _emit_notify(
-                self,
-                &((NMDhcpClientNotifyData){.notify_type  = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE,
-                                           .lease_update = {
-                                               .l3cd     = priv->l3cd_curr,
-                                               .accepted = TRUE,
-                                           }}));
+            _emit_notify(self,
+                         NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE,
+                         .lease_update = {
+                             .l3cd     = priv->l3cd_curr,
+                             .accepted = TRUE,
+                         });
         }
     }
 wait_dhcp_commit_done:
@@ -1318,7 +1310,7 @@ wait_dhcp_commit_done:
             if (acd_state != NM_OPTION_BOOL_DEFAULT) {
                 _LOGD("acd: acd %s for %s",
                       acd_state ? "ready" : "conflict",
-                      _nm_utils_inet4_ntop(priv->v4.acd.addr, sbuf_addr));
+                      nm_inet4_ntop(priv->v4.acd.addr, sbuf_addr));
                 nm_l3cfg_commit_type_clear(priv->config.l3cfg, &priv->v4.acd.l3cfg_commit_handle);
                 priv->v4.acd.state       = acd_state;
                 priv->v4.acd.done_source = nm_g_idle_add_source(_acd_complete_on_idle_cb, self);
@@ -1327,6 +1319,19 @@ wait_dhcp_commit_done:
     }
 }
 
+static gboolean
+_previous_lease_timeout_cb(gpointer user_data)
+{
+    NMDhcpClient        *self = user_data;
+    NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    nm_clear_g_source_inst(&priv->previous_lease_timeout_source);
+
+    _nm_dhcp_client_notify(self, NM_DHCP_CLIENT_EVENT_TYPE_TIMEOUT, NULL);
+
+    return G_SOURCE_CONTINUE;
+}
+
 gboolean
 nm_dhcp_client_start(NMDhcpClient *self, GError **error)
 {
@@ -1358,6 +1363,23 @@ nm_dhcp_client_start(NMDhcpClient *self, GError **error)
 
     _no_lease_timeout_schedule(self);
 
+    if (priv->config.previous_lease) {
+        /* We got passed a previous lease (during a reapply). For a few seconds, we
+         * will pretend that this is current lease. */
+        priv->l3cd_curr = g_steal_pointer(&priv->config.previous_lease);
+
+        /* Schedule a timeout for when we give up using this lease. Note
+         * that then we will emit a NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE event
+         * and the lease is gone. Note that NMDevice ignores that and will
+         * keep using the lease.
+         *
+         * At the same time, we have _no_lease_timeout_schedule() ticking, when
+         * that expires, we will emit a NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT
+         * signal, which causes NMDevice to clear the lease. */
+        priv->previous_lease_timeout_source =
+            nm_g_timeout_add_seconds_source(15, _previous_lease_timeout_cb, self);
+    }
+
     if (IS_IPv4)
         return NM_DHCP_CLIENT_GET_CLASS(self)->ip4_start(self, error);
 
@@ -1438,6 +1460,8 @@ nm_dhcp_client_stop(NMDhcpClient *self, gboolean release)
     if (priv->is_stopped)
         return;
 
+    nm_clear_g_source_inst(&priv->previous_lease_timeout_source);
+
     priv->is_stopped = TRUE;
 
     if (priv->invocation) {
@@ -1597,15 +1621,11 @@ maybe_add_option(NMDhcpClient *self, GHashTable *hash, const char *key, GVariant
 void
 nm_dhcp_client_emit_ipv6_prefix_delegated(NMDhcpClient *self, const NMPlatformIP6Address *prefix)
 {
-    const NMDhcpClientNotifyData notify_data = {
-        .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED,
-        .prefix_delegated =
-            {
-                .prefix = prefix,
-            },
-    };
-
-    _emit_notify(self, &notify_data);
+    _emit_notify(self,
+                 NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED,
+                 .prefix_delegated = {
+                     .prefix = prefix,
+                 });
 }
 
 gboolean
@@ -1621,7 +1641,7 @@ nm_dhcp_client_handle_event(gpointer               unused,
     nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL;
     NMDhcpClientEventType                   client_event_type;
     NMPlatformIP6Address                    prefix = {
-                           0,
+        0,
     };
 
     g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE);
@@ -1751,14 +1771,14 @@ nm_dhcp_client_server_id_is_rejected(NMDhcpClient *self, gconstpointer addr)
             in_addr_t mask;
             int       r_prefix;
 
-            if (!nm_utils_parse_inaddr_prefix_bin(AF_INET,
-                                                  priv->config.reject_servers[i],
-                                                  NULL,
-                                                  &r_addr,
-                                                  &r_prefix))
+            if (!nm_inet_parse_with_prefix_bin(AF_INET,
+                                               priv->config.reject_servers[i],
+                                               NULL,
+                                               &r_addr,
+                                               &r_prefix))
                 nm_assert_not_reached();
 
-            mask = _nm_utils_ip4_prefix_to_netmask(r_prefix < 0 ? 32 : r_prefix);
+            mask = nm_ip4_addr_netmask_from_prefix(r_prefix < 0 ? 32 : r_prefix);
             if ((addr4 & mask) == (r_addr & mask))
                 return TRUE;
         }
@@ -1783,6 +1803,8 @@ config_init(NMDhcpClientConfig *config, const NMDhcpClientConfig *src)
 
     g_object_ref(config->l3cfg);
 
+    nm_l3_config_data_ref_and_seal(config->previous_lease);
+
     nm_g_bytes_ref(config->hwaddr);
     nm_g_bytes_ref(config->bcast_hwaddr);
     nm_g_bytes_ref(config->vendor_class_identifier);
@@ -1843,6 +1865,8 @@ config_clear(NMDhcpClientConfig *config)
 {
     g_object_unref(config->l3cfg);
 
+    nm_clear_l3cd(&config->previous_lease);
+
     nm_clear_pointer(&config->hwaddr, g_bytes_unref);
     nm_clear_pointer(&config->bcast_hwaddr, g_bytes_unref);
     nm_clear_pointer(&config->vendor_class_identifier, g_bytes_unref);
@@ -1919,6 +1943,7 @@ dispose(GObject *object)
 
     watch_cleanup(self);
 
+    nm_clear_g_source_inst(&priv->previous_lease_timeout_source);
     nm_clear_g_source_inst(&priv->no_lease_timeout_source);
 
     if (!NM_IS_IPv4(priv->config.addr_family)) {