dhcp/dhclient: fix honoring "ipv6.dhcp-duid" when explicitly set

Previously, we only set the "default-duid" line in the lease file. That
means, if the lease already contained a matching entry with a
"dhcp6.client-id" option, it was not honored. That is wrong.

If the profile has "ipv6.dhcp-duid" set, then we must use it and get
rid of those options from the lease.

It's easy to reproduce:

    PROFILE=eth1

    nmcli connection down "$PROFILE"
    rm -f /var/lib/NetworkManager/*lease
    nmcli connection modify "$PROFILE" ipv6.dhcp-duid "aa:bb:cc:dd:00:00:11"
    nmcli connection up "$PROFILE"
    # Verify the expected duid in /var/lib/NetworkManager/*lease and "/run/NetworkManager/devices/$IFINDEX"

    nmcli connection modify "$PROFILE" ipv6.dhcp-duid "aa:bb:cc:dd:00:00:22"
    nmcli connection up "$PROFILE"
    # Check the DUID again.

(cherry picked from commit 1d85608e1c)
This commit is contained in:
Thomas Haller 2022-12-14 10:37:15 +01:00
parent b7d343af05
commit 6bc03e9c95
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
4 changed files with 40 additions and 7 deletions

View file

@ -524,6 +524,20 @@ nm_dhcp_dhclient_create_config(const char *interface,
return g_string_free(g_steal_pointer(&new_contents), FALSE);
}
/* In the lease file, dhclient will write "option dhcp6.client-id $HEXSTR". This
* function does the same. */
static char *
nm_dhcp_dhclient_escape_duid_as_hex(GBytes *duid)
{
const guint8 *s;
gsize len;
nm_assert(duid);
s = g_bytes_get_data(duid, &len);
return nm_utils_bin2hexstr_fuller(s, len, ':', FALSE, FALSE, NULL);
}
/* Roughly follow what dhclient's quotify_buf() and pretty_escape() functions do */
char *
nm_dhcp_dhclient_escape_duid(GBytes *duid)
@ -648,14 +662,18 @@ nm_dhcp_dhclient_read_duid(const char *leasefile, GError **error)
}
gboolean
nm_dhcp_dhclient_save_duid(const char *leasefile, GBytes *duid, GError **error)
nm_dhcp_dhclient_save_duid(const char *leasefile,
GBytes *duid,
gboolean enforce_duid,
GError **error)
{
gs_free char *escaped_duid = NULL;
gs_free const char **lines = NULL;
nm_auto_free_gstring GString *s = NULL;
const char *const *iter;
gs_free char *contents = NULL;
gsize contents_len = 0;
gs_free char *conflicting_duid_line = NULL;
gs_free char *contents = NULL;
gsize contents_len = 0;
g_return_val_if_fail(leasefile != NULL, FALSE);
@ -702,6 +720,17 @@ nm_dhcp_dhclient_save_duid(const char *leasefile, GBytes *duid, GError **error)
continue;
}
if (enforce_duid & NM_STR_HAS_PREFIX(l, "option dhcp6.client-id ")) {
/* we want to use our duid. Skip the per-lease client-id. */
if (!conflicting_duid_line) {
gs_free char *duid_hex = nm_dhcp_dhclient_escape_duid_as_hex(duid);
conflicting_duid_line = g_strdup_printf("option dhcp6.client-id %s;", duid_hex);
}
/* We adjust the duid line and set what we want. */
l = conflicting_duid_line;
}
g_string_append_len(s, str, prefix_len);
g_string_append(s, l);
if (ends_with_r) {

View file

@ -29,6 +29,9 @@ GBytes *nm_dhcp_dhclient_unescape_duid(const char *duid);
GBytes *nm_dhcp_dhclient_read_duid(const char *leasefile, GError **error);
gboolean nm_dhcp_dhclient_save_duid(const char *leasefile, GBytes *duid, GError **error);
gboolean nm_dhcp_dhclient_save_duid(const char *leasefile,
GBytes *duid,
gboolean enforce_duid,
GError **error);
#endif /* __NETWORKMANAGER_DHCP_DHCLIENT_UTILS_H__ */

View file

@ -418,6 +418,7 @@ dhclient_start(NMDhcpClient *client,
if (set_duid && addr_family == AF_INET6) {
if (!nm_dhcp_dhclient_save_duid(priv->lease_file,
nm_dhcp_client_get_effective_client_id(client),
client_config->v6.enforce_duid,
&local)) {
nm_utils_error_set(error,
NM_UTILS_ERROR_UNKNOWN,

View file

@ -897,7 +897,7 @@ test_read_commented_duid_from_leasefile(void)
static void
_check_duid_impl(const guint8 *duid_bin,
gsize duid_len,
gboolean enforce_duid, /* Unused at the moment. */
gboolean enforce_duid,
const char *old_content,
const char *new_content)
{
@ -919,7 +919,7 @@ _check_duid_impl(const guint8 *duid_bin,
duid = g_bytes_new(duid_bin, duid_len);
success = nm_dhcp_dhclient_save_duid(path, duid, &error);
success = nm_dhcp_dhclient_save_duid(path, duid, enforce_duid, &error);
nmtst_assert_success(success, error);
success = g_file_get_contents(path, &contents, &contents_len, &error);
@ -1060,7 +1060,7 @@ test_write_duid(void)
" max-life 120;\n }\n }\n option fqdn.encoded true;\n option "
"fqdn.server-update true;\n option fqdn.no-client-update false;\n option fqdn.fqdn "
"\"dff6de4fcb0f\";\n option fqdn.hostname \"dff6de4fcb0f\";\n option dhcp6.client-id "
"aa:b:cc:d:ee:f;\n option dhcp6.server-id 0:1:0:1:2b:2c:4d:1d:0:0:0:0:0:0;\n option "
"aa:b:cc:d:ee:e;\n option dhcp6.server-id 0:1:0:1:2b:2c:4d:1d:0:0:0:0:0:0;\n option "
"dhcp6.name-servers 192:168:121:0:ce0f:f1ff:fece:1;\n option dhcp6.fqdn "
"1:c:64:66:66:36:64:65:34:66:63:62:30:66;\n option dhcp6.status-code success "
"\"success\";\n}\n");