]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dhcp-message: add SIP server option support
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 19 Apr 2026 07:09:10 +0000 (16:09 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 13 May 2026 00:41:24 +0000 (09:41 +0900)
The DHCP option 120 (SIP server) option takes a list of addresses or
domain names, and the first byte in the data classifies which type is
stored. Let's extend _addresses() and _domains() to make them support
the SIP server option.

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

index 00e38ea9eeca5a627db88535031b650ef51ca342..50cc25aded68d8d5dc52846874765b6b8e0172ca 100644 (file)
@@ -191,6 +191,24 @@ int dhcp_message_append_option_addresses(sd_dhcp_message *message, uint8_t code,
         if (size_multiply_overflow(sizeof(struct in_addr), n_addr))
                 return -ENOBUFS;
 
+        if (code == SD_DHCP_OPTION_SIP_SERVER) {
+                if (dhcp_message_has_option(message, SD_DHCP_OPTION_SIP_SERVER))
+                        return -EEXIST;
+
+                size_t len = size_add(1, sizeof(struct in_addr) * n_addr);
+                if (len == SIZE_MAX)
+                        return -ENOBUFS;
+
+                _cleanup_free_ uint8_t *buf = new(uint8_t, len);
+                if (!buf)
+                        return -ENOMEM;
+
+                buf[0] = 1; /* 'enc' field, 0: domains, 1: addresses */
+                memcpy(buf + 1, addr, sizeof(struct in_addr) * n_addr);
+
+                return dhcp_message_append_option(message, code, len, buf);
+        }
+
         return dhcp_message_append_option(message, code, sizeof(struct in_addr) * n_addr, addr);
 }
 
@@ -423,11 +441,22 @@ int dhcp_message_get_option_addresses(sd_dhcp_message *message, uint8_t code, si
         assert(message);
         assert(ret_n_addr || !ret_addr);
 
-        _cleanup_(iovec_done) struct iovec iov = {};
-        r = dhcp_message_get_option_alloc(message, code, &iov);
+        _cleanup_(iovec_done) struct iovec iov_free = {};
+        r = dhcp_message_get_option_alloc(message, code, &iov_free);
         if (r < 0)
                 return r;
 
+        struct iovec iov = iov_free;
+        if (code == SD_DHCP_OPTION_SIP_SERVER) {
+                if (!iovec_is_set(&iov))
+                        return -EBADMSG;
+
+                if (*(uint8_t*) iov.iov_base != 1) /* 'enc' field, 0: domains, 1: addresses */
+                        return -ENODATA;
+
+                iovec_inc(&iov, 1);
+        }
+
         if (iov.iov_len % sizeof(struct in_addr) != 0)
                 return -EBADMSG;
 
@@ -435,8 +464,17 @@ int dhcp_message_get_option_addresses(sd_dhcp_message *message, uint8_t code, si
         if (n == 0)
                 return -ENODATA;
 
-        if (ret_addr)
-                *ret_addr = (struct in_addr*) TAKE_PTR(iov.iov_base);
+        if (ret_addr) {
+                if (code == SD_DHCP_OPTION_SIP_SERVER) {
+                        struct in_addr *addr = newdup(struct in_addr, iov.iov_base, n);
+                        if (!addr)
+                                return -ENOMEM;
+                        *ret_addr = addr;
+                } else {
+                        *ret_addr = iov.iov_base;
+                        TAKE_STRUCT(iov_free);
+                }
+        }
         if (ret_n_addr)
                 *ret_n_addr = n;
         return 0;
@@ -625,7 +663,7 @@ int dhcp_message_get_option_domains(sd_dhcp_message *message, uint8_t code, char
 
         assert(message);
 
-        /* This is mostly for SD_DHCP_OPTION_DOMAIN_SEARCH. */
+        /* This is mostly for SD_DHCP_OPTION_DOMAIN_SEARCH and SD_DHCP_OPTION_SIP_SERVER. */
 
         _cleanup_(iovec_done) struct iovec iov = {};
         r = dhcp_message_get_option_alloc(message, code, &iov);
@@ -635,6 +673,17 @@ int dhcp_message_get_option_domains(sd_dhcp_message *message, uint8_t code, char
         const uint8_t *buf = iov.iov_base;
         size_t len = iov.iov_len;
 
+        if (code == SD_DHCP_OPTION_SIP_SERVER) {
+                if (len == 0)
+                        return -EBADMSG;
+
+                if (buf[0] != 0) /* 'enc' field, 0: domains, 1: addresses */
+                        return -ENODATA;
+
+                len--;
+                buf++;
+        }
+
         _cleanup_strv_free_ char **names = NULL;
         size_t n_names = 0;
 
index ddb3823f25435a6ea8c8c2e24487c4fd9aa0b6b3..1aa5830008323697fed674a0d3ca99cc565943e0 100644 (file)
@@ -68,7 +68,8 @@ static void verify_address(sd_dhcp_message *m, const struct in_addr *expected) {
 
 static void verify_addresses(
                 sd_dhcp_message *m,
-                size_t n_ntp, const struct in_addr *ntp) {
+                size_t n_ntp, const struct in_addr *ntp,
+                size_t n_sip, const struct in_addr *sip) {
 
         struct in_addr a;
         ASSERT_OK(dhcp_message_get_option_be32(m, SD_DHCP_OPTION_NTP_SERVER, &a.s_addr));
@@ -81,6 +82,14 @@ static void verify_addresses(
         ASSERT_OK(dhcp_message_get_option_addresses(m, SD_DHCP_OPTION_NTP_SERVER, &n, &addrs));
         ASSERT_EQ(n, n_ntp);
         ASSERT_EQ(memcmp(addrs, ntp, sizeof(struct in_addr) * n), 0);
+
+        ASSERT_ERROR(dhcp_message_get_option_be32(m, SD_DHCP_OPTION_SIP_SERVER, NULL), ENODATA);
+        ASSERT_ERROR(dhcp_message_get_option_address(m, SD_DHCP_OPTION_SIP_SERVER, NULL), ENODATA);
+
+        addrs = mfree(addrs);
+        ASSERT_OK(dhcp_message_get_option_addresses(m, SD_DHCP_OPTION_SIP_SERVER, &n, &addrs));
+        ASSERT_EQ(n, n_sip);
+        ASSERT_EQ(memcmp(addrs, sip, sizeof(struct in_addr) * n), 0);
 }
 
 static void verify_string(sd_dhcp_message *m, const char *expected) {
@@ -160,6 +169,14 @@ TEST(dhcp_message) {
                 { .s_addr = htobe32(0xC0000204) },
         };
 
+        /* 192.0.2.17 - 20 */
+        struct in_addr sip[4] = {
+                { .s_addr = htobe32(0xC0000211) },
+                { .s_addr = htobe32(0xC0000212) },
+                { .s_addr = htobe32(0xC0000213) },
+                { .s_addr = htobe32(0xC0000214) },
+        };
+
         sd_dhcp_client_id id = {
                 .raw = { 1, 3, 3, 3, 3, 3, 3, },
                 .size = 7,
@@ -242,7 +259,10 @@ TEST(dhcp_message) {
         /* multiple addresses */
         ASSERT_OK(dhcp_message_append_option_address(m, SD_DHCP_OPTION_NTP_SERVER, &ntp[0]));
         ASSERT_OK(dhcp_message_append_option_addresses(m, SD_DHCP_OPTION_NTP_SERVER, ELEMENTSOF(ntp) - 1, ntp + 1));
-        verify_addresses(m, ELEMENTSOF(ntp), ntp);
+        ASSERT_OK(dhcp_message_append_option_addresses(m, SD_DHCP_OPTION_SIP_SERVER, ELEMENTSOF(sip), sip));
+        ASSERT_ERROR(dhcp_message_append_option_addresses(m, SD_DHCP_OPTION_SIP_SERVER, ELEMENTSOF(sip), sip), EEXIST);
+        ASSERT_OK(dhcp_message_append_option_addresses(m, SD_DHCP_OPTION_SIP_SERVER, 0, NULL));
+        verify_addresses(m, ELEMENTSOF(ntp), ntp, ELEMENTSOF(sip), sip);
 
         /* string */
         ASSERT_ERROR(dhcp_message_get_option_string(m, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, NULL), ENODATA);
@@ -316,7 +336,7 @@ TEST(dhcp_message) {
         verify_u16(m2, 512);
         verify_sec(m2, lease_time);
         verify_address(m2, &addr);
-        verify_addresses(m2, ELEMENTSOF(ntp), ntp);
+        verify_addresses(m2, ELEMENTSOF(ntp), ntp, ELEMENTSOF(sip), sip);
         verify_string(m2, vendor_class);
         verify_client_id(m2, &id);
         verify_prl(m2, prl);
@@ -346,6 +366,24 @@ static void test_domains_one(size_t len, const uint8_t *data, char * const *expe
         ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_DOMAIN_SEARCH, len - len / 2, data + len / 2));
         ASSERT_OK(dhcp_message_get_option_domains(m, SD_DHCP_OPTION_DOMAIN_SEARCH, &strv));
         ASSERT_TRUE(strv_equal(strv, expected));
+
+        strv = strv_free(strv);
+
+        _cleanup_free_ uint8_t *sip = new(uint8_t, len + 1);
+        sip[0] = 0;
+        memcpy(sip + 1, data, len);
+
+        ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_SIP_SERVER, len + 1, sip));
+        ASSERT_OK(dhcp_message_get_option_domains(m, SD_DHCP_OPTION_SIP_SERVER, &strv));
+        ASSERT_TRUE(strv_equal(strv, expected));
+
+        dhcp_message_remove_option(m, SD_DHCP_OPTION_SIP_SERVER);
+        strv = strv_free(strv);
+
+        ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_SIP_SERVER, (len + 1) / 2, sip));
+        ASSERT_OK(dhcp_message_append_option(m, SD_DHCP_OPTION_SIP_SERVER, len + 1 - (len + 1) / 2, sip + (len + 1) / 2));
+        ASSERT_OK(dhcp_message_get_option_domains(m, SD_DHCP_OPTION_SIP_SERVER, &strv));
+        ASSERT_TRUE(strv_equal(strv, expected));
 }
 
 static void test_domains_fail(size_t len, const uint8_t *data) {