summaryrefslogtreecommitdiffstats
path: root/src/libsystemd-network/sd-dhcp-client-id.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsystemd-network/sd-dhcp-client-id.c')
-rw-r--r--src/libsystemd-network/sd-dhcp-client-id.c196
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;
+}