diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index db80585a22c..8b651636caf 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -97,6 +97,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia); int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix); 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_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 3b7c89edd7b..d1a02da35fd 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -167,6 +167,36 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { return r; } +int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class) { + _cleanup_free_ uint8_t *p = NULL; + size_t total = 0, offset = 0; + char **s; + + assert_return(buf && *buf && buflen && user_class, -EINVAL); + + STRV_FOREACH(s, user_class) { + size_t len = strlen(*s); + uint8_t *q; + + if (len > 0xffff) + return -ENAMETOOLONG; + + q = realloc(p, total + len + 2); + if (!q) + return -ENOMEM; + + p = q; + + unaligned_write_be16(&p[offset], len); + memcpy(&p[offset + 2], *s, len); + + offset += 2 + len; + total += 2 + len; + } + + return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_USER_CLASS, total, p); +} + int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix) { DHCP6Option *option = (DHCP6Option *)buf; size_t i = sizeof(*option) + sizeof(pd->ia_pd); diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 51716c28bb6..0240f1b7c7d 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -67,6 +67,7 @@ struct sd_dhcp6_client { size_t req_opts_len; char *fqdn; char *mudurl; + char **user_class; sd_event_source *receive_message; usec_t retransmit_time; uint8_t retransmit_count; @@ -385,6 +386,27 @@ int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mud return free_and_strdup(&client->mudurl, mudurl); } +int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char **user_class) { + _cleanup_strv_free_ char **s = NULL; + char **p; + + assert_return(client, -EINVAL); + assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(user_class, -EINVAL); + + STRV_FOREACH(p, user_class) + if (strlen(*p) > UINT16_MAX) + return -ENAMETOOLONG; + + s = strv_copy((char **) user_class); + if (!s) + return -ENOMEM; + + client->user_class = TAKE_PTR(s); + + return 0; +} + int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) { assert_return(client, -EINVAL); assert_return(delegation, -EINVAL); @@ -565,6 +587,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (client->user_class) { + r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); + 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) @@ -611,6 +639,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (client->user_class) { + r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); + 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) @@ -645,6 +679,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (client->user_class) { + r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); + 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) @@ -1602,7 +1642,10 @@ static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { free(client->req_opts); free(client->fqdn); free(client->mudurl); + ordered_hashmap_free(client->extra_options); + strv_free(client->user_class); + return mfree(client); } diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index d365fc76380..8011457b761 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -125,6 +125,10 @@ int sd_dhcp6_client_set_request_option( int sd_dhcp6_client_set_request_mud_url( sd_dhcp6_client *client, const char *mudurl); + +int sd_dhcp6_client_set_request_user_class( + sd_dhcp6_client *client, + char** user_class); int sd_dhcp6_client_set_prefix_delegation_hint( sd_dhcp6_client *client, uint8_t prefixlen,