]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dhcp: use struct iovec_wrapper to manage user class 42045/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 23 Mar 2026 18:53:51 +0000 (03:53 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 12 May 2026 11:06:28 +0000 (20:06 +0900)
src/libsystemd-network/dhcp-client-internal.h
src/libsystemd-network/dhcp-option.c
src/libsystemd-network/sd-dhcp-client.c
src/network/networkd-dhcp-common.c
src/network/networkd-dhcp-common.h
src/network/networkd-dhcp4.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/systemd/sd-dhcp-client.h

index 8e23f7d931439acf9679dfbe147a1edf00290ae1..7db0a91fa94a1448b8349b281b4fd8ce5c533b1a 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "dhcp-client-id-internal.h"
 #include "ether-addr-util.h"
+#include "iovec-wrapper.h"
 #include "network-common.h"
 #include "sd-forward.h"
 #include "socket-util.h"
@@ -55,7 +56,7 @@ struct sd_dhcp_client {
         char *hostname;
         char *vendor_class_identifier;
         char *mudurl;
-        char **user_class;
+        struct iovec_wrapper user_class;
         uint32_t mtu;
         usec_t fallback_lease_lifetime;
         uint32_t xid;
@@ -93,6 +94,7 @@ int dhcp_client_get_state(sd_dhcp_client *client);
 
 int dhcp_client_set_extra_options(sd_dhcp_client *client, TLV *options);
 int dhcp_client_set_vendor_options(sd_dhcp_client *client, TLV *options);
+int dhcp_client_set_user_class(sd_dhcp_client *client, const struct iovec_wrapper *user_class);
 
 int client_receive_message_raw(
                 sd_event_source *s,
index da5fd64af67c20606baec41f6c9e7ef2e09c39aa..c5e8b2979443316c1cf41b1b73c27f372c8299ca 100644 (file)
@@ -12,7 +12,6 @@
 #include "hostname-util.h"
 #include "memory-util.h"
 #include "string-util.h"
-#include "strv.h"
 #include "utf8.h"
 
 /* Append type-length value structure to the options buffer */
@@ -57,43 +56,6 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
                 *offset += 1;
                 break;
 
-        case SD_DHCP_OPTION_USER_CLASS: {
-                /* When called with raw data (optlen > 0), e.g. from SendOption=, append as a plain TLV.
-                 * The structured handling below expects optval to be a strv. */
-                if (optlen > 0)
-                        return dhcp_option_append_tlv(options, size, offset, code, optlen, optval);
-
-                size_t total = 0;
-
-                if (strv_isempty((char **) optval))
-                        return -EINVAL;
-
-                STRV_FOREACH(s, (const char* const*) optval) {
-                        size_t len = strlen(*s);
-
-                        if (len > 255 || len == 0)
-                                return -EINVAL;
-
-                        total += 1 + len;
-                }
-
-                if (*offset + 2 + total > size)
-                        return -ENOBUFS;
-
-                options[*offset] = code;
-                options[*offset + 1] = total;
-                *offset += 2;
-
-                STRV_FOREACH(s, (const char* const*) optval) {
-                        size_t len = strlen(*s);
-
-                        options[*offset] = len;
-                        memcpy(&options[*offset + 1], *s, len);
-                        *offset += 1 + len;
-                }
-
-                break;
-        }
         case SD_DHCP_OPTION_SIP_SERVER:
                 if (*offset + 3 + optlen > size)
                         return -ENOBUFS;
index 0b13e925f2669459dfd4b090c7e75fcffc59eafb..8b517982b65cd06fcb2a20eea715c400f0ba19c8 100644 (file)
@@ -27,7 +27,6 @@
 #include "sort-util.h"
 #include "string-table.h"
 #include "string-util.h"
-#include "strv.h"
 #include "time-util.h"
 #include "web-util.h"
 
@@ -434,28 +433,30 @@ int sd_dhcp_client_set_mud_url(
         return free_and_strdup(&client->mudurl, mudurl);
 }
 
-int sd_dhcp_client_set_user_class(
-                sd_dhcp_client *client,
-                char * const *user_class) {
-
-        char **s = NULL;
+int dhcp_client_set_user_class(sd_dhcp_client *client, const struct iovec_wrapper *user_class) {
+        int r;
 
         assert_return(client, -EINVAL);
         assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
-        assert_return(!strv_isempty(user_class), -EINVAL);
 
-        STRV_FOREACH(p, user_class) {
-                size_t n = strlen(*p);
+        if (iovw_isempty(user_class)) {
+                iovw_done_free(&client->user_class);
+                return 0;
+        }
 
-                if (n > 255 || n == 0)
+        _cleanup_(iovw_done_free) struct iovec_wrapper iovw = {};
+        FOREACH_ARRAY(iovec, user_class->iovec, user_class->count) {
+                if (iovec->iov_len == 0 || iovec->iov_len > UINT8_MAX)
                         return -EINVAL;
-        }
 
-        s = strv_copy(user_class);
-        if (!s)
-                return -ENOMEM;
+                r = iovw_extend_iov(&iovw, iovec);
+                if (r < 0)
+                        return r;
+        }
 
-        return strv_free_and_replace(client->user_class, s);
+        iovw_done_free(&client->user_class);
+        client->user_class = TAKE_STRUCT(iovw);
+        return 0;
 }
 
 int sd_dhcp_client_set_client_port(
@@ -933,12 +934,26 @@ static int client_append_common_discover_request_options(sd_dhcp_client *client,
                         return r;
         }
 
-        if (client->user_class) {
-                r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
-                                       SD_DHCP_OPTION_USER_CLASS,
-                                       /* optlen= */ 0, client->user_class);
-                if (r < 0)
-                        return r;
+        if (!iovw_isempty(&client->user_class)) {
+                size_t sz = iovw_size(&client->user_class) + client->user_class.count;
+                if (sz <= UINT8_MAX) {
+                        _cleanup_free_ uint8_t *buf = new(uint8_t, sz);
+                        if (!buf)
+                                return -ENOMEM;
+
+                        uint8_t *p = buf;
+                        FOREACH_ARRAY(iovec, client->user_class.iovec, client->user_class.count) {
+                                assert(iovec->iov_len > 0 && iovec->iov_len <= UINT8_MAX);
+                                *p++ = iovec->iov_len;
+                                p = mempcpy(p, iovec->iov_base, iovec->iov_len);
+                        }
+
+                        r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
+                                               SD_DHCP_OPTION_USER_CLASS,
+                                               sz, buf);
+                        if (r < 0)
+                                return r;
+                }
         }
 
         if (client->extra_options) {
@@ -2297,7 +2312,7 @@ static sd_dhcp_client* dhcp_client_free(sd_dhcp_client *client) {
         free(client->hostname);
         free(client->vendor_class_identifier);
         free(client->mudurl);
-        client->user_class = strv_free(client->user_class);
+        iovw_done_free(&client->user_class);
         tlv_unref(client->extra_options);
         tlv_unref(client->vendor_options);
         free(client->ifname);
index d7353d07ed41c9191a792f4f5b2ba18212059fc4..5d83835580971c60c4a4de795db4274c8acfce35 100644 (file)
@@ -636,7 +636,7 @@ int config_parse_iaid(
         return 0;
 }
 
-int config_parse_dhcp_user_or_vendor_class(
+int config_parse_dhcp4_user_class(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -648,46 +648,76 @@ int config_parse_dhcp_user_or_vendor_class(
                 void *data,
                 void *userdata) {
 
-        char ***l = ASSERT_PTR(data);
+        struct iovec_wrapper *iovw = ASSERT_PTR(data);
         int r;
 
         assert(lvalue);
         assert(rvalue);
-        assert(IN_SET(ltype, AF_INET, AF_INET6));
 
         if (isempty(rvalue)) {
-                *l = strv_free(*l);
+                iovw_done_free(iovw);
                 return 0;
         }
 
         for (const char *p = rvalue;;) {
                 _cleanup_free_ char *w = NULL;
-                size_t len;
 
                 r = extract_first_word(&p, &w, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
-                if (r == -ENOMEM)
-                        return log_oom();
-                if (r < 0) {
-                        log_syntax(unit, LOG_WARNING, filename, line, r,
-                                   "Failed to split user classes option, ignoring: %s", rvalue);
+                if (r < 0)
+                        return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
+                if (r == 0)
                         return 0;
+
+                size_t len = strlen(w);
+                if (len > UINT8_MAX || len == 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "The length of the %s entry '%s' is not in the range 1…255, ignoring.", lvalue, w);
+                        continue;
                 }
+
+                r = iovw_consume(iovw, TAKE_PTR(w), len);
+                if (r < 0)
+                        return log_oom();
+        }
+}
+
+int config_parse_dhcp6_user_or_vendor_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 = ASSERT_PTR(data);
+        int r;
+
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                *l = strv_free(*l);
+                return 0;
+        }
+
+        for (const char *p = rvalue;;) {
+                _cleanup_free_ char *w = NULL;
+
+                r = extract_first_word(&p, &w, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
+                if (r < 0)
+                        return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
                 if (r == 0)
                         return 0;
 
-                len = strlen(w);
-                if (ltype == AF_INET) {
-                        if (len > UINT8_MAX || len == 0) {
-                                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                                           "%s length is not in the range 1…255, ignoring.", w);
-                                continue;
-                        }
-                } else {
-                        if (len > UINT16_MAX || len == 0) {
-                                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                                           "%s length is not in the range 1…65535, ignoring.", w);
-                                continue;
-                        }
+                size_t len = strlen(w);
+                if (len > UINT16_MAX || len == 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "The length of the %s entry '%s' is not in the range 1…65535, ignoring.", lvalue, w);
+                        continue;
                 }
 
                 r = strv_consume(l, TAKE_PTR(w));
index 0b88d7790c17ae81e024e69152106074217f794d..6be8bcd6dc3746358e82db944f689eff26b10478 100644 (file)
@@ -81,7 +81,8 @@ CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_route_metric);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_send_hostname);
 CONFIG_PARSER_PROTOTYPE(config_parse_iaid);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_or_ra_route_table);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_or_vendor_class);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp4_user_class);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_user_or_vendor_class);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_send_option);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_option);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_option_tlv);
index 7304a049b699d0ab67de3aa4303d2421999c0b96..b5210e9ddfc91ee6fbbc68efe198ffbaea0fa965 100644 (file)
@@ -1643,11 +1643,9 @@ static int dhcp4_configure(Link *link) {
                                 return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set MUD URL: %m");
                 }
 
-                if (link->network->dhcp_user_class) {
-                        r = sd_dhcp_client_set_user_class(link->dhcp_client, link->network->dhcp_user_class);
-                        if (r < 0)
-                                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set user class: %m");
-                }
+                r = dhcp_client_set_user_class(link->dhcp_client, &link->network->dhcp_user_class);
+                if (r < 0)
+                        return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set user class: %m");
         }
 
         if (link->network->dhcp_client_port > 0) {
index 0f1c4e57c46f48201d60b748454f6a7f5ad38a2e..b925cd3a2b47da47a68684a785990c86885bba80 100644 (file)
@@ -278,7 +278,7 @@ DHCPv4.RequestBroadcast,                         config_parse_tristate,
 DHCPv4.VendorClassIdentifier,                    config_parse_string,                            CONFIG_PARSE_STRING_SAFE,               offsetof(Network, dhcp_vendor_class_identifier)
 DHCPv4.MUDURL,                                   config_parse_mud_url,                           0,                                      offsetof(Network, dhcp_mudurl)
 DHCPv4.MaxAttempts,                              config_parse_dhcp_max_attempts,                 0,                                      0
-DHCPv4.UserClass,                                config_parse_dhcp_user_or_vendor_class,         AF_INET,                                offsetof(Network, dhcp_user_class)
+DHCPv4.UserClass,                                config_parse_dhcp4_user_class,                  0,                                      offsetof(Network, dhcp_user_class)
 DHCPv4.IAID,                                     config_parse_iaid,                              AF_INET,                                0
 DHCPv4.DUIDType,                                 config_parse_network_duid_type,                 0,                                      0
 DHCPv4.DUIDRawData,                              config_parse_network_duid_rawdata,              0,                                      0
@@ -318,8 +318,8 @@ DHCPv6.MUDURL,                                   config_parse_mud_url,
 DHCPv6.SendHostname,                             config_parse_dhcp_send_hostname,                AF_INET6,                               0
 DHCPv6.Hostname,                                 config_parse_hostname,                          0,                                      offsetof(Network, dhcp6_hostname)
 DHCPv6.RequestOptions,                           config_parse_dhcp_request_options,              AF_INET6,                               0
-DHCPv6.UserClass,                                config_parse_dhcp_user_or_vendor_class,         AF_INET6,                               offsetof(Network, dhcp6_user_class)
-DHCPv6.VendorClass,                              config_parse_dhcp_user_or_vendor_class,         AF_INET6,                               offsetof(Network, dhcp6_vendor_class)
+DHCPv6.UserClass,                                config_parse_dhcp6_user_or_vendor_class,        0,                                      offsetof(Network, dhcp6_user_class)
+DHCPv6.VendorClass,                              config_parse_dhcp6_user_or_vendor_class,        0,                                      offsetof(Network, dhcp6_vendor_class)
 DHCPv6.SendVendorOption,                         config_parse_dhcp6_send_option,                 0,                                      offsetof(Network, dhcp6_client_send_vendor_options)
 DHCPv6.PrefixDelegationHint,                     config_parse_dhcp6_pd_prefix_hint,              0,                                      0
 DHCPv6.UnassignedSubnetPolicy,                   config_parse_dhcp_pd_prefix_route_type,         0,                                      offsetof(Network, dhcp6_pd_prefix_route_type)
@@ -664,7 +664,7 @@ DHCP.Hostname,                                   config_parse_hostname,
 DHCP.RequestBroadcast,                           config_parse_tristate,                          0,                                      offsetof(Network, dhcp_broadcast)
 DHCP.CriticalConnection,                         config_parse_tristate,                          0,                                      offsetof(Network, dhcp_critical)
 DHCP.VendorClassIdentifier,                      config_parse_string,                            CONFIG_PARSE_STRING_SAFE,               offsetof(Network, dhcp_vendor_class_identifier)
-DHCP.UserClass,                                  config_parse_dhcp_user_or_vendor_class,         AF_INET,                                offsetof(Network, dhcp_user_class)
+DHCP.UserClass,                                  config_parse_dhcp4_user_class,                  0,                                      offsetof(Network, dhcp_user_class)
 DHCP.IAID,                                       config_parse_iaid,                              AF_INET,                                0
 DHCP.DUIDType,                                   config_parse_network_duid_type,                 0,                                      0
 DHCP.DUIDRawData,                                config_parse_network_duid_rawdata,              0,                                      0
index 0397e77f810032e9a22625059a94d6c1f5781583..5cd47ca0a81ee5dfa3072bb38fb02824b34b2a8d 100644 (file)
@@ -786,7 +786,7 @@ static Network *network_free(Network *network) {
         free(network->dhcp_label);
         set_free(network->dhcp_deny_listed_ip);
         set_free(network->dhcp_allow_listed_ip);
-        strv_free(network->dhcp_user_class);
+        iovw_done_free(&network->dhcp_user_class);
         set_free(network->dhcp_request_options);
         tlv_done(&network->dhcp_extra_options);
         tlv_done(&network->dhcp_vendor_options);
index 26012612a15c880700170890f423917abcc5740a..c4020a4341af1e005c605eca1d3ba06c3df5f875 100644 (file)
@@ -7,6 +7,7 @@
 #include "bridge.h"
 #include "firewall-util.h"
 #include "ipoib.h"
+#include "iovec-wrapper.h"
 #include "net-condition.h"
 #include "network-util.h"
 #include "networkd-bridge-vlan.h"
@@ -124,7 +125,7 @@ typedef struct Network {
         bool dhcp_iaid_set;
         char *dhcp_vendor_class_identifier;
         char *dhcp_mudurl;
-        char **dhcp_user_class;
+        struct iovec_wrapper dhcp_user_class;
         char *dhcp_hostname;
         char *dhcp_label;
         uint64_t dhcp_max_attempts;
index c83aaac2c9d38cc6e84814ad249a11cedb3a271b..4e7e79da65667e6a5831702479bf790bdac14e4d 100644 (file)
@@ -129,9 +129,6 @@ int sd_dhcp_client_set_vendor_class_identifier(
 int sd_dhcp_client_set_mud_url(
                 sd_dhcp_client *client,
                 const char *mudurl);
-int sd_dhcp_client_set_user_class(
-                sd_dhcp_client *client,
-                char * const *user_class);
 int sd_dhcp_client_get_lease(
                 sd_dhcp_client *client,
                 sd_dhcp_lease **ret);