#include "dhcp-server-request.h"
#include "fd-util.h"
#include "iovec-util.h"
+#include "iovec-wrapper.h"
#include "tests.h"
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),
};
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));
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));
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) {