]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dhcp-message: introduce dhcp_message_{append,get}_option_length_prefixed_data()
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 19 Apr 2026 06:27:45 +0000 (15:27 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 13 May 2026 00:41:24 +0000 (09:41 +0900)
This is for e.g. User Class option.

src/libsystemd-network/dhcp-message.c
src/libsystemd-network/dhcp-message.h
src/libsystemd-network/test-dhcp-message.c

index e1bea1cd2ce593eafd7958bb282166b1da7d970b..c2c25ed6ff665f73c053dd65e1c0be674451d528 100644 (file)
@@ -325,6 +325,27 @@ int dhcp_message_append_option_sub_tlv(sd_dhcp_message *message, uint8_t code, c
         return dhcp_message_append_option(message, code, iov.iov_len, iov.iov_base);
 }
 
+int dhcp_message_append_option_length_prefixed_data(
+                sd_dhcp_message *message,
+                uint8_t code,
+                size_t length_size,
+                const struct iovec_wrapper *iovw) {
+
+        int r;
+
+        assert(message);
+
+        _cleanup_(iovec_done) struct iovec iov = {};
+        r = iovw_merge(iovw, length_size, &iov);
+        if (r < 0)
+                return r;
+
+        if (!iovec_is_set(&iov))
+                return 0;
+
+        return dhcp_message_append_option(message, code, iov.iov_len, iov.iov_base);
+}
+
 int dhcp_message_get_option(sd_dhcp_message *message, uint8_t code, size_t length, void *ret) {
         int r;
 
@@ -625,6 +646,34 @@ int dhcp_message_get_option_sub_tlv(sd_dhcp_message *message, uint8_t code, TLVF
         return 0;
 }
 
+int dhcp_message_get_option_length_prefixed_data(
+                sd_dhcp_message *message,
+                uint8_t code,
+                size_t length_size,
+                struct iovec_wrapper *ret) {
+
+        int r;
+
+        assert(message);
+
+        _cleanup_(iovec_done) struct iovec iov = {};
+        r = dhcp_message_get_option_alloc(message, code, &iov);
+        if (r < 0)
+                return r;
+
+        _cleanup_(iovw_done_free) struct iovec_wrapper iovw = {};
+        r = iovec_split(&iov, length_size, &iovw);
+        if (r < 0)
+                return r;
+
+        if (iovw_isempty(&iovw))
+                return -ENODATA;
+
+        if (ret)
+                *ret = TAKE_STRUCT(iovw);
+        return 0;
+}
+
 static int dhcp_message_verify_header(
                 const struct iovec *iov,
                 uint8_t op,
index e4d98ad3e40a0ef66501413b20cc7dbb1c78fdbd..4b85a26e9221326d3bcb08931e548a9b8331507c 100644 (file)
@@ -43,6 +43,7 @@ int dhcp_message_append_option_client_id(sd_dhcp_message *message, const sd_dhcp
 int dhcp_message_append_option_parameter_request_list(sd_dhcp_message *message, Set *prl);
 int dhcp_message_append_option_hostname(sd_dhcp_message *message, uint8_t flags, bool is_client, const char *hostname);
 int dhcp_message_append_option_sub_tlv(sd_dhcp_message *message, uint8_t code, const TLV *tlv);
+int dhcp_message_append_option_length_prefixed_data(sd_dhcp_message *message, uint8_t code, size_t length_size, const struct iovec_wrapper *iovw);
 
 int dhcp_message_get_option(sd_dhcp_message *message, uint8_t code, size_t length, void *ret);
 int dhcp_message_get_option_alloc(sd_dhcp_message *message, uint8_t code, struct iovec *ret);
@@ -60,6 +61,7 @@ int dhcp_message_get_option_fqdn(sd_dhcp_message *message, uint8_t *ret_flags, c
 int dhcp_message_get_option_dns_name(sd_dhcp_message *message, uint8_t code, char **ret);
 int dhcp_message_get_option_hostname(sd_dhcp_message *message, char **ret);
 int dhcp_message_get_option_sub_tlv(sd_dhcp_message *message, uint8_t code, TLVFlag flags, TLV **ret);
+int dhcp_message_get_option_length_prefixed_data(sd_dhcp_message *message, uint8_t code, size_t length_size, struct iovec_wrapper *ret);
 
 int dhcp_message_parse(
                 const struct iovec *iov,
index 9989e502e2413af53cb954e2fd95d982465172c0..effdf92dc709bd3721ea508f177abe7b94d1b7a4 100644 (file)
@@ -128,6 +128,12 @@ static void verify_sub_tlv(sd_dhcp_message *m, TLV *expected) {
         ASSERT_TRUE(iovec_equal(&iov, &iov_expected));
 }
 
+static void verify_length_prefixed_data(sd_dhcp_message *m, const struct iovec_wrapper *expected) {
+        _cleanup_(iovw_done_free) struct iovec_wrapper iovw = {};
+        ASSERT_OK(dhcp_message_get_option_length_prefixed_data(m, SD_DHCP_OPTION_USER_CLASS, 1, &iovw));
+        ASSERT_TRUE(iovw_equal(&iovw, expected));
+}
+
 TEST(dhcp_message) {
         _cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *m = NULL;
 
@@ -167,6 +173,16 @@ TEST(dhcp_message) {
         const char *vendor_class = "hogehoge";
         char **root_path = STRV_MAKE("/path/to/root", "/hogehoge/foofoo");
 
+        _cleanup_(iovw_done_free) struct iovec_wrapper user_class = {}, user_class_1 = {}, user_class_2 = {};
+        FOREACH_STRING(s, "hoge", "foo", "bar") {
+                ASSERT_OK(iovw_extend(&user_class, s, strlen(s)));
+                ASSERT_OK(iovw_extend(&user_class_1, s, strlen(s)));
+        }
+        FOREACH_STRING(s, "aaa", "bbb", "ccc") {
+                ASSERT_OK(iovw_extend(&user_class, s, strlen(s)));
+                ASSERT_OK(iovw_extend(&user_class_2, s, strlen(s)));
+        }
+
         _cleanup_(tlv_done) TLV vendor = TLV_INIT(TLV_DHCP4_SUBOPTION);
         for (unsigned i = 0; i < 3; i++) {
                 uint8_t buf[255];
@@ -269,6 +285,11 @@ TEST(dhcp_message) {
         ASSERT_ERROR(dhcp_message_append_option_sub_tlv(m, SD_DHCP_OPTION_VENDOR_SPECIFIC_INFORMATION, &vendor), EEXIST);
         verify_sub_tlv(m, &vendor);
 
+        /* user class */
+        ASSERT_OK(dhcp_message_append_option_length_prefixed_data(m, SD_DHCP_OPTION_USER_CLASS, 1, &user_class_1));
+        ASSERT_OK(dhcp_message_append_option_length_prefixed_data(m, SD_DHCP_OPTION_USER_CLASS, 1, &user_class_2));
+        verify_length_prefixed_data(m, &user_class);
+
         /* build and parse */
         _cleanup_(iovw_done_free) struct iovec_wrapper iovw = {};
         ASSERT_OK(dhcp_message_build(m, &iovw));
@@ -301,6 +322,7 @@ TEST(dhcp_message) {
         verify_prl(m2, prl);
         verify_hostname(m2, hostname);
         verify_sub_tlv(m2, &vendor);
+        verify_length_prefixed_data(m2, &user_class);
 
         /* build again, and verify the packet */
         _cleanup_(iovw_done_free) struct iovec_wrapper iovw2 = {};