From: Yu Watanabe Date: Sun, 10 May 2026 16:51:12 +0000 (+0900) Subject: test-dhcp-server: use sd_dhcp_message object to build test message X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=f1c47ab00ba80a0cda033ac10a6885413a492594;p=thirdparty%2Fsystemd.git test-dhcp-server: use sd_dhcp_message object to build test message --- diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c index 18c6d1fd460..d25c31219e4 100644 --- a/src/libsystemd-network/test-dhcp-server.c +++ b/src/libsystemd-network/test-dhcp-server.c @@ -12,6 +12,7 @@ #include "dhcp-server-request.h" #include "fd-util.h" #include "iovec-util.h" +#include "iovec-wrapper.h" #include "tests.h" TEST(basic) { @@ -61,50 +62,32 @@ TEST(basic) { ASSERT_OK(sd_dhcp_server_start(server)); } +static int process_one(sd_dhcp_server *server, sd_dhcp_message *message) { + _cleanup_(iovw_done_free) struct iovec_wrapper iovw = {}; + ASSERT_OK(dhcp_message_build(message, &iovw)); + + _cleanup_(iovec_done) struct iovec iov = {}; + ASSERT_OK(iovw_concat(&iovw, &iov)); + return dhcp_server_process_message(server, &iov, /* mh= */ NULL); +} + TEST(dhcp_server_process_message) { - struct { - DHCPMessageHeader header; - struct { - uint8_t code; - uint8_t length; - uint8_t type; - } _packed_ option_type; - struct { - uint8_t code; - uint8_t length; - be32_t address; - } _packed_ option_requested_ip; - struct { - uint8_t code; - uint8_t length; - be32_t address; - } _packed_ option_server_id; - struct { - uint8_t code; - uint8_t length; - uint8_t id[7]; - } _packed_ option_client_id; - struct { - uint8_t code; - uint8_t length; - uint8_t hostname[6]; - } _packed_ option_hostname; - uint8_t end; - } _packed_ test = { - .header.op = BOOTREQUEST, - .header.htype = ARPHRD_ETHER, - .header.hlen = ETHER_ADDR_LEN, - .header.xid = htobe32(0x12345678), - .header.chaddr = { 'A', 'B', 'C', 'D', 'E', 'F' }, - .header.magic = htobe32(DHCP_MAGIC_COOKIE), - .option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE, - .option_type.length = 1, - .option_type.type = DHCP_DISCOVER, - .option_hostname.code = SD_DHCP_OPTION_HOST_NAME, - .option_hostname.length = 6, - .option_hostname.hostname = { 'T', 'E', 'S', 'T', 'H', 'N' }, - .end = SD_DHCP_OPTION_END, + static const struct hw_addr_data hw_addr = { + .length = ETH_ALEN, + .ether = {{ 'A', 'B', 'C', 'D', 'E', 'F' }}, }; + + _cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *m = NULL; + ASSERT_OK(dhcp_message_new(&m)); + ASSERT_OK(dhcp_message_init_header( + m, + BOOTREQUEST, + 0x12345678, + ARPHRD_ETHER, + &hw_addr)); + + ASSERT_OK(dhcp_message_append_option_hostname(m, /* flags= */ 0, /* is_client= */ false, "TESTHN")); + struct in_addr address_lo = { .s_addr = htobe32(INADDR_LOOPBACK), }; @@ -132,75 +115,74 @@ TEST(dhcp_server_process_message) { server->socket_fd = TAKE_FD(socket_fd[0]); ASSERT_OK(sd_dhcp_server_start(server)); - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_OFFER); - - test.end = 0; - /* TODO, shouldn't this fail? */ - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_OFFER); - test.end = SD_DHCP_OPTION_END; - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_OFFER); - - test.option_type.code = 0; - test.option_type.length = 0; - test.option_type.type = 0; - ASSERT_ERROR(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), ENODATA); - test.option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE; - test.option_type.length = 1; - test.option_type.type = DHCP_DISCOVER; - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_OFFER); - - test.header.op = 0; - ASSERT_ERROR(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), EBADMSG); - test.header.op = BOOTREQUEST; - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_OFFER); - - /* Neither client ID nor hardware type is set. There is no way to manage the bound lease for the request. */ - test.header.htype = 0; - ASSERT_ERROR(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), EBADMSG); - test.header.htype = ARPHRD_ETHER; - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_OFFER); - - test.header.hlen = 0; - ASSERT_ERROR(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), EBADMSG); - test.header.hlen = ETHER_ADDR_LEN; - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_OFFER); - - /* DHCPREQUEST (init-boot) without requested IP */ - test.option_type.type = DHCP_REQUEST; - ASSERT_ERROR(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), ENODATA); - - test.option_requested_ip.code = SD_DHCP_OPTION_REQUESTED_IP_ADDRESS; - test.option_requested_ip.length = 4; - test.option_requested_ip.address = htobe32(0x12345678); - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_NAK); - test.option_server_id.code = SD_DHCP_OPTION_SERVER_IDENTIFIER; - test.option_server_id.length = 4; - test.option_server_id.address = htobe32(INADDR_LOOPBACK); - test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3); - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_ACK); - - test.option_server_id.address = htobe32(0x12345678); - test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3); - ASSERT_OK_ZERO(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL)); - test.option_server_id.address = htobe32(INADDR_LOOPBACK); - test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 4); - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_ACK); - test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3); - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_ACK); - - test.option_client_id.code = SD_DHCP_OPTION_CLIENT_IDENTIFIER; - test.option_client_id.length = 7; - test.option_client_id.id[0] = 0x01; - test.option_client_id.id[1] = 'A'; - test.option_client_id.id[2] = 'B'; - test.option_client_id.id[3] = 'C'; - test.option_client_id.id[4] = 'D'; - test.option_client_id.id[5] = 'E'; - test.option_client_id.id[6] = 'F'; - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_ACK); - - test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 30); - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_ACK); + ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, DHCP_DISCOVER)); + ASSERT_OK_EQ(process_one(server, m), DHCP_OFFER); + + /* Missing Message Type option */ + dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE); + ASSERT_ERROR(process_one(server, m), ENODATA); + ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, DHCP_DISCOVER)); + + /* Invalid op */ + m->header.op = 0; + ASSERT_ERROR(process_one(server, m), EBADMSG); + m->header.op = BOOTREQUEST; + + /* Invalid htype */ + m->header.htype = 0; + ASSERT_ERROR(process_one(server, m), EBADMSG); + m->header.htype = ARPHRD_ETHER; + + /* Invalid hlen */ + m->header.hlen = 0; + ASSERT_ERROR(process_one(server, m), EBADMSG); + m->header.hlen = ETHER_ADDR_LEN; + + /* DHCPREQUEST (init-reboot) without Requested IP Address option */ + dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE); + ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, DHCP_REQUEST)); + ASSERT_ERROR(process_one(server, m), ENODATA); + + /* DHCPREQUEST (init-reboot) with an invalid Requested IP Address option */ + ASSERT_OK(dhcp_message_append_option_be32(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, htobe32(0x12345678))); + ASSERT_OK_EQ(process_one(server, m), DHCP_NAK); + + /* DHCPREQUEST (selecting) with Requested IP address */ + ASSERT_OK(dhcp_message_append_option_be32(m, SD_DHCP_OPTION_SERVER_IDENTIFIER, htobe32(INADDR_LOOPBACK))); + dhcp_message_remove_option(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS); + ASSERT_OK(dhcp_message_append_option_be32(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, htobe32(INADDR_LOOPBACK + 3))); + ASSERT_OK_EQ(process_one(server, m), DHCP_ACK); + + /* DHCPREQUEST (selecting) with unmatching server address (silently ignored). */ + dhcp_message_remove_option(m, SD_DHCP_OPTION_SERVER_IDENTIFIER); + ASSERT_OK(dhcp_message_append_option_be32(m, SD_DHCP_OPTION_SERVER_IDENTIFIER, htobe32(0x12345678))); + ASSERT_OK_ZERO(process_one(server, m)); + dhcp_message_remove_option(m, SD_DHCP_OPTION_SERVER_IDENTIFIER); + ASSERT_OK(dhcp_message_append_option_be32(m, SD_DHCP_OPTION_SERVER_IDENTIFIER, htobe32(INADDR_LOOPBACK))); + + /* DHCPREQUEST (selecting) with another address */ + dhcp_message_remove_option(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS); + ASSERT_OK(dhcp_message_append_option_be32(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, htobe32(INADDR_LOOPBACK + 4))); + ASSERT_OK_EQ(process_one(server, m), DHCP_ACK); + + /* Request the previous address again. */ + dhcp_message_remove_option(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS); + ASSERT_OK(dhcp_message_append_option_be32(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, htobe32(INADDR_LOOPBACK + 3))); + ASSERT_OK_EQ(process_one(server, m), DHCP_ACK); + + /* With client ID */ + struct sd_dhcp_client_id client_id = { + .size = 7, + .id.type = 1, + .id.eth.haddr = { 'A', 'B', 'C', 'D', 'E', 'F' }, + }; + ASSERT_OK(dhcp_message_append_option_client_id(m, &client_id)); + ASSERT_OK_EQ(process_one(server, m), DHCP_ACK); + + /* Request a different address with client ID */ + dhcp_message_remove_option(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS); + ASSERT_OK(dhcp_message_append_option_be32(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, htobe32(INADDR_LOOPBACK + 30))); + ASSERT_OK_EQ(process_one(server, m), DHCP_ACK); /* add the static lease for the client ID */ ASSERT_OK(sd_dhcp_server_stop(server)); @@ -213,26 +195,35 @@ TEST(dhcp_server_process_message) { ASSERT_OK(sd_dhcp_server_start(server)); /* discover */ - test.option_type.type = DHCP_DISCOVER; - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_OFFER); + dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE); + ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, DHCP_DISCOVER)); + ASSERT_OK_EQ(process_one(server, m), DHCP_OFFER); /* request neither bound nor static address */ - test.option_type.type = DHCP_REQUEST; - test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 29); - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_NAK); + dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE); + ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, DHCP_REQUEST)); + dhcp_message_remove_option(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS); + ASSERT_OK(dhcp_message_append_option_be32(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, htobe32(INADDR_LOOPBACK + 29))); + ASSERT_OK_EQ(process_one(server, m), DHCP_NAK); /* request the currently assigned address */ - test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 30); - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_NAK); + dhcp_message_remove_option(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS); + ASSERT_OK(dhcp_message_append_option_be32(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, htobe32(INADDR_LOOPBACK + 30))); + ASSERT_OK_EQ(process_one(server, m), DHCP_NAK); /* request the new static address */ - test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 31); - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_ACK); + dhcp_message_remove_option(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS); + ASSERT_OK(dhcp_message_append_option_be32(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, htobe32(INADDR_LOOPBACK + 31))); + ASSERT_OK_EQ(process_one(server, m), DHCP_ACK); /* release the bound static lease */ - test.header.ciaddr = htobe32(INADDR_LOOPBACK + 31); - test.option_type.type = DHCP_RELEASE; - ASSERT_OK_ZERO(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL)); + m->header.ciaddr = htobe32(INADDR_LOOPBACK + 31); + dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE); + ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, DHCP_RELEASE)); + ASSERT_OK_ZERO(process_one(server, m)); + m->header.ciaddr = 0; + dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE); + ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, DHCP_REQUEST)); /* drop the static lease for the client ID */ ASSERT_OK(sd_dhcp_server_stop(server)); @@ -245,30 +236,35 @@ TEST(dhcp_server_process_message) { ASSERT_OK(sd_dhcp_server_start(server)); /* request a new non-static address */ - test.header.ciaddr = 0; - test.option_type.type = DHCP_REQUEST; - test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 29); - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_ACK); + dhcp_message_remove_option(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS); + ASSERT_OK(dhcp_message_append_option_be32(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, htobe32(INADDR_LOOPBACK + 29))); + ASSERT_OK_EQ(process_one(server, m), DHCP_ACK); /* request address reserved for static lease (unmatching client ID) */ - test.option_client_id.id[6] = 'H'; - test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 42); - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_NAK); + client_id.id.eth.haddr[5] = 'H'; + dhcp_message_remove_option(m, SD_DHCP_OPTION_CLIENT_IDENTIFIER); + ASSERT_OK(dhcp_message_append_option_client_id(m, &client_id)); + dhcp_message_remove_option(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS); + ASSERT_OK(dhcp_message_append_option_be32(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, htobe32(INADDR_LOOPBACK + 42))); + ASSERT_OK_EQ(process_one(server, m), DHCP_NAK); /* request unmatching address */ - test.option_client_id.id[6] = 'G'; - test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 41); - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_NAK); + client_id.id.eth.haddr[5] = 'G'; + dhcp_message_remove_option(m, SD_DHCP_OPTION_CLIENT_IDENTIFIER); + ASSERT_OK(dhcp_message_append_option_client_id(m, &client_id)); + dhcp_message_remove_option(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS); + ASSERT_OK(dhcp_message_append_option_be32(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, htobe32(INADDR_LOOPBACK + 41))); + ASSERT_OK_EQ(process_one(server, m), DHCP_NAK); /* request matching address */ - test.option_client_id.id[6] = 'G'; - test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 42); - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_ACK); + dhcp_message_remove_option(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS); + ASSERT_OK(dhcp_message_append_option_be32(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, htobe32(INADDR_LOOPBACK + 42))); + ASSERT_OK_EQ(process_one(server, m), DHCP_ACK); /* try again */ - test.option_client_id.id[6] = 'G'; - test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 42); - ASSERT_OK_EQ(dhcp_server_process_message(server, &IOVEC_MAKE(&test, sizeof(test)), NULL), DHCP_ACK); + dhcp_message_remove_option(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS); + ASSERT_OK(dhcp_message_append_option_be32(m, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, htobe32(INADDR_LOOPBACK + 42))); + ASSERT_OK_EQ(process_one(server, m), DHCP_ACK); } TEST(sd_dhcp_server_set_static_lease) {