diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.c b/src/dhcp-manager/nm-dhcp-dhclient-utils.c index fdfb142ea..8dc7902f4 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient-utils.c +++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.c @@ -364,3 +364,57 @@ nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error) return duid; } +gboolean +nm_dhcp_dhclient_save_duid (const char *leasefile, + const char *escaped_duid, + GError **error) +{ + char **lines = NULL, **iter, *l; + GString *s; + gboolean success; + gsize len = 0; + + g_return_val_if_fail (leasefile != NULL, FALSE); + g_return_val_if_fail (escaped_duid != NULL, FALSE); + + if (g_file_test (leasefile, G_FILE_TEST_EXISTS)) { + char *contents = NULL; + + if (!g_file_get_contents (leasefile, &contents, &len, error)) { + g_prefix_error (error, "failed to read lease file %s: ", leasefile); + return FALSE; + } + + /* If the file already contains an uncommented DUID, leave it */ + g_assert (contents); + lines = g_strsplit_set (contents, "\n\r", -1); + g_free (contents); + for (iter = lines; iter && *iter; iter++) { + l = *iter; + while (g_ascii_isspace (*l)) + l++; + if (g_str_has_prefix (l, DUID_PREFIX)) { + g_strfreev (lines); + return TRUE; + } + } + } + + s = g_string_sized_new (len + 50); + g_string_append_printf (s, DUID_PREFIX "%s\";\n", escaped_duid); + + /* Preserve existing leasefile contents */ + if (lines) { + for (iter = lines; iter && *iter; iter++) + g_string_append (s, *iter[0] ? *iter : "\n"); + g_strfreev (lines); + } + + success = g_file_set_contents (leasefile, s->str, -1, error); + if (!success) + g_prefix_error (error, "failed to set DUID in lease file %s: ", leasefile); + + g_string_free (s, TRUE); + return success; +} + diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.h b/src/dhcp-manager/nm-dhcp-dhclient-utils.h index 93c5c5e85..95cb05680 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient-utils.h +++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.h @@ -40,5 +40,9 @@ GByteArray *nm_dhcp_dhclient_unescape_duid (const char *duid); GByteArray *nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error); +gboolean nm_dhcp_dhclient_save_duid (const char *leasefile, + const char *escaped_duid, + GError **error); + #endif /* NM_DHCP_DHCLIENT_UTILS_H */ diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index ac05dc1f2..be1ec2991 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -53,6 +53,8 @@ typedef struct { const char *def_leasefile; char *lease_file; char *pid_file; + gboolean lease_duid_found; + gboolean default_duid_found; } NMDHCPDhclientPrivate; const char * @@ -446,6 +448,7 @@ dhclient_child_setup (gpointer user_data G_GNUC_UNUSED) static GPid dhclient_start (NMDHCPClient *client, const char *mode_opt, + const GByteArray *duid, gboolean release) { NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client); @@ -496,6 +499,36 @@ dhclient_start (NMDHCPClient *client, return -1; } + if (ipv6 && duid) { + char *escaped = NULL; + + escaped = nm_dhcp_dhclient_escape_duid (duid); + + /* Save the generated DUID into the default leasefile for persistent storage */ + if (!priv->default_duid_found) { + nm_log_dbg (LOGD_DHCP6, "Saving DHCPv6 DUID '%s' to leasefile %s", + escaped, priv->def_leasefile); + if (!nm_dhcp_dhclient_save_duid (priv->def_leasefile, escaped, &error)) { + g_assert (error); + nm_log_warn (LOGD_DHCP6, "Could not save DUID: %s", error->message); + g_clear_error (&error); + } + } + + /* Save the DUID to the network-specific leasefile so it'll actually get used */ + if (!priv->lease_duid_found) { + nm_log_dbg (LOGD_DHCP6, "Saving DHCPv6 DUID '%s' to leasefile %s", + escaped, priv->lease_file); + if (!nm_dhcp_dhclient_save_duid (priv->lease_file, escaped, &error)) { + g_assert (error); + nm_log_warn (LOGD_DHCP6, "Could not save DUID: %s", error->message); + g_clear_error (&error); + } + } + + g_free (escaped); + } + argv = g_ptr_array_new (); g_ptr_array_add (argv, (gpointer) priv->path); @@ -576,7 +609,7 @@ ip4_start (NMDHCPClient *client, return -1; } - return dhclient_start (client, NULL, FALSE); + return dhclient_start (client, NULL, NULL, FALSE); } static GPid @@ -598,7 +631,7 @@ ip6_start (NMDHCPClient *client, return -1; } - return dhclient_start (client, info_only ? "-S" : "-N", FALSE); + return dhclient_start (client, info_only ? "-S" : "-N", duid, FALSE); } static void @@ -620,7 +653,7 @@ stop (NMDHCPClient *client, gboolean release, const GByteArray *duid) if (release) { GPid rpid; - rpid = dhclient_start (client, NULL, TRUE); + rpid = dhclient_start (client, NULL, duid, TRUE); if (rpid > 0) { /* Wait a few seconds for the release to happen */ nm_dhcp_client_stop_pid (rpid, nm_dhcp_client_get_iface (client), 5); @@ -642,6 +675,7 @@ get_duid (NMDHCPClient *client) TRUE); nm_log_dbg (LOGD_DHCP, "Looking for DHCPv6 DUID in '%s'.", leasefile); duid = nm_dhcp_dhclient_read_duid (leasefile, &error); + priv->lease_duid_found = !!duid; g_free (leasefile); if (error) { @@ -656,6 +690,7 @@ get_duid (NMDHCPClient *client) /* Otherwise read the default machine-wide DUID */ nm_log_dbg (LOGD_DHCP, "Looking for default DHCPv6 DUID in '%s'.", priv->def_leasefile); duid = nm_dhcp_dhclient_read_duid (priv->def_leasefile, &error); + priv->default_duid_found = !!duid; if (error) { nm_log_warn (LOGD_DHCP, "Failed to read leasefile '%s': (%d) %s", priv->def_leasefile,