diff options
author | Thomas Haller <thaller@redhat.com> | 2018-12-14 08:55:42 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2018-12-14 13:49:54 +0100 |
commit | 672852c4d3786b2ed1418fd885bcaafb46afc427 (patch) | |
tree | 4c907a5fbc4fdf534129c18cda62c86053cb6e88 /src/devices | |
parent | 6e48e99be4fe11e9bf1e2c2a20b1caadb63377d3 (diff) | |
download | NetworkManager-672852c4d3786b2ed1418fd885bcaafb46afc427.tar.gz |
dhcp: support generating DHCP client-id/duid for infiniband
https://bugzilla.redhat.com/show_bug.cgi?id=1658057
Diffstat (limited to 'src/devices')
-rw-r--r-- | src/devices/nm-device.c | 164 |
1 files changed, 129 insertions, 35 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index e4a1e1a478..956d8c781b 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -7572,16 +7572,35 @@ get_dhcp_timeout (NMDevice *self, int addr_family) return timeout ?: NM_DHCP_TIMEOUT_DEFAULT; } +static void +_ASSERT_arp_type (guint16 arp_type, + const guint8 *hwaddr, + gsize hwaddr_len) +{ + /* we actually only support ethernet and infiniband below. Assert that + * the arp-type and the address length correspond. */ + nm_assert (NM_IN_SET (arp_type, ARPHRD_ETHER, ARPHRD_INFINIBAND)); + nm_assert (arp_type <= 255); + nm_assert (hwaddr_len > 0); + nm_assert (arp_type != ARPHRD_ETHER || hwaddr_len == ETH_ALEN); + nm_assert (arp_type != ARPHRD_INFINIBAND || hwaddr_len == INFINIBAND_ALEN); + nm_assert (hwaddr); +} + static GBytes * -dhcp4_get_client_id_mac (const guint8 *hwaddr /* ETH_ALEN bytes */) +dhcp4_get_client_id_mac (guint16 arp_type, + const guint8 *hwaddr, + gsize hwaddr_len) { guint8 *client_id_buf; - guint8 hwaddr_type = ARPHRD_ETHER; + const guint8 hwaddr_type = arp_type; - client_id_buf = g_malloc (ETH_ALEN + 1); + _ASSERT_arp_type (arp_type, hwaddr, hwaddr_len); + + client_id_buf = g_malloc (hwaddr_len + 1); client_id_buf[0] = hwaddr_type; - memcpy (&client_id_buf[1], hwaddr, ETH_ALEN); - return g_bytes_new_take (client_id_buf, ETH_ALEN + 1); + memcpy (&client_id_buf[1], hwaddr, hwaddr_len); + return g_bytes_new_take (client_id_buf, hwaddr_len + 1); } static GBytes * @@ -7596,6 +7615,7 @@ dhcp4_get_client_id (NMDevice *self, const char *fail_reason; guint8 hwaddr_bin_buf[NM_UTILS_HWADDR_LEN_MAX]; const guint8 *hwaddr_bin; + int arp_type; gsize hwaddr_len; GBytes *result; gs_free char *logstr1 = NULL; @@ -7621,17 +7641,18 @@ dhcp4_get_client_id (NMDevice *self, if (nm_streq (client_id, "mac")) { if (!hwaddr) { - fail_reason = "failed to get current MAC address"; + fail_reason = "missing link-layer address"; goto out_fail; } hwaddr_bin = g_bytes_get_data (hwaddr, &hwaddr_len); - if (hwaddr_len != ETH_ALEN) { - fail_reason = "MAC address is not ethernet"; + arp_type = nm_utils_detect_arp_type_from_addrlen (hwaddr_len); + if (arp_type < 0) { + fail_reason = "unsupported link-layer address"; goto out_fail; } - result = dhcp4_get_client_id_mac (hwaddr_bin); + result = dhcp4_get_client_id_mac ((guint16) arp_type, hwaddr_bin, hwaddr_len); goto out_good; } @@ -7640,20 +7661,20 @@ dhcp4_get_client_id (NMDevice *self, hwaddr_str = nm_device_get_permanent_hw_address (self); if (!hwaddr_str) { - fail_reason = "failed to get permanent MAC address"; + fail_reason = "missing permanent link-layer address"; goto out_fail; } if (!_nm_utils_hwaddr_aton (hwaddr_str, hwaddr_bin_buf, sizeof (hwaddr_bin_buf), &hwaddr_len)) g_return_val_if_reached (NULL); - if (hwaddr_len != ETH_ALEN) { - /* unsupported type. */ - fail_reason = "MAC address is not ethernet"; + arp_type = nm_utils_detect_arp_type_from_addrlen (hwaddr_len); + if (arp_type < 0) { + fail_reason = "unsupported permanent link-layer address"; goto out_fail; } - result = dhcp4_get_client_id_mac (hwaddr_bin_buf); + result = dhcp4_get_client_id_mac ((guint16) arp_type, hwaddr_bin_buf, hwaddr_len); goto out_good; } @@ -8308,38 +8329,46 @@ dhcp6_prefix_delegated (NMDhcpClient *client, #define EPOCH_DATETIME_200001010000 946684800 static GBytes * -generate_duid_llt (const guint8 *hwaddr /* ETH_ALEN bytes */, +generate_duid_llt (guint16 arp_type, + const guint8 *hwaddr, + gsize hwaddr_len, gint64 time) { guint8 *arr; const guint16 duid_type = htons (1); - const guint16 hw_type = htons (ARPHRD_ETHER); + const guint16 hw_type = htons (arp_type); const guint32 duid_time = htonl (NM_MAX (0, time - EPOCH_DATETIME_200001010000)); - arr = g_new (guint8, 2 + 2 + 4 + ETH_ALEN); + _ASSERT_arp_type (arp_type, hwaddr, hwaddr_len); + + arr = g_new (guint8, 2 + 2 + 4 + hwaddr_len); memcpy (&arr[0], &duid_type, 2); memcpy (&arr[2], &hw_type, 2); memcpy (&arr[4], &duid_time, 4); - memcpy (&arr[8], hwaddr, ETH_ALEN); + memcpy (&arr[8], hwaddr, hwaddr_len); - return g_bytes_new_take (arr, 2 + 2 + 4 + ETH_ALEN); + return g_bytes_new_take (arr, 2 + 2 + 4 + hwaddr_len); } static GBytes * -generate_duid_ll (const guint8 *hwaddr /* ETH_ALEN bytes */) +generate_duid_ll (guint16 arp_type, + const guint8 *hwaddr, + gsize hwaddr_len) { guint8 *arr; const guint16 duid_type = htons (3); - const guint16 hw_type = htons (ARPHRD_ETHER); + const guint16 hw_type = htons (arp_type); - arr = g_new (guint8, 2 + 2 + ETH_ALEN); + _ASSERT_arp_type (arp_type, hwaddr, hwaddr_len); + + arr = g_new (guint8, 2 + 2 + hwaddr_len); memcpy (&arr[0], &duid_type, 2); memcpy (&arr[2], &hw_type, 2); - memcpy (&arr[4], hwaddr, ETH_ALEN); + memcpy (&arr[4], hwaddr, hwaddr_len); - return g_bytes_new_take (arr, 2 + 2 + ETH_ALEN); + return g_bytes_new_take (arr, 2 + 2 + hwaddr_len); } static GBytes * @@ -8409,6 +8438,9 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole GBytes *duid_out; gboolean duid_enforce = TRUE; gs_free char *logstr1 = NULL; + const guint8 *hwaddr_bin; + gsize hwaddr_len; + int arp_type; s_ip6 = nm_connection_get_setting_ip6_config (connection); duid = nm_setting_ip6_config_get_dhcp_duid (NM_SETTING_IP6_CONFIG (s_ip6)); @@ -8441,15 +8473,18 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole duid_error = "missing link-layer address"; goto out_fail; } - if (g_bytes_get_size (hwaddr) != ETH_ALEN) { + + hwaddr_bin = g_bytes_get_data (hwaddr, &hwaddr_len); + arp_type = nm_utils_detect_arp_type_from_addrlen (hwaddr_len); + if (arp_type < 0) { duid_error = "unsupported link-layer address"; goto out_fail; } - if (nm_streq (duid, "ll")) { - duid_out = generate_duid_ll (g_bytes_get_data (hwaddr, NULL)); - } else { - duid_out = generate_duid_llt (g_bytes_get_data (hwaddr, NULL), + if (nm_streq (duid, "ll")) + duid_out = generate_duid_ll (arp_type, hwaddr_bin, hwaddr_len); + else { + duid_out = generate_duid_llt (arp_type, hwaddr_bin, hwaddr_len, nm_utils_host_id_get_timestamp_ns () / NM_UTILS_NS_PER_SECOND); } @@ -8457,6 +8492,12 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole } if (NM_IN_STRSET (duid, "stable-ll", "stable-llt", "stable-uuid")) { + /* preferably, we would salt the checksum differently for each @duid type. We missed + * to do that initially, so most types use the DEFAULT_SALT. + * + * Implemenations that are added later, should use a distinct salt instead, + * like "stable-ll"/"stable-llt" with ARPHRD_INFINIBAND below. */ + const guint32 DEFAULT_SALT = 670531087u; nm_auto_free_checksum GChecksum *sum = NULL; NMUtilsStableType stable_type; const char *stable_id = NULL; @@ -8465,19 +8506,50 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole gsize host_id_len; union { guint8 sha256[NM_UTILS_CHECKSUM_LENGTH_SHA256]; - guint8 hwaddr[ETH_ALEN]; + guint8 hwaddr_eth[ETH_ALEN]; + guint8 hwaddr_infiniband[INFINIBAND_ALEN]; NMUuid uuid; struct _nm_packed { guint8 hwaddr[ETH_ALEN]; guint32 timestamp; - } llt; + } llt_eth; + struct _nm_packed { + guint8 hwaddr[INFINIBAND_ALEN]; + guint32 timestamp; + } llt_infiniband; } digest; stable_id = _get_stable_id (self, connection, &stable_type); if (!stable_id) g_return_val_if_reached (NULL); - salted_header = htonl (670531087 + stable_type); + if (NM_IN_STRSET (duid, "stable-ll", "stable-llt")) { + /* for stable LL/LLT DUIDs, we still need a hardware address to detect + * the arp-type. Alternatively, we would be able to detect it based on + * other means (e.g. NMDevice type), but instead require the hardware + * address to be present. This is at least consistent with the "ll"/"llt" + * modes above. */ + if (!hwaddr) { + duid_error = "missing link-layer address"; + goto out_fail; + } + if ((arp_type = nm_utils_detect_arp_type_from_addrlen (g_bytes_get_size (hwaddr))) < 0) { + duid_error = "unsupported link-layer address"; + goto out_fail; + } + + if (arp_type == ARPHRD_ETHER) + salted_header = DEFAULT_SALT; + else { + nm_assert (arp_type == ARPHRD_INFINIBAND); + salted_header = 0x42492CEFu + ((guint32) arp_type); + } + } else { + salted_header = DEFAULT_SALT; + arp_type = -1; + } + + salted_header = htonl (salted_header + ((guint32) stable_type)); nm_utils_host_id_get (&host_id, &host_id_len); @@ -8490,9 +8562,19 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole G_STATIC_ASSERT_EXPR (sizeof (digest) == sizeof (digest.sha256)); if (nm_streq (duid, "stable-ll")) { - duid_out = generate_duid_ll (digest.hwaddr); + switch (arp_type) { + case ARPHRD_ETHER: + duid_out = generate_duid_ll (arp_type, digest.hwaddr_eth, sizeof (digest.hwaddr_eth)); + break; + case ARPHRD_INFINIBAND: + duid_out = generate_duid_ll (arp_type, digest.hwaddr_infiniband, sizeof (digest.hwaddr_infiniband)); + break; + default: + g_return_val_if_reached (NULL); + } } else if (nm_streq (duid, "stable-llt")) { gint64 time; + guint32 timestamp; #define EPOCH_DATETIME_THREE_YEARS (356 * 24 * 3600 * 3) @@ -8505,9 +8587,21 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole /* don't use too old timestamps. They cannot be expressed in DUID-LLT and * would all be truncated to zero. */ time = NM_MAX (time, EPOCH_DATETIME_200001010000 + EPOCH_DATETIME_THREE_YEARS); - time -= unaligned_read_be32 (&digest.llt.timestamp) % EPOCH_DATETIME_THREE_YEARS; - duid_out = generate_duid_llt (digest.llt.hwaddr, time); + switch (arp_type) { + case ARPHRD_ETHER: + timestamp = unaligned_read_be32 (&digest.llt_eth.timestamp); + time -= timestamp % EPOCH_DATETIME_THREE_YEARS; + duid_out = generate_duid_llt (arp_type, digest.llt_eth.hwaddr, sizeof (digest.llt_eth.hwaddr), time); + break; + case ARPHRD_INFINIBAND: + timestamp = unaligned_read_be32 (&digest.llt_infiniband.timestamp); + time -= timestamp % EPOCH_DATETIME_THREE_YEARS; + duid_out = generate_duid_llt (arp_type, digest.llt_infiniband.hwaddr, sizeof (digest.llt_infiniband.hwaddr), time); + break; + default: + g_return_val_if_reached (NULL); + } } else { nm_assert (nm_streq (duid, "stable-uuid")); duid_out = generate_duid_uuid (&digest.uuid); |