lwip TCP ephemeral source port is not random.

owendelong
Posts: 6
Joined: Mon Jul 20, 2015 9:02 pm

lwip TCP ephemeral source port is not random.

Postby owendelong » Fri Jul 31, 2015 12:01 am

Originally, I posted this to the ESP8266/Arduino development here:
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.

ESP_Faye
Posts: 1646
Joined: Mon Oct 27, 2014 11:08 am

Re: lwip TCP ephemeral source port is not random.

Postby ESP_Faye » Fri Jul 31, 2015 7:15 pm

Hi,

In non-OS SDK, there is an API : espconn_port to return an available port.

Thanks for your interest in ESP8266 !

owendelong
Posts: 6
Joined: Mon Jul 20, 2015 9:02 pm

Re: lwip TCP ephemeral source port is not random.

Postby owendelong » Sat Aug 01, 2015 1:11 am

I think you are missing the point of my post.

In the SDK used by the ESP8266/Arduino port, the underlying LWIP library implements a function tcp_new_port as part of the tcp_client library portion of the LWIP library.

This library is provided as a binary library by Espressif rather than as open source.

As such, I have to count on you guys to fix it.

Currently that function works by sequential port allocation starting from a fixed value that is reset on every reset. Thus, the first tcp_connect() call from every reboot will always use source port 4096, the second will use 4097, etc.

This is not best practice and can lead to problems as I have already documented.

Yes, there are workarounds, but if you modify the code as I have suggested, the problem is eliminated without negative consequence.

This is a relatively simple library update to resolve a bug in the current implementation.

ESP_Faye
Posts: 1646
Joined: Mon Oct 27, 2014 11:08 am

Re: lwip TCP ephemeral source port is not random.

Postby ESP_Faye » Mon Aug 03, 2015 1:48 pm

Hi,

We updated tcp_new_port in esp_iot_rtos_sdk

Could it solve your problem ?

Who is online

Users browsing this forum: No registered users and 67 guests