From 35ce68710cc51fae2e185a6d5edff31a6d035002 Mon Sep 17 00:00:00 2001 From: Max Prokhorov Date: Fri, 30 Aug 2019 10:58:05 +0300 Subject: [PATCH] Test: etharp_gratuitous (#1877) * test: etharp_gratuitous * proper checks before calling etharp_gratuitous, move includes * disable at runtime * ms values * reload * debug * reword * filter by ifnum instead of checking for AP mode * drop station_if check 2.3.0/lwip1 builds netif->num increments on for each sta or ap lwip2 keeps those constant, but that seems like a implementation detail might break in the future anyways... --- code/espurna/config/general.h | 19 ++++++++++ code/espurna/config/prototypes.h | 6 ++++ code/espurna/wifi.ino | 61 ++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h index 93df61b4..fa6cd3ad 100644 --- a/code/espurna/config/general.h +++ b/code/espurna/config/general.h @@ -554,6 +554,25 @@ #define WIFI_PROPAGATION_CONST 4 // This is typically something between 2.7 to 4.3 (free space is 2) #endif +// ref: https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/kconfig.html#config-lwip-esp-gratuitous-arp +// ref: https://github.com/xoseperez/espurna/pull/1877#issuecomment-525612546 +// +// Broadcast gratuitous ARP periodically to update ARP tables on the AP and all devices on the same network. +// Helps to solve compatibility issues when ESP fails to timely reply to ARP requests, causing the device's ARP table entry to expire. + +#ifndef WIFI_GRATUITOUS_ARP_SUPPORT +#define WIFI_GRATUITOUS_ARP_SUPPORT 1 +#endif + +// Interval is randomized on each boot in range from ..._MIN to ..._MAX (ms) +#ifndef WIFI_GRATUITOUS_ARP_INTERVAL_MIN +#define WIFI_GRATUITOUS_ARP_INTERVAL_MIN 15000 +#endif + +#ifndef WIFI_GRATUITOUS_ARP_INTERVAL_MAX +#define WIFI_GRATUITOUS_ARP_INTERVAL_MAX 30000 +#endif + // ----------------------------------------------------------------------------- // WEB // ----------------------------------------------------------------------------- diff --git a/code/espurna/config/prototypes.h b/code/espurna/config/prototypes.h index 50661a36..22ee654e 100644 --- a/code/espurna/config/prototypes.h +++ b/code/espurna/config/prototypes.h @@ -367,6 +367,12 @@ using wifi_callback_f = std::function +#else // LWIP_VERSION_MAJOR >= 2 +#include +#endif + // ----------------------------------------------------------------------------- // THERMOSTAT // ----------------------------------------------------------------------------- diff --git a/code/espurna/wifi.ino b/code/espurna/wifi.ino index 515063e8..02e11d74 100644 --- a/code/espurna/wifi.ino +++ b/code/espurna/wifi.ino @@ -15,6 +15,11 @@ bool _wifi_smartconfig_running = false; bool _wifi_smartconfig_initial = false; uint8_t _wifi_ap_mode = WIFI_AP_FALLBACK; +#if WIFI_GRATUITOUS_ARP_SUPPORT +unsigned long _wifi_gratuitous_arp_interval = 0; +unsigned long _wifi_gratuitous_arp_last = 0; +#endif + // ----------------------------------------------------------------------------- // PRIVATE // ----------------------------------------------------------------------------- @@ -91,6 +96,14 @@ void _wifiConfigure() { sleep_mode = constrain(sleep_mode, 0, 2); WiFi.setSleepMode(static_cast(sleep_mode)); + + #if WIFI_GRATUITOUS_ARP_SUPPORT + _wifi_gratuitous_arp_last = millis(); + _wifi_gratuitous_arp_interval = getSetting("wifiGarpIntvl", secureRandom( + WIFI_GRATUITOUS_ARP_INTERVAL_MIN, WIFI_GRATUITOUS_ARP_INTERVAL_MAX + )).toInt(); + #endif + } void _wifiScan(uint32_t client_id = 0) { @@ -504,6 +517,47 @@ void _wifiWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& #endif +// ----------------------------------------------------------------------------- +// SUPPORT +// ----------------------------------------------------------------------------- + +#if WIFI_GRATUITOUS_ARP_SUPPORT + +// ref: lwip src/core/netif.c netif_issue_reports(...) +// ref: esp-lwip/core/ipv4/etharp.c garp_tmr() +// TODO: only for ipv4, need (?) a different method with ipv6 +bool _wifiSendGratuitousArp() { + + bool result = false; + for (netif* interface = netif_list; interface != nullptr; interface = interface->next) { + if ( + (interface->flags & NETIF_FLAG_ETHARP) + && (interface->hwaddr_len == ETHARP_HWADDR_LEN) + #if LWIP_VERSION_MAJOR == 1 + && (!ip_addr_isany(&interface->ip_addr)) + #else + && (!ip4_addr_isany_val(*netif_ip4_addr(interface))) + #endif + && (interface->flags & NETIF_FLAG_LINK_UP) + && (interface->flags & NETIF_FLAG_UP) + ) { + etharp_gratuitous(interface); + result = true; + } + } + + return result; +} + +void _wifiSendGratuitousArp(unsigned long interval) { + if (millis() - _wifi_gratuitous_arp_last > interval) { + _wifi_gratuitous_arp_last = millis(); + _wifiSendGratuitousArp(); + } +} + +#endif // WIFI_GRATUITOUS_ARP_SUPPORT + // ----------------------------------------------------------------------------- // INFO // ----------------------------------------------------------------------------- @@ -730,4 +784,11 @@ void wifiLoop() { _wifiCheckAP(); } + #if WIFI_GRATUITOUS_ARP_SUPPORT + // Only send out gra arp when in STA mode + if (_wifi_gratuitous_arp_interval) { + _wifiSendGratuitousArp(_wifi_gratuitous_arp_interval); + } + #endif + }