From: Yu Watanabe Date: Sun, 19 Apr 2026 06:27:45 +0000 (+0900) Subject: dhcp-message: introduce dhcp_message_{append,get}_option_length_prefixed_data() X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=295a4dd6edf4a407a687e418ce97b9d5a6b033e2;p=thirdparty%2Fsystemd.git dhcp-message: introduce dhcp_message_{append,get}_option_length_prefixed_data() This is for e.g. User Class option. --- diff --git a/src/libsystemd-network/dhcp-message.c b/src/libsystemd-network/dhcp-message.c index e1bea1cd2ce..c2c25ed6ff6 100644 --- a/src/libsystemd-network/dhcp-message.c +++ b/src/libsystemd-network/dhcp-message.c @@ -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, diff --git a/src/libsystemd-network/dhcp-message.h b/src/libsystemd-network/dhcp-message.h index e4d98ad3e40..4b85a26e922 100644 --- a/src/libsystemd-network/dhcp-message.h +++ b/src/libsystemd-network/dhcp-message.h @@ -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, diff --git a/src/libsystemd-network/test-dhcp-message.c b/src/libsystemd-network/test-dhcp-message.c index 9989e502e24..effdf92dc70 100644 --- a/src/libsystemd-network/test-dhcp-message.c +++ b/src/libsystemd-network/test-dhcp-message.c @@ -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 = {};