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

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

index 92f7d5ed8c0e5a2e93abf24d2e2f1436fbc43f8a..e1bea1cd2ce593eafd7958bb282166b1da7d970b 100644 (file)
@@ -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,
index ad1688389288eca76c51df57a512ecb7781b61fe..e4d98ad3e40a0ef66501413b20cc7dbb1c78fdbd 100644 (file)
@@ -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,
index 816edc9fc69e7edd539dabba7bd99d8d166eeb9a..9989e502e2413af53cb954e2fd95d982465172c0 100644 (file)
@@ -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 = {};