mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
fix(lwip): Add unit test for dhcps opt parsing
This commit is contained in:
@@ -452,6 +452,14 @@ menu "LWIP"
|
||||
Enabling this option allows DHCP server to support temporary static ARP entries
|
||||
for DHCP Client. This will help the DHCP server to send the DHCP OFFER and DHCP ACK using IP unicast.
|
||||
|
||||
config LWIP_DHCPS_TEST_PARSE_OPTIONS
|
||||
bool "Expose parse_options() for unit testing"
|
||||
default n
|
||||
depends on LWIP_DHCPS
|
||||
help
|
||||
Enables a non-static wrapper around the internal parse_options() function
|
||||
so it can be called from test code. Only enable this for testing builds.
|
||||
|
||||
endmenu # DHCPS
|
||||
|
||||
menuconfig LWIP_AUTOIP
|
||||
|
||||
@@ -1726,4 +1726,18 @@ bool dhcps_get_hostname_on_mac(dhcps_t *dhcps, const u8_t *mac, char *out, size_
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_LWIP_DHCPS_TEST_PARSE_OPTIONS
|
||||
u8_t dhcps_test_parse_options(u8_t *optptr, s16_t len)
|
||||
{
|
||||
dhcps_t *dhcps = dhcps_new();
|
||||
if (dhcps == NULL) {
|
||||
return 0;
|
||||
}
|
||||
IP4_ADDR(&dhcps->client_address, 192, 168, 4, 2);
|
||||
u8_t result = parse_options(dhcps, optptr, len);
|
||||
mem_free(dhcps);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // ESP_DHCPS
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -484,6 +484,134 @@ TEST(lwip, sntp_client_time_2048)
|
||||
test_sntp_timestamps(2048, false); // NTP timestamp MSB is cleared for time after 2036
|
||||
}
|
||||
|
||||
/*
|
||||
* DHCP option parser tests
|
||||
*
|
||||
* These exercise the bounds-checking in parse_options() by constructing raw
|
||||
* option byte arrays and verifying correct behaviour for well-formed,
|
||||
* truncated, and malformed inputs.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_LWIP_DHCPS_TEST_PARSE_OPTIONS
|
||||
|
||||
extern u8_t dhcps_test_parse_options(u8_t *optptr, s16_t len);
|
||||
|
||||
#define TEST_OPT_PAD 0
|
||||
#define TEST_OPT_MSG_TYPE 53
|
||||
#define TEST_OPT_REQ_IPADDR 50
|
||||
#define TEST_OPT_END 255
|
||||
#define TEST_STATE_OFFER 1
|
||||
#define TEST_STATE_ACK 3
|
||||
#define TEST_STATE_IDLE 5
|
||||
|
||||
TEST(lwip, dhcps_parse_options_well_formed_discover)
|
||||
{
|
||||
u8_t opts[] = {
|
||||
TEST_OPT_MSG_TYPE, 1, 1, /* DHCPDISCOVER */
|
||||
TEST_OPT_END
|
||||
};
|
||||
u8_t state = dhcps_test_parse_options(opts, sizeof(opts));
|
||||
TEST_ASSERT_EQUAL_UINT8(TEST_STATE_OFFER, state);
|
||||
}
|
||||
|
||||
TEST(lwip, dhcps_parse_options_well_formed_request_ack)
|
||||
{
|
||||
u8_t opts[] = {
|
||||
TEST_OPT_MSG_TYPE, 1, 3, /* DHCPREQUEST */
|
||||
TEST_OPT_REQ_IPADDR, 4, 192, 168, 4, 2, /* matches client_address */
|
||||
TEST_OPT_END
|
||||
};
|
||||
u8_t state = dhcps_test_parse_options(opts, sizeof(opts));
|
||||
TEST_ASSERT_EQUAL_UINT8(TEST_STATE_ACK, state);
|
||||
}
|
||||
|
||||
TEST(lwip, dhcps_parse_options_pad_bytes_skipped)
|
||||
{
|
||||
u8_t opts[] = {
|
||||
TEST_OPT_PAD,
|
||||
TEST_OPT_PAD,
|
||||
TEST_OPT_PAD,
|
||||
TEST_OPT_MSG_TYPE, 1, 1, /* DHCPDISCOVER */
|
||||
TEST_OPT_END
|
||||
};
|
||||
u8_t state = dhcps_test_parse_options(opts, sizeof(opts));
|
||||
TEST_ASSERT_EQUAL_UINT8(TEST_STATE_OFFER, state);
|
||||
}
|
||||
|
||||
TEST(lwip, dhcps_parse_options_truncated_length_byte)
|
||||
{
|
||||
/* Option code at the last byte with no room for the length byte.
|
||||
* Before the fix this would read 1 byte OOB. */
|
||||
u8_t opts[] = { TEST_OPT_MSG_TYPE };
|
||||
u8_t state = dhcps_test_parse_options(opts, sizeof(opts));
|
||||
TEST_ASSERT_EQUAL_UINT8(TEST_STATE_IDLE, state);
|
||||
}
|
||||
|
||||
TEST(lwip, dhcps_parse_options_truncated_option_data)
|
||||
{
|
||||
/* MSG_TYPE says length=1 but the data byte is outside the buffer.
|
||||
* Before the fix this would read 1 byte OOB via optptr[2]. */
|
||||
u8_t opts[] = { TEST_OPT_MSG_TYPE, 1 };
|
||||
u8_t state = dhcps_test_parse_options(opts, sizeof(opts));
|
||||
TEST_ASSERT_EQUAL_UINT8(TEST_STATE_IDLE, state);
|
||||
}
|
||||
|
||||
TEST(lwip, dhcps_parse_options_oversized_length)
|
||||
{
|
||||
/* Option declares a length far exceeding the buffer. */
|
||||
u8_t opts[] = { TEST_OPT_MSG_TYPE, 200, 1, TEST_OPT_END };
|
||||
u8_t state = dhcps_test_parse_options(opts, (s16_t)sizeof(opts));
|
||||
TEST_ASSERT_EQUAL_UINT8(TEST_STATE_IDLE, state);
|
||||
}
|
||||
|
||||
TEST(lwip, dhcps_parse_options_no_end_marker)
|
||||
{
|
||||
/* Well-formed option but no END marker — parser must stop at buffer end. */
|
||||
u8_t opts[] = { TEST_OPT_MSG_TYPE, 1, 1 };
|
||||
u8_t state = dhcps_test_parse_options(opts, sizeof(opts));
|
||||
TEST_ASSERT_EQUAL_UINT8(TEST_STATE_OFFER, state);
|
||||
}
|
||||
|
||||
TEST(lwip, dhcps_parse_options_empty)
|
||||
{
|
||||
u8_t opts[] = { 0 };
|
||||
u8_t state = dhcps_test_parse_options(opts, 0);
|
||||
TEST_ASSERT_EQUAL_UINT8(TEST_STATE_IDLE, state);
|
||||
}
|
||||
|
||||
TEST(lwip, dhcps_parse_options_only_pads)
|
||||
{
|
||||
u8_t opts[] = { TEST_OPT_PAD, TEST_OPT_PAD, TEST_OPT_PAD };
|
||||
u8_t state = dhcps_test_parse_options(opts, sizeof(opts));
|
||||
TEST_ASSERT_EQUAL_UINT8(TEST_STATE_IDLE, state);
|
||||
}
|
||||
|
||||
TEST(lwip, dhcps_parse_options_unknown_option_skipped)
|
||||
{
|
||||
/* Unknown option 99 with length 3 should be skipped, then MSG_TYPE parsed. */
|
||||
u8_t opts[] = {
|
||||
99, 3, 0xAA, 0xBB, 0xCC,
|
||||
TEST_OPT_MSG_TYPE, 1, 1, /* DHCPDISCOVER */
|
||||
TEST_OPT_END
|
||||
};
|
||||
u8_t state = dhcps_test_parse_options(opts, sizeof(opts));
|
||||
TEST_ASSERT_EQUAL_UINT8(TEST_STATE_OFFER, state);
|
||||
}
|
||||
|
||||
TEST(lwip, dhcps_parse_options_req_ipaddr_truncated)
|
||||
{
|
||||
/* REQ_IPADDR claims 4 bytes but buffer only has room for 2. */
|
||||
u8_t opts[] = {
|
||||
TEST_OPT_MSG_TYPE, 1, 3,
|
||||
TEST_OPT_REQ_IPADDR, 4, 192, 168
|
||||
};
|
||||
u8_t state = dhcps_test_parse_options(opts, sizeof(opts));
|
||||
/* Parser should stop before the truncated REQ_IPADDR, return based on type alone. */
|
||||
TEST_ASSERT(state != 0);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_LWIP_DHCPS_TEST_PARSE_OPTIONS */
|
||||
|
||||
TEST_GROUP_RUNNER(lwip)
|
||||
{
|
||||
RUN_TEST_CASE(lwip, localhost_ping_test)
|
||||
@@ -493,6 +621,19 @@ TEST_GROUP_RUNNER(lwip)
|
||||
RUN_TEST_CASE(lwip, sntp_client_time_2015)
|
||||
RUN_TEST_CASE(lwip, sntp_client_time_2048)
|
||||
RUN_TEST_CASE(lwip, dhcp_arp_probe_self_mac_is_ok)
|
||||
#ifdef CONFIG_LWIP_DHCPS_TEST_PARSE_OPTIONS
|
||||
RUN_TEST_CASE(lwip, dhcps_parse_options_well_formed_discover)
|
||||
RUN_TEST_CASE(lwip, dhcps_parse_options_well_formed_request_ack)
|
||||
RUN_TEST_CASE(lwip, dhcps_parse_options_pad_bytes_skipped)
|
||||
RUN_TEST_CASE(lwip, dhcps_parse_options_truncated_length_byte)
|
||||
RUN_TEST_CASE(lwip, dhcps_parse_options_truncated_option_data)
|
||||
RUN_TEST_CASE(lwip, dhcps_parse_options_oversized_length)
|
||||
RUN_TEST_CASE(lwip, dhcps_parse_options_no_end_marker)
|
||||
RUN_TEST_CASE(lwip, dhcps_parse_options_empty)
|
||||
RUN_TEST_CASE(lwip, dhcps_parse_options_only_pads)
|
||||
RUN_TEST_CASE(lwip, dhcps_parse_options_unknown_option_skipped)
|
||||
RUN_TEST_CASE(lwip, dhcps_parse_options_req_ipaddr_truncated)
|
||||
#endif
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
|
||||
@@ -3,3 +3,6 @@ CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n
|
||||
|
||||
# Added to enable compilation of DHCP last IP restore feature
|
||||
CONFIG_LWIP_DHCP_RESTORE_LAST_IP=y
|
||||
|
||||
# Expose DHCP server option parser for unit testing
|
||||
CONFIG_LWIP_DHCPS_TEST_PARSE_OPTIONS=y
|
||||
|
||||
Reference in New Issue
Block a user