https://github.com/esp8266/Arduino/issues/632
However, once I started actually looking at code, I found that the problem is inside the LWIP library, speciffically here:
Code: Select all
/**
* Allocate a new local TCP port.
*
* @return a new (free) local TCP port number
*/
static u16_t
tcp_new_port(void)
{
u8_t i;
u16_t n = 0;
struct tcp_pcb *pcb;
again:
if (tcp_port++ == TCP_LOCAL_PORT_RANGE_END) {
tcp_port = TCP_LOCAL_PORT_RANGE_START;
}
/* Check all PCB lists. */
for (i = 0; i < NUM_TCP_PCB_LISTS; i++) {
for(pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) {
if (pcb->local_port == tcp_port) {
if (++n > (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START)) {
return 0;
}
goto again;
}
}
}
return tcp_port;
}
I suggest modifying as follows:
replace:
Code: Select all
again:
if (tcp_port++ == TCP_LOCAL_PORT_RANGE_END) {
tcp_port = TCP_LOCAL_PORT_RANGE_START;
}
with
Code: Select all
tcp_port = random_port_number();
again:
if (tcp_port++ == TCP_LOCAL_PORT_RANGE_END) {
tcp_port = TCP_LOCAL_PORT_RANGE_START;
}
And create a function like
Code: Select all
uint16_t random_port_number()
{
static bool seeded;
if (! seeded)
{
randomSeed(analogReed(A0));
seeded=true;
}
return(random(TCP_LOCAL_PORT_RANGE_START, TCP_LOCAL_PORT_RANGE_END));
}
In this way, the starting point for the sequential port search is randomized so that different source ports will get assigned across reboots.
This will prevent deadly embraces that can occur when the source port is consistent across reboots and the server is holding an old connection one of the wait states related to the TCP termination process (previous transaction did not complete cleanly and ESP8266 reboots).
I ran into this with an application that posts sensor data to a web site every 60 seconds. The application collects the data, connects to the WiFi network, connects to the server, uses a query string to submit the data, and then sleeps (Deep Sleep) until the next sampling period.
Sometimes, the system would fail to finish the connection close process cleanly and thereafter, it would continuously fail to connect to the web server because the SYN packet being sent to open the connection matched an existing connection in a closing wait state and so the server would retry closing the connection (and reset the timers). Thus, the old (dead) session would never die on the server side and the new session could never be created by the client. Randomizing the ephemeral source port will prevent this from occurring and is BCP for TCP libraries in general.
This should be a relatively simple change to the library. Since I don't have the Espressif source code, I cannot test this mechanism in place, but I have tested similar code manipulating the tcp_port starting point from an external library by changing the scb and it is operational and has resolved my issue.