From af1c0de0e124c22858fce99534d4de1f7ed340a7 Mon Sep 17 00:00:00 2001 From: Susant Sahani <145210+ssahani@users.noreply.github.com> Date: Mon, 7 May 2018 17:51:02 +0530 Subject: [PATCH] networkd: add support to send DHCP user class option (#7499) This patch add support to enables to send User Class option code 77 RFC 3004. This option MAY carry multiple User Classes. The format of this option is as follows: Code Len Value +-----+-----+--------------------- . . . --+ | 77 | N | User Class Data ('Len' octets) | +-----+-----+--------------------- . . . --+ where Value consists of one or more instances of User Class Data. Each instance of User Class Data is formatted as follows: UC_Len_i User_Class_Data_i +--------+------------------------ . . . --+ | L_i | Opaque-Data ('UC_Len_i' octets) | +--------+------------------------ . . . --+ 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 an 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. UserClass= hello world how are you Closes: RFC: #5134 --- man/systemd.network.xml | 10 +++++ src/libsystemd-network/dhcp-option.c | 29 +++++++++++++ src/libsystemd-network/sd-dhcp-client.c | 32 ++++++++++++++ src/network/networkd-dhcp4.c | 6 +++ src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.c | 53 ++++++++++++++++++++++++ src/network/networkd-network.h | 3 +- src/systemd/sd-dhcp-client.h | 4 ++ 8 files changed, 137 insertions(+), 1 deletion(-) 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);