From a40038c28abec50ce9ddda6631d34743141b2239 Mon Sep 17 00:00:00 2001 From: Colin Foster Date: Mon, 28 Oct 2024 19:50:06 -0500 Subject: [PATCH] test-dhcp-client: add test for bootp clients Verify that BOOTP replies are successfully handled by the sd-dhcp-client when configured for BOOTP. --- src/libsystemd-network/test-dhcp-client.c | 213 ++++++++++++++++++++++ 1 file changed, 213 insertions(+) diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index a5732a6da48..85c178a7733 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -35,6 +35,14 @@ static struct hw_addr_data hw_addr = { }; typedef int (*test_callback_recv_t)(size_t size, DHCPMessage *dhcp); +struct bootp_addr_data { + uint8_t *offer_buf; + int offer_len; + int netmask_offset; + int ip_offset; +}; +struct bootp_addr_data *bootp_test_context; + static bool verbose = true; static int test_fd[2]; static test_callback_recv_t callback_recv; @@ -532,6 +540,204 @@ static void test_addr_acq(sd_event *e) { xid = 0; } +static uint8_t test_addr_bootp_reply[] = { + 0x45, 0x00, 0x01, 0x48, 0x00, 0x00, 0x40, 0x00, + 0xff, 0x11, 0x70, 0xa3, 0x0a, 0x00, 0x00, 0x02, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x43, 0x00, 0x44, + 0x01, 0x2c, 0x2b, 0x91, 0x02, 0x01, 0x06, 0x00, + 0x69, 0xd3, 0x79, 0x11, 0x17, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0a, 0x46, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x50, 0x2d, 0xf4, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x63, 0x82, 0x53, 0x63, 0x01, 0x04, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static uint8_t test_addr_bootp_reply_bootpd[] = { + 0x45, 0x00, 0x01, 0x48, 0xbe, 0xad, 0x40, 0x00, + 0x40, 0x11, 0x73, 0x43, 0xc0, 0xa8, 0x43, 0x31, + 0xc0, 0xa8, 0x43, 0x32, 0x00, 0x43, 0x00, 0x44, + 0x01, 0x34, 0x08, 0xfa, 0x02, 0x01, 0x06, 0x00, + 0x82, 0x57, 0xda, 0xf1, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x43, 0x32, + 0xc0, 0xa8, 0x43, 0x31, 0x00, 0x00, 0x00, 0x00, + 0xc2, 0x3e, 0xa5, 0x53, 0x57, 0x72, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x64, 0x65, 0x62, 0x69, 0x61, 0x6e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x63, 0x82, 0x53, 0x63, 0x01, 0x04, 0xff, 0xff, + 0xff, 0xf0, 0x03, 0x04, 0xc0, 0xa8, 0x43, 0x31, + 0x06, 0x04, 0x0a, 0x00, 0x01, 0x01, 0x0c, 0x15, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2d, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2d, 0x74, + 0x72, 0x69, 0x78, 0x69, 0x65, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static struct bootp_addr_data bootp_addr_data[] = { + { + .offer_buf = test_addr_bootp_reply, + .offer_len = sizeof(test_addr_bootp_reply), + .netmask_offset = 270, + .ip_offset = 44, + }, + { + .offer_buf = test_addr_bootp_reply_bootpd, + .offer_len = sizeof(test_addr_bootp_reply_bootpd), + .netmask_offset = 270, + .ip_offset = 44, + }, +}; + +static int test_bootp_acquired(sd_dhcp_client *client, int event, + void *userdata) { + sd_event *e = userdata; + sd_dhcp_lease *lease; + struct in_addr addr; + + assert_se(client); + assert_se(IN_SET(event, SD_DHCP_CLIENT_EVENT_IP_ACQUIRE, SD_DHCP_CLIENT_EVENT_SELECTING)); + + assert_se(sd_dhcp_client_get_lease(client, &lease) >= 0); + assert_se(lease); + + assert_se(sd_dhcp_lease_get_address(lease, &addr) >= 0); + assert_se(memcmp(&addr.s_addr, &bootp_test_context->offer_buf[bootp_test_context->ip_offset], + sizeof(addr.s_addr)) == 0); + + assert_se(sd_dhcp_lease_get_netmask(lease, &addr) >= 0); + assert_se(memcmp(&addr.s_addr, &bootp_test_context->offer_buf[bootp_test_context->netmask_offset], + sizeof(addr.s_addr)) == 0); + + if (verbose) + log_info(" BOOTP address acquired"); + + sd_event_exit(e, 0); + + return 0; +} + +static int test_bootp_recv_request(size_t size, DHCPMessage *request) { + uint16_t udp_check = 0; + int res; + + xid = request->xid; + + if (verbose) + log_info(" recv BOOTP Request 0x%08x", be32toh(xid)); + + callback_recv = NULL; + + memcpy(&bootp_test_context->offer_buf[26], &udp_check, sizeof(udp_check)); + memcpy(&bootp_test_context->offer_buf[32], &xid, sizeof(xid)); + memcpy(&bootp_test_context->offer_buf[56], hw_addr.bytes, hw_addr.length); + + res = write(test_fd[1], bootp_test_context->offer_buf, + bootp_test_context->offer_len); + assert_se(res == bootp_test_context->offer_len); + + if (verbose) + log_info(" sent BOOTP Reply"); + + return 0; +}; + +static void test_acquire_bootp(sd_event *e) { + sd_dhcp_client *client; + int res, r; + + if (verbose) + log_info("* %s", __func__); + + r = sd_dhcp_client_new(&client, false); + assert_se(r >= 0); + assert_se(client); + + r = sd_dhcp_client_attach_event(client, e, 0); + assert_se(r >= 0); + + r = sd_dhcp_client_set_bootp(client, true); + assert_se(r >= 0); + + assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0); + assert_se(sd_dhcp_client_set_mac(client, hw_addr.bytes, bcast_addr.bytes, hw_addr.length, ARPHRD_ETHER) >= 0); + + assert_se(sd_dhcp_client_set_callback(client, test_bootp_acquired, e) >= 0); + + callback_recv = test_bootp_recv_request; + + assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME, + 30 * USEC_PER_SEC, 0, + NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0); + + res = sd_dhcp_client_start(client); + assert_se(IN_SET(res, 0, -EINPROGRESS)); + + assert_se(sd_event_loop(e) >= 0); + + assert_se(sd_dhcp_client_set_callback(client, NULL, NULL) >= 0); + assert_se(sd_dhcp_client_stop(client) >= 0); + sd_dhcp_client_unref(client); + + test_fd[1] = safe_close(test_fd[1]); + + callback_recv = NULL; + xid = 0; +} + int main(int argc, char *argv[]) { _cleanup_(sd_event_unrefp) sd_event *e; @@ -549,6 +755,13 @@ int main(int argc, char *argv[]) { test_discover_message(e); test_addr_acq(e); + for (size_t i = 0; i < sizeof(bootp_addr_data) / sizeof(bootp_addr_data[0]); i++) { + sd_event_unref(e); + assert_se(sd_event_new(&e) >= 0); + bootp_test_context = &bootp_addr_data[i]; + test_acquire_bootp(e); + } + #if HAVE_VALGRIND_VALGRIND_H /* Make sure the async_close thread has finished. * valgrind would report some of the phread_* structures