diff --git a/test/unit/Filelists.cmake b/test/unit/Filelists.cmake index 25cc3c6b8..e10360d00 100644 --- a/test/unit/Filelists.cmake +++ b/test/unit/Filelists.cmake @@ -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 ) diff --git a/test/unit/Filelists.mk b/test/unit/Filelists.mk index 327625594..a3a9d8619 100644 --- a/test/unit/Filelists.mk +++ b/test/unit/Filelists.mk @@ -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 diff --git a/test/unit/dhcp/test_dhcp.c b/test/unit/dhcp/test_dhcp.c index e1cedb1b7..4fc285d5d 100644 --- a/test/unit/dhcp/test_dhcp.c +++ b/test/unit/dhcp/test_dhcp.c @@ -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); } diff --git a/test/unit/esp_platform_hooks.c b/test/unit/esp_platform_hooks.c new file mode 100644 index 000000000..3f38bb24a --- /dev/null +++ b/test/unit/esp_platform_hooks.c @@ -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 + +#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 diff --git a/test/unit/lwipopts.h b/test/unit/lwipopts.h index d910fbd52..36adf955b 100644 --- a/test/unit/lwipopts.h +++ b/test/unit/lwipopts.h @@ -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 */