diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 55db523e79c..da8b4663b8a 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1245,6 +1245,16 @@ + + UserClass= + + A DHCPv4 client can use UserClass option to identify the type or category of user or applications + it represents. The information contained in this option is a string that represents the user class of which + the client is a member. Each class sets an identifying string of information to be used by the DHCP + service to classify clients. Takes a whitespace-separated list of strings. + + + DUIDType= diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c index c95597a00bb..4832ee6a2cd 100644 --- a/src/libsystemd-network/dhcp-option.c +++ b/src/libsystemd-network/dhcp-option.c @@ -12,6 +12,7 @@ #include "alloc-util.h" #include "utf8.h" +#include "strv.h" #include "dhcp-internal.h" @@ -35,6 +36,34 @@ static int option_append(uint8_t options[], size_t size, size_t *offset, *offset += 1; break; + case SD_DHCP_OPTION_USER_CLASS: { + size_t len = 0; + char **s; + + STRV_FOREACH(s, (char **) optval) + len += strlen(*s) + 1; + + if (size < *offset + len + 2) + return -ENOBUFS; + + options[*offset] = code; + options[*offset + 1] = len; + *offset += 2; + + STRV_FOREACH(s, (char **) optval) { + len = strlen(*s); + + if (len > 255) + return -ENAMETOOLONG; + + options[*offset] = len; + + memcpy_safe(&options[*offset + 1], *s, len); + *offset += len + 1; + } + + break; + } default: if (size < *offset + optlen + 2) return -ENOBUFS; diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 8305815df66..5660271330c 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -27,6 +27,7 @@ #include "random-util.h" #include "string-util.h" #include "util.h" +#include "strv.h" #define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */ #define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN) @@ -83,6 +84,7 @@ struct sd_dhcp_client { size_t client_id_len; char *hostname; char *vendor_class_identifier; + char **user_class; uint32_t mtu; uint32_t xid; usec_t start_time; @@ -440,6 +442,26 @@ int sd_dhcp_client_set_vendor_class_identifier( return free_and_strdup(&client->vendor_class_identifier, vci); } +int sd_dhcp_client_set_user_class( + sd_dhcp_client *client, + const char* const* user_class) { + + _cleanup_strv_free_ char **s = NULL; + char **p; + + STRV_FOREACH(p, (char **) user_class) + if (strlen(*p) > 255) + return -ENAMETOOLONG; + + s = strv_copy((char **) user_class); + if (!s) + return -ENOMEM; + + client->user_class = TAKE_PTR(s); + + return 0; +} + int sd_dhcp_client_set_client_port( sd_dhcp_client *client, uint16_t port) { @@ -763,6 +785,15 @@ static int client_send_discover(sd_dhcp_client *client) { return r; } + if (client->user_class) { + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_USER_CLASS, + strv_length(client->user_class), + client->user_class); + if (r < 0) + return r; + } + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL); if (r < 0) @@ -1918,6 +1949,7 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) { free(client->req_opts); free(client->hostname); free(client->vendor_class_identifier); + client->user_class = strv_free(client->user_class); return mfree(client); } diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index a1d300166bc..d2e79cdda60 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -748,6 +748,12 @@ int dhcp4_configure(Link *link) { return r; } + if (link->network->dhcp_user_class) { + r = sd_dhcp_client_set_user_class(link->dhcp_client, (const char **) link->network->dhcp_user_class); + if (r < 0) + return r; + } + if (link->network->dhcp_client_port) { r = sd_dhcp_client_set_client_port(link->dhcp_client, link->network->dhcp_client_port); if (r < 0) diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 1a0da586515..216572aeb4a 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -125,6 +125,7 @@ DHCP.Hostname, config_parse_hostname, DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast) DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical) DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier) +DHCP.UserClass, config_parse_dhcp_user_class, 0, offsetof(Network, dhcp_user_class) DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid.type) DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid) DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 2592377a6f7..aafb192baf0 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -361,6 +361,7 @@ void network_free(Network *network) { free(network->description); free(network->dhcp_vendor_class_identifier); + strv_free(network->dhcp_user_class); free(network->dhcp_hostname); free(network->mac); @@ -1387,6 +1388,58 @@ int config_parse_ntp( return 0; } +int config_parse_dhcp_user_class( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + char ***l = data; + int r; + + assert(l); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + *l = strv_free(*l); + return 0; + } + + for (;;) { + _cleanup_free_ char *w = NULL; + + r = extract_first_word(&rvalue, &w, NULL, 0); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to split user classes option, ignoring: %s", rvalue); + break; + } + if (r == 0) + break; + + if (strlen(w) > 255) { + log_syntax(unit, LOG_ERR, filename, line, r, "%s length is not in the range 1-255, ignoring.", w); + continue; + } + + r = strv_push(l, w); + if (r < 0) + return log_oom(); + + w = NULL; + } + + return 0; +} + int config_parse_dhcp_route_table(const char *unit, const char *filename, unsigned line, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 060c526b9ee..e4bb0ba83a3 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -126,6 +126,7 @@ struct Network { AddressFamilyBoolean dhcp; DHCPClientIdentifier dhcp_client_identifier; char *dhcp_vendor_class_identifier; + char **dhcp_user_class; char *dhcp_hostname; unsigned dhcp_route_metric; uint32_t dhcp_route_table; @@ -288,7 +289,7 @@ int config_parse_dhcp_server_ntp(const char *unit, const char *filename, unsigne int config_parse_dnssec_negative_trust_anchors(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_dhcp_use_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_lldp_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_dhcp_route_table(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_dhcp_route_table(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);int config_parse_dhcp_user_class(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_ntp(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); /* Legacy IPv4LL support */ int config_parse_ipv4ll(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index 789cc50174c..fd0a5693629 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -82,6 +82,7 @@ enum { SD_DHCP_OPTION_REBINDING_T2_TIME = 59, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61, + SD_DHCP_OPTION_USER_CLASS = 77, SD_DHCP_OPTION_FQDN = 81, SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, @@ -154,6 +155,9 @@ int sd_dhcp_client_set_hostname( int sd_dhcp_client_set_vendor_class_identifier( sd_dhcp_client *client, const char *vci); +int sd_dhcp_client_set_user_class( + sd_dhcp_client *client, + const char* const *user_class); int sd_dhcp_client_get_lease( sd_dhcp_client *client, sd_dhcp_lease **ret);