lwip-users
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[lwip-users] Dead PCB-problem when IP-address changes


From: Anders Carlman
Subject: [lwip-users] Dead PCB-problem when IP-address changes
Date: Fri, 28 Mar 2003 12:08:00 +0100

I recently discovered an unpleasant pitfall when i made a small test program 
with DHCP-client-functionality. However, although the problems can arise when 
using DHCP, I believe the solution should be applied on a "lower level", since 
the problems can occur also when using (and changing) a static IP-address...
Here's what happened:
1. My test program first initialized the netif with a default, static IP-address
2. Then the DHCP was started.
3. Immidiately after that a connection was established and some data was 
received (using blocking sockets)...

What then happened when the program was started was that in the middle of 
receiving data, the program suddenly froze, and nothing more happened. After 
some digging I found the reason: The binding of the dhcp-supplied address 
wasn't complete until a while after the connection had already been 
established. So the connection was made using the "old" static IP-address and 
when the local address suddenly changed, the data to the "old" address was 
discarded, which explained the freezing.
The problem here is not that the connection itself is destroyed, of course you 
can't expect a TCP connection to "survive" an address change. However, the way 
active connections (pcb:s) are handled at address changes, i.e. not handled at 
all, is a problem. In some cases tcp calls to a "dead" pcb will simply return 
with an error, which is OK. But in other cases, e.g. blocking accept and 
receive calls, the application will enter a "waiting forever" deadlock, which 
is quite bad... In the case of my test program the problem could be avoided by 
wating until the dhcp state was "bound", before doing the connection. However 
leases can expire and renewals can occur at any time, and I'm sure there could 
be other scenarios too, when the IP-address is changed in an asynchronous 
fashion (for example by a manual call to netif_set_ipaddr())...

I think the following is a good solution:
In the netif_set_ipaddr()-function some code can be added that aborts active 
pcbs that belongs to the IP address of the netif, before the address is 
actually changed. The abort should be made by calling tcp_abort()-for all 
related pcbs. tcp_abort() will not only remove the pcbs, but it will also call 
the error-callback functions, telling the "listeners" that the connection has 
been aborted. In the socket case, this will "unlock" all blocking calls and 
make them return with an error, thus preventing deadlock situations, like the 
one that happened to me. When it comes to listening PCB:s, I believe they don't 
have to be aborted, instead the listening address of related PCB:s is simply 
changed to the "new" address. Look at my alternative implementation of 
netif_set_ipaddr():

/*-----------------------------------------------------------------------------------*/
void
netif_set_ipaddr(struct netif *netif, struct ip_addr *ipaddr)
{
  // Handling of obsolete pcbs
  struct tcp_pcb *pcb;
  struct tcp_pcb_listen *lpcb;

  pcb=tcp_active_pcbs;
  while(pcb != NULL)
  {
        if(ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr)))
        {
            // The PCB is connected using the old ipaddr and must be aborted
            struct tcp_pcb *next=pcb->next;
            tcp_abort(pcb);
            pcb=next;
        }
        else
            pcb=pcb->next;
  }

  for(lpcb = tcp_listen_pcbs; lpcb != NULL; lpcb = lpcb->next)
  {
     if(ip_addr_cmp(&(lpcb->local_ip), &(netif->ip_addr)))
     {
          // The PCB is listening to the old ipaddr and is set to listen to the 
new one instead
          ip_addr_set(&(lpcb->local_ip), ipaddr);
     }
  }

  ip_addr_set(&(netif->ip_addr), ipaddr);
  DEBUGF(NETIF_DEBUG, ("netif: setting IP address of interface %c%c to 
%d.%d.%d.%d\n",
         netif->name[0], netif->name[1],
         (u8_t)(ntohl(ipaddr->addr) >> 24 & 0xff),
         (u8_t)(ntohl(ipaddr->addr) >> 16 & 0xff),
         (u8_t)(ntohl(ipaddr->addr) >> 8 & 0xff),
         (u8_t)(ntohl(ipaddr->addr) & 0xff)));
}

---------------
Regards
Anders Carlman




reply via email to

[Prev in Thread] Current Thread [Next in Thread]