diff options
Diffstat (limited to 'src/libsystemd-network/sd-dhcp-client-id.c')
-rw-r--r-- | src/libsystemd-network/sd-dhcp-client-id.c | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/src/libsystemd-network/sd-dhcp-client-id.c b/src/libsystemd-network/sd-dhcp-client-id.c new file mode 100644 index 0000000..cab04f0 --- /dev/null +++ b/src/libsystemd-network/sd-dhcp-client-id.c @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "alloc-util.h" +#include "dhcp-client-id-internal.h" +#include "iovec-util.h" +#include "unaligned.h" +#include "utf8.h" + +int sd_dhcp_client_id_clear(sd_dhcp_client_id *client_id) { + assert_return(client_id, -EINVAL); + + *client_id = (sd_dhcp_client_id) {}; + return 0; +} + +int sd_dhcp_client_id_is_set(const sd_dhcp_client_id *client_id) { + if (!client_id) + return false; + + return client_id_size_is_valid(client_id->size); +} + +int sd_dhcp_client_id_get(const sd_dhcp_client_id *client_id, uint8_t *ret_type, const void **ret_data, size_t *ret_size) { + assert_return(sd_dhcp_client_id_is_set(client_id), -EINVAL); + assert_return(ret_type, -EINVAL); + assert_return(ret_data, -EINVAL); + assert_return(ret_size, -EINVAL); + + *ret_type = client_id->id.type; + *ret_data = client_id->id.data; + *ret_size = client_id->size - offsetof(typeof(client_id->id), data); + return 0; +} + +int sd_dhcp_client_id_get_raw(const sd_dhcp_client_id *client_id, const void **ret_data, size_t *ret_size) { + assert_return(sd_dhcp_client_id_is_set(client_id), -EINVAL); + assert_return(ret_data, -EINVAL); + assert_return(ret_size, -EINVAL); + + /* Unlike sd_dhcp_client_id_get(), this returns whole client ID including its type. */ + + *ret_data = client_id->raw; + *ret_size = client_id->size; + return 0; +} + +int sd_dhcp_client_id_set( + sd_dhcp_client_id *client_id, + uint8_t type, + const void *data, + size_t data_size) { + + assert_return(client_id, -EINVAL); + assert_return(data, -EINVAL); + + if (!client_id_data_size_is_valid(data_size)) + return -EINVAL; + + client_id->id.type = type; + memcpy(client_id->id.data, data, data_size); + + client_id->size = offsetof(typeof(client_id->id), data) + data_size; + return 0; +} + +int sd_dhcp_client_id_set_raw( + sd_dhcp_client_id *client_id, + const void *data, + size_t data_size) { + + assert_return(client_id, -EINVAL); + assert_return(data, -EINVAL); + + /* Unlike sd_dhcp_client_id_set(), this takes whole client ID including its type. */ + + if (!client_id_size_is_valid(data_size)) + return -EINVAL; + + memcpy(client_id->raw, data, data_size); + + client_id->size = data_size; + return 0; +} + +int sd_dhcp_client_id_set_iaid_duid( + sd_dhcp_client_id *client_id, + uint32_t iaid, + sd_dhcp_duid *duid) { + + assert_return(client_id, -EINVAL); + assert_return(duid, -EINVAL); + assert_return(sd_dhcp_duid_is_set(duid), -ESTALE); + + client_id->id.type = 255; + unaligned_write_be32(&client_id->id.ns.iaid, iaid); + memcpy(&client_id->id.ns.duid, &duid->duid, duid->size); + + client_id->size = offsetof(typeof(client_id->id), ns.duid) + duid->size; + return 0; +} + +int sd_dhcp_client_id_to_string(const sd_dhcp_client_id *client_id, char **ret) { + _cleanup_free_ char *t = NULL; + size_t len; + int r; + + assert_return(sd_dhcp_client_id_is_set(client_id), -EINVAL); + assert_return(ret, -EINVAL); + + len = client_id->size - offsetof(typeof(client_id->id), data); + + switch (client_id->id.type) { + case 0: + if (utf8_is_printable((char *) client_id->id.gen.data, len)) + r = asprintf(&t, "%.*s", (int) len, client_id->id.gen.data); + else + r = asprintf(&t, "DATA"); + break; + case 1: + if (len == sizeof_field(sd_dhcp_client_id, id.eth)) + r = asprintf(&t, "%02x:%02x:%02x:%02x:%02x:%02x", + client_id->id.eth.haddr[0], + client_id->id.eth.haddr[1], + client_id->id.eth.haddr[2], + client_id->id.eth.haddr[3], + client_id->id.eth.haddr[4], + client_id->id.eth.haddr[5]); + else + r = asprintf(&t, "ETHER"); + break; + case 2 ... 254: + r = asprintf(&t, "ARP/LL"); + break; + case 255: + if (len < sizeof(uint32_t)) + r = asprintf(&t, "IAID/DUID"); + else { + uint32_t iaid = be32toh(client_id->id.ns.iaid); + /* TODO: check and stringify DUID */ + r = asprintf(&t, "IAID:0x%x/DUID", iaid); + } + break; + default: + assert_not_reached(); + } + if (r < 0) + return -ENOMEM; + + *ret = TAKE_PTR(t); + return 0; +} + +int sd_dhcp_client_id_to_string_from_raw(const void *data, size_t data_size, char **ret) { + sd_dhcp_client_id client_id; + int r; + + assert_return(data, -EINVAL); + assert_return(ret, -EINVAL); + + r = sd_dhcp_client_id_set_raw(&client_id, data, data_size); + if (r < 0) + return r; + + return sd_dhcp_client_id_to_string(&client_id, ret); +} + +void client_id_hash_func(const sd_dhcp_client_id *client_id, struct siphash *state) { + assert(sd_dhcp_client_id_is_set(client_id)); + assert(state); + + siphash24_compress_typesafe(client_id->size, state); + siphash24_compress(client_id->raw, client_id->size, state); +} + +int client_id_compare_func(const sd_dhcp_client_id *a, const sd_dhcp_client_id *b) { + assert(sd_dhcp_client_id_is_set(a)); + assert(sd_dhcp_client_id_is_set(b)); + + return memcmp_nn(a->raw, a->size, b->raw, b->size); +} + +int json_dispatch_client_id(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + sd_dhcp_client_id *client_id = ASSERT_PTR(userdata); + _cleanup_(iovec_done) struct iovec iov = {}; + int r; + + r = json_dispatch_byte_array_iovec(name, variant, flags, &iov); + if (r < 0) + return r; + + r = sd_dhcp_client_id_set_raw(client_id, iov.iov_base, iov.iov_len); + if (r < 0) + return json_log(variant, flags, r, "Failed to set DHCP client ID from JSON field '%s': %m", strna(name)); + + return 0; +} |