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); } }
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); }
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; } }
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); } } }
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; }
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; }
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); } }
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; }
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); } }
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); }
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; }
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); }
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; } }
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); } } }
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); }
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; }
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; }