Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test: etharp_gratuitous #1877

Merged
merged 10 commits into from
Aug 30, 2019
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions code/espurna/config/general.h
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,26 @@
#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
//
// Help solve compatibility issues with some routers.
// If the ARP table of the AP is old, and the AP doesn't send ARP request to update it's ARP table,
// this will lead to the STA sending IP packet fail. Thus we send gratuitous ARP periodically to let AP update it's ARP table.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strictly speaking, it is not the sending of an packet that fails, but receiving.
The other hosts in the network may no longer know how to retrieve what MAC address to use as destination when sending a packet to some IP-address. Also the other components on the network, like switch or AP may have the MAC address removed from their tables and thus no longer be able to route the packet to the correct port.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wording is weird, true... that was a blatant c/p from the esp-lwip Kconfig entry. Reworded to mention ARP tables.


#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
// -----------------------------------------------------------------------------
Expand Down
6 changes: 6 additions & 0 deletions code/espurna/config/prototypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,12 @@ using wifi_callback_f = std::function<void(justwifi_messages_t code, char * para
void wifiRegister(wifi_callback_f callback);
bool wifiConnected();

#if LWIP_VERSION_MAJOR == 1
#include <netif/etharp.h>
#else // LWIP_VERSION_MAJOR >= 2
#include <lwip/etharp.h>
#endif

// -----------------------------------------------------------------------------
// THERMOSTAT
// -----------------------------------------------------------------------------
Expand Down
61 changes: 61 additions & 0 deletions code/espurna/wifi.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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
// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -84,6 +89,14 @@ void _wifiConfigure() {
sleep_mode = constrain(sleep_mode, 0, 2);

WiFi.setSleepMode(static_cast<WiFiSleepType_t>(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) {
Expand Down Expand Up @@ -491,6 +504,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
&& (interface->flags & NETIF_FLAG_LINK_UP)
&& (!ip4_addr_isany_val(*netif_ip4_addr(interface)))
#else
&& (!ip_addr_isany(&interface->ip_addr))
#endif
&& (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
// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -685,4 +739,11 @@ void wifiLoop() {
_wifiCheckAP();
}

#if WIFI_GRATUITOUS_ARP_SUPPORT
// Only send out gra arp when in STA mode
if (_wifi_gratuitous_arp_interval && ((WiFi.getMode() & WIFI_AP) == 0)) {
_wifiSendGratuitousArp(_wifi_gratuitous_arp_interval);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With STA+AP mode the WiFi radio is always on, which makes it less likely ARP requests may be missed.
But it may still be useful to send it if you get a (new) IP address.
Just make sure to only send it to the STATION_IF if you want to distinguish this.

N.B. the AP and STA use different MAC addresses, so it should be no problem to send out Gratuitous ARP to the AP interface. (as long as you're sending the right IP of course :) )

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is copied from the idf, basically trying to emulate GARP flag that they check. No flag here, so STATION_IF seems like a solution.

}
#endif

}