dhcp-message: introduce dhcp_message_{append,get}_option_sub_tlv()

This is for e.g. Vendor-Specific Information option.
This commit is contained in:
Yu Watanabe
2026-05-13 09:41:19 +09:00
parent 3d66a4550a
commit a43dfaa31b
3 changed files with 75 additions and 0 deletions
+46
View 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,
+2
View 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,
@@ -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 = {};