summaryrefslogtreecommitdiff
path: root/libdhcp/tests/test_client.c
blob: aae9e6ce41cb449352fedccf8b3e15c150156d74 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/* libdhcp/tests/test_client.c - Tests for libdhcp client
 *
 * Copyright (C) 2025  Luke T. Shumaker <lukeshu@lukeshu.com>
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

#include <string.h> /* for memcpy() */

#include <libcr/coroutine.h>
#include <libhw/generic/net.h>
#include <libhw/host_alarmclock.h>
#include <libmisc/_intercept.h>

#include <libdhcp/client.h>

#include "test.h"

/******************************************************************************/

uint32_t __lm_rand_uint31(void) { assert_notreached("should not call rand_uint31"); }
uint32_t __lm_rand_uint32(void) {
	static unsigned cnt = 0;
	switch (cnt++) {
	case 0: return 0xe840c679; /* match the XID in the hard-coded messages below */
	default: assert_notreached("too many calls to rand_uint32()");
	}
}
uint64_t __lm_rand_uint62(void) { assert_notreached("should not call rand_uint62"); }
uint64_t __lm_rand_uint63(void) { assert_notreached("should not call rand_uint63"); }
uint64_t __lm_rand_uint64(void) { assert_notreached("should not call rand_uint64"); }

/******************************************************************************/

struct test_udp {
};
LO_IMPLEMENTATION_STATIC(io_closer, struct test_udp, test_udp);
LO_IMPLEMENTATION_STATIC(net_packet_conn, struct test_udp, test_udp);

static error test_udp_sendto(struct test_udp *LM_UNUSED(self), void *LM_UNUSED(buf), size_t LM_UNUSED(len), struct net_ip4_addr LM_UNUSED(node), uint16_t LM_UNUSED(port)) {
	static unsigned cnt = 0;
	if (cnt++ % 2 == 0)
		return error_new(E_EUNKNOWN);
	return ERROR_NULL;
}

static size_t_or_error test_udp_recvfrom(struct test_udp *LM_UNUSED(self), void *buf, size_t len, struct net_ip4_addr *ret_node, uint16_t *ret_port) {
	/* I got these from recording a session against my actual WiFi router.  */
	static const uint8_t resp_offer[] = {0x02,0x01,0x06,0x00,0xE8,0x40,0xC6,0x79,0x00,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0xC0,0xA8,0x0A,0x78,0xC0,0xA8,0x0A,0x01,0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0x35,0x01,0x02,0x36,0x04,0xC0,0xA8,0x0A,0x01,0x33,0x04,0x00,0x00,0xA8,0xC0,0x3A,0x04,0x00,0x00,0x54,0x60,0x3B,0x04,0x00,0x00,0x93,0xA8,0x01,0x04,0xFF,0xFF,0xFF,0x00,0x1C,0x04,0xC0,0xA8,0x0A,0xFF,0x03,0x04,0xC0,0xA8,0x0A,0x01,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
	static const uint8_t resp_ack[]   = {0x02,0x01,0x06,0x00,0xE8,0x40,0xC6,0x79,0x00,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0xC0,0xA8,0x0A,0x78,0xC0,0xA8,0x0A,0x01,0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0x35,0x01,0x05,0x36,0x04,0xC0,0xA8,0x0A,0x01,0x33,0x04,0x00,0x00,0xA8,0xC0,0x3A,0x04,0x00,0x00,0x54,0x60,0x3B,0x04,0x00,0x00,0x93,0xA8,0x01,0x04,0xFF,0xFF,0xFF,0x00,0x1C,0x04,0xC0,0xA8,0x0A,0xFF,0x03,0x04,0xC0,0xA8,0x0A,0x01,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};

	static unsigned cnt = 0;
	const void *resp;
	size_t resp_len;
	switch (cnt++) {
	case 0: return ERROR_NEW_ERR(size_t, error_new(E_EUNKNOWN));
	case 1: resp = resp_offer; resp_len = sizeof(resp_offer); break;
	case 2: return ERROR_NEW_ERR(size_t, error_new(E_EUNKNOWN));
	case 3: resp = resp_ack; resp_len = sizeof(resp_ack); break;
	default: return ERROR_NEW_ERR(size_t, error_new(E_NET_ERECV_TIMEOUT));
	}
	test_assert(len >= resp_len);
	memcpy(buf, resp, resp_len);
	*ret_node = ((struct net_ip4_addr){{192,168,10,1}});
	*ret_port = 67;
	return ERROR_NEW_VAL(size_t, resp_len);
}

static void test_udp_set_recv_deadline(struct test_udp *LM_UNUSED(self), uint64_t LM_UNUSED(ns_since_boot)) {
	/* Do nothing?  */
}

static error test_udp_close(struct test_udp *LM_UNUSED(self)) {
	assert_notreached("not implemented");
}

/******************************************************************************/

struct test_iface {
	struct net_iface_config cfg;

	struct test_udp         conn;
};
LO_IMPLEMENTATION_STATIC(net_iface, struct test_iface, test);

static struct net_eth_addr test_hwaddr(struct test_iface *LM_UNUSED(self)) {
	struct net_eth_addr ret = {{1, 2, 3, 4, 5, 6}};
	return ret;
}

static void test_ifup(struct test_iface *LM_UNUSED(self), struct net_iface_config LM_UNUSED(cfg)) {
	cr_exit();
}

static void test_ifdown(struct test_iface *LM_UNUSED(self)) {
	assert_notreached("not implemented");
}

static bool test_arp_ping(struct test_iface *LM_UNUSED(self), struct net_ip4_addr LM_UNUSED(addr)) {
	return false;
}

static lo_interface net_stream_listener test_tcp_listen(struct test_iface *LM_UNUSED(self), uint16_t LM_UNUSED(local_port)) {
	assert_notreached("not implemented");
}

static net_stream_conn_or_error test_tcp_dial(struct test_iface *LM_UNUSED(self), struct net_ip4_addr LM_UNUSED(remote_node), uint16_t LM_UNUSED(remote_port)) {
	assert_notreached("not implemented");
}

static lo_interface net_packet_conn test_udp_conn(struct test_iface *self, uint16_t local_port) {
	bool once = false;
	test_assert(local_port == 68);
	test_assert(!once);
	once = true;
	return LO_BOX(net_packet_conn, &self->conn);
}

/******************************************************************************/

COROUTINE dhcp_cr(void *) {
	cr_begin();
	struct test_iface iface = {};
	dhcp_client_main(LO_BOX(net_iface, &iface), "test-client");
	cr_end();
}

int main() {
	struct hostclock clock_monotonic = {
		.clock_id = CLOCK_MONOTONIC,
	};
	bootclock = LO_BOX(alarmclock, &clock_monotonic);

	coroutine_add("dhcp", dhcp_cr, NULL);
	coroutine_main();
	return 0;
}