diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c index 4b7e7ee0ab2..a8ed98f29a9 100644 --- a/src/libsystemd-network/dhcp-identifier.c +++ b/src/libsystemd-network/dhcp-identifier.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include + #include "libudev.h" #include "sd-id128.h" @@ -14,6 +16,7 @@ #define SYSTEMD_PEN 43793 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09) #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03) +#define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */ int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len) { struct duid d; @@ -46,6 +49,56 @@ int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len) { return 0; } +int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) { + uint16_t time_from_2000y; + + assert(duid); + assert(len); + assert(addr); + + if (arp_type == ARPHRD_ETHER) + assert_return(addr_len == ETH_ALEN, -EINVAL); + else if (arp_type == ARPHRD_INFINIBAND) + assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); + else + return -EINVAL; + + if (t < USEC_2000) + time_from_2000y = 0; + else + time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff); + + unaligned_write_be16(&duid->type, DUID_TYPE_LLT); + unaligned_write_be16(&duid->llt.htype, arp_type); + unaligned_write_be32(&duid->llt.time, time_from_2000y); + memcpy(duid->llt.haddr, addr, addr_len); + + *len = sizeof(duid->type) + sizeof(duid->llt.htype) + sizeof(duid->llt.time) + addr_len; + + return 0; +} + +int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) { + assert(duid); + assert(len); + assert(addr); + + if (arp_type == ARPHRD_ETHER) + assert_return(addr_len == ETH_ALEN, -EINVAL); + else if (arp_type == ARPHRD_INFINIBAND) + assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); + else + return -EINVAL; + + unaligned_write_be16(&duid->type, DUID_TYPE_LL); + unaligned_write_be16(&duid->ll.htype, arp_type); + memcpy(duid->ll.haddr, addr, addr_len); + + *len = sizeof(duid->type) + sizeof(duid->ll.htype) + addr_len; + + return 0; +} + int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { sd_id128_t machine_id; uint64_t hash; diff --git a/src/libsystemd-network/dhcp-identifier.h b/src/libsystemd-network/dhcp-identifier.h index cebfa8f4a83..64315d3a3bd 100644 --- a/src/libsystemd-network/dhcp-identifier.h +++ b/src/libsystemd-network/dhcp-identifier.h @@ -5,6 +5,7 @@ #include "macro.h" #include "sparse-endian.h" +#include "time-util.h" #include "unaligned.h" typedef enum DUIDType { @@ -52,6 +53,8 @@ struct duid { } _packed_; int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len); +int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len); +int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len); int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len); int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len); int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id); diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 6f541f65774..62c57c8c9fe 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -383,18 +383,34 @@ static int dhcp_client_set_iaid_duid( len = sizeof(client->client_id.ns.duid.type) + duid_len; } else switch (duid_type) { + case DUID_TYPE_LLT: + if (!client->mac_addr || client->mac_addr_len == 0) + return -EOPNOTSUPP; + + r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, 0, client->mac_addr, client->mac_addr_len, client->arp_type, &len); + if (r < 0) + return r; + break; case DUID_TYPE_EN: r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len); if (r < 0) return r; break; + case DUID_TYPE_LL: + if (!client->mac_addr || client->mac_addr_len == 0) + return -EOPNOTSUPP; + + r = dhcp_identifier_set_duid_ll(&client->client_id.ns.duid, client->mac_addr, client->mac_addr_len, client->arp_type, &len); + if (r < 0) + return r; + break; case DUID_TYPE_UUID: r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len); if (r < 0) return r; break; default: - return -EOPNOTSUPP; + return -EINVAL; } client->client_id_len = sizeof(client->client_id.type) + len + diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 31382b54311..d4831361bf7 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -203,18 +203,34 @@ int sd_dhcp6_client_set_duid( client->duid_len = sizeof(client->duid.type) + duid_len; } else switch (duid_type) { + case DUID_TYPE_LLT: + if (!client->mac_addr || client->mac_addr_len == 0) + return -EOPNOTSUPP; + + r = dhcp_identifier_set_duid_llt(&client->duid, 0, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); + if (r < 0) + return r; + break; case DUID_TYPE_EN: r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); if (r < 0) return r; break; + case DUID_TYPE_LL: + if (!client->mac_addr || client->mac_addr_len == 0) + return -EOPNOTSUPP; + + r = dhcp_identifier_set_duid_ll(&client->duid, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); + if (r < 0) + return r; + break; case DUID_TYPE_UUID: r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len); if (r < 0) return r; break; default: - return -EOPNOTSUPP; + return -EINVAL; } return 0;