📄 tcp.c
字号:
}
/** Retrieves the connection info structure of a given connection to a host.
*
* \param Port TCP port on the device in the connection, specified in big endian
* \param RemoteAddress Remote protocol IP address of the connected host
* \param RemotePort Remote TCP port of the connected host, specified in big endian
*
* \return ConnectionInfo structure of the connection if found, NULL otherwise
*/
TCP_ConnectionInfo_t* TCP_GetConnectionInfo(uint16_t Port, IP_Address_t RemoteAddress, uint16_t RemotePort)
{
/* Note, Port number should be specified in BIG endian to simplify network code */
for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++)
{
/* Find port entry in the table */
if ((ConnectionStateTable[CSTableEntry].Port == Port) &&
IP_COMPARE(&ConnectionStateTable[CSTableEntry].RemoteAddress, &RemoteAddress) &&
ConnectionStateTable[CSTableEntry].RemotePort == RemotePort)
{
return &ConnectionStateTable[CSTableEntry].Info;
}
}
return NULL;
}
/** Processes a TCP packet inside an Ethernet frame, and writes the appropriate response
* to the output Ethernet frame if one is created by a application handler.
*
* \param IPHeaderInStart Pointer to the start of the incoming packet's IP header
* \param TCPHeaderInStart Pointer to the start of the incoming packet's TCP header
* \param TCPHeaderOutStart Pointer to the start of the outgoing packet's TCP header
*
* \return The number of bytes written to the out Ethernet frame if any, NO_RESPONSE if no
* response was generated, NO_PROCESS if the packet processing was deferred until the
* next Ethernet packet handler iteration
*/
int16_t TCP_ProcessTCPPacket(void* IPHeaderInStart, void* TCPHeaderInStart, void* TCPHeaderOutStart)
{
IP_Header_t* IPHeaderIN = (IP_Header_t*)IPHeaderInStart;
TCP_Header_t* TCPHeaderIN = (TCP_Header_t*)TCPHeaderInStart;
TCP_Header_t* TCPHeaderOUT = (TCP_Header_t*)TCPHeaderOutStart;
TCP_ConnectionInfo_t* ConnectionInfo;
DecodeTCPHeader(TCPHeaderInStart);
bool PacketResponse = false;
/* Check if the destination port is open and allows incoming connections */
if (TCP_GetPortState(TCPHeaderIN->DestinationPort) == TCP_Port_Open)
{
/* Detect SYN from host to start a connection */
if (TCPHeaderIN->Flags & TCP_FLAG_SYN)
TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress, TCPHeaderIN->SourcePort, TCP_Connection_Listen);
/* Detect RST from host to abort existing connection */
if (TCPHeaderIN->Flags & TCP_FLAG_RST)
{
TCPHeaderOUT->Flags = (TCP_FLAG_RST | TCP_FLAG_ACK);
PacketResponse = true;
TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
TCPHeaderIN->SourcePort, TCP_Connection_Closed);
}
else
{
/* Process the incoming TCP packet based on the current connection state for the sender and port */
switch (TCP_GetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress, TCPHeaderIN->SourcePort))
{
case TCP_Connection_Listen:
if (TCPHeaderIN->Flags == TCP_FLAG_SYN)
{
/* SYN connection when closed starts a connection with a peer */
TCPHeaderOUT->Flags = (TCP_FLAG_SYN | TCP_FLAG_ACK);
PacketResponse = true;
TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress, TCPHeaderIN->SourcePort,
TCP_Connection_SYNReceived);
ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress, TCPHeaderIN->SourcePort);
ConnectionInfo->SequenceNumberIn = (SwapEndian_32(TCPHeaderIN->SequenceNumber) + 1);
ConnectionInfo->SequenceNumberOut = 0;
ConnectionInfo->Buffer.InUse = false;
}
break;
case TCP_Connection_SYNReceived:
if (TCPHeaderIN->Flags == TCP_FLAG_ACK)
{
/* ACK during the connection process completes the connection to a peer */
TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
TCPHeaderIN->SourcePort, TCP_Connection_Established);
ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
TCPHeaderIN->SourcePort);
ConnectionInfo->SequenceNumberOut++;
}
break;
case TCP_Connection_Established:
if (TCPHeaderIN->Flags == (TCP_FLAG_FIN | TCP_FLAG_ACK))
{
/* FIN ACK when connected to a peer starts the finalization process */
TCPHeaderOUT->Flags = (TCP_FLAG_FIN | TCP_FLAG_ACK);
PacketResponse = true;
TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
TCPHeaderIN->SourcePort, TCP_Connection_CloseWait);
ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
TCPHeaderIN->SourcePort);
ConnectionInfo->SequenceNumberIn++;
ConnectionInfo->SequenceNumberOut++;
}
else if ((TCPHeaderIN->Flags == TCP_FLAG_ACK) || (TCPHeaderIN->Flags == (TCP_FLAG_ACK | TCP_FLAG_PSH)))
{
ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
TCPHeaderIN->SourcePort);
/* Check if the buffer is currently in use either by a buffered data to send, or receive */
if ((ConnectionInfo->Buffer.InUse == false) && (ConnectionInfo->Buffer.Ready == false))
{
ConnectionInfo->Buffer.Direction = TCP_PACKETDIR_IN;
ConnectionInfo->Buffer.InUse = true;
ConnectionInfo->Buffer.Length = 0;
}
/* Check if the buffer has been claimed by us to read in data from the peer */
if ((ConnectionInfo->Buffer.Direction == TCP_PACKETDIR_IN) &&
(ConnectionInfo->Buffer.Length != TCP_WINDOW_SIZE))
{
uint16_t IPOffset = (IPHeaderIN->HeaderLength * sizeof(uint32_t));
uint16_t TCPOffset = (TCPHeaderIN->DataOffset * sizeof(uint32_t));
uint16_t DataLength = (SwapEndian_16(IPHeaderIN->TotalLength) - IPOffset - TCPOffset);
/* Copy the packet data into the buffer */
memcpy(&ConnectionInfo->Buffer.Data[ConnectionInfo->Buffer.Length],
&((uint8_t*)TCPHeaderInStart)[TCPOffset],
DataLength);
ConnectionInfo->SequenceNumberIn += DataLength;
ConnectionInfo->Buffer.Length += DataLength;
/* Check if the buffer is full or if the PSH flag is set, if so indicate buffer ready */
if ((!(TCP_WINDOW_SIZE - ConnectionInfo->Buffer.Length)) || (TCPHeaderIN->Flags & TCP_FLAG_PSH))
{
ConnectionInfo->Buffer.InUse = false;
ConnectionInfo->Buffer.Ready = true;
TCPHeaderOUT->Flags = TCP_FLAG_ACK;
PacketResponse = true;
}
}
else
{
/* Buffer is currently in use by the application, defer processing of the incoming packet */
return NO_PROCESS;
}
}
break;
case TCP_Connection_Closing:
ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
TCPHeaderIN->SourcePort);
TCPHeaderOUT->Flags = (TCP_FLAG_ACK | TCP_FLAG_FIN);
PacketResponse = true;
ConnectionInfo->Buffer.InUse = false;
TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
TCPHeaderIN->SourcePort, TCP_Connection_FINWait1);
break;
case TCP_Connection_FINWait1:
if (TCPHeaderIN->Flags == (TCP_FLAG_FIN | TCP_FLAG_ACK))
{
ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
TCPHeaderIN->SourcePort);
TCPHeaderOUT->Flags = TCP_FLAG_ACK;
PacketResponse = true;
ConnectionInfo->SequenceNumberIn++;
ConnectionInfo->SequenceNumberOut++;
TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
TCPHeaderIN->SourcePort, TCP_Connection_Closed);
}
else if (TCPHeaderIN->Flags == TCP_FLAG_ACK)
{
TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
TCPHeaderIN->SourcePort, TCP_Connection_FINWait2);
}
break;
case TCP_Connection_FINWait2:
if (TCPHeaderIN->Flags == (TCP_FLAG_FIN | TCP_FLAG_ACK))
{
ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
TCPHeaderIN->SourcePort);
TCPHeaderOUT->Flags = TCP_FLAG_ACK;
PacketResponse = true;
ConnectionInfo->SequenceNumberIn++;
ConnectionInfo->SequenceNumberOut++;
TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
TCPHeaderIN->SourcePort, TCP_Connection_Closed);
}
break;
case TCP_Connection_CloseWait:
if (TCPHeaderIN->Flags == TCP_FLAG_ACK)
{
TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
TCPHeaderIN->SourcePort, TCP_Connection_Closed);
}
break;
}
}
}
else
{
/* Port is not open, indicate via a RST/ACK response to the sender */
TCPHeaderOUT->Flags = (TCP_FLAG_RST | TCP_FLAG_ACK);
PacketResponse = true;
}
/* Check if we need to respond to the sent packet */
if (PacketResponse)
{
ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
TCPHeaderIN->SourcePort);
TCPHeaderOUT->SourcePort = TCPHeaderIN->DestinationPort;
TCPHeaderOUT->DestinationPort = TCPHeaderIN->SourcePort;
TCPHeaderOUT->SequenceNumber = SwapEndian_32(ConnectionInfo->SequenceNumberOut);
TCPHeaderOUT->AcknowledgmentNumber = SwapEndian_32(ConnectionInfo->SequenceNumberIn);
TCPHeaderOUT->DataOffset = (sizeof(TCP_Header_t) / sizeof(uint32_t));
if (!(ConnectionInfo->Buffer.InUse))
TCPHeaderOUT->WindowSize = SwapEndian_16(TCP_WINDOW_SIZE);
else
TCPHeaderOUT->WindowSize = SwapEndian_16(TCP_WINDOW_SIZE - ConnectionInfo->Buffer.Length);
TCPHeaderOUT->UrgentPointer = 0;
TCPHeaderOUT->Checksum = 0;
TCPHeaderOUT->Reserved = 0;
TCPHeaderOUT->Checksum = TCP_Checksum16(TCPHeaderOUT, IPHeaderIN->DestinationAddress,
IPHeaderIN->SourceAddress, sizeof(TCP_Header_t));
return sizeof(TCP_Header_t);
}
return NO_RESPONSE;
}
/** Calculates the appropriate TCP checksum, consisting of the addition of the one's compliment of each word,
* complimented.
*
* \param TCPHeaderOutStart Pointer to the start of the packet's outgoing TCP header
* \param SourceAddress Source protocol IP address of the outgoing IP header
* \param SourceAddress DestinationAddress protocol IP address of the outgoing IP header
* \param TCPOutSize Size in bytes of the TCP data header and payload
*
* \return A 16-bit TCP checksum value
*/
static uint16_t TCP_Checksum16(void* TCPHeaderOutStart, IP_Address_t SourceAddress,
IP_Address_t DestinationAddress, uint16_t TCPOutSize)
{
uint32_t Checksum = 0;
/* TCP/IP checksums are the addition of the one's compliment of each word including the IP pseudo-header,
complimented */
Checksum += ((uint16_t*)&SourceAddress)[0];
Checksum += ((uint16_t*)&SourceAddress)[1];
Checksum += ((uint16_t*)&DestinationAddress)[0];
Checksum += ((uint16_t*)&DestinationAddress)[1];
Checksum += SwapEndian_16(PROTOCOL_TCP);
Checksum += SwapEndian_16(TCPOutSize);
for (uint8_t CurrWord = 0; CurrWord < (TCPOutSize >> 1); CurrWord++)
Checksum += ((uint16_t*)TCPHeaderOutStart)[CurrWord];
if (TCPOutSize & 0x01)
Checksum += (((uint16_t*)TCPHeaderOutStart)[TCPOutSize >> 1] & 0x00FF);
while (Checksum & 0xFFFF0000)
Checksum = ((Checksum & 0xFFFF) + (Checksum >> 16));
return ~Checksum;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -