Example #1
0
void tcpStateLastAck(Socket *socket, TcpHeader *segment, size_t length)
{
   //Debug message
   TRACE_DEBUG("TCP FSM: LAST-ACK state\r\n");

   //First check sequence number
   if(tcpCheckSequenceNumber(socket, segment, length))
      return;

   //Check the RST bit
   if(segment->flags & TCP_FLAG_RST)
   {
      //Enter CLOSED state
      tcpChangeState(socket, TCP_STATE_CLOSED);
      //Return immediately
      return;
   }

   //Check the SYN bit
   if(tcpCheckSyn(socket, segment, length))
      return;
   //If the ACK bit is off drop the segment and return
   if(!(segment->flags & TCP_FLAG_ACK))
      return;

   //The only thing that can arrive in this state is an
   //acknowledgment of our FIN
   if(segment->ackNum == socket->sndNxt)
   {
      //Enter CLOSED state
      tcpChangeState(socket, TCP_STATE_CLOSED);
   }
}
Example #2
0
void tcpStateSynReceived(Socket *socket, TcpHeader *segment,
   const NetBuffer *buffer, size_t offset, size_t length)
{
   //Debug message
   TRACE_DEBUG("TCP FSM: SYN-RECEIVED state\r\n");

   //First check sequence number
   if(tcpCheckSequenceNumber(socket, segment, length))
      return;

   //Check the RST bit
   if(segment->flags & TCP_FLAG_RST)
   {
      //Return to CLOSED state
      tcpChangeState(socket, TCP_STATE_CLOSED);

      //Number of times TCP connections have made a direct transition to the
      //CLOSED state from either the SYN-SENT state or the SYN-RECEIVED state
      MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpAttemptFails, 1);

      //Return immediately
      return;
   }

   //Check the SYN bit
   if(tcpCheckSyn(socket, segment, length))
      return;

   //If the ACK bit is off drop the segment and return
   if(!(segment->flags & TCP_FLAG_ACK))
      return;

   //Make sure the acknowledgment number is valid
   if(segment->ackNum != socket->sndNxt)
   {
      //If the segment acknowledgment is not acceptable, form a reset
      //segment and send it
      tcpSendSegment(socket, TCP_FLAG_RST, segment->ackNum, 0, 0, FALSE);

      //Drop the segment and return
      return;
   }

   //Update the send window before entering ESTABLISHED state (see RFC 1122 4.2.2.20)
   socket->sndWnd = segment->window;
   socket->sndWl1 = segment->seqNum;
   socket->sndWl2 = segment->ackNum;

   //Maximum send window it has seen so far on the connection
   socket->maxSndWnd = segment->window;

   //Enter ESTABLISHED state
   tcpChangeState(socket, TCP_STATE_ESTABLISHED);
   //And continue processing...
   tcpStateEstablished(socket, segment, buffer, offset, length);
}
Example #3
0
error_t tcpAbort(Socket *socket)
{
   error_t error;

   //Check current state
   switch(socket->state)
   {
   //SYN-RECEIVED, ESTABLISHED, FIN-WAIT-1
   //FIN-WAIT-2 or CLOSE-WAIT state?
   case TCP_STATE_SYN_RECEIVED:
   case TCP_STATE_ESTABLISHED:
   case TCP_STATE_FIN_WAIT_1:
   case TCP_STATE_FIN_WAIT_2:
   case TCP_STATE_CLOSE_WAIT:
      //Send a reset segment
      error = tcpSendSegment(socket, TCP_FLAG_RST, socket->sndNxt, 0, 0, FALSE);
      //Enter CLOSED state
      tcpChangeState(socket, TCP_STATE_CLOSED);
      //Delete TCB
      tcpDeleteControlBlock(socket);
      //Mark the socket as closed
      socket->type = SOCKET_TYPE_UNUSED;
      //Return status code
      return error;

   //TIME-WAIT state?
   case TCP_STATE_TIME_WAIT:
#if (TCP_2MSL_TIMER > 0)
      //The user doe not own the socket anymore...
      socket->ownedFlag = FALSE;
      //TCB will be deleted and socket will be closed
      //when the 2MSL timer will elapse
      return NO_ERROR;
#else
      //Enter CLOSED state
      tcpChangeState(socket, TCP_STATE_CLOSED);
      //Delete TCB
      tcpDeleteControlBlock(socket);
      //Mark the socket as closed
      socket->type = SOCKET_TYPE_UNUSED;
      //No error to report
      return NO_ERROR;
#endif

   //Any other state?
   default:
      //Enter CLOSED state
      tcpChangeState(socket, TCP_STATE_CLOSED);
      //Delete TCB
      tcpDeleteControlBlock(socket);
      //Mark the socket as closed
      socket->type = SOCKET_TYPE_UNUSED;
      //No error to report
      return NO_ERROR;
   }
}
Example #4
0
void tcpStateFinWait2(Socket *socket, TcpHeader *segment,
   const NetBuffer *buffer, size_t offset, size_t length)
{
   //Debug message
   TRACE_DEBUG("TCP FSM: FIN-WAIT-2 state\r\n");

   //First check sequence number
   if(tcpCheckSequenceNumber(socket, segment, length))
      return;

   //Check the RST bit
   if(segment->flags & TCP_FLAG_RST)
   {
      //Switch to the CLOSED state
      tcpChangeState(socket, TCP_STATE_CLOSED);
      //Return immediately
      return;
   }

   //Check the SYN bit
   if(tcpCheckSyn(socket, segment, length))
      return;
   //Check the ACK field
   if(tcpCheckAck(socket, segment, length))
      return;
   //Process the segment text
   if(length > 0)
      tcpProcessSegmentData(socket, segment, buffer, offset, length);

   //Check the FIN bit
   if(segment->flags & TCP_FLAG_FIN)
   {
      //The FIN can only be acknowledged if all the segment data
      //has been successfully transferred to the receive buffer
      if(socket->rcvNxt == (segment->seqNum + length))
      {
         //Advance RCV.NXT over the FIN
         socket->rcvNxt++;
         //Send an acknowledgement for the FIN
         tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt, socket->rcvNxt, 0, FALSE);

         //Release previously allocated resources
         tcpDeleteControlBlock(socket);
         //Start the 2MSL timer
         tcpTimerStart(&socket->timeWaitTimer, TCP_2MSL_TIMER);
         //Switch to the TIME_WAIT state
         tcpChangeState(socket, TCP_STATE_TIME_WAIT);
      }
   }
}
Example #5
0
error_t tcpListen(Socket *socket)
{
   //Socket already connected?
   if(socket->state != TCP_STATE_CLOSED)
      return ERROR_ALREADY_CONNECTED;

   //Place the socket in the listening state
   tcpChangeState(socket, TCP_STATE_LISTEN);

   //Successful processing
   return NO_ERROR;
}
Example #6
0
Socket *tcpKillOldestConnection(void)
{
   uint_t i;
   Socket *socket;
   Socket *oldestSocket;

   //Keep track of the oldest socket in the TIME-WAIT state
   oldestSocket = NULL;

   //Loop through socket descriptors
   for(i = 0; i < SOCKET_MAX_COUNT; i++)
   {
      //Point to the current socket descriptor
      socket = &socketTable[i];

      //TCP connection found?
      if(socket->type == SOCKET_TYPE_STREAM)
      {
         //Check current state
         if(socket->state == TCP_STATE_TIME_WAIT)
         {
            //Keep track of the oldest socket in the TIME-WAIT state
            if(oldestSocket == NULL)
            {
               //Save socket handle
               oldestSocket = socket;
            }
            else if(timeCompare(socket->timeWaitTimer.startTime,
               oldestSocket->timeWaitTimer.startTime) < 0)
            {
               //Save socket handle
               oldestSocket = socket;
            }
         }
      }
   }

   //Any connection in the TIME-WAIT state?
   if(oldestSocket != NULL)
   {
      //Enter CLOSED state
      tcpChangeState(oldestSocket, TCP_STATE_CLOSED);
      //Delete TCB
      tcpDeleteControlBlock(oldestSocket);
      //Mark the socket as closed
      oldestSocket->type = SOCKET_TYPE_UNUSED;
   }

   //The oldest connection in the TIME-WAIT state can be reused
   //when the socket table runs out of space
   return oldestSocket;
}
Example #7
0
void tcpStateClosing(Socket *socket, TcpHeader *segment, size_t length)
{
   //Debug message
   TRACE_DEBUG("TCP FSM: CLOSING state\r\n");

   //First check sequence number
   if(tcpCheckSequenceNumber(socket, segment, length))
      return;

   //Check the RST bit
   if(segment->flags & TCP_FLAG_RST)
   {
      //Enter CLOSED state
      tcpChangeState(socket, TCP_STATE_CLOSED);
      //Return immediately
      return;
   }

   //Check the SYN bit
   if(tcpCheckSyn(socket, segment, length))
      return;
   //Check the ACK field
   if(tcpCheckAck(socket, segment, length))
      return;

   //If the ACK acknowledges our FIN then enter the TIME-WAIT
   //state, otherwise ignore the segment
   if(segment->ackNum == socket->sndNxt)
   {
      //Release previously allocated resources
      tcpDeleteControlBlock(socket);
      //Start the 2MSL timer
      tcpTimerStart(&socket->timeWaitTimer, TCP_2MSL_TIMER);
      //Switch to the TIME-WAIT state
      tcpChangeState(socket, TCP_STATE_TIME_WAIT);
   }
}
Example #8
0
error_t tcpListen(Socket *socket, uint_t backlog)
{
   //Socket already connected?
   if(socket->state != TCP_STATE_CLOSED)
      return ERROR_ALREADY_CONNECTED;

   //Set the size of the SYN queue
   socket->synQueueSize = (backlog > 0) ? backlog : TCP_DEFAULT_SYN_QUEUE_SIZE;
   //Limit the number of pending connections
   socket->synQueueSize = MIN(socket->synQueueSize, TCP_MAX_SYN_QUEUE_SIZE);

   //Place the socket in the listening state
   tcpChangeState(socket, TCP_STATE_LISTEN);

   //Successful processing
   return NO_ERROR;
}
Example #9
0
void tcpStateTimeWait(Socket *socket, TcpHeader *segment, size_t length)
{
   //Debug message
   TRACE_DEBUG("TCP FSM: TIME-WAIT state\r\n");

   //First check sequence number
   if(tcpCheckSequenceNumber(socket, segment, length))
      return;

   //Check the RST bit
   if(segment->flags & TCP_FLAG_RST)
   {
      //Enter CLOSED state
      tcpChangeState(socket, TCP_STATE_CLOSED);

      //Dispose the socket if the user does not have the ownership anymore
      if(!socket->ownedFlag)
      {
         //Delete the TCB
         tcpDeleteControlBlock(socket);
         //Mark the socket as closed
         socket->type = SOCKET_TYPE_UNUSED;
      }

      //Return immediately
      return;
   }

   //Check the SYN bit
   if(tcpCheckSyn(socket, segment, length))
      return;
   //If the ACK bit is off drop the segment and return
   if(!(segment->flags & TCP_FLAG_ACK))
      return;

   //The only thing that can arrive in this state is a retransmission
   //of the remote FIN. Acknowledge it and restart the 2 MSL timeout
   if(segment->flags & TCP_FLAG_FIN)
   {
      //Send an acknowledgement for the FIN
      tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt, socket->rcvNxt, 0, FALSE);
      //Restart the 2MSL timer
      tcpTimerStart(&socket->timeWaitTimer, TCP_2MSL_TIMER);
   }
}
Example #10
0
void tcpStateCloseWait(Socket *socket, TcpHeader *segment, size_t length)
{
   uint_t flags = 0;

   //Debug message
   TRACE_DEBUG("TCP FSM: CLOSE-WAIT state\r\n");

   //First check sequence number
   if(tcpCheckSequenceNumber(socket, segment, length))
      return;

   //Check the RST bit
   if(segment->flags & TCP_FLAG_RST)
   {
      //Switch to the CLOSED state
      tcpChangeState(socket, TCP_STATE_CLOSED);

      //Number of times TCP connections have made a direct transition to the
      //CLOSED state from either the ESTABLISHED state or the CLOSE-WAIT state
      MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpEstabResets, 1);

      //Return immediately
      return;
   }

   //Check the SYN bit
   if(tcpCheckSyn(socket, segment, length))
      return;
   //Check the ACK field
   if(tcpCheckAck(socket, segment, length))
      return;

#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
   //Duplicate AK received?
   if(socket->dupAckCount > 0)
      flags = SOCKET_FLAG_NO_DELAY;
#endif

   //The Nagle algorithm should be implemented to coalesce
   //short segments (refer to RFC 1122 4.2.3.4)
   tcpNagleAlgo(socket, flags);
}
Example #11
0
error_t tcpConnect(Socket *socket)
{
   error_t error;
   uint_t event;

   //Socket already connected?
   if(socket->state != TCP_STATE_CLOSED)
      return ERROR_ALREADY_CONNECTED;

   //The user owns the socket
   socket->ownedFlag = TRUE;

   //Number of chunks that comprise the TX and the RX buffers
   socket->txBuffer.maxChunkCount = arraysize(socket->txBuffer.chunk);
   socket->rxBuffer.maxChunkCount = arraysize(socket->rxBuffer.chunk);

   //Allocate transmit buffer
   error = netBufferSetLength((NetBuffer *) &socket->txBuffer, socket->txBufferSize);
   //Allocate receive buffer
   if(!error)
      error = netBufferSetLength((NetBuffer *) &socket->rxBuffer, socket->rxBufferSize);

   //Failed to allocate memory?
   if(error)
   {
      //Free any previously allocated memory
      tcpDeleteControlBlock(socket);
      //Report an error to the caller
      return error;
   }

   //Default MSS value
   socket->mss = MIN(TCP_DEFAULT_MSS, TCP_MAX_MSS);
   //An initial send sequence number is selected
   socket->iss = netGetRand();
   //Initialize TCP control block
   socket->sndUna = socket->iss;
   socket->sndNxt = socket->iss + 1;
   socket->rcvUser = 0;
   socket->rcvWnd = socket->rxBufferSize;
   //Default retransmission timeout
   socket->rto = TCP_INITIAL_RTO;

   //Send a SYN segment
   error = tcpSendSegment(socket, TCP_FLAG_SYN, socket->iss, 0, 0, TRUE);
   //Failed to send TCP segment?
   if(error) return error;

   //Switch to the SYN-SENT state
   tcpChangeState(socket, TCP_STATE_SYN_SENT);
   //Wait for the connection to be established
   event = tcpWaitForEvents(socket, SOCKET_EVENT_CONNECTED |
      SOCKET_EVENT_CLOSED, socket->timeout);

   //Connection successfully established?
   if(event == SOCKET_EVENT_CONNECTED)
      return NO_ERROR;
   //Failed to establish connection?
   else if(event == SOCKET_EVENT_CLOSED)
      return ERROR_CONNECTION_FAILED;
   //Timeout exception?
   else
      return ERROR_TIMEOUT;
}
Example #12
0
void tcpStateEstablished(Socket *socket, TcpHeader *segment,
   const NetBuffer *buffer, size_t offset, size_t length)
{
   uint_t flags = 0;

   //Debug message
   TRACE_DEBUG("TCP FSM: ESTABLISHED state\r\n");

   //First check sequence number
   if(tcpCheckSequenceNumber(socket, segment, length))
      return;

   //Check the RST bit
   if(segment->flags & TCP_FLAG_RST)
   {
      //Switch to the CLOSED state
      tcpChangeState(socket, TCP_STATE_CLOSED);

      //Number of times TCP connections have made a direct transition to the
      //CLOSED state from either the ESTABLISHED state or the CLOSE-WAIT state
      MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpEstabResets, 1);

      //Return immediately
      return;
   }

   //Check the SYN bit
   if(tcpCheckSyn(socket, segment, length))
      return;
   //Check the ACK field
   if(tcpCheckAck(socket, segment, length))
      return;
   //Process the segment text
   if(length > 0)
      tcpProcessSegmentData(socket, segment, buffer, offset, length);

   //Check the FIN bit
   if(segment->flags & TCP_FLAG_FIN)
   {
      //The FIN can only be acknowledged if all the segment data
      //has been successfully transferred to the receive buffer
      if(socket->rcvNxt == (segment->seqNum + length))
      {
         //Advance RCV.NXT over the FIN
         socket->rcvNxt++;
         //Send an acknowledgement for the FIN
         tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt, socket->rcvNxt, 0, FALSE);
         //Switch to the CLOSE-WAIT state
         tcpChangeState(socket, TCP_STATE_CLOSE_WAIT);
      }
   }

#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
   //Duplicate AK received?
   if(socket->dupAckCount > 0)
      flags = SOCKET_FLAG_NO_DELAY;
#endif

   //The Nagle algorithm should be implemented to coalesce
   //short segments (refer to RFC 1122 4.2.3.4)
   tcpNagleAlgo(socket, flags);
}
Example #13
0
void tcpProcessSegment(NetInterface *interface,
   IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset)
{
   uint_t i;
   size_t length;
   Socket *socket;
   Socket *passiveSocket;
   TcpHeader *segment;

   //Total number of segments received, including those received in error
   MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInSegs, 1);
   MIB2_INC_COUNTER64(mib2Base.tcpGroup.tcpHCInSegs, 1);

   //A TCP implementation must silently discard an incoming
   //segment that is addressed to a broadcast or multicast
   //address (see RFC 1122 4.2.3.10)
#if (IPV4_SUPPORT == ENABLED)
   if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
   {
      //Ensure the destination address is not a broadcast address
      if(ipv4IsBroadcastAddr(interface, pseudoHeader->ipv4Data.destAddr))
         return;
      //Ensure the destination address is not a multicast address
      if(ipv4IsMulticastAddr(pseudoHeader->ipv4Data.destAddr))
         return;
   }
   else
#endif
#if (IPV6_SUPPORT == ENABLED)
   if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
   {
      //Ensure the destination address is not a multicast address
      if(ipv6IsMulticastAddr(&pseudoHeader->ipv6Data.destAddr))
         return;
   }
   else
#endif
   {
      //This should never occur...
      return;
   }

   //Retrieve the length of the TCP segment
   length = netBufferGetLength(buffer) - offset;

   //Point to the TCP header
   segment = netBufferAt(buffer, offset);
   //Sanity check
   if(segment == NULL)
      return;

   //Ensure the TCP header is valid
   if(length < sizeof(TcpHeader))
   {
      //Debug message
      TRACE_WARNING("TCP segment length is invalid!\r\n");
      //Total number of segments received in error
      MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInErrs, 1);
      //Exit immediately
      return;
   }

   //Check header length
   if(segment->dataOffset < 5 || (segment->dataOffset * 4) > length)
   {
      //Debug message
      TRACE_WARNING("TCP header length is invalid!\r\n");
      //Total number of segments received in error
      MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInErrs, 1);
      //Exit immediately
      return;
   }

   //Verify TCP checksum
   if(ipCalcUpperLayerChecksumEx(pseudoHeader->data,
      pseudoHeader->length, buffer, offset, length) != 0x0000)
   {
      //Debug message
      TRACE_WARNING("Wrong TCP header checksum!\r\n");
      //Total number of segments received in error
      MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInErrs, 1);
      //Exit immediately
      return;
   }

   //No matching socket in the LISTEN state for the moment
   passiveSocket = NULL;

   //Look through opened sockets
   for(i = 0; i < SOCKET_MAX_COUNT; i++)
   {
      //Point to the current socket
      socket = socketTable + i;

      //TCP socket found?
      if(socket->type != SOCKET_TYPE_STREAM)
         continue;
      //Check whether the socket is bound to a particular interface
      if(socket->interface && socket->interface != interface)
         continue;
      //Check destination port number
      if(socket->localPort != ntohs(segment->destPort))
         continue;

#if (IPV4_SUPPORT == ENABLED)
      //An IPv4 packet was received?
      if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
      {
         //Destination IP address filtering
         if(socket->localIpAddr.length)
         {
            //An IPv4 address is expected
            if(socket->localIpAddr.length != sizeof(Ipv4Addr))
               continue;
            //Filter out non-matching addresses
            if(socket->localIpAddr.ipv4Addr != pseudoHeader->ipv4Data.destAddr)
               continue;
         }
         //Source IP address filtering
         if(socket->remoteIpAddr.length)
         {
            //An IPv4 address is expected
            if(socket->remoteIpAddr.length != sizeof(Ipv4Addr))
               continue;
            //Filter out non-matching addresses
            if(socket->remoteIpAddr.ipv4Addr != pseudoHeader->ipv4Data.srcAddr)
               continue;
         }
      }
      else
#endif
#if (IPV6_SUPPORT == ENABLED)
      //An IPv6 packet was received?
      if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
      {
         //Destination IP address filtering
         if(socket->localIpAddr.length)
         {
            //An IPv6 address is expected
            if(socket->localIpAddr.length != sizeof(Ipv6Addr))
               continue;
            //Filter out non-matching addresses
            if(!ipv6CompAddr(&socket->localIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.destAddr))
               continue;
         }
         //Source IP address filtering
         if(socket->remoteIpAddr.length)
         {
            //An IPv6 address is expected
            if(socket->remoteIpAddr.length != sizeof(Ipv6Addr))
               continue;
            //Filter out non-matching addresses
            if(!ipv6CompAddr(&socket->remoteIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.srcAddr))
               continue;
         }
      }
      else
#endif
      //An invalid packet was received?
      {
         //This should never occur...
         continue;
      }

      //Keep track of the first matching socket in the LISTEN state
      if(socket->state == TCP_STATE_LISTEN && !passiveSocket)
         passiveSocket = socket;
      //Source port filtering
      if(socket->remotePort != ntohs(segment->srcPort))
         continue;

      //A matching socket has been found
      break;
   }

   //If no matching socket has been found then try to
   //use the first matching socket in the LISTEN state
   if(i >= SOCKET_MAX_COUNT) socket = passiveSocket;

   //Offset to the first data byte
   offset += segment->dataOffset * 4;
   //Calculate the length of the data
   length -= segment->dataOffset * 4;

   //Debug message
   TRACE_DEBUG("%s: TCP segment received (%" PRIuSIZE " data bytes)...\r\n",
      formatSystemTime(osGetSystemTime(), NULL), length);

   //Dump TCP header contents for debugging purpose
   if(!socket)
      tcpDumpHeader(segment, length, 0, 0);
   else
      tcpDumpHeader(segment, length, socket->irs, socket->iss);

   //Convert from network byte order to host byte order
   segment->srcPort = ntohs(segment->srcPort);
   segment->destPort = ntohs(segment->destPort);
   segment->seqNum = ntohl(segment->seqNum);
   segment->ackNum = ntohl(segment->ackNum);
   segment->window = ntohs(segment->window);
   segment->urgentPointer = ntohs(segment->urgentPointer);

   //Specified port is unreachable?
   if(!socket)
   {
      //An incoming segment not containing a RST causes
      //a reset to be sent in response
      if(!(segment->flags & TCP_FLAG_RST))
         tcpSendResetSegment(interface, pseudoHeader, segment, length);

      //Return immediately
      return;
   }

   //Check current state
   switch(socket->state)
   {
   //Process CLOSED state
   case TCP_STATE_CLOSED:
      //This is the default state that each connection starts in before
      //the process of establishing it begins
      tcpStateClosed(interface, pseudoHeader, segment, length);
      break;
   //Process LISTEN state
   case TCP_STATE_LISTEN:
      //A device (normally a server) is waiting to receive a synchronize (SYN)
      //message from a client. It has not yet sent its own SYN message
      tcpStateListen(socket, interface, pseudoHeader, segment, length);
      break;
   //Process SYN_SENT state
   case TCP_STATE_SYN_SENT:
      //The device (normally a client) has sent a synchronize (SYN) message and
      //is waiting for a matching SYN from the other device (usually a server)
      tcpStateSynSent(socket, segment, length);
      break;
   //Process SYN_RECEIVED state
   case TCP_STATE_SYN_RECEIVED:
      //The device has both received a SYN from its partner and sent its own SYN.
      //It is now waiting for an ACK to its SYN to finish connection setup
      tcpStateSynReceived(socket, segment, buffer, offset, length);
      break;
   //Process ESTABLISHED state
   case TCP_STATE_ESTABLISHED:
      //Data can be exchanged freely once both devices in the connection enter
      //this state. This will continue until the connection is closed
      tcpStateEstablished(socket, segment, buffer, offset, length);
      break;
   //Process CLOSE_WAIT state
   case TCP_STATE_CLOSE_WAIT:
      //The device has received a close request (FIN) from the other device. It
      //must now wait for the application to acknowledge this request and
      //generate a matching request
      tcpStateCloseWait(socket, segment, length);
      break;
   //Process LAST_ACK state
   case TCP_STATE_LAST_ACK:
      //A device that has already received a close request and acknowledged it,
      //has sent its own FIN and is waiting for an ACK to this request
      tcpStateLastAck(socket, segment, length);
      break;
   //Process FIN_WAIT_1 state
   case TCP_STATE_FIN_WAIT_1:
      //A device in this state is waiting for an ACK for a FIN it has sent, or
      //is waiting for a connection termination request from the other device
      tcpStateFinWait1(socket, segment, buffer, offset, length);
      break;
   //Process FIN_WAIT_2 state
   case TCP_STATE_FIN_WAIT_2:
      //A device in this state has received an ACK for its request to terminate the
      //connection and is now waiting for a matching FIN from the other device
      tcpStateFinWait2(socket, segment, buffer, offset, length);
      break;
   //Process CLOSING state
   case TCP_STATE_CLOSING:
      //The device has received a FIN from the other device and sent an ACK for
      //it, but not yet received an ACK for its own FIN message
      tcpStateClosing(socket, segment, length);
      break;
   //Process TIME_WAIT state
   case TCP_STATE_TIME_WAIT:
      //The device has now received a FIN from the other device and acknowledged
      //it, and sent its own FIN and received an ACK for it. We are done, except
      //for waiting to ensure the ACK is received and prevent potential overlap
      //with new connections
      tcpStateTimeWait(socket, segment, length);
      break;
   //Invalid state...
   default:
      //Back to the CLOSED state
      tcpChangeState(socket, TCP_STATE_CLOSED);
      //Silently discard incoming packet
      break;
   }
}
Example #14
0
void tcpStateSynSent(Socket *socket, TcpHeader *segment, size_t length)
{
   TcpOption *option;

   //Debug message
   TRACE_DEBUG("TCP FSM: SYN-SENT state\r\n");

   //Check the ACK bit
   if(segment->flags & TCP_FLAG_ACK)
   {
      //Make sure the acknowledgment number is valid
      if(segment->ackNum != socket->sndNxt)
      {
         //Send a reset segment unless the RST bit is set
         if(!(segment->flags & TCP_FLAG_RST))
            tcpSendSegment(socket, TCP_FLAG_RST, segment->ackNum, 0, 0, FALSE);

         //Drop the segment and return
         return;
      }
   }

   //Check the RST bit
   if(segment->flags & TCP_FLAG_RST)
   {
      //Make sure the ACK is acceptable
      if(segment->flags & TCP_FLAG_ACK)
      {
         //Enter CLOSED state
         tcpChangeState(socket, TCP_STATE_CLOSED);

         //Number of times TCP connections have made a direct transition to the
         //CLOSED state from either the SYN-SENT state or the SYN-RECEIVED state
         MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpAttemptFails, 1);
      }

      //Drop the segment and return
      return;
   }

   //Check the SYN bit
   if(segment->flags & TCP_FLAG_SYN)
   {
      //Save initial receive sequence number
      socket->irs = segment->seqNum;
      //Initialize RCV.NXT pointer
      socket->rcvNxt = segment->seqNum + 1;

      //If there is an ACK, SND.UNA should be advanced to equal SEG.ACK
      if(segment->flags & TCP_FLAG_ACK)
         socket->sndUna = segment->ackNum;

      //Compute retransmission timeout
      tcpComputeRto(socket);

      //Any segments on the retransmission queue which are thereby
      //acknowledged should be removed
      tcpUpdateRetransmitQueue(socket);

      //Get the maximum segment size
      option = tcpGetOption(segment, TCP_OPTION_MAX_SEGMENT_SIZE);

      //Specified option found?
      if(option != NULL && option->length == 4)
      {
         //Retrieve MSS value
         memcpy(&socket->mss, option->value, 2);
         //Convert from network byte order to host byte order
         socket->mss = ntohs(socket->mss);

         //Debug message
         TRACE_DEBUG("Remote host MSS = %" PRIu16 "\r\n", socket->mss);

         //Make sure that the MSS advertised by the peer is acceptable
         socket->mss = MIN(socket->mss, TCP_MAX_MSS);
         socket->mss = MAX(socket->mss, TCP_MIN_MSS);
      }

#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
      //Initial congestion window
      socket->cwnd = MIN(TCP_INITIAL_WINDOW * socket->mss, socket->txBufferSize);
#endif

      //Check whether our SYN has been acknowledged (SND.UNA > ISS)
      if(TCP_CMP_SEQ(socket->sndUna, socket->iss) > 0)
      {
         //Update the send window before entering ESTABLISHED state (see RFC 1122 4.2.2.20)
         socket->sndWnd = segment->window;
         socket->sndWl1 = segment->seqNum;
         socket->sndWl2 = segment->ackNum;

         //Maximum send window it has seen so far on the connection
         socket->maxSndWnd = segment->window;

         //Form an ACK segment and send it
         tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt, socket->rcvNxt, 0, FALSE);
         //Switch to the ESTABLISHED state
         tcpChangeState(socket, TCP_STATE_ESTABLISHED);
      }
      else
      {
         //Form an SYN ACK segment and send it
         tcpSendSegment(socket, TCP_FLAG_SYN | TCP_FLAG_ACK, socket->iss, socket->rcvNxt, 0, TRUE);
         //Enter SYN-RECEIVED state
         tcpChangeState(socket, TCP_STATE_SYN_RECEIVED);
      }
   }
}
Example #15
0
void tcpTick(void)
{
   error_t error;
   uint_t i;
   uint_t n;
   uint_t u;

   //Enter critical section
   osAcquireMutex(&socketMutex);

   //Loop through opened sockets
   for(i = 0; i < SOCKET_MAX_COUNT; i++)
   {
      //Shortcut to the current socket
      Socket *socket = socketTable + i;
      //Check socket type
      if(socket->type != SOCKET_TYPE_STREAM)
         continue;
      //Check the current state of the TCP state machine
      if(socket->state == TCP_STATE_CLOSED)
         continue;

      //Is there any packet in the retransmission queue?
      if(socket->retransmitQueue != NULL)
      {
         //Retransmission timeout?
         if(tcpTimerElapsed(&socket->retransmitTimer))
         {
            //When a TCP sender detects segment loss using the retransmission
            //timer and the given segment has not yet been resent by way of
            //the retransmission timer, the value of ssthresh must be updated
            if(!socket->retransmitCount)
            {
               //Amount of data that has been sent but not yet acknowledged
               uint_t flightSize = socket->sndNxt - socket->sndUna;
               //Adjust ssthresh value
               socket->ssthresh = MAX(flightSize / 2, 2 * socket->mss);
            }

            //Furthermore, upon a timeout cwnd must be set to no more than
            //the loss window, LW, which equals 1 full-sized segment
            socket->cwnd = MIN(TCP_LOSS_WINDOW * socket->mss, socket->txBufferSize);

            //Make sure the maximum number of retransmissions has not been reached
            if(socket->retransmitCount < TCP_MAX_RETRIES)
            {
               //Debug message
               TRACE_INFO("%s: TCP segment retransmission #%u (%u data bytes)...\r\n",
                  formatSystemTime(osGetSystemTime(), NULL), socket->retransmitCount + 1,
                  socket->retransmitQueue->length);

               //Retransmit the earliest segment that has not been
               //acknowledged by the TCP receiver
               tcpRetransmitSegment(socket);

               //Use exponential back-off algorithm to calculate the new RTO
               socket->rto = MIN(socket->rto * 2, TCP_MAX_RTO);
               //Restart retransmission timer
               tcpTimerStart(&socket->retransmitTimer, socket->rto);
               //Increment retransmission counter
               socket->retransmitCount++;
            }
            else
            {
               //The maximum number of retransmissions has been exceeded
               tcpChangeState(socket, TCP_STATE_CLOSED);
               //Turn off the retransmission timer
               tcpTimerStop(&socket->retransmitTimer);
            }

            //TCP must use Karn's algorithm for taking RTT samples. That is, RTT
            //samples must not be made using segments that were retransmitted
            socket->rttBusy = FALSE;
         }
      }

      //Check the current state of the TCP state machine
      if(socket->state == TCP_STATE_CLOSED)
         continue;

      //The persist timer is used when the remote host advertises
      //a window size of zero
      if(!socket->sndWnd && socket->wndProbeInterval)
      {
         //Time to send a new probe?
         if(tcpTimerElapsed(&socket->persistTimer))
         {
            //Make sure the maximum number of retransmissions has not been reached
            if(socket->wndProbeCount < TCP_MAX_RETRIES)
            {
               //Debug message
               TRACE_INFO("%s: TCP zero window probe #%u...\r\n",
                  formatSystemTime(osGetSystemTime(), NULL), socket->wndProbeCount + 1);

               //Zero window probes usually have the sequence number one less than expected
               tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt - 1, socket->rcvNxt, 0, FALSE);
               //The interval between successive probes should be increased exponentially
               socket->wndProbeInterval = MIN(socket->wndProbeInterval * 2, TCP_MAX_PROBE_INTERVAL);
               //Restart the persist timer
               tcpTimerStart(&socket->persistTimer, socket->wndProbeInterval);
               //Increment window probe counter
               socket->wndProbeCount++;
            }
            else
            {
               //Enter CLOSED state
               tcpChangeState(socket, TCP_STATE_CLOSED);
            }
         }
      }

      //To avoid a deadlock, it is necessary to have a timeout to force
      //transmission of data, overriding the SWS avoidance algorithm. In
      //practice, this timeout should seldom occur (see RFC 1122 4.2.3.4)
      if(socket->state == TCP_STATE_ESTABLISHED || socket->state == TCP_STATE_CLOSE_WAIT)
      {
         //The override timeout occurred?
         if(socket->sndUser && tcpTimerElapsed(&socket->overrideTimer))
         {
            //The amount of data that can be sent at any given time is
            //limited by the receiver window and the congestion window
            n = MIN(socket->sndWnd, socket->cwnd);
            n = MIN(n, socket->txBufferSize);

            //Retrieve the size of the usable window
            u = n - (socket->sndNxt - socket->sndUna);

            //Send as much data as possible
            while(socket->sndUser > 0)
            {
               //The usable window size may become zero or negative,
               //preventing packet transmission
               if((int_t) u <= 0) break;

               //Calculate the number of bytes to send at a time
               n = MIN(u, socket->sndUser);
               n = MIN(n, socket->mss);

               //Send TCP segment
               error = tcpSendSegment(socket, TCP_FLAG_PSH | TCP_FLAG_ACK,
                  socket->sndNxt, socket->rcvNxt, n, TRUE);
               //Failed to send TCP segment?
               if(error) break;

               //Advance SND.NXT pointer
               socket->sndNxt += n;
               //Adjust the number of bytes buffered but not yet sent
               socket->sndUser -= n;
            }

            //Check whether the transmitter can accept more data
            tcpUpdateEvents(socket);

            //Restart override timer if necessary
            if(socket->sndUser > 0)
               tcpTimerStart(&socket->overrideTimer, TCP_OVERRIDE_TIMEOUT);
         }
      }

      //The FIN-WAIT-2 timer prevents the connection
      //from staying in the FIN-WAIT-2 state forever
      if(socket->state == TCP_STATE_FIN_WAIT_2)
      {
         //Maximum FIN-WAIT-2 time has elapsed?
         if(tcpTimerElapsed(&socket->finWait2Timer))
         {
            //Debug message
            TRACE_WARNING("TCP FIN-WAIT-2 timer elapsed...\r\n");
            //Enter CLOSED state
            tcpChangeState(socket, TCP_STATE_CLOSED);
         }
      }

      //TIME-WAIT timer
      if(socket->state == TCP_STATE_TIME_WAIT)
      {
         //2MSL time has elapsed?
         if(tcpTimerElapsed(&socket->timeWaitTimer))
         {
            //Debug message
            TRACE_WARNING("TCP 2MSL timer elapsed (socket %u)...\r\n", i);
            //Enter CLOSED state
            tcpChangeState(socket, TCP_STATE_CLOSED);

            //Dispose the socket if the user does not have the ownership anymore
            if(!socket->ownedFlag)
            {
               //Delete the TCB
               tcpDeleteControlBlock(socket);
               //Mark the socket as closed
               socket->type = SOCKET_TYPE_UNUSED;
            }
         }
      }
   }

   //Leave critical section
   osReleaseMutex(&socketMutex);
}
Example #16
0
Socket *tcpAccept(Socket *socket, IpAddr *clientIpAddr, uint16_t *clientPort)
{
   error_t error;
   Socket *newSocket;
   TcpSynQueueItem *queueItem;

   //Ensure the socket was previously placed in the listening state
   if(tcpGetState(socket) != TCP_STATE_LISTEN)
      return NULL;

   //Enter critical section
   osAcquireMutex(&socketMutex);

   //Wait for an connection attempt
   while(1)
   {
      //The SYN queue is empty?
      if(!socket->synQueue)
      {
         //Set the events the application is interested in
         socket->eventMask = SOCKET_EVENT_RX_READY;
         //Reset the event object
         osResetEvent(&socket->event);
         //Leave critical section
         osReleaseMutex(&socketMutex);
         //Wait until a SYN message is received from a client
         osWaitForEvent(&socket->event, socket->timeout);
         //Enter critical section
         osAcquireMutex(&socketMutex);
      }

      //Check whether the queue is still empty
      if(!socket->synQueue)
      {
         //Timeout error
         newSocket = NULL;
         //Exit immediately
         break;
      }

      //Point to the first item in the receive queue
      queueItem = socket->synQueue;

      //Return the client IP address and port number
      if(clientIpAddr)
         *clientIpAddr = queueItem->srcAddr;
      if(clientPort)
         *clientPort = queueItem->srcPort;

      //Leave critical section
      osReleaseMutex(&socketMutex);
      //Create a new socket to handle the incoming connection request
      newSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
      //Enter critical section
      osAcquireMutex(&socketMutex);

      //Socket successfully created?
      if(newSocket != NULL)
      {
         //The user owns the socket
         newSocket->ownedFlag = TRUE;

         //Inherit settings from the listening socket
         newSocket->txBufferSize = socket->txBufferSize;
         newSocket->rxBufferSize = socket->rxBufferSize;

         //Number of chunks that comprise the TX and the RX buffers
         newSocket->txBuffer.maxChunkCount = arraysize(newSocket->txBuffer.chunk);
         newSocket->rxBuffer.maxChunkCount = arraysize(newSocket->rxBuffer.chunk);

         //Allocate transmit buffer
         error = netBufferSetLength((NetBuffer *) &newSocket->txBuffer, newSocket->txBufferSize);

         //Check status code
         if(!error)
         {
            //Allocate receive buffer
            error = netBufferSetLength((NetBuffer *) &newSocket->rxBuffer, newSocket->rxBufferSize);
         }

         //Transmit and receive buffers successfully allocated?
         if(!error)
         {
            //Bind the newly created socket to the appropriate interface
            newSocket->interface = queueItem->interface;
            //Bind the socket to the specified address
            newSocket->localIpAddr = queueItem->destAddr;
            newSocket->localPort = socket->localPort;
            //Save the port number and the IP address of the remote host
            newSocket->remoteIpAddr = queueItem->srcAddr;
            newSocket->remotePort = queueItem->srcPort;
            //Save the maximum segment size
            newSocket->mss = queueItem->mss;

            //Initialize TCP control block
            newSocket->iss = netGetRand();
            newSocket->irs = queueItem->isn;
            newSocket->sndUna = newSocket->iss;
            newSocket->sndNxt = newSocket->iss + 1;
            newSocket->rcvNxt = newSocket->irs + 1;
            newSocket->rcvUser = 0;
            newSocket->rcvWnd = newSocket->rxBufferSize;

            //Default retransmission timeout
            newSocket->rto = TCP_INITIAL_RTO;

#if (TCP_CONGESTION_CONTROL_SUPPORT == ENABLED)
            //Initial congestion window
            newSocket->cwnd = MIN(TCP_INITIAL_WINDOW * newSocket->mss, newSocket->txBufferSize);
            //Slow start threshold should be set arbitrarily high
            newSocket->ssthresh = UINT16_MAX;
#endif

            //Send a SYN ACK control segment
            error = tcpSendSegment(newSocket, TCP_FLAG_SYN | TCP_FLAG_ACK,
               newSocket->iss, newSocket->rcvNxt, 0, TRUE);

            //TCP segment successfully sent?
            if(!error)
            {
               //Remove the item from the SYN queue
               socket->synQueue = queueItem->next;
               //Deallocate memory buffer
               memPoolFree(queueItem);
               //Update the state of events
               tcpUpdateEvents(socket);

               //The connection state should be changed to SYN-RECEIVED
               tcpChangeState(newSocket, TCP_STATE_SYN_RECEIVED);

               //We are done...
               break;
            }
         }

         //Dispose the socket
         tcpAbort(newSocket);
      }

      //Debug message
      TRACE_WARNING("Cannot accept TCP connection!\r\n");

      //Remove the item from the SYN queue
      socket->synQueue = queueItem->next;
      //Deallocate memory buffer
      memPoolFree(queueItem);

      //Wait for the next connection attempt
   }

   //Leave critical section
   osReleaseMutex(&socketMutex);

   //Return a handle to the newly created socket
   return newSocket;
}
Example #17
0
error_t tcpShutdown(Socket *socket, uint_t how)
{
   error_t error;
   uint_t event;

   //Disable transmission?
   if(how == SOCKET_SD_SEND || how == SOCKET_SD_BOTH)
   {
      //Check current state
      switch(socket->state)
      {
      //CLOSED or LISTEN state?
      case TCP_STATE_CLOSED:
      case TCP_STATE_LISTEN:
         //Connection does not exist
         return ERROR_NOT_CONNECTED;

      //SYN-RECEIVED or ESTABLISHED state?
      case TCP_STATE_SYN_RECEIVED:
      case TCP_STATE_ESTABLISHED:
         //Flush the send buffer
         error = tcpSend(socket, NULL, 0, NULL, SOCKET_FLAG_NO_DELAY);
         //Any error to report?
         if(error) return error;

         //Make sure all the data has been sent out
         event = tcpWaitForEvents(socket, SOCKET_EVENT_TX_DONE, socket->timeout);
         //Timeout error?
         if(event != SOCKET_EVENT_TX_DONE)
            return ERROR_TIMEOUT;

         //Send a FIN segment
         error = tcpSendSegment(socket, TCP_FLAG_FIN | TCP_FLAG_ACK,
            socket->sndNxt, socket->rcvNxt, 0, TRUE);
         //Failed to send FIN segment?
         if(error) return error;

         //Sequence number expected to be received
         socket->sndNxt++;
         //Switch to the FIN-WAIT1 state
         tcpChangeState(socket, TCP_STATE_FIN_WAIT_1);

         //Wait for the FIN to be acknowledged
         event = tcpWaitForEvents(socket, SOCKET_EVENT_TX_SHUTDOWN, socket->timeout);
         //Timeout interval elapsed?
         if(event != SOCKET_EVENT_TX_SHUTDOWN)
            return ERROR_TIMEOUT;

         //Continue processing...
         break;

      //CLOSE-WAIT state?
      case TCP_STATE_CLOSE_WAIT:
         //Flush the send buffer
         error = tcpSend(socket, NULL, 0, NULL, SOCKET_FLAG_NO_DELAY);
         //Any error to report?
         if(error) return error;

         //Make sure all the data has been sent out
         event = tcpWaitForEvents(socket, SOCKET_EVENT_TX_DONE, socket->timeout);
         //Timeout error?
         if(event != SOCKET_EVENT_TX_DONE)
            return ERROR_TIMEOUT;

         //Send a FIN segment
         error = tcpSendSegment(socket, TCP_FLAG_FIN | TCP_FLAG_ACK,
            socket->sndNxt, socket->rcvNxt, 0, TRUE);
         //Failed to send FIN segment?
         if(error) return error;

         //Sequence number expected to be received
         socket->sndNxt++;
         //Switch to the LAST-ACK state
         tcpChangeState(socket, TCP_STATE_LAST_ACK);

         //Wait for the FIN to be acknowledged
         event = tcpWaitForEvents(socket, SOCKET_EVENT_TX_SHUTDOWN, socket->timeout);
         //Timeout interval elapsed?
         if(event != SOCKET_EVENT_TX_SHUTDOWN)
            return ERROR_TIMEOUT;

         //Continue processing...
         break;

      //SYN-SENT, FIN-WAIT-1, FIN-WAIT-2, CLOSING,
      //TIME-WAIT or LAST-ACK state?
      default:
         //Continue processing...
         break;
      }
   }

   //Disable reception?
   if(how == SOCKET_SD_RECEIVE || how == SOCKET_SD_BOTH)
   {
      //Check current state
      switch(socket->state)
      {
      //CLOSED or LISTEN state?
      case TCP_STATE_CLOSED:
      case TCP_STATE_LISTEN:
         //Connection does not exist
         return ERROR_NOT_CONNECTED;

      //SYN-SENT, SYN-RECEIVED, ESTABLISHED, FIN-WAIT-1 or FIN-WAIT-2 state?
      case TCP_STATE_SYN_SENT:
      case TCP_STATE_SYN_RECEIVED:
      case TCP_STATE_ESTABLISHED:
      case TCP_STATE_FIN_WAIT_1:
      case TCP_STATE_FIN_WAIT_2:
         //Wait for a FIN to be received
         event = tcpWaitForEvents(socket, SOCKET_EVENT_RX_SHUTDOWN, socket->timeout);
         //Timeout interval elapsed?
         if(event != SOCKET_EVENT_RX_SHUTDOWN)
            return ERROR_TIMEOUT;
         //A FIN segment has been received
         break;

      //CLOSING, TIME-WAIT, CLOSE-WAIT or LAST-ACK state?
      default:
         //A FIN segment has already been received
         break;
      }
   }

   //Successful operation
   return NO_ERROR;
}