From: Yu Watanabe Date: Sun, 19 Apr 2026 06:34:52 +0000 (+0900) Subject: dhcp-message: introduce dhcp_message_{append,get}_option_sub_tlv() X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a43dfaa31bae5c2f95cf84736a81d6d18b1a3577;p=thirdparty%2Fsystemd.git dhcp-message: introduce dhcp_message_{append,get}_option_sub_tlv() This is for e.g. Vendor-Specific Information option. --- diff --git a/src/libsystemd-network/dhcp-message.c b/src/libsystemd-network/dhcp-message.c index 92f7d5ed8c0..e1bea1cd2ce 100644 --- a/src/libsystemd-network/dhcp-message.c +++ b/src/libsystemd-network/dhcp-message.c @@ -306,6 +306,25 @@ int dhcp_message_append_option_hostname(sd_dhcp_message *message, uint8_t flags, return dhcp_message_append_option_fqdn(message, flags, is_client, hostname); } +int dhcp_message_append_option_sub_tlv(sd_dhcp_message *message, uint8_t code, const TLV *tlv) { + int r; + + assert(message); + + if (tlv_isempty(tlv)) + return 0; + + if (dhcp_message_has_option(message, code)) + return -EEXIST; + + _cleanup_(iovec_done) struct iovec iov = {}; + r = tlv_build(tlv, &iov); + if (r < 0) + return r; + + 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; @@ -579,6 +598,33 @@ int dhcp_message_get_option_hostname(sd_dhcp_message *message, char **ret) { return dhcp_message_get_option_dns_name(message, SD_DHCP_OPTION_HOST_NAME, ret); } +int dhcp_message_get_option_sub_tlv(sd_dhcp_message *message, uint8_t code, TLVFlag flags, TLV **ret) { + int r; + + assert(message); + assert(!FLAGS_SET(flags, TLV_TEMPORARY)); + + _cleanup_(iovec_done) struct iovec iov = {}; + r = dhcp_message_get_option_alloc(message, code, &iov); + if (r < 0) + return r; + + _cleanup_(tlv_unrefp) TLV *tlv = tlv_new(flags); + if (!tlv) + return -ENOMEM; + + r = tlv_parse(tlv, &iov); + if (r < 0) + return r; + + if (tlv_isempty(tlv)) + return -ENODATA; + + if (ret) + *ret = TAKE_PTR(tlv); + 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 ad168838928..e4d98ad3e40 100644 --- a/src/libsystemd-network/dhcp-message.h +++ b/src/libsystemd-network/dhcp-message.h @@ -42,6 +42,7 @@ int dhcp_message_append_option_string(sd_dhcp_message *message, uint8_t code, co int dhcp_message_append_option_client_id(sd_dhcp_message *message, const sd_dhcp_client_id *id); 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_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); @@ -58,6 +59,7 @@ int dhcp_message_get_option_parameter_request_list(sd_dhcp_message *message, Set int dhcp_message_get_option_fqdn(sd_dhcp_message *message, uint8_t *ret_flags, char **ret_fqdn); 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_parse( const struct iovec *iov, diff --git a/src/libsystemd-network/test-dhcp-message.c b/src/libsystemd-network/test-dhcp-message.c index 816edc9fc69..9989e502e24 100644 --- a/src/libsystemd-network/test-dhcp-message.c +++ b/src/libsystemd-network/test-dhcp-message.c @@ -114,6 +114,20 @@ static void verify_hostname(sd_dhcp_message *m, const char *expected) { ASSERT_STREQ(s, expected); } +static void verify_sub_tlv(sd_dhcp_message *m, TLV *expected) { + _cleanup_(tlv_unrefp) TLV *tlv = NULL; + ASSERT_OK(dhcp_message_get_option_sub_tlv( + m, + SD_DHCP_OPTION_VENDOR_SPECIFIC_INFORMATION, + TLV_DHCP4_SUBOPTION, + &tlv)); + + _cleanup_(iovec_done) struct iovec iov = {}, iov_expected = {}; + ASSERT_OK(tlv_build(tlv, &iov)); + ASSERT_OK(tlv_build(expected, &iov_expected)); + ASSERT_TRUE(iovec_equal(&iov, &iov_expected)); +} + TEST(dhcp_message) { _cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *m = NULL; @@ -153,6 +167,13 @@ TEST(dhcp_message) { const char *vendor_class = "hogehoge"; char **root_path = STRV_MAKE("/path/to/root", "/hogehoge/foofoo"); + _cleanup_(tlv_done) TLV vendor = TLV_INIT(TLV_DHCP4_SUBOPTION); + for (unsigned i = 0; i < 3; i++) { + uint8_t buf[255]; + memset(buf, 42 + i, sizeof(buf)); + ASSERT_OK(tlv_append(&vendor, i + 1, 255, buf)); + } + ASSERT_OK(dhcp_message_init_header( m, BOOTREQUEST, @@ -243,6 +264,11 @@ TEST(dhcp_message) { ASSERT_OK(dhcp_message_append_option_hostname(m, /* flags= */ 0, /* is_client= */ false, hostname)); verify_hostname(m, hostname); + /* vendor specific */ + ASSERT_OK(dhcp_message_append_option_sub_tlv(m, SD_DHCP_OPTION_VENDOR_SPECIFIC_INFORMATION, &vendor)); + ASSERT_ERROR(dhcp_message_append_option_sub_tlv(m, SD_DHCP_OPTION_VENDOR_SPECIFIC_INFORMATION, &vendor), EEXIST); + verify_sub_tlv(m, &vendor); + /* build and parse */ _cleanup_(iovw_done_free) struct iovec_wrapper iovw = {}; ASSERT_OK(dhcp_message_build(m, &iovw)); @@ -274,6 +300,7 @@ TEST(dhcp_message) { verify_client_id(m2, &id); verify_prl(m2, prl); verify_hostname(m2, hostname); + verify_sub_tlv(m2, &vendor); /* build again, and verify the packet */ _cleanup_(iovw_done_free) struct iovec_wrapper iovw2 = {};