Webserver+3d print
cyclone_tcp/core/tcp.c
- Committer:
- Sergunb
- Date:
- 2017-02-04
- Revision:
- 0:8918a71cdbe9
File content as of revision 0:8918a71cdbe9:
/** * @file tcp.c * @brief TCP (Transmission Control Protocol) * * @section License * * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. * * This file is part of CycloneTCP Open. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * @author Oryx Embedded SARL (www.oryx-embedded.com) * @version 1.7.6 **/ //Switch to the appropriate trace level #define TRACE_LEVEL TCP_TRACE_LEVEL //Dependencies #include <string.h> #include "core/net.h" #include "core/socket.h" #include "core/tcp.h" #include "core/tcp_misc.h" #include "core/tcp_timer.h" #include "mibs/mib2_module.h" #include "debug.h" //Check TCP/IP stack configuration #if (TCP_SUPPORT == ENABLED) //Tick counter to handle periodic operations systime_t tcpTickCounter; //Ephemeral ports are used for dynamic port assignment static uint16_t tcpDynamicPort; /** * @brief TCP related initialization * @return Error code **/ error_t tcpInit(void) { //Reset ephemeral port number tcpDynamicPort = 0; //Successful initialization return NO_ERROR; } /** * @brief Get an ephemeral port number * @return Ephemeral port **/ uint16_t tcpGetDynamicPort(void) { uint_t port; //Retrieve current port number port = tcpDynamicPort; //Invalid port number? if(port < SOCKET_EPHEMERAL_PORT_MIN || port > SOCKET_EPHEMERAL_PORT_MAX) { //Generate a random port number port = SOCKET_EPHEMERAL_PORT_MIN + netGetRand() % (SOCKET_EPHEMERAL_PORT_MAX - SOCKET_EPHEMERAL_PORT_MIN + 1); } //Next dynamic port to use if(port < SOCKET_EPHEMERAL_PORT_MAX) { //Increment port number tcpDynamicPort = port + 1; } else { //Wrap around if necessary tcpDynamicPort = SOCKET_EPHEMERAL_PORT_MIN; } //Return an ephemeral port number return port; } /** * @brief Establish a TCP connection * @param[in] socket Handle to an unconnected socket * @param[in] remoteIpAddr IP address of the remote host * @param[in] remotePort Remote port number that will be used to establish the connection * @return Error code **/ error_t tcpConnect(Socket *socket, const IpAddr *remoteIpAddr, uint16_t remotePort) { error_t error; uint_t event; //Check current TCP state if(socket->state == TCP_STATE_CLOSED) { //Save port number and IP address of the remote host socket->remoteIpAddr = *remoteIpAddr; socket->remotePort = remotePort; //Select the source address and the relevant network interface //to use when establishing the connection error = ipSelectSourceAddr(&socket->interface, &socket->remoteIpAddr, &socket->localIpAddr); //Any error to report? if(error) return error; //Make sure the source address is valid if(ipIsUnspecifiedAddr(&socket->localIpAddr)) return ERROR_NOT_CONFIGURED; //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; } //The SMSS is the size of the largest segment that the sender can transmit socket->smss = MIN(TCP_DEFAULT_MSS, TCP_MAX_MSS); //The RMSS is the size of the largest segment the receiver is willing to accept socket->rmss = MIN(socket->rxBufferSize, 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; #if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED) //Default congestion state socket->congestState = TCP_CONGEST_STATE_IDLE; //Initial congestion window socket->cwnd = MIN(TCP_INITIAL_WINDOW * socket->smss, socket->txBufferSize); //Slow start threshold should be set arbitrarily high socket->ssthresh = UINT16_MAX; //Recover is set to the initial send sequence number socket->recover = socket->iss; #endif //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); //Number of times TCP connections have made a direct transition to //the SYN-SENT state from the CLOSED state MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpActiveOpens, 1); } //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; } /** * @brief Place a socket in the listening state * * Place a socket in a state in which it is listening for an incoming connection * * @param[in] socket Socket to place in the listening state * @param[in] backlog backlog The maximum length of the pending connection queue. * If this parameter is zero, then the default backlog value is used instead * @return Error code **/ 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; } /** * @brief Permit an incoming connection attempt on a TCP socket * @param[in] socket Handle to a socket previously placed in a listening state * @param[out] clientIpAddr IP address of the client * @param[out] clientPort Port number used by the client * @return Handle to the socket in which the actual connection is made **/ 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; //Get exclusive access osAcquireMutex(&netMutex); //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); //Release exclusive access osReleaseMutex(&netMutex); //Wait until a SYN message is received from a client osWaitForEvent(&socket->event, socket->timeout); //Get exclusive access osAcquireMutex(&netMutex); } //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; //Release exclusive access osReleaseMutex(&netMutex); //Create a new socket to handle the incoming connection request newSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP); //Get exclusive access osAcquireMutex(&netMutex); //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; //The SMSS is the size of the largest segment that the sender //can transmit newSocket->smss = queueItem->mss; //The RMSS is the size of the largest segment the receiver is //willing to accept newSocket->rmss = MIN(newSocket->rxBufferSize, TCP_MAX_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_CONGEST_CONTROL_SUPPORT == ENABLED) //Default congestion state socket->congestState = TCP_CONGEST_STATE_IDLE; //Initial congestion window newSocket->cwnd = MIN(TCP_INITIAL_WINDOW * newSocket->smss, newSocket->txBufferSize); //Slow start threshold should be set arbitrarily high newSocket->ssthresh = UINT16_MAX; //Recover is set to the initial send sequence number newSocket->recover = newSocket->iss; #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); //Number of times TCP connections have made a direct transition to //the SYN-RECEIVED state from the LISTEN state MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpPassiveOpens, 1); //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 } //Release exclusive access osReleaseMutex(&netMutex); //Return a handle to the newly created socket return newSocket; } /** * @brief Send data to a connected socket * @param[in] socket Handle that identifies a connected socket * @param[in] data Pointer to a buffer containing the data to be transmitted * @param[in] length Number of bytes to be transmitted * @param[out] written Actual number of bytes written (optional parameter) * @param[in] flags Set of flags that influences the behavior of this function * @return Error code **/ error_t tcpSend(Socket *socket, const uint8_t *data, size_t length, size_t *written, uint_t flags) { uint_t n; uint_t totalLength; uint_t event; //Check whether the socket is in the listening state if(socket->state == TCP_STATE_LISTEN) return ERROR_NOT_CONNECTED; //Actual number of bytes written totalLength = 0; //Send as much data as possible do { //Wait until there is more room in the send buffer event = tcpWaitForEvents(socket, SOCKET_EVENT_TX_READY, socket->timeout); //A timeout exception occurred? if(event != SOCKET_EVENT_TX_READY) return ERROR_TIMEOUT; //Check current TCP state switch(socket->state) { //ESTABLISHED or CLOSE-WAIT state? case TCP_STATE_ESTABLISHED: case TCP_STATE_CLOSE_WAIT: //The send buffer is now available for writing break; //LAST-ACK, FIN-WAIT-1, FIN-WAIT-2, CLOSING or TIME-WAIT state? case TCP_STATE_LAST_ACK: case TCP_STATE_FIN_WAIT_1: case TCP_STATE_FIN_WAIT_2: case TCP_STATE_CLOSING: case TCP_STATE_TIME_WAIT: //The connection is being closed return ERROR_CONNECTION_CLOSING; //CLOSED state? default: //The connection was reset by remote side? return (socket->resetFlag) ? ERROR_CONNECTION_RESET : ERROR_NOT_CONNECTED; } //Determine the actual number of bytes in the send buffer n = socket->sndUser + socket->sndNxt - socket->sndUna; //Exit immediately if the transmission buffer is full (sanity check) if(n >= socket->txBufferSize) return ERROR_FAILURE; //Number of bytes available for writing n = socket->txBufferSize - n; //Calculate the number of bytes to copy at a time n = MIN(n, length - totalLength); //Any data to copy? if(n > 0) { //Copy user data to send buffer tcpWriteTxBuffer(socket, socket->sndNxt + socket->sndUser, data, n); //Update the number of data buffered but not yet sent socket->sndUser += n; //Advance data pointer data += n; //Update byte counter totalLength += n; //Total number of data that have been written if(written != NULL) *written = totalLength; //Update TX events tcpUpdateEvents(socket); //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->sndUser == n) tcpTimerStart(&socket->overrideTimer, TCP_OVERRIDE_TIMEOUT); } //The Nagle algorithm should be implemented to coalesce //short segments (refer to RFC 1122 4.2.3.4) tcpNagleAlgo(socket, flags); //Send as much data as possible } while(totalLength < length); //The SOCKET_FLAG_WAIT_ACK flag causes the function to //wait for acknowledgment from the remote side if(flags & SOCKET_FLAG_WAIT_ACK) { //Wait for the data to be acknowledged event = tcpWaitForEvents(socket, SOCKET_EVENT_TX_ACKED, socket->timeout); //A timeout exception occurred? if(event != SOCKET_EVENT_TX_ACKED) return ERROR_TIMEOUT; //The connection was closed before an acknowledgment was received? if(socket->state != TCP_STATE_ESTABLISHED && socket->state != TCP_STATE_CLOSE_WAIT) return ERROR_NOT_CONNECTED; } //Successful write operation return NO_ERROR; } /** * @brief Receive data from a connected socket * @param[in] socket Handle that identifies a connected socket * @param[out] data Buffer where to store the incoming data * @param[in] size Maximum number of bytes that can be received * @param[out] received Number of bytes that have been received * @param[in] flags Set of flags that influences the behavior of this function * @return Error code **/ error_t tcpReceive(Socket *socket, uint8_t *data, size_t size, size_t *received, uint_t flags) { uint_t i; uint_t n; uint_t event; uint32_t seqNum; systime_t timeout; //Retrieve the break character code char_t c = LSB(flags); //No data has been read yet *received = 0; //Check whether the socket is in the listening state if(socket->state == TCP_STATE_LISTEN) return ERROR_NOT_CONNECTED; //Read as much data as possible while(*received < size) { //The SOCKET_FLAG_DONT_WAIT enables non-blocking operation timeout = (flags & SOCKET_FLAG_DONT_WAIT) ? 0 : socket->timeout; //Wait for data to be available for reading event = tcpWaitForEvents(socket, SOCKET_EVENT_RX_READY, timeout); //A timeout exception occurred? if(event != SOCKET_EVENT_RX_READY) return ERROR_TIMEOUT; //Check current TCP state switch(socket->state) { //ESTABLISHED, FIN-WAIT-1 or FIN-WAIT-2 state? case TCP_STATE_ESTABLISHED: case TCP_STATE_FIN_WAIT_1: case TCP_STATE_FIN_WAIT_2: //Sequence number of the first byte to read seqNum = socket->rcvNxt - socket->rcvUser; //Data is available in the receive buffer break; //CLOSE-WAIT, LAST-ACK, CLOSING or TIME-WAIT state? case TCP_STATE_CLOSE_WAIT: case TCP_STATE_LAST_ACK: case TCP_STATE_CLOSING: case TCP_STATE_TIME_WAIT: //The user must be satisfied with data already on hand if(!socket->rcvUser) { if(*received > 0) return NO_ERROR; else return ERROR_END_OF_STREAM; } //Sequence number of the first byte to read seqNum = (socket->rcvNxt - 1) - socket->rcvUser; //Data is available in the receive buffer break; //CLOSED state? default: //The connection was reset by remote side? if(socket->resetFlag) return ERROR_CONNECTION_RESET; //The connection has not yet been established? if(!socket->closedFlag) return ERROR_NOT_CONNECTED; //The user must be satisfied with data already on hand if(!socket->rcvUser) { if(*received > 0) return NO_ERROR; else return ERROR_END_OF_STREAM; } //Sequence number of the first byte to read seqNum = (socket->rcvNxt - 1) - socket->rcvUser; //Data is available in the receive buffer break; } //Sanity check if(!socket->rcvUser) return ERROR_FAILURE; //Calculate the number of bytes to read at a time n = MIN(socket->rcvUser, size - *received); //Copy data from circular buffer tcpReadRxBuffer(socket, seqNum, data, n); //Read data until a break character is encountered? if(flags & SOCKET_FLAG_BREAK_CHAR) { //Search for the specified break character for(i = 0; i < n && data[i] != c; i++); //Adjust the number of data to read n = MIN(n, i + 1); } //Total number of data that have been read *received += n; //Remaining data still available in the receive buffer socket->rcvUser -= n; //Update the receive window tcpUpdateReceiveWindow(socket); //Update RX event state tcpUpdateEvents(socket); //The SOCKET_FLAG_BREAK_CHAR flag causes the function to stop reading //data as soon as the specified break character is encountered if(flags & SOCKET_FLAG_BREAK_CHAR) { //Check whether a break character has been found if(data[n - 1] == c) break; } //The SOCKET_FLAG_WAIT_ALL flag causes the function to return //only when the requested number of bytes have been read else if(!(flags & SOCKET_FLAG_WAIT_ALL)) { break; } //Advance data pointer data += n; } //Successful read operation return NO_ERROR; } /** * @brief Shutdown gracefully reception, transmission, or both * * Note that socketShutdown() does not close the socket, and resources attached * to the socket will not be freed until socketClose() is invoked * * @param[in] socket Handle to a socket * @param[in] how Flag that describes what types of operation will no longer be allowed * @return Error code **/ 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; //FIN-WAIT-1, CLOSING or LAST-ACK state? case TCP_STATE_FIN_WAIT_1: case TCP_STATE_CLOSING: case 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-2 or TIME-WAIT 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; } /** * @brief Abort an existing TCP connection * @param[in] socket Handle identifying the socket to close * @return Error code **/ 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; } } /** * @brief Get the current state of the TCP FSM * @param[in] socket Handle identifying the socket * @return TCP FSM state **/ TcpState tcpGetState(Socket *socket) { TcpState state; //Get exclusive access osAcquireMutex(&netMutex); //Get TCP FSM current state state = socket->state; //Release exclusive access osReleaseMutex(&netMutex); //Return current state return state; } /** * @brief Kill the oldest socket in the TIME-WAIT state * @return Handle identifying the oldest TCP connection in the TIME-WAIT state. * NULL is returned if no socket is currently in the TIME-WAIT state **/ 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; } #endif