]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test-dhcp-client: add test for bootp clients
authorColin Foster <colin.foster@in-advantage.com>
Tue, 29 Oct 2024 00:50:06 +0000 (19:50 -0500)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 13 Jun 2025 05:15:04 +0000 (14:15 +0900)
Verify that BOOTP replies are successfully handled by the sd-dhcp-client
when configured for BOOTP.

Co-authored-by: Avram Dorfman <dorfman@est.org>
src/libsystemd-network/test-dhcp-client.c

index 72d8ad4d0cf04bbc860f88604d98677beda9d5a4..cc08c3fbd221119f59c16315ad58043c2121883a 100644 (file)
@@ -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;
+        size_t 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;
@@ -531,6 +539,202 @@ 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, 0x36, 0x04, 0x0a, 0x00, 0x00, 0x02,
+        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_dhcp_lease *lease = NULL;
+        sd_event *e = userdata;
+        struct in_addr addr;
+
+        ASSERT_NOT_NULL(client);
+        assert_se(IN_SET(event, SD_DHCP_CLIENT_EVENT_IP_ACQUIRE, SD_DHCP_CLIENT_EVENT_SELECTING));
+
+        ASSERT_OK(sd_dhcp_client_get_lease(client, &lease));
+        ASSERT_NOT_NULL(lease);
+
+        ASSERT_OK(sd_dhcp_lease_get_address(lease, &addr));
+        ASSERT_EQ(memcmp(&addr.s_addr, &bootp_test_context->offer_buf[bootp_test_context->ip_offset],
+                      sizeof(addr.s_addr)), 0);
+
+        ASSERT_OK(sd_dhcp_lease_get_netmask(lease, &addr));
+        ASSERT_EQ(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;
+        size_t 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_EQ(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 = NULL;
+        int res;
+
+        if (verbose)
+                log_info("* %s", __func__);
+
+        ASSERT_OK(sd_dhcp_client_new(&client, false));
+        ASSERT_NOT_NULL(client);
+
+        ASSERT_OK(sd_dhcp_client_attach_event(client, e, 0));
+
+        ASSERT_OK(sd_dhcp_client_set_bootp(client, true));
+
+        ASSERT_OK(sd_dhcp_client_set_ifindex(client, 42));
+        ASSERT_OK(sd_dhcp_client_set_mac(client, hw_addr.bytes, bcast_addr.bytes, hw_addr.length, ARPHRD_ETHER));
+
+        ASSERT_OK(sd_dhcp_client_set_callback(client, test_bootp_acquired, e));
+
+        callback_recv = test_bootp_recv_request;
+
+        ASSERT_OK(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
+                                             30 * USEC_PER_SEC, 0,
+                                             NULL, INT_TO_PTR(-ETIMEDOUT)));
+
+        res = sd_dhcp_client_start(client);
+        assert_se(IN_SET(res, 0, -EINPROGRESS));
+
+        ASSERT_OK(sd_event_loop(e));
+
+        ASSERT_OK(sd_dhcp_client_set_callback(client, NULL, NULL));
+        ASSERT_OK(sd_dhcp_client_stop(client));
+        client = sd_dhcp_client_unref(client);
+        ASSERT_NULL(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;
 
@@ -548,6 +752,13 @@ int main(int argc, char *argv[]) {
         test_discover_message(e);
         test_addr_acq(e);
 
+        FOREACH_ELEMENT(i, bootp_addr_data) {
+                sd_event_unref(e);
+                ASSERT_OK(sd_event_new(&e));
+                bootp_test_context = 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