dhcp/test: Add dhcp option tests for platform hooks

Testing various cominations of extra dhcp options:
* MTU
* client-id
* VSI/VCI

Ref IDF-4817
This commit is contained in:
David Cermak
2022-03-25 21:28:51 +01:00
parent 0633e7d14b
commit 632ac61f95
5 changed files with 378 additions and 2 deletions
+1
View File
@@ -30,4 +30,5 @@ set(LWIP_TESTFILES
${LWIP_TESTDIR}/tcp/test_tcp_state.c
${LWIP_TESTDIR}/tcp/test_tcp.c
${LWIP_TESTDIR}/udp/test_udp.c
${LWIP_TESTDIR}/esp_platform_hooks.c
)
+2 -1
View File
@@ -49,5 +49,6 @@ TESTFILES=$(TESTDIR)/lwip_unittests.c \
$(TESTDIR)/tcp/test_tcp_oos.c \
$(TESTDIR)/tcp/test_tcp_state.c \
$(TESTDIR)/tcp/test_tcp.c \
$(TESTDIR)/udp/test_udp.c
$(TESTDIR)/udp/test_udp.c \
$(TESTDIR)/esp_platform_hooks.c
+177 -1
View File
@@ -3,10 +3,16 @@
#include "lwip/netif.h"
#include "lwip/dhcp.h"
#include "lwip/prot/dhcp.h"
#include "lwip/prot/iana.h"
#include "lwip/etharp.h"
#include "netif/ethernet.h"
static struct netif net_test;
static u8_t last_message_type = 0;
#if ESP_LWIP && LWIP_DHCP_ENABLE_VENDOR_SPEC_IDS
static const char vci_string[] = "test-vci";
#endif
static const u8_t broadcast[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
@@ -122,6 +128,7 @@ static enum tcase {
TEST_LWIP_DHCP_RELAY,
TEST_LWIP_DHCP_NAK_NO_ENDMARKER,
TEST_LWIP_DHCP_INVALID_OVERLOAD,
TEST_LWIP_DHCP_OPTS,
TEST_NONE
} tcase;
@@ -217,6 +224,32 @@ static void check_pkt(struct pbuf *p, u32_t pos, const u8_t *mem, u32_t len)
fail_if(memcmp(&data[pos], mem, len), "data at pos %d, len %d in packet %d did not match", pos, len, txpacket);
}
static u32_t get_opt(u8_t opt, struct pbuf *p, u8_t *mem, u32_t max_len)
{
const u32_t magic_cookie_pos = 278;
u8_t *options = (u8_t *)p->payload + magic_cookie_pos + 4;
u8_t *end = (u8_t *)p->payload + p->tot_len;
check_pkt(p, magic_cookie_pos, magic_cookie, sizeof(magic_cookie));
while (options < end) {
u8_t op = *options++;
u8_t len;
if (op == DHCP_OPTION_PAD) {
options++;
continue;
}
len = *options++;
if (op == opt && max_len >= len) {
memcpy(mem, options, len);
return len;
}
options += len;
}
return 0;
}
static void check_pkt_fuzzy(struct pbuf *p, u32_t startpos, const u8_t *mem, u32_t len)
{
int found;
@@ -423,6 +456,43 @@ static err_t lwip_tx_func(struct netif *netif, struct pbuf *p)
break;
}
break;
case TEST_LWIP_DHCP_OPTS:
switch (txpacket) {
case 1:
case 2:
case 7:
{
u8_t requested_opts[16];
u8_t len = get_opt(DHCP_OPTION_MESSAGE_TYPE, p, requested_opts, sizeof(requested_opts));
fail_unless(len > 0);
last_message_type = requested_opts[0];
len = get_opt(DHCP_OPTION_PARAMETER_REQUEST_LIST, p, requested_opts, sizeof(requested_opts));
#if ESP_LWIP
#if LWIP_DHCP_ENABLE_VENDOR_SPEC_IDS
/* Check the opt 43 is amond the requested items */
fail_unless(len > 0 && memchr(requested_opts, 43, len) != NULL);
/* Test the vendor class ID option is available */
len = get_opt(60, p, requested_opts, sizeof(requested_opts));
fail_unless(len > 0 && memcmp(requested_opts, vci_string, sizeof(vci_string)) == 0);
#else /* !LWIP_DHCP_ENABLE_VENDOR_SPEC_IDS */
fail_unless(memchr(requested_opts, 43, len) == NULL); /* VSI mustn't be in the reqested opts */
#endif /* LWIP_DHCP_ENABLE_VENDOR_SPEC_IDS */
#if LWIP_DHCP_ENABLE_CLIENT_ID
/* Test the client ID option is available */
len = get_opt(DHCP_OPTION_CLIENT_ID, p, requested_opts, sizeof(requested_opts));
fail_unless(len > 0 && requested_opts[0] == LWIP_IANA_HWTYPE_ETHERNET &&
memcmp(requested_opts + 1, netif->hwaddr, len - 1) == 0);
#endif /* LWIP_DHCP_ENABLE_CLIENT_ID */
#else /* ! ESP_LWIP */
/* vanilla lwip: at least subnet mask should be in the requested opt list */
fail_unless(len > 0 && memchr(requested_opts, DHCP_OPTION_SUBNET_MASK, len) != NULL);
#endif /* ESP_LWIP */
}
break;
default:
break;
}
break;
default:
break;
@@ -1046,6 +1116,111 @@ START_TEST(test_dhcp_invalid_overload)
}
END_TEST
START_TEST(test_options)
{
u8_t dhcp_with_opts[512];
ip4_addr_t addr;
ip4_addr_t netmask;
ip4_addr_t gw;
u32_t xid;
int i;
u8_t *optptr;
LWIP_UNUSED_ARG(_i);
tcase = TEST_LWIP_DHCP_OPTS;
setdebug(0);
IP4_ADDR(&addr, 0, 0, 0, 0);
IP4_ADDR(&netmask, 0, 0, 0, 0);
IP4_ADDR(&gw, 0, 0, 0, 0);
net_test.mtu = 1500;
netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
netif_set_link_up(&net_test);
netif_set_up(&net_test);
#if ESP_LWIP && LWIP_DHCP_ENABLE_VENDOR_SPEC_IDS
fail_unless(dhcp_set_vendor_class_identifier(sizeof(vci_string), vci_string) == ERR_OK);
#endif
dhcp_start(&net_test);
fail_unless(txpacket == 1); /* DHCP discover sent */
xid = htonl(netif_dhcp_data(&net_test)->xid);
memcpy(dhcp_with_opts, dhcp_offer, sizeof(dhcp_offer));
memcpy(&dhcp_with_opts[46], &xid, 4); /* insert correct transaction id */
optptr = &dhcp_with_opts[309]; /* point to the END marker of the original packet */
*optptr++ = DHCP_OPTION_MTU;
*optptr++ = 2;
*optptr++ = 0x02; /* MTU size is 512 */
*optptr++ = 0x00;
*optptr++ = 43; /* insert VSI info */
*optptr++ = 4;
*optptr++ = 0x01;
*optptr++ = 0x02;
*optptr++ = 0x03;
*optptr++ = 0x04;
*optptr++ = 0xFF; /* new End marker */
last_message_type = 0;
send_pkt(&net_test, dhcp_with_opts, sizeof(dhcp_with_opts));
/* MTU should not be updated in selection state */
fail_unless(net_test.mtu == 1500);
#if ESP_LWIP && LWIP_DHCP_ENABLE_VENDOR_SPEC_IDS
{
char vsi[4];
u32_t vsi_expect = 0x01020304UL;
fail_unless(dhcp_get_vendor_specific_information(sizeof(vsi), vsi) == ERR_OK);
fail_unless(memcmp(vsi, &vsi_expect, 4) == 0);
};
#endif
fail_unless(txpacket == 2, "TX %d packets, expected 2", txpacket); /* DHCP request sent */
fail_unless(last_message_type == DHCP_REQUEST);
memcpy(dhcp_with_opts, dhcp_ack, sizeof(dhcp_ack));
optptr = &dhcp_with_opts[309]; /* point to the END marker of the original packet */
*optptr++ = DHCP_OPTION_MTU;
*optptr++ = 2;
*optptr++ = 0x02; /* MTU size is 512 */
*optptr++ = 0x00;
*optptr++ = 0xFF; /* new End marker */
xid = htonl(netif_dhcp_data(&net_test)->xid); /* xid updated */
memcpy(&dhcp_with_opts[46], &xid, 4);
send_pkt(&net_test, dhcp_with_opts, sizeof(dhcp_with_opts));
#if ESP_LWIP && LWIP_DHCP_ENABLE_MTU_UPDATE
/* MTU should have been updated now */
fail_unless(net_test.mtu == 512);
#else
/* MTU option is ignored in vanilla lwip */
fail_unless(net_test.mtu == 1500);
#endif
last_message_type = 0;
/* wait after the rebinding period to expect:
* 4 ARP packets (pkt 3, 4, 5, 6)
* 1 DHCP request packet with renewal */
for (i = 0; i < 1200; i++) {
tick_lwip();
if (last_message_type == DHCP_REQUEST)
break;
}
fail_unless(txpacket == 7, "TX %d packets, expected 7", txpacket); /* DHCP renewal */
#if ESP_LWIP && LWIP_DHCP_ENABLE_VENDOR_SPEC_IDS
dhcp_free_vendor_class_identifier();
#endif
tcase = TEST_NONE;
dhcp_stop(&net_test);
dhcp_cleanup(&net_test);
netif_remove(&net_test);
}
END_TEST
/** Create the suite including all tests for this module */
Suite *
dhcp_suite(void)
@@ -1055,7 +1230,8 @@ dhcp_suite(void)
TESTFUNC(test_dhcp_nak),
TESTFUNC(test_dhcp_relayed),
TESTFUNC(test_dhcp_nak_no_endmarker),
TESTFUNC(test_dhcp_invalid_overload)
TESTFUNC(test_dhcp_invalid_overload),
TESTFUNC(test_options)
};
return create_suite("DHCP", tests, sizeof(tests)/sizeof(testfunc), dhcp_setup, dhcp_teardown);
}
+169
View File
@@ -0,0 +1,169 @@
#include "lwip/prot/dhcp.h"
#include "lwip/dhcp.h"
#include "lwip/netif.h"
#include "lwip/prot/iana.h"
#if ESP_LWIP
#include <string.h>
#if LWIP_DHCP_ENABLE_VENDOR_SPEC_IDS
#define DHCP_OPTION_VCI 60
#define DHCP_OPTION_VSI_MAX 16
static u8_t vendor_class_len = 0;
static char *vendor_class_buf = NULL;
static u32_t dhcp_option_vsi[DHCP_OPTION_VSI_MAX] = {0};
void dhcp_free_vendor_class_identifier(void)
{
mem_free(vendor_class_buf);
}
int dhcp_get_vendor_specific_information(uint8_t len, char * str)
{
u8_t copy_len = 0;
if (len == 0 || str == NULL) {
return ERR_ARG;
}
copy_len = LWIP_MIN(len, sizeof(dhcp_option_vsi));
memcpy(str, dhcp_option_vsi, copy_len);
return ERR_OK;
}
int dhcp_set_vendor_class_identifier(uint8_t len, const char * str)
{
if (len == 0 || str == NULL) {
return ERR_ARG;
}
if (vendor_class_buf && vendor_class_len != len) {
mem_free(vendor_class_buf);
vendor_class_buf = NULL;
}
if (!vendor_class_buf) {
vendor_class_buf = (char *)mem_malloc(len + 1);
if (vendor_class_buf == NULL) {
return ERR_MEM;
}
vendor_class_len = len;
}
memcpy(vendor_class_buf, str, len);
return ERR_OK;
}
#endif /* LWIP_DHCP_ENABLE_VENDOR_SPEC_IDS */
void dhcp_parse_extra_opts(struct dhcp *dhcp, uint8_t state, uint8_t option, uint8_t len, struct pbuf* p, uint16_t offset)
{
LWIP_UNUSED_ARG(dhcp);
LWIP_UNUSED_ARG(state);
LWIP_UNUSED_ARG(option);
LWIP_UNUSED_ARG(len);
LWIP_UNUSED_ARG(p);
LWIP_UNUSED_ARG(offset);
#if LWIP_DHCP_ENABLE_MTU_UPDATE
if ((option == DHCP_OPTION_MTU) &&
(state == DHCP_STATE_REBOOTING || state == DHCP_STATE_REBINDING ||
state == DHCP_STATE_RENEWING || state == DHCP_STATE_REQUESTING)) {
u32_t mtu = 0;
struct netif *netif;
LWIP_ERROR("dhcp_parse_extra_opts(): MTU option's len != 2", len == 2, return;);
LWIP_ERROR("dhcp_parse_extra_opts(): extracting MTU option failed",
pbuf_copy_partial(p, &mtu, 2, offset) == 2, return;);
mtu = lwip_htons((u16_t)mtu);
NETIF_FOREACH(netif) {
/* find the netif related to this dhcp */
if (dhcp == netif_dhcp_data(netif)) {
if (mtu < netif->mtu) {
netif->mtu = mtu;
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_parse_extra_opts(): Negotiated netif MTU is %d\n", netif->mtu));
}
return;
}
}
} /* DHCP_OPTION_MTU */
#endif /* LWIP_DHCP_ENABLE_MTU_UPDATE */
#if LWIP_DHCP_ENABLE_VENDOR_SPEC_IDS
if ((option == DHCP_OPTION_VSI) &&
(state == DHCP_STATE_REBOOTING || state == DHCP_STATE_REBINDING ||
state == DHCP_STATE_RENEWING || state == DHCP_STATE_REQUESTING || state == DHCP_STATE_SELECTING)) {
u8_t n;
u32_t value;
u16_t copy_len;
for (n = 0; n < DHCP_OPTION_VSI_MAX && len > 0; n++) {
copy_len = LWIP_MIN(len, 4);
LWIP_ERROR("dhcp_parse_extra_opts(): extracting VSI option failed",
pbuf_copy_partial(p, &value, copy_len, offset) == copy_len, return;);
dhcp_option_vsi[n] = lwip_htonl(value);
len -= copy_len;
}
} /* DHCP_OPTION_VSI */
#endif /* LWIP_DHCP_ENABLE_VENDOR_SPEC_IDS */
}
void dhcp_append_extra_opts(struct netif *netif, uint8_t state, struct dhcp_msg *msg_out, uint16_t *options_out_len)
{
LWIP_UNUSED_ARG(netif);
LWIP_UNUSED_ARG(state);
LWIP_UNUSED_ARG(msg_out);
LWIP_UNUSED_ARG(options_out_len);
#if LWIP_DHCP_ENABLE_CLIENT_ID
if (state == DHCP_STATE_RENEWING || state == DHCP_STATE_REBINDING ||
state == DHCP_STATE_REBOOTING || state == DHCP_STATE_OFF ||
state == DHCP_STATE_REQUESTING || state == DHCP_STATE_BACKING_OFF || state == DHCP_STATE_SELECTING) {
size_t i;
u8_t *options = msg_out->options + *options_out_len;
LWIP_ERROR("dhcp_append(client_id): options_out_len + 3 + netif->hwaddr_len <= DHCP_OPTIONS_LEN",
*options_out_len + 3U + netif->hwaddr_len <= DHCP_OPTIONS_LEN, return;);
*options_out_len = *options_out_len + netif->hwaddr_len + 3;
*options++ = DHCP_OPTION_CLIENT_ID;
*options++ = netif->hwaddr_len + 1; /* option size */
*options++ = LWIP_IANA_HWTYPE_ETHERNET;
for (i = 0; i < netif->hwaddr_len; i++) {
*options++ = netif->hwaddr[i];
}
}
#endif /* LWIP_DHCP_ENABLE_CLIENT_ID */
#if LWIP_DHCP_ENABLE_VENDOR_SPEC_IDS
if (state == DHCP_STATE_RENEWING || state == DHCP_STATE_REBINDING ||
state == DHCP_STATE_REBOOTING || state == DHCP_STATE_OFF ||
state == DHCP_STATE_REQUESTING || state == DHCP_STATE_BACKING_OFF || state == DHCP_STATE_SELECTING) {
size_t i;
const char *p = NULL;
u8_t len = 0;
if (vendor_class_buf && vendor_class_len) {
p = vendor_class_buf;
len = vendor_class_len;
} else {
#if LWIP_NETIF_HOSTNAME
if (netif->hostname != NULL && strlen(netif->hostname) < 0xff) {
p = netif->hostname;
len = (u8_t)namelen;
}
#endif /* LWIP_NETIF_HOSTNAME */
}
LWIP_ERROR("dhcp_append(vci): options_out_len + 3 + vci_size <= DHCP_OPTIONS_LEN",
*options_out_len + 3U + len <= DHCP_OPTIONS_LEN, return;);
if (p) {
u8_t *options = msg_out->options + *options_out_len;
*options_out_len = *options_out_len + len + 3;
*options++ = DHCP_OPTION_VCI;
*options++ = len;
for (i = 0; i < len; i ++) {
*options++ = p[i];
}
}
return;
}
#endif /* LWIP_DHCP_ENABLE_VENDOR_SPEC_IDS */
}
#endif
+29
View File
@@ -84,6 +84,13 @@
#define LWIP_MEM_ILLEGAL_FREE(msg) /* to nothing */
#ifdef ESP_LWIP
#define LWIP_DHCP_ENABLE_VENDOR_SPEC_IDS 1
#define LWIP_DHCP_ENABLE_CLIENT_ID 1
#define LWIP_DHCP_ENABLE_MTU_UPDATE 1
#if LWIP_DHCP_ENABLE_VENDOR_SPEC_IDS
#define DHCP_OPTION_VSI 43
#define LWIP_HOOK_DHCP_EXTRA_REQUEST_OPTIONS , DHCP_OPTION_VSI
#endif
/* Enable Espressif specific options */
/* DHCP options*/
@@ -108,6 +115,26 @@ static inline uint32_t timeout_from_offered(uint32_t lease, uint32_t min)
#define DHCP_CALC_TIMEOUT_FROM_OFFERED_T2_REBIND(dhcp) \
timeout_from_offered((dhcp)->offered_t2_rebind, ((dhcp)->t0_timeout/8)*7 /* 87.5% */ )
struct dhcp;
struct pbuf;
struct dhcp;
struct netif;
struct dhcp_msg;
void dhcp_parse_extra_opts(struct dhcp *dhcp, uint8_t state, uint8_t option, uint8_t len, struct pbuf* p, uint16_t offset);
void dhcp_append_extra_opts(struct netif *netif, uint8_t state, struct dhcp_msg *msg_out, uint16_t *options_out_len);
int dhcp_set_vendor_class_identifier(uint8_t len, const char * str);
int dhcp_get_vendor_specific_information(uint8_t len, char * str);
void dhcp_free_vendor_class_identifier(void);
#define LWIP_HOOK_DHCP_PARSE_OPTION(netif, dhcp, state, msg, msg_type, option, len, pbuf, offset) \
do { LWIP_UNUSED_ARG(msg); \
dhcp_parse_extra_opts(dhcp, state, option, len, pbuf, offset); \
} while(0)
#define LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, state, msg, msg_type, options_len_ptr) \
dhcp_append_extra_opts(netif, state, msg, options_len_ptr);
/* NAPT options */
#ifdef IP_NAPT
#define IP_NAPT_MAX 16
@@ -126,6 +153,8 @@ u32_t esp_random(void);
#endif /* ESP_TEST_DEBUG */
#else
#define ESP_LWIP 0
#define ESP_DHCP 0
#define ESP_DHCP_DISABLE_VENDOR_CLASS_IDENTIFIER 1
#endif /* ESP_LWIP */
#endif /* LWIP_HDR_LWIPOPTS_H */