device: add "ipv4.dhcp-client-id=ipv6-duid" property for RFC4361

RFC4361 intends to set the same IAID/DUID for both DHCPv4 and DHCPv6.
Previously, we didn't have a mode for that.

Of course, you could always set "ipv4.dhcp-client-id" and
"ipv6.dhcp-duid" to (the same) hex string, but there was no
automatic mode. Instead we had:

- "ipv4.dhcp-client-id=duid" which sets the client ID to a stable,
  generated DUID. However, there was no option so that the same
  DUID/IAID would be automatically used for DHCPv6.

- there are various special values for "ipv6.dhcp-duid" which generate
  a stable DUIDs. However, those values did not work for
  "ipv4.dhcp-client-id".

Solve that by adding "ipv4.dhcp-client-id=ipv6-duid" which indicates to use
the DUID from DHCPv6's "ipv6.dhcp-duid" setting. As IAID it will prefer "ipv4.dhcp-iaid"
(if set), but fallback to "ipv6.dhcp-iaid".

https://tools.ietf.org/html/rfc4361

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/618

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/718
This commit is contained in:
Thomas Haller 2021-01-04 17:32:47 +01:00
parent 6914e19e80
commit 800e226334
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
6 changed files with 83 additions and 20 deletions

View file

@ -652,7 +652,7 @@
<property name="ignore-auto-dns"
description="When &quot;method&quot; is set to &quot;auto&quot; and this property to TRUE, automatically configured name servers and search domains are ignored and only name servers and search domains specified in the &quot;dns&quot; and &quot;dns-search&quot; properties, if any, are used." />
<property name="dhcp-client-id"
description="A string sent to the DHCP server to identify the local machine which the DHCP server may use to customize the DHCP lease and options. When the property is a hex string (&apos;aa:bb:cc&apos;) it is interpreted as a binary client ID, in which case the first byte is assumed to be the &apos;type&apos; field as per RFC 2132 section 9.14 and the remaining bytes may be an hardware address (e.g. &apos;01:xx:xx:xx:xx:xx:xx&apos; where 1 is the Ethernet ARP type and the rest is a MAC address). If the property is not a hex string it is considered as a non-hardware-address client ID and the &apos;type&apos; field is set to 0. The special values &quot;mac&quot; and &quot;perm-mac&quot; are supported, which use the current or permanent MAC address of the device to generate a client identifier with type ethernet (01). Currently, these options only work for ethernet type of links. The special value &quot;duid&quot; generates a RFC4361-compliant client identifier based on a hash of the interface name as IAID and /etc/machine-id. The special value &quot;stable&quot; is supported to generate a type 0 client identifier based on the stable-id (see connection.stable-id) and a per-host key. If you set the stable-id, you may want to include the &quot;${DEVICE}&quot; or &quot;${MAC}&quot; specifier to get a per-device key. If unset, a globally configured default is used. If still unset, the default depends on the DHCP plugin." />
description="A string sent to the DHCP server to identify the local machine which the DHCP server may use to customize the DHCP lease and options. When the property is a hex string (&apos;aa:bb:cc&apos;) it is interpreted as a binary client ID, in which case the first byte is assumed to be the &apos;type&apos; field as per RFC 2132 section 9.14 and the remaining bytes may be an hardware address (e.g. &apos;01:xx:xx:xx:xx:xx:xx&apos; where 1 is the Ethernet ARP type and the rest is a MAC address). If the property is not a hex string it is considered as a non-hardware-address client ID and the &apos;type&apos; field is set to 0. The special values &quot;mac&quot; and &quot;perm-mac&quot; are supported, which use the current or permanent MAC address of the device to generate a client identifier with type ethernet (01). Currently, these options only work for ethernet type of links. The special value &quot;ipv6-duid&quot; uses the DUID from &quot;ipv6.dhcp-duid&quot; property as an RFC4361-compliant client identifier. As IAID it uses &quot;ipv4.dhcp-iaid&quot; and falls back to &quot;ipv6.dhcp-iaid&quot; if unset. The special value &quot;duid&quot; generates a RFC4361-compliant client identifier based on &quot;ipv4.dhcp-iaid&quot; and uses a DUID generated by hashing /etc/machine-id. The special value &quot;stable&quot; is supported to generate a type 0 client identifier based on the stable-id (see connection.stable-id) and a per-host key. If you set the stable-id, you may want to include the &quot;${DEVICE}&quot; or &quot;${MAC}&quot; specifier to get a per-device key. If unset, a globally configured default is used. If still unset, the default depends on the DHCP plugin." />
<property name="dhcp-iaid"
description="A string containing the &quot;Identity Association Identifier&quot; (IAID) used by the DHCP client. The property is a 32-bit decimal value or a special value among &quot;mac&quot;, &quot;perm-mac&quot;, &quot;ifname&quot; and &quot;stable&quot;. When set to &quot;mac&quot; (or &quot;perm-mac&quot;), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to &quot;ifname&quot;, the IAID is computed by hashing the interface name. The special value &quot;stable&quot; can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be &quot;ifname&quot;. Note that at the moment this property is ignored for IPv6 by dhclient, which always derives the IAID from the MAC address." />
<property name="dhcp-timeout"

View file

@ -227,7 +227,7 @@
#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_TTL N_("The TTL to assign to tunneled packets. 0 is a special value meaning that packets inherit the TTL value.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ADDRESSES N_("Array of IP addresses.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DAD_TIMEOUT N_("Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network. If an address conflict is detected, the activation will fail. A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero). A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID N_("A string sent to the DHCP server to identify the local machine which the DHCP server may use to customize the DHCP lease and options. When the property is a hex string ('aa:bb:cc') it is interpreted as a binary client ID, in which case the first byte is assumed to be the 'type' field as per RFC 2132 section 9.14 and the remaining bytes may be an hardware address (e.g. '01:xx:xx:xx:xx:xx:xx' where 1 is the Ethernet ARP type and the rest is a MAC address). If the property is not a hex string it is considered as a non-hardware-address client ID and the 'type' field is set to 0. The special values \"mac\" and \"perm-mac\" are supported, which use the current or permanent MAC address of the device to generate a client identifier with type ethernet (01). Currently, these options only work for ethernet type of links. The special value \"duid\" generates a RFC4361-compliant client identifier based on a hash of the interface name as IAID and /etc/machine-id. The special value \"stable\" is supported to generate a type 0 client identifier based on the stable-id (see connection.stable-id) and a per-host key. If you set the stable-id, you may want to include the \"${DEVICE}\" or \"${MAC}\" specifier to get a per-device key. If unset, a globally configured default is used. If still unset, the default depends on the DHCP plugin.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID N_("A string sent to the DHCP server to identify the local machine which the DHCP server may use to customize the DHCP lease and options. When the property is a hex string ('aa:bb:cc') it is interpreted as a binary client ID, in which case the first byte is assumed to be the 'type' field as per RFC 2132 section 9.14 and the remaining bytes may be an hardware address (e.g. '01:xx:xx:xx:xx:xx:xx' where 1 is the Ethernet ARP type and the rest is a MAC address). If the property is not a hex string it is considered as a non-hardware-address client ID and the 'type' field is set to 0. The special values \"mac\" and \"perm-mac\" are supported, which use the current or permanent MAC address of the device to generate a client identifier with type ethernet (01). Currently, these options only work for ethernet type of links. The special value \"ipv6-duid\" uses the DUID from \"ipv6.dhcp-duid\" property as an RFC4361-compliant client identifier. As IAID it uses \"ipv4.dhcp-iaid\" and falls back to \"ipv6.dhcp-iaid\" if unset. The special value \"duid\" generates a RFC4361-compliant client identifier based on \"ipv4.dhcp-iaid\" and uses a DUID generated by hashing /etc/machine-id. The special value \"stable\" is supported to generate a type 0 client identifier based on the stable-id (see connection.stable-id) and a per-host key. If you set the stable-id, you may want to include the \"${DEVICE}\" or \"${MAC}\" specifier to get a per-device key. If unset, a globally configured default is used. If still unset, the default depends on the DHCP plugin.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_FQDN N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified FQDN will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-hostname\" are mutually exclusive and cannot be set at the same time.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-fqdn\" are mutually exclusive and cannot be set at the same time.")
#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME_FLAGS N_("Flags for the DHCP hostname and FQDN. Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) and NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE (0x4). When no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is not set, the standard FQDN flags are set in the request: NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) for IPv4 and NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1) for IPv6. When this property is set to the default value NM_DHCP_HOSTNAME_FLAG_NONE (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also NM_DHCP_HOSTNAME_FLAG_NONE (0x0), then the standard FQDN flags described above are sent in the DHCP requests.")

View file

@ -808,8 +808,12 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
* with type ethernet (01). Currently, these options only work for ethernet
* type of links.
*
* The special value "ipv6-duid" uses the DUID from "ipv6.dhcp-duid" property as
* an RFC4361-compliant client identifier. As IAID it uses "ipv4.dhcp-iaid"
* and falls back to "ipv6.dhcp-iaid" if unset.
*
* The special value "duid" generates a RFC4361-compliant client identifier based
* on a hash of the interface name as IAID and /etc/machine-id.
* on "ipv4.dhcp-iaid" and uses a DUID generated by hashing /etc/machine-id.
*
* The special value "stable" is supported to generate a type 0 client identifier based
* on the stable-id (see connection.stable-id) and a per-host key. If you set the

View file

@ -1501,6 +1501,7 @@ out:
* @self: the #NMDevice
* @addr_family: the address family
* @connection: the connection
* @log_silent: whether to log the result.
* @out_is_explicit: on return, %TRUE if the user set a valid IAID in
* the connection or in global configuration; %FALSE if the connection
* property was empty and no valid global configuration was provided.
@ -1511,6 +1512,7 @@ static guint32
_prop_get_ipvx_dhcp_iaid(NMDevice * self,
int addr_family,
NMConnection *connection,
gboolean log_silent,
gboolean * out_is_explicit)
{
const int IS_IPv4 = NM_IS_IPv4(addr_family);
@ -1534,10 +1536,12 @@ _prop_get_ipvx_dhcp_iaid(NMDevice * self,
iaid_str = NM_IAID_IFNAME;
is_explicit = FALSE;
} else if (!_nm_utils_iaid_verify(iaid_str, NULL)) {
_LOGW(LOGD_DEVICE,
"invalid global default '%s' for ipv%c.dhcp-iaid",
iaid_str,
nm_utils_addr_family_to_char(addr_family));
if (!log_silent) {
_LOGW(LOGD_DEVICE,
"invalid global default '%s' for ipv%c.dhcp-iaid",
iaid_str,
nm_utils_addr_family_to_char(addr_family));
}
iaid_str = NM_IAID_IFNAME;
is_explicit = FALSE;
}
@ -1611,21 +1615,25 @@ _prop_get_ipvx_dhcp_iaid(NMDevice * self,
out_fail:
nm_assert(fail_reason);
_LOGW(LOGD_DEVICE | LOGD_DHCPX(IS_IPv4) | LOGD_IPX(IS_IPv4),
"ipv%c.dhcp-iaid: failure to generate IAID: %s. Using interface-name based IAID",
nm_utils_addr_family_to_char(addr_family),
fail_reason);
if (!log_silent) {
_LOGW(LOGD_DEVICE | LOGD_DHCPX(IS_IPv4) | LOGD_IPX(IS_IPv4),
"ipv%c.dhcp-iaid: failure to generate IAID: %s. Using interface-name based IAID",
nm_utils_addr_family_to_char(addr_family),
fail_reason);
}
is_explicit = FALSE;
iface = nm_device_get_ip_iface(self);
iaid = nm_utils_create_dhcp_iaid(TRUE, (const guint8 *) iface, strlen(iface));
out_good:
_LOGD(LOGD_DEVICE | LOGD_DHCPX(IS_IPv4) | LOGD_IPX(IS_IPv4),
"ipv%c.dhcp-iaid: using %u (0x%08x) IAID (str: '%s', explicit %d)",
nm_utils_addr_family_to_char(addr_family),
iaid,
iaid,
iaid_str,
is_explicit);
if (!log_silent) {
_LOGD(LOGD_DEVICE | LOGD_DHCPX(IS_IPv4) | LOGD_IPX(IS_IPv4),
"ipv%c.dhcp-iaid: using %u (0x%08x) IAID (str: '%s', explicit %d)",
nm_utils_addr_family_to_char(addr_family),
iaid,
iaid,
iaid_str,
is_explicit);
}
NM_SET_OUT(out_is_explicit, is_explicit);
return iaid;
}
@ -1783,12 +1791,37 @@ _prop_get_ipv4_dhcp_client_id(NMDevice *self, NMConnection *connection, GBytes *
}
if (nm_streq(client_id, "duid")) {
guint32 iaid = _prop_get_ipvx_dhcp_iaid(self, AF_INET, connection, NULL);
guint32 iaid = _prop_get_ipvx_dhcp_iaid(self, AF_INET, connection, FALSE, NULL);
result = nm_utils_dhcp_client_id_systemd_node_specific(iaid);
goto out_good;
}
if (nm_streq(client_id, "ipv6-duid")) {
gs_unref_bytes GBytes *duid = NULL;
gboolean iaid_is_explicit;
guint32 iaid;
const guint8 * duid_arr;
gsize duid_len;
iaid = _prop_get_ipvx_dhcp_iaid(self, AF_INET, connection, FALSE, &iaid_is_explicit);
if (!iaid_is_explicit)
iaid = _prop_get_ipvx_dhcp_iaid(self, AF_INET6, connection, FALSE, &iaid_is_explicit);
duid = _prop_get_ipv6_dhcp_duid(self, connection, hwaddr, NULL);
nm_assert(duid);
duid_arr = g_bytes_get_data(duid, &duid_len);
nm_assert(duid_arr);
nm_assert(duid_len >= 2u + 1u);
nm_assert(duid_len <= 2u + 128u);
result = nm_utils_dhcp_client_id_duid(iaid, duid_arr, duid_len);
goto out_good;
}
if (nm_streq(client_id, "stable")) {
nm_auto_free_checksum GChecksum *sum = NULL;
guint8 digest[NM_UTILS_CHECKSUM_LENGTH_SHA1];
@ -9802,7 +9835,7 @@ dhcp6_start_with_link_ready(NMDevice *self, NMConnection *connection)
if (pllink)
hwaddr = nmp_link_address_get_as_bytes(&pllink->l_address);
iaid = _prop_get_ipvx_dhcp_iaid(self, AF_INET6, connection, &iaid_explicit);
iaid = _prop_get_ipvx_dhcp_iaid(self, AF_INET6, connection, TRUE, &iaid_explicit);
duid = _prop_get_ipv6_dhcp_duid(self, connection, hwaddr, &enforce_duid);
priv->dhcp_data_6.client = nm_dhcp_manager_start_ip6(

View file

@ -4035,6 +4035,30 @@ nm_utils_create_dhcp_iaid(gboolean legacy_unstable_byteorder,
}
}
GBytes *
nm_utils_dhcp_client_id_duid(guint32 iaid, const guint8 *duid, gsize duid_len)
{
struct _nm_packed {
guint8 type;
guint32 iaid;
guint8 duid[];
} * client_id;
gsize total_size;
/* the @duid must include the 16 bit duid-type and the data (of max 128 bytes). */
g_return_val_if_fail(duid_len > 2 && duid_len < 128 + 2, NULL);
g_return_val_if_fail(duid, NULL);
total_size = sizeof(*client_id) + duid_len;
client_id = g_malloc(total_size);
client_id->type = 255;
unaligned_write_be32(&client_id->iaid, iaid);
memcpy(client_id->duid, duid, duid_len);
return g_bytes_new_take(client_id, total_size);
}
/**
* nm_utils_dhcp_client_id_systemd_node_specific_full:
* @iaid: the IAID (identity association identifier) in native byte order

View file

@ -449,6 +449,8 @@ guint32 nm_utils_create_dhcp_iaid(gboolean legacy_unstable_byteorder,
const guint8 *interface_id,
gsize interface_id_len);
GBytes *nm_utils_dhcp_client_id_duid(guint32 iaid, const guint8 *duid, gsize duid_len);
GBytes *nm_utils_dhcp_client_id_systemd_node_specific_full(guint32 iaid,
const guint8 *machine_id,
gsize machine_id_len);