diff --git a/components/lwip/CMakeLists.txt b/components/lwip/CMakeLists.txt index a8ce574e59..5108e5cca3 100644 --- a/components/lwip/CMakeLists.txt +++ b/components/lwip/CMakeLists.txt @@ -57,7 +57,6 @@ if(CONFIG_LWIP_ENABLE) "lwip/src/core/ipv4/ip4_napt.c" "lwip/src/core/ipv4/ip4_addr.c" "lwip/src/core/ipv4/ip4_frag.c" - "lwip/src/core/ipv4/acd.c" "lwip/src/core/ipv6/dhcp6.c" "lwip/src/core/ipv6/ethip6.c" "lwip/src/core/ipv6/icmp6.c" @@ -136,7 +135,13 @@ if(CONFIG_LWIP_ENABLE) "lwip/src/netif/ppp/vj.c") endif() - if(NOT ${target} STREQUAL "linux") + if(CONFIG_LWIP_DHCP_DOES_ARP_CHECK) + list(APPEND srcs "port/acd_dhcp_check.c") + elseif(CONFIG_LWIP_DHCP_DOES_ACD_CHECK) + list(APPEND srcs "lwip/src/core/ipv4/acd.c") + endif() + +if(NOT ${target} STREQUAL "linux") # Support for vfs and linker fragments only for target builds set(linker_fragments linker.lf) if(CONFIG_VFS_SUPPORT_IO) diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 4b1cbd1676..4519ac99bd 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -318,13 +318,29 @@ menu "LWIP" Set TCPIP task receive mail box size. Generally bigger value means higher throughput but more memory. The value should be bigger than UDP/TCP mail box size. - config LWIP_DHCP_DOES_ARP_CHECK - bool "DHCP: Perform ARP check on any offered address" - default y + choice LWIP_DHCP_CHECKS_OFFERED_ADDRESS + prompt "Choose how DHCP validates offered IP" + default LWIP_DHCP_DOES_ARP_CHECK depends on LWIP_IPV4 help - Enabling this option performs a check (via ARP request) if the offered IP address - is not already in use by another host on the network. + Choose the preferred way of DHCP client to check if the offered address + is available: + * Using Address Conflict Detection (ACD) module assures that the offered IP address + is properly probed and announced before binding in DHCP. This conforms to RFC5227, + but takes several seconds. + * Using ARP check, we only send two ARP requests to check for replies. This process + lasts 1 - 2 seconds. + * No conflict detection: We directly bind the offered address. + + config LWIP_DHCP_DOES_ARP_CHECK + bool "DHCP provides simple ARP check" + depends on !LWIP_AUTOIP + config LWIP_DHCP_DOES_ACD_CHECK + bool "DHCP provides Address Conflict Detection (ACD)" + config LWIP_DHCP_DOES_NOT_CHECK_OFFERED_IP + bool "DHCP does not detect conflict on the offered IP" + + endchoice config LWIP_DHCP_DISABLE_CLIENT_ID bool "DHCP: Disable Use of HW address as client identification" diff --git a/components/lwip/port/acd_dhcp_check.c b/components/lwip/port/acd_dhcp_check.c new file mode 100644 index 0000000000..e29ff984c5 --- /dev/null +++ b/components/lwip/port/acd_dhcp_check.c @@ -0,0 +1,138 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "lwip/opt.h" + +/* don't build if not configured for use in lwipopts.h */ +#if LWIP_IPV4 && DHCP_DOES_ARP_CHECK + +#include "lwip/acd.h" +#include "lwip/dhcp.h" +#include "lwip/prot/dhcp.h" +#include "lwip/timeouts.h" + +#define ACD_DHCP_ARP_REPLY_TIMEOUT_MS 500 + +static void +acd_dhcp_check_timeout_cb(void *arg); + +static void +acd_suspend(struct netif *netif) +{ + struct dhcp *dhcp; + struct acd *acd; + if (netif == NULL || (dhcp = netif_dhcp_data(netif)) == NULL) { + return; + } + acd = &dhcp->acd; + acd->state = ACD_STATE_OFF; + sys_untimeout(acd_dhcp_check_timeout_cb, (void *)netif); +} + +void +acd_remove(struct netif *netif, struct acd *acd) +{ + acd_suspend(netif); + acd->acd_conflict_callback = NULL; +} + +void +acd_netif_ip_addr_changed(struct netif *netif, const ip_addr_t *old_addr, + const ip_addr_t *new_addr) +{ + acd_suspend(netif); +} + +void +acd_network_changed_link_down(struct netif *netif) +{ + acd_suspend(netif); +} + +void +acd_arp_reply(struct netif *netif, struct etharp_hdr *hdr) +{ + struct dhcp *dhcp; + ip4_addr_t sipaddr; + struct eth_addr netifaddr; + struct acd *acd; + if (netif == NULL || (dhcp = netif_dhcp_data(netif)) == NULL) { + return; + } + acd = &dhcp->acd; + // Check that we're looking for ARP reply in ACD_PROBING state and DHCP_CHECKING state + if (hdr->opcode != PP_HTONS(ARP_REPLY) || dhcp->state != DHCP_STATE_CHECKING || + acd->state != ACD_STATE_PROBING || acd->acd_conflict_callback == NULL) { + return; + } + SMEMCPY(netifaddr.addr, netif->hwaddr, ETH_HWADDR_LEN); + IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr); + LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("acd_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n", + ip4_addr_get_u32(&sipaddr))); + /* did another host (not our mac addr) respond with the address we were offered by the DHCP server? */ + if (ip4_addr_eq(&sipaddr, &dhcp->offered_ip_addr) && + !eth_addr_eq(&netifaddr, &hdr->shwaddr)) { + LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("acd_arp_reply(): arp reply matched with offered address, declining\n")); + dhcp->acd.acd_conflict_callback(netif, ACD_DECLINE); + } +} + +err_t +acd_add(struct netif *netif, struct acd *acd, + acd_conflict_callback_t acd_conflict_callback) +{ + acd->acd_conflict_callback = acd_conflict_callback; + return ERR_OK; +} + +static void send_probe_once(struct netif *netif) +{ + struct dhcp *dhcp = netif_dhcp_data(netif); + if (etharp_query(netif, &dhcp->offered_ip_addr, NULL) != ERR_OK) { + LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("acd_send_probe_once(): could not perform ARP query\n")); + return; + } + if (dhcp->tries < 255) { + dhcp->tries++; + } + LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("acd_send_probe_once(): set request timeout %"U16_F" msecs\n", ACD_DHCP_ARP_REPLY_TIMEOUT_MS)); + sys_timeout(ACD_DHCP_ARP_REPLY_TIMEOUT_MS, acd_dhcp_check_timeout_cb, (void *)netif); +} + +static void +acd_dhcp_check_timeout_cb(void *arg) +{ + struct netif *netif = (struct netif *)arg; + struct dhcp *dhcp; + struct acd *acd; + if (netif == NULL || (dhcp = netif_dhcp_data(netif)) == NULL) { + return; + } + acd = &dhcp->acd; + if (acd->state != ACD_STATE_PROBING || acd->acd_conflict_callback == NULL || dhcp->state != DHCP_STATE_CHECKING) { + return; + } + LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("acd_dhcp_check_timeout_cb(): CHECKING, ARP request timed out\n")); + if (dhcp->tries <= 1) { + send_probe_once(netif); + } else { + // No conflict detected + dhcp->acd.acd_conflict_callback(netif, ACD_IP_OK); + } +} + +err_t +acd_start(struct netif *netif, struct acd *acd, ip4_addr_t ipaddr) +{ + if (netif == NULL || netif_dhcp_data(netif) == NULL) { + return ERR_ARG; + } + acd->state = ACD_STATE_PROBING; + send_probe_once(netif); + return ERR_OK; +} + +#endif /* LWIP_IPV4 && DHCP_DOES_ARP_CHECK */ diff --git a/components/lwip/port/include/lwipopts.h b/components/lwip/port/include/lwipopts.h index db455ce0f4..15e2c4267b 100644 --- a/components/lwip/port/include/lwipopts.h +++ b/components/lwip/port/include/lwipopts.h @@ -308,12 +308,23 @@ extern "C" { #define LWIP_DHCP 1 /** - * DHCP_DOES_ARP_CHECK==1: Do an ARP check on the offered address. + * LWIP_DHCP_CHECKS_OFFERED_ADDRESS: + * - Using Address Conflict Detection (ACD) module assures that the offered IP address + * is properly probed and announced before binding in DHCP. This conforms to RFC5227, + * but takes several seconds. + * - Using ARP check, we only send two ARP requests to check for replies. This process + * lasts 1 - 2 seconds. + * - No conflict detection: We directly bind the offered address. */ #ifdef CONFIG_LWIP_DHCP_DOES_ARP_CHECK #define DHCP_DOES_ARP_CHECK 1 +#define LWIP_DHCP_DOES_ACD_CHECK 1 +#elif CONFIG_LWIP_DHCP_DOES_ACD_CHECK +#define DHCP_DOES_ARP_CHECK 0 +#define LWIP_DHCP_DOES_ACD_CHECK 1 #else #define DHCP_DOES_ARP_CHECK 0 +#define LWIP_DHCP_DOES_ACD_CHECK 0 #endif /**