From: Yu Watanabe Date: Sun, 19 Apr 2026 07:09:10 +0000 (+0900) Subject: dhcp-message: add SIP server option support X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e178adc6e167e4079adb35f41bf32d3aa0ebf69f;p=thirdparty%2Fsystemd.git dhcp-message: add SIP server option support 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. --- diff --git a/src/libsystemd-network/dhcp-message.c b/src/libsystemd-network/dhcp-message.c index 00e38ea9eec..50cc25aded6 100644 --- a/src/libsystemd-network/dhcp-message.c +++ b/src/libsystemd-network/dhcp-message.c @@ -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; diff --git a/src/libsystemd-network/test-dhcp-message.c b/src/libsystemd-network/test-dhcp-message.c index ddb3823f254..1aa58300083 100644 --- a/src/libsystemd-network/test-dhcp-message.c +++ b/src/libsystemd-network/test-dhcp-message.c @@ -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) {