Bug 17748 - New sockets formed on a listening socket do not inherit properties
Summary: New sockets formed on a listening socket do not inherit properties
Keywords:
Status: CLOSED NOTABUG
Alias: None
Product: Red Hat Linux
Classification: Retired
Component: kernel
Version: 6.2
Hardware: i586
OS: Linux
low
low
Target Milestone: ---
Assignee: Michael K. Johnson
QA Contact:
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2000-09-20 16:41 UTC by Tim Boura
Modified: 2008-05-01 15:37 UTC (History)
0 users

Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Clone Of:
Environment:
Last Closed: 2002-12-15 01:55:18 UTC
Embargoed:


Attachments (Terms of Use)

Description Tim Boura 2000-09-20 16:41:53 UTC
I hope this is the correct place to report this....

I have been porting some programs to Linux and came across some problems 
with the networking layer, investigation discovered that the cause of this 
was recv blocking on a socket that should be non-blocking.

More investigation showed that while the listening socket was non-blocking 
sockets formed from connections on that listening socket were _not_ non-
blocking. Changing the connection accepting code to set the socket back to 
non-blocking fixed the problems.

The TCP/IP documentation we have clearly states that the new socket should 
inherit this property from the listening socket that is forming it and 
this is what does happen on both the PC and Acorn.

Comment 1 Jeff Johnson 2000-10-06 21:03:46 UTC
Can you provide a simple test case for consideration? Thanks ...

Comment 2 Tim Boura 2000-10-09 08:56:43 UTC
The following function is used to open a socket, which is then turned into a 
listening socket.

/******************************************************************************
*  Function     : tcpip_open_tcpsocket                                          
*
*                                                                              *
*  Description  : Create tcp (stream) socket using specific port.               
*
*                 Uses real port nums ie 1024 and 0 for any.                  *
*                                                                              *
*  Entry        :                                                               
*
*    ptrSocketHandle - Where the socket handle will be returned.               *
*    iPortNum - The port number to be used (in network byte order).            *
*                                                                              *
*  Exit         :                                                               
*
*    NO_ERROR - If sucessful.                                                   
*
*    Error code if not.                                                         
*
*                                                                              *
******************************************************************************/
int tcpip_open_tcpsocket( int *ptrSocketHandle, int iPortNum )
{                        
#if (defined PC) || (defined VX_WORKS)
   sockad            sockadHostAddr;
#endif
   sockad            sockadSocketAddr;
   int               iSocketHandle;
   int               iOptionValue;
#if TCPIP_SOCKET_LIST
   SocketListStruct  *ptrNewTCPSocket = NULL;
#endif
   
   /* Check to make sure that passed parameters are okay. */
   if( ptrSocketHandle == NULL )
      ERR_QUIT( ERR_LVL_WARNING, "tcpip_open_tcpsocket: Invalid socket handle 
address pointer.", ERR_NULL_POINTER );
   
   /* Create a new socket.
     AF_INET is currently only valid address format, type TCP, and with no 
     specific protocol. */
   if( ( iSocketHandle = socket( AF_INET, SOCK_STREAM, 0 )) == INVALID_SOCKET )
      ERR_QUIT_1( ERR_LVL_SERIOUS, "tcpip_open_tcpsocket: Could not form socket 
(%s).", TCPIP_ERROR(), tcpip_get_error_description(TCPIP_ERROR()) );
   
   /* Do not set option so new socket can have same addr/port as previous 
sockets. */
   
   /* Bind port and allow rx from anybody. */
   
   /* Get an initialised but blank sockad. */
   if( tcpip_make_stn( &sockadSocketAddr, "", "" ) == NULL )
      ERR_QUIT( ERR_LVL_WARNING, "tcpip_open_tcpsocket: Could not form host 
address.", ERR_TCPIP_ADDR );
   
#ifdef PC
   /* Get the host address. We want to bind the socket to the right address - 
     especially if there is more than one. Thsi function will either return 
     the user's explicit choice, or the first on the list. */
   tcpip_host_addr( &sockadHostAddr );
   
   /* Set up the socket address, with the chosen ip address, and the specified 
port. */
   sockadSocketAddr.sin_addr = sockadHostAddr.sin_addr;
#elif defined VX_WORKS
   sockadSocketAddr.sin_addr.s_addr = INADDR_ANY;
#endif
   sockadSocketAddr.sin_port = (unsigned short) iPortNum;
   
   /* Bind the port to the desired sockad. */
   if( bind( iSocketHandle, (struct sockaddr *) &sockadSocketAddr, sizeof
(sockadSocketAddr) ) == SOCKET_ERROR )
   {
      int iError = TCPIP_ERROR();
      tcpip_socket_closedown( &iSocketHandle, TCPIP_ABORT_ALL ); /* Close 
socket before quitting. */
      if (iError != ERR_ADDRINUSE)
         ERR_QUIT_1( ERR_LVL_SERIOUS, "tcpip_open_tcpsocket: Could not bind 
socket (%s).", iError, tcpip_get_error_description(iError) );
      else
         return ERR_TCPIP_PORT_UNAVAILABLE;
   }
   
   /* Set socket i/o options concerning NON_BLOCKING. */
#ifdef TCPIP_NON_BLOCKING
   iOptionValue = 1;
#ifdef VX_WORKS
   if( socketioctl( iSocketHandle, FIONBIO, (int) &iOptionValue ) == 
SOCKET_ERROR )
#elif defined UNIX
   if (fcntl( iSocketHandle, F_SETFL, fcntl(iSocketHandle, F_GETFL) | 
O_NONBLOCK) < 0)
#else
   if( socketioctl( iSocketHandle, FIONBIO, (ULONG *) &iOptionValue ) == 
SOCKET_ERROR )
#endif
   { 
      int iError = TCPIP_ERROR();
      tcpip_socket_closedown( &iSocketHandle, TCPIP_ABORT_ALL ); /* Close 
socket before quitting. */
      ERR_QUIT_1( ERR_LVL_SERIOUS, "tcpip_open_tcpsocket: Could not set 
NON_BLOCKING mode (%s).", iError, tcpip_get_error_description(iError) );
   }                                
#endif

            
      
   /* Set socket i/o options concerning asynchronise data transfers.
     Enable socket events. */
#ifdef TCPIP_ASYNC
   iOptionValue = 1;
   if( socketioctl( iSocketHandle, FIOASYNC, &iOptionValue ) == SOCKET_ERROR )
   {
      int iError = TCPIP_ERROR();
      tcpip_socket_closedown( &iSocketHandle, TCPIP_ABORT_ALL ); /* Close 
socket before quitting. */
      ERR_QUIT_1( ERR_LVL_SERIOUS, "tcpip_open_tcpsocket: Could not set 
asynchronise mode (%s).", iError, tcpip_get_error_description(iError) );
   }
#endif
   
   /* Forces tcp to send small packets without collecting them together. */
#ifdef TCPIP_NODELAY                     
   iOptionValue = 1;
   if( setsockopt( iSocketHandle, IPPROTO_TCP, TCP_NODELAY, (char*) 
&iOptionValue, sizeof(iOptionValue) ) == SOCKET_ERROR )
   { 
      int iError = TCPIP_ERROR();
      tcpip_socket_closedown( &iSocketHandle, TCPIP_ABORT_ALL ); /* Close 
socket before quitting. */
      ERR_QUIT_1( ERR_LVL_SERIOUS, "tcpip_open_tcpsocket: Could not set 
asynchronise mode (%s).", iError, tcpip_get_error_description(iError) );
   }      
#endif
   
   /* Uses KEEP_ALIVE socket option. */
#ifdef TCPIP_KEEP_ALIVE                     
   iOptionValue = TRUE;
   if( setsockopt( iSocketHandle, SOL_SOCKET, SO_KEEPALIVE, (char*) 
&iOptionValue, sizeof(iOptionValue) ) == SOCKET_ERROR )
   { 
      int iError = TCPIP_ERROR();
      tcpip_socket_closedown( &iSocketHandle, TCPIP_ABORT_ALL ); /* Close 
socket before quitting. */
      ERR_QUIT_1( ERR_LVL_SERIOUS, "tcpip_open_tcpsocket: Could not set 
KEEP_ALIVE socket option (%s).", iError, tcpip_get_error_description(iError) );
   }      
#endif
   
   /* Return handle to user. */
   *ptrSocketHandle = iSocketHandle;
   
#if TCPIP_SOCKET_LIST
   /* If all has gone well, then form a new socket list. */
   ptrNewTCPSocket = heap_malloc( ptrOpenSocketsHeap );
   
   /* Fill the socket list structure. */
   tcpip_socket_type(ptrNewTCPSocket)     = TCPIP_TCP;
   tcpip_socket_handle(ptrNewTCPSocket)   = iSocketHandle;
   time( tcpip_socket_time(ptrNewTCPSocket) );
   
   /* Add new the socket list to the "currently open sockets" linked list. */
   list_addtail( &OpenSocketsList, tcpip_socket_list(ptrNewTCPSocket) );
#endif
   
   return NO_ERROR;
} /* End of tcpip_open_tcpsocket. */


When a connection is recieved on the listening socket it is processed by the 
following function:

/*********************************************************************
*  Function     : tcpip_poll_listen                            *
*                                                    *
*  Description  : Poll listening tcp socket for pending connections. *
*                                                                    *
*  Entry        :                                        *
*    iSocketHandle - Handle supplied by successful tcpip_open..       *
*    ptrNewSocketHandle - Where handle to any new connection will be *
*                   stored.                            *
*                    Will contain the handle to the new socket. *
*                     If NO_ERROR but blocking occurred, then    *
*                   INVALID_SOCKET will be returned.          *
*    ptrSourceAddr - Where the address of the sending machine will    *
*               be stored.                               *
*                If NULL then use udp, otherwise tcp.          *
*                                                                    *
*  Exit         :                                         *
*    NO_ERROR - If sucessful.                               *
*    Error code if not.                                           *
*                                                                    *
*********************************************************************/
int tcpip_poll_listen( int iSocketHandle, int *ptrNewSocketHandle, sockad 
*ptrSourceAddr )
{
#ifdef TCPIP_NON_BLOCKING
   int iOptionValue;
#endif
   int iAddrLen = sizeof(sockad);

#if TCPIP_SOCKET_LIST
   SocketListStruct  *ptrNewTCPSocket = NULL;
#endif
   
   
   *ptrNewSocketHandle = INVALID_SOCKET; /* Set handle to INVALID_SOCKET in 
case of error. */
   
   /* Check to make sure that passed parameters are okay. */
   if( ptrSourceAddr == NULL )
      ERR_QUIT( ERR_LVL_WARNING, "tcpip_poll_listen: Invalid source address 
pointer.", ERR_NULL_POINTER );
   
   /* Accept any pending connections. */
   if( ( *ptrNewSocketHandle = accept( iSocketHandle, (struct sockaddr *) 
ptrSourceAddr, &iAddrLen ) ) == INVALID_SOCKET )
   {
      int iError = TCPIP_ERROR();
      
      /* If a blocking error, then ignore. */
      if( iError != ERR_WOULDBLOCK )
         ERR_QUIT_1( ERR_LVL_SERIOUS, "tcpip_poll_listen: Could not form new 
socket (%s).", TCPIP_ERROR(), tcpip_get_error_description(TCPIP_ERROR()) );
   }
   
   /* Add the new socket to the socket list. */
#if TCPIP_SOCKET_LIST
   /* If all has gone well, then form a new socket list. */
   ptrNewTCPSocket = heap_malloc( ptrOpenSocketsHeap );
   
   /* Fill the socket list structure. */
   tcpip_socket_type(ptrNewTCPSocket)     = TCPIP_UDP;
   tcpip_socket_handle(ptrNewTCPSocket)   = iSocketHandle;
   time( tcpip_socket_time(ptrNewTCPSocket) );
   
   /* Add new the socket list to the "currently open sockets" linked list. */
   list_addtail( &OpenSocketsList, tcpip_socket_list(ptrNewTCPSocket) );
#endif

/**************This code should not be required*****************/   
   /* Set socket i/o options concerning NON_BLOCKING. */
#ifdef TCPIP_NON_BLOCKING
   iOptionValue = 1;
#ifdef VX_WORKS
   if( socketioctl( iSocketHandle, FIONBIO, (int) &iOptionValue ) == 
SOCKET_ERROR )
#elif defined UNIX
   if (fcntl( *ptrNewSocketHandle, F_SETFL, fcntl(*ptrNewSocketHandle, F_GETFL) 
| O_NONBLOCK) < 0)
#else
   if( socketioctl( iSocketHandle, FIONBIO, (ULONG *) &iOptionValue ) == 
SOCKET_ERROR )
#endif
/**************End code that should not be needed*****************/   
   { 
      int iError = TCPIP_ERROR();
      tcpip_socket_closedown( ptrNewSocketHandle, TCPIP_ABORT_ALL ); /* Close 
socket before quitting. */
      ERR_QUIT_1( ERR_LVL_SERIOUS, "tcpip_poll_listen: Could not set 
NON_BLOCKING mode (%s).", iError, tcpip_get_error_description(iError) );
   }                                
#endif

      
   return NO_ERROR;
} /* End of tcpip_poll_listen. */

This function, as you can see, has to set the non-blocking option for the new 
socket - however the TCP/IP documentation says that the new socket should 
inherit all such properties from the listening socket that forms it.

As a precaution the non-blocking option is now set for all platforms, however 
it took quite a while to track down the problem and could well trip other 
people up as well.

Thanks,
   Tim


Comment 3 Jeff Johnson 2000-12-31 15:35:07 UTC
Here 's what the kernel folks have to say:

   From: Alan Cox <alan>
   Date: Sat, 30 Dec 2000 20:28:28 -0500 (EST)

   > The TCP/IP documentation we have clearly states that the new
   > socket should inherit this property from the listening socket
   > that is forming it and this is what does happen on both the PC
   > and Acorn.

   I can't find that in my copy of the POSIX 1003.1g draft. I can't
   find it in my copy of the XNS docs. I've never seen a standards
   body cite for it. If he has one then he may well be right but
   that'll break some of our userspace

Also, this gets the usual standard disclaimer:

        Even if I were to change this behavior to what you expect
        today, you would be ill advised to assume it in your
        applications since this would make your application not
        work with every other existing version of the Linus kernel.

This behavior isn't changing till at least 2.5.x for this and
other reasons (RedHat's inetd is the main one, it breaks when
accept'd sockets behave as this bugzilla reporter expects it
to :-(, it basically assumes Linux non-block propagation to accept()
socket file-descriptors)

Later,
David S. Miller
davem

Changing component to kernel ...


Note You need to log in before you can comment on or make changes to this bug.