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