diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 2880bc4770..b0d1216eed 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -11,12 +11,14 @@ #include "sd-event.h" #include "list.h" +#include "hashmap.h" #include "macro.h" #include "sparse-endian.h" typedef struct sd_dhcp6_option { unsigned n_ref; + uint32_t enterprise_identifier; uint16_t option; void *data; size_t length; @@ -99,6 +101,7 @@ int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Add int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class); int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **user_class); +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options); int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen, uint8_t **optvalue); int dhcp6_option_parse_status(DHCP6Option *option, size_t len); diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index f763474e6e..fa43587686 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -79,6 +79,39 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, return 0; } +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options) { + sd_dhcp6_option *options; + Iterator i; + int r; + + assert(buf); + assert(*buf); + assert(buflen); + assert(vendor_options); + + ORDERED_HASHMAP_FOREACH(options, vendor_options, i) { + _cleanup_free_ uint8_t *p = NULL; + size_t total; + + total = 4 + 2 + 2 + options->length; + + p = malloc(total); + if (!p) + return -ENOMEM; + + unaligned_write_be32(p, options->enterprise_identifier); + unaligned_write_be16(p + 4, options->option); + unaligned_write_be16(p + 6, options->length); + memcpy(p + 8, options->data, options->length); + + r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_OPTS, total, p); + if (r < 0) + return r; + } + + return 0; +} + int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { uint16_t len; uint8_t *ia_hdr; @@ -683,7 +716,7 @@ static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) { return mfree(i); } -int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, sd_dhcp6_option **ret) { +int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret) { assert_return(ret, -EINVAL); assert_return(length == 0 || data, -EINVAL); @@ -698,6 +731,7 @@ int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, sd_dhc *p = (sd_dhcp6_option) { .n_ref = 1, .option = option, + .enterprise_identifier = enterprise_identifier, .length = length, .data = TAKE_PTR(q), }; diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 76a16a18c1..8d13aef4e7 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -81,6 +81,7 @@ struct sd_dhcp6_client { usec_t information_request_time_usec; usec_t information_refresh_time_usec; OrderedHashmap *extra_options; + OrderedHashmap *vendor_options; }; static const uint16_t default_req_opts[] = { @@ -225,6 +226,25 @@ int sd_dhcp6_client_set_prefix_delegation_hint( return 0; } +int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v) { + int r; + + assert_return(client, -EINVAL); + assert_return(v, -EINVAL); + + r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp6_option_hash_ops); + if (r < 0) + return r; + + r = ordered_hashmap_put(client->vendor_options, v, v); + if (r < 0) + return r; + + sd_dhcp6_option_ref(v); + + return 1; +} + static int client_ensure_duid(sd_dhcp6_client *client) { if (client->duid_len != 0) return 0; @@ -622,6 +642,13 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (!ordered_hashmap_isempty(client->vendor_options)) { + r = dhcp6_option_append_vendor_option(&opt, &optlen, + client->vendor_options); + if (r < 0) + return r; + } + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) { r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd, &client->hint_pd_prefix); if (r < 0) @@ -680,6 +707,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (!ordered_hashmap_isempty(client->vendor_options)) { + r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options); + if (r < 0) + return r; + } + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) { r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL); if (r < 0) @@ -726,6 +759,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (!ordered_hashmap_isempty(client->vendor_options)) { + r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options); + if (r < 0) + return r; + } + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) { r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL); if (r < 0) diff --git a/src/systemd/meson.build b/src/systemd/meson.build index 63d0829b67..62baf7784e 100644 --- a/src/systemd/meson.build +++ b/src/systemd/meson.build @@ -24,6 +24,7 @@ _not_installed_headers = ''' sd-dhcp-client.h sd-dhcp-lease.h sd-dhcp-option.h + sd-dhcp6-option.h sd-dhcp-server.h sd-ipv4acd.h sd-ipv4ll.h diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index c342be4b29..58a17a5a3f 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -143,7 +143,10 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request); int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request); -int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id); +int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, + uint32_t transaction_id); +int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, + sd_dhcp6_option *v); int sd_dhcp6_client_get_lease( sd_dhcp6_client *client, diff --git a/src/systemd/sd-dhcp6-option.h b/src/systemd/sd-dhcp6-option.h index c0f1c4df08..88a4986315 100644 --- a/src/systemd/sd-dhcp6-option.h +++ b/src/systemd/sd-dhcp6-option.h @@ -26,7 +26,7 @@ _SD_BEGIN_DECLARATIONS; typedef struct sd_dhcp6_option sd_dhcp6_option; -int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, sd_dhcp6_option **ret); +int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret); sd_dhcp6_option *sd_dhcp6_option_ref(sd_dhcp6_option *ra); sd_dhcp6_option *sd_dhcp6_option_unref(sd_dhcp6_option *ra);