diff options
Diffstat (limited to 'lib/lufa/Demos/Device/LowLevel/RNDISEthernet/Lib/TCP.c')
| -rw-r--r-- | lib/lufa/Demos/Device/LowLevel/RNDISEthernet/Lib/TCP.c | 631 |
1 files changed, 631 insertions, 0 deletions
diff --git a/lib/lufa/Demos/Device/LowLevel/RNDISEthernet/Lib/TCP.c b/lib/lufa/Demos/Device/LowLevel/RNDISEthernet/Lib/TCP.c new file mode 100644 index 000000000..a6f1f6adf --- /dev/null +++ b/lib/lufa/Demos/Device/LowLevel/RNDISEthernet/Lib/TCP.c | |||
| @@ -0,0 +1,631 @@ | |||
| 1 | /* | ||
| 2 | LUFA Library | ||
| 3 | Copyright (C) Dean Camera, 2017. | ||
| 4 | |||
| 5 | dean [at] fourwalledcubicle [dot] com | ||
| 6 | www.lufa-lib.org | ||
| 7 | */ | ||
| 8 | |||
| 9 | /* | ||
| 10 | Copyright 2017 Dean Camera (dean [at] fourwalledcubicle [dot] com) | ||
| 11 | |||
| 12 | Permission to use, copy, modify, distribute, and sell this | ||
| 13 | software and its documentation for any purpose is hereby granted | ||
| 14 | without fee, provided that the above copyright notice appear in | ||
| 15 | all copies and that both that the copyright notice and this | ||
| 16 | permission notice and warranty disclaimer appear in supporting | ||
| 17 | documentation, and that the name of the author not be used in | ||
| 18 | advertising or publicity pertaining to distribution of the | ||
| 19 | software without specific, written prior permission. | ||
| 20 | |||
| 21 | The author disclaims all warranties with regard to this | ||
| 22 | software, including all implied warranties of merchantability | ||
| 23 | and fitness. In no event shall the author be liable for any | ||
| 24 | special, indirect or consequential damages or any damages | ||
| 25 | whatsoever resulting from loss of use, data or profits, whether | ||
| 26 | in an action of contract, negligence or other tortious action, | ||
| 27 | arising out of or in connection with the use or performance of | ||
| 28 | this software. | ||
| 29 | */ | ||
| 30 | |||
| 31 | /** \file | ||
| 32 | * | ||
| 33 | * Transmission Control Protocol (TCP) packet handling routines. This protocol handles the reliable in-order transmission | ||
| 34 | * and reception of packets to and from devices on a network, to "ports" on the device. It is used in situations where data | ||
| 35 | * delivery must be reliable and correct, e.g. HTTP, TELNET and most other non-streaming protocols. | ||
| 36 | */ | ||
| 37 | |||
| 38 | #define INCLUDE_FROM_TCP_C | ||
| 39 | #include "TCP.h" | ||
| 40 | |||
| 41 | /** Port state table array. This contains the current status of TCP ports in the device. To save on space, only open ports are | ||
| 42 | * stored - closed ports may be overwritten at any time, and the system will assume any ports not present in the array are closed. This | ||
| 43 | * allows for MAX_OPEN_TCP_PORTS to be less than the number of ports used by the application if desired. | ||
| 44 | */ | ||
| 45 | TCP_PortState_t PortStateTable[MAX_OPEN_TCP_PORTS]; | ||
| 46 | |||
| 47 | /** Connection state table array. This contains the current status of TCP connections in the device. To save on space, only active | ||
| 48 | * (non-closed) connections are stored - closed connections may be overwritten at any time, and the system will assume any connections | ||
| 49 | * not present in the array are closed. | ||
| 50 | */ | ||
| 51 | TCP_ConnectionState_t ConnectionStateTable[MAX_TCP_CONNECTIONS]; | ||
| 52 | |||
| 53 | |||
| 54 | /** Task to handle the calling of each registered application's callback function, to process and generate TCP packets at the application | ||
| 55 | * level. If an application produces a response, this task constructs the appropriate Ethernet frame and places it into the Ethernet OUT | ||
| 56 | * buffer for later transmission. | ||
| 57 | */ | ||
| 58 | void TCP_Task(void) | ||
| 59 | { | ||
| 60 | /* Run each application in sequence, to process incoming and generate outgoing packets */ | ||
| 61 | for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++) | ||
| 62 | { | ||
| 63 | /* Find the corresponding port entry in the port table */ | ||
| 64 | for (uint8_t PTableEntry = 0; PTableEntry < MAX_OPEN_TCP_PORTS; PTableEntry++) | ||
| 65 | { | ||
| 66 | /* Run the application handler for the port */ | ||
| 67 | if ((PortStateTable[PTableEntry].Port == ConnectionStateTable[CSTableEntry].Port) && | ||
| 68 | (PortStateTable[PTableEntry].State == TCP_Port_Open)) | ||
| 69 | { | ||
| 70 | PortStateTable[PTableEntry].ApplicationHandler(&ConnectionStateTable[CSTableEntry], | ||
| 71 | &ConnectionStateTable[CSTableEntry].Info.Buffer); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | /* Bail out early if there is already a frame waiting to be sent in the Ethernet OUT buffer */ | ||
| 77 | if (FrameOUT.FrameLength) | ||
| 78 | return; | ||
| 79 | |||
| 80 | /* Send response packets from each application as the TCP packet buffers are filled by the applications */ | ||
| 81 | for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++) | ||
| 82 | { | ||
| 83 | /* For each completely received packet, pass it along to the listening application */ | ||
| 84 | if ((ConnectionStateTable[CSTableEntry].Info.Buffer.Direction == TCP_PACKETDIR_OUT) && | ||
| 85 | (ConnectionStateTable[CSTableEntry].Info.Buffer.Ready)) | ||
| 86 | { | ||
| 87 | Ethernet_Frame_Header_t* FrameOUTHeader = (Ethernet_Frame_Header_t*)&FrameOUT.FrameData; | ||
| 88 | IP_Header_t* IPHeaderOUT = (IP_Header_t*)&FrameOUT.FrameData[sizeof(Ethernet_Frame_Header_t)]; | ||
| 89 | TCP_Header_t* TCPHeaderOUT = (TCP_Header_t*)&FrameOUT.FrameData[sizeof(Ethernet_Frame_Header_t) + | ||
| 90 | sizeof(IP_Header_t)]; | ||
| 91 | void* TCPDataOUT = &FrameOUT.FrameData[sizeof(Ethernet_Frame_Header_t) + | ||
| 92 | sizeof(IP_Header_t) + | ||
| 93 | sizeof(TCP_Header_t)]; | ||
| 94 | |||
| 95 | uint16_t PacketSize = ConnectionStateTable[CSTableEntry].Info.Buffer.Length; | ||
| 96 | |||
| 97 | /* Fill out the TCP data */ | ||
| 98 | TCPHeaderOUT->SourcePort = ConnectionStateTable[CSTableEntry].Port; | ||
| 99 | TCPHeaderOUT->DestinationPort = ConnectionStateTable[CSTableEntry].RemotePort; | ||
| 100 | TCPHeaderOUT->SequenceNumber = SwapEndian_32(ConnectionStateTable[CSTableEntry].Info.SequenceNumberOut); | ||
| 101 | TCPHeaderOUT->AcknowledgmentNumber = SwapEndian_32(ConnectionStateTable[CSTableEntry].Info.SequenceNumberIn); | ||
| 102 | TCPHeaderOUT->DataOffset = (sizeof(TCP_Header_t) / sizeof(uint32_t)); | ||
| 103 | TCPHeaderOUT->WindowSize = SwapEndian_16(TCP_WINDOW_SIZE); | ||
| 104 | |||
| 105 | TCPHeaderOUT->Flags = TCP_FLAG_ACK; | ||
| 106 | TCPHeaderOUT->UrgentPointer = 0; | ||
| 107 | TCPHeaderOUT->Checksum = 0; | ||
| 108 | TCPHeaderOUT->Reserved = 0; | ||
| 109 | |||
| 110 | memcpy(TCPDataOUT, ConnectionStateTable[CSTableEntry].Info.Buffer.Data, PacketSize); | ||
| 111 | |||
| 112 | ConnectionStateTable[CSTableEntry].Info.SequenceNumberOut += PacketSize; | ||
| 113 | |||
| 114 | TCPHeaderOUT->Checksum = TCP_Checksum16(TCPHeaderOUT, &ServerIPAddress, | ||
| 115 | &ConnectionStateTable[CSTableEntry].RemoteAddress, | ||
| 116 | (sizeof(TCP_Header_t) + PacketSize)); | ||
| 117 | |||
| 118 | PacketSize += sizeof(TCP_Header_t); | ||
| 119 | |||
| 120 | /* Fill out the response IP header */ | ||
| 121 | IPHeaderOUT->TotalLength = SwapEndian_16(sizeof(IP_Header_t) + PacketSize); | ||
| 122 | IPHeaderOUT->TypeOfService = 0; | ||
| 123 | IPHeaderOUT->HeaderLength = (sizeof(IP_Header_t) / sizeof(uint32_t)); | ||
| 124 | IPHeaderOUT->Version = 4; | ||
| 125 | IPHeaderOUT->Flags = 0; | ||
| 126 | IPHeaderOUT->FragmentOffset = 0; | ||
| 127 | IPHeaderOUT->Identification = 0; | ||
| 128 | IPHeaderOUT->HeaderChecksum = 0; | ||
| 129 | IPHeaderOUT->Protocol = PROTOCOL_TCP; | ||
| 130 | IPHeaderOUT->TTL = DEFAULT_TTL; | ||
| 131 | IPHeaderOUT->SourceAddress = ServerIPAddress; | ||
| 132 | IPHeaderOUT->DestinationAddress = ConnectionStateTable[CSTableEntry].RemoteAddress; | ||
| 133 | |||
| 134 | IPHeaderOUT->HeaderChecksum = Ethernet_Checksum16(IPHeaderOUT, sizeof(IP_Header_t)); | ||
| 135 | |||
| 136 | PacketSize += sizeof(IP_Header_t); | ||
| 137 | |||
| 138 | /* Fill out the response Ethernet frame header */ | ||
| 139 | FrameOUTHeader->Source = ServerMACAddress; | ||
| 140 | FrameOUTHeader->Destination = (MAC_Address_t){{0x02, 0x00, 0x02, 0x00, 0x02, 0x00}}; | ||
| 141 | FrameOUTHeader->EtherType = SwapEndian_16(ETHERTYPE_IPV4); | ||
| 142 | |||
| 143 | PacketSize += sizeof(Ethernet_Frame_Header_t); | ||
| 144 | |||
| 145 | /* Set the response length in the buffer and indicate that a response is ready to be sent */ | ||
| 146 | FrameOUT.FrameLength = PacketSize; | ||
| 147 | |||
| 148 | ConnectionStateTable[CSTableEntry].Info.Buffer.Ready = false; | ||
| 149 | |||
| 150 | break; | ||
| 151 | } | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | /** Initializes the TCP protocol handler, clearing the port and connection state tables. This must be called before TCP packets are | ||
| 156 | * processed. | ||
| 157 | */ | ||
| 158 | void TCP_Init(void) | ||
| 159 | { | ||
| 160 | /* Initialize the port state table with all CLOSED entries */ | ||
| 161 | for (uint8_t PTableEntry = 0; PTableEntry < MAX_OPEN_TCP_PORTS; PTableEntry++) | ||
| 162 | PortStateTable[PTableEntry].State = TCP_Port_Closed; | ||
| 163 | |||
| 164 | /* Initialize the connection table with all CLOSED entries */ | ||
| 165 | for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++) | ||
| 166 | ConnectionStateTable[CSTableEntry].State = TCP_Connection_Closed; | ||
| 167 | } | ||
| 168 | |||
| 169 | /** Sets the state and callback handler of the given port, specified in big endian to the given state. | ||
| 170 | * | ||
| 171 | * \param[in] Port Port whose state and callback function to set, specified in big endian | ||
| 172 | * \param[in] State New state of the port, a value from the \ref TCP_PortStates_t enum | ||
| 173 | * \param[in] Handler Application callback handler for the port | ||
| 174 | * | ||
| 175 | * \return Boolean \c true if the port state was set, \c false otherwise (no more space in the port state table) | ||
| 176 | */ | ||
| 177 | bool TCP_SetPortState(const uint16_t Port, | ||
| 178 | const uint8_t State, | ||
| 179 | void (*Handler)(TCP_ConnectionState_t*, TCP_ConnectionBuffer_t*)) | ||
| 180 | { | ||
| 181 | /* Note, Port number should be specified in BIG endian to simplify network code */ | ||
| 182 | |||
| 183 | /* Check to see if the port entry is already in the port state table */ | ||
| 184 | for (uint8_t PTableEntry = 0; PTableEntry < MAX_OPEN_TCP_PORTS; PTableEntry++) | ||
| 185 | { | ||
| 186 | /* Find existing entry for the port in the table, update it if found */ | ||
| 187 | if (PortStateTable[PTableEntry].Port == Port) | ||
| 188 | { | ||
| 189 | PortStateTable[PTableEntry].State = State; | ||
| 190 | PortStateTable[PTableEntry].ApplicationHandler = Handler; | ||
| 191 | return true; | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | /* Check if trying to open the port -- if so we need to find an unused (closed) entry and replace it */ | ||
| 196 | if (State == TCP_Port_Open) | ||
| 197 | { | ||
| 198 | for (uint8_t PTableEntry = 0; PTableEntry < MAX_OPEN_TCP_PORTS; PTableEntry++) | ||
| 199 | { | ||
| 200 | /* Find a closed port entry in the table, change it to the given port and state */ | ||
| 201 | if (PortStateTable[PTableEntry].State == TCP_Port_Closed) | ||
| 202 | { | ||
| 203 | PortStateTable[PTableEntry].Port = Port; | ||
| 204 | PortStateTable[PTableEntry].State = State; | ||
| 205 | PortStateTable[PTableEntry].ApplicationHandler = Handler; | ||
| 206 | return true; | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | /* Port not in table and no room to add it, return failure */ | ||
| 211 | return false; | ||
| 212 | } | ||
| 213 | else | ||
| 214 | { | ||
| 215 | /* Port not in table but trying to close it, so operation successful */ | ||
| 216 | return true; | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | /** Retrieves the current state of a given TCP port, specified in big endian. | ||
| 221 | * | ||
| 222 | * \param[in] Port TCP port whose state is to be retrieved, given in big-endian | ||
| 223 | * | ||
| 224 | * \return A value from the \ref TCP_PortStates_t enum | ||
| 225 | */ | ||
| 226 | uint8_t TCP_GetPortState(const uint16_t Port) | ||
| 227 | { | ||
| 228 | /* Note, Port number should be specified in BIG endian to simplify network code */ | ||
| 229 | |||
| 230 | for (uint8_t PTableEntry = 0; PTableEntry < MAX_OPEN_TCP_PORTS; PTableEntry++) | ||
| 231 | { | ||
| 232 | /* Find existing entry for the port in the table, return the port status if found */ | ||
| 233 | if (PortStateTable[PTableEntry].Port == Port) | ||
| 234 | return PortStateTable[PTableEntry].State; | ||
| 235 | } | ||
| 236 | |||
| 237 | /* Port not in table, assume closed */ | ||
| 238 | return TCP_Port_Closed; | ||
| 239 | } | ||
| 240 | |||
| 241 | /** Sets the connection state of the given port, remote address and remote port to the given TCP connection state. If the | ||
| 242 | * connection exists in the connection state table it is updated, otherwise it is created if possible. | ||
| 243 | * | ||
| 244 | * \param[in] Port TCP port of the connection on the device, specified in big endian | ||
| 245 | * \param[in] RemoteAddress Remote protocol IP address of the connected device | ||
| 246 | * \param[in] RemotePort TCP port of the remote device in the connection, specified in big endian | ||
| 247 | * \param[in] State TCP connection state, a value from the \ref TCP_ConnectionStates_t enum | ||
| 248 | * | ||
| 249 | * \return Boolean \c true if the connection was updated or created, \c false otherwise (no more space in the connection state table) | ||
| 250 | */ | ||
| 251 | bool TCP_SetConnectionState(const uint16_t Port, | ||
| 252 | const IP_Address_t* RemoteAddress, | ||
| 253 | const uint16_t RemotePort, | ||
| 254 | const uint8_t State) | ||
| 255 | { | ||
| 256 | /* Note, Port number should be specified in BIG endian to simplify network code */ | ||
| 257 | |||
| 258 | for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++) | ||
| 259 | { | ||
| 260 | /* Find port entry in the table */ | ||
| 261 | if ((ConnectionStateTable[CSTableEntry].Port == Port) && | ||
| 262 | IP_COMPARE(&ConnectionStateTable[CSTableEntry].RemoteAddress, RemoteAddress) && | ||
| 263 | ConnectionStateTable[CSTableEntry].RemotePort == RemotePort) | ||
| 264 | { | ||
| 265 | ConnectionStateTable[CSTableEntry].State = State; | ||
| 266 | return true; | ||
| 267 | } | ||
| 268 | } | ||
| 269 | |||
| 270 | for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++) | ||
| 271 | { | ||
| 272 | /* Find empty entry in the table */ | ||
| 273 | if (ConnectionStateTable[CSTableEntry].State == TCP_Connection_Closed) | ||
| 274 | { | ||
| 275 | ConnectionStateTable[CSTableEntry].Port = Port; | ||
| 276 | ConnectionStateTable[CSTableEntry].RemoteAddress = *RemoteAddress; | ||
| 277 | ConnectionStateTable[CSTableEntry].RemotePort = RemotePort; | ||
| 278 | ConnectionStateTable[CSTableEntry].State = State; | ||
| 279 | return true; | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | return false; | ||
| 284 | } | ||
| 285 | |||
| 286 | /** Retrieves the current state of a given TCP connection to a host. | ||
| 287 | * | ||
| 288 | * \param[in] Port TCP port on the device in the connection, specified in big endian | ||
| 289 | * \param[in] RemoteAddress Remote protocol IP address of the connected host | ||
| 290 | * \param[in] RemotePort Remote TCP port of the connected host, specified in big endian | ||
| 291 | * | ||
| 292 | * \return A value from the \ref TCP_ConnectionStates_t enum | ||
| 293 | */ | ||
| 294 | uint8_t TCP_GetConnectionState(const uint16_t Port, | ||
| 295 | const IP_Address_t* RemoteAddress, | ||
| 296 | const uint16_t RemotePort) | ||
| 297 | { | ||
| 298 | /* Note, Port number should be specified in BIG endian to simplify network code */ | ||
| 299 | |||
| 300 | for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++) | ||
| 301 | { | ||
| 302 | /* Find port entry in the table */ | ||
| 303 | if ((ConnectionStateTable[CSTableEntry].Port == Port) && | ||
| 304 | IP_COMPARE(&ConnectionStateTable[CSTableEntry].RemoteAddress, RemoteAddress) && | ||
| 305 | ConnectionStateTable[CSTableEntry].RemotePort == RemotePort) | ||
| 306 | |||
| 307 | { | ||
| 308 | return ConnectionStateTable[CSTableEntry].State; | ||
| 309 | } | ||
| 310 | } | ||
| 311 | |||
| 312 | return TCP_Connection_Closed; | ||
| 313 | } | ||
| 314 | |||
| 315 | /** Retrieves the connection info structure of a given connection to a host. | ||
| 316 | * | ||
| 317 | * \param[in] Port TCP port on the device in the connection, specified in big endian | ||
| 318 | * \param[in] RemoteAddress Remote protocol IP address of the connected host | ||
| 319 | * \param[in] RemotePort Remote TCP port of the connected host, specified in big endian | ||
| 320 | * | ||
| 321 | * \return ConnectionInfo structure of the connection if found, NULL otherwise | ||
| 322 | */ | ||
| 323 | TCP_ConnectionInfo_t* TCP_GetConnectionInfo(const uint16_t Port, | ||
| 324 | const IP_Address_t* RemoteAddress, | ||
| 325 | const uint16_t RemotePort) | ||
| 326 | { | ||
| 327 | /* Note, Port number should be specified in BIG endian to simplify network code */ | ||
| 328 | |||
| 329 | for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++) | ||
| 330 | { | ||
| 331 | /* Find port entry in the table */ | ||
| 332 | if ((ConnectionStateTable[CSTableEntry].Port == Port) && | ||
| 333 | IP_COMPARE(&ConnectionStateTable[CSTableEntry].RemoteAddress, RemoteAddress) && | ||
| 334 | ConnectionStateTable[CSTableEntry].RemotePort == RemotePort) | ||
| 335 | { | ||
| 336 | return &ConnectionStateTable[CSTableEntry].Info; | ||
| 337 | } | ||
| 338 | } | ||
| 339 | |||
| 340 | return NULL; | ||
| 341 | } | ||
| 342 | |||
| 343 | /** Processes a TCP packet inside an Ethernet frame, and writes the appropriate response | ||
| 344 | * to the output Ethernet frame if one is created by a application handler. | ||
| 345 | * | ||
| 346 | * \param[in] IPHeaderInStart Pointer to the start of the incoming packet's IP header | ||
| 347 | * \param[in] TCPHeaderInStart Pointer to the start of the incoming packet's TCP header | ||
| 348 | * \param[out] TCPHeaderOutStart Pointer to the start of the outgoing packet's TCP header | ||
| 349 | * | ||
| 350 | * \return The number of bytes written to the out Ethernet frame if any, NO_RESPONSE if no | ||
| 351 | * response was generated, NO_PROCESS if the packet processing was deferred until the | ||
| 352 | * next Ethernet packet handler iteration | ||
| 353 | */ | ||
| 354 | int16_t TCP_ProcessTCPPacket(void* IPHeaderInStart, | ||
| 355 | void* TCPHeaderInStart, | ||
| 356 | void* TCPHeaderOutStart) | ||
| 357 | { | ||
| 358 | IP_Header_t* IPHeaderIN = (IP_Header_t*)IPHeaderInStart; | ||
| 359 | TCP_Header_t* TCPHeaderIN = (TCP_Header_t*)TCPHeaderInStart; | ||
| 360 | TCP_Header_t* TCPHeaderOUT = (TCP_Header_t*)TCPHeaderOutStart; | ||
| 361 | |||
| 362 | TCP_ConnectionInfo_t* ConnectionInfo; | ||
| 363 | |||
| 364 | DecodeTCPHeader(TCPHeaderInStart); | ||
| 365 | |||
| 366 | bool PacketResponse = false; | ||
| 367 | |||
| 368 | /* Check if the destination port is open and allows incoming connections */ | ||
| 369 | if (TCP_GetPortState(TCPHeaderIN->DestinationPort) == TCP_Port_Open) | ||
| 370 | { | ||
| 371 | /* Detect SYN from host to start a connection */ | ||
| 372 | if (TCPHeaderIN->Flags & TCP_FLAG_SYN) | ||
| 373 | TCP_SetConnectionState(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, TCPHeaderIN->SourcePort, TCP_Connection_Listen); | ||
| 374 | |||
| 375 | /* Detect RST from host to abort existing connection */ | ||
| 376 | if (TCPHeaderIN->Flags & TCP_FLAG_RST) | ||
| 377 | { | ||
| 378 | if (TCP_SetConnectionState(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, | ||
| 379 | TCPHeaderIN->SourcePort, TCP_Connection_Closed)) | ||
| 380 | { | ||
| 381 | TCPHeaderOUT->Flags = (TCP_FLAG_RST | TCP_FLAG_ACK); | ||
| 382 | PacketResponse = true; | ||
| 383 | } | ||
| 384 | } | ||
| 385 | else | ||
| 386 | { | ||
| 387 | /* Process the incoming TCP packet based on the current connection state for the sender and port */ | ||
| 388 | switch (TCP_GetConnectionState(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, TCPHeaderIN->SourcePort)) | ||
| 389 | { | ||
| 390 | case TCP_Connection_Listen: | ||
| 391 | if (TCPHeaderIN->Flags == TCP_FLAG_SYN) | ||
| 392 | { | ||
| 393 | /* SYN connection starts a connection with a peer */ | ||
| 394 | if (TCP_SetConnectionState(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, | ||
| 395 | TCPHeaderIN->SourcePort, TCP_Connection_SYNReceived)) | ||
| 396 | { | ||
| 397 | TCPHeaderOUT->Flags = (TCP_FLAG_SYN | TCP_FLAG_ACK); | ||
| 398 | |||
| 399 | ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, TCPHeaderIN->SourcePort); | ||
| 400 | |||
| 401 | ConnectionInfo->SequenceNumberIn = (SwapEndian_32(TCPHeaderIN->SequenceNumber) + 1); | ||
| 402 | ConnectionInfo->SequenceNumberOut = 0; | ||
| 403 | ConnectionInfo->Buffer.InUse = false; | ||
| 404 | } | ||
| 405 | else | ||
| 406 | { | ||
| 407 | TCPHeaderOUT->Flags = TCP_FLAG_RST; | ||
| 408 | } | ||
| 409 | |||
| 410 | PacketResponse = true; | ||
| 411 | } | ||
| 412 | |||
| 413 | break; | ||
| 414 | case TCP_Connection_SYNReceived: | ||
| 415 | if (TCPHeaderIN->Flags == TCP_FLAG_ACK) | ||
| 416 | { | ||
| 417 | /* ACK during the connection process completes the connection to a peer */ | ||
| 418 | |||
| 419 | TCP_SetConnectionState(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, | ||
| 420 | TCPHeaderIN->SourcePort, TCP_Connection_Established); | ||
| 421 | |||
| 422 | ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, | ||
| 423 | TCPHeaderIN->SourcePort); | ||
| 424 | |||
| 425 | ConnectionInfo->SequenceNumberOut++; | ||
| 426 | } | ||
| 427 | |||
| 428 | break; | ||
| 429 | case TCP_Connection_Established: | ||
| 430 | if (TCPHeaderIN->Flags == (TCP_FLAG_FIN | TCP_FLAG_ACK)) | ||
| 431 | { | ||
| 432 | /* FIN ACK when connected to a peer starts the finalization process */ | ||
| 433 | |||
| 434 | TCPHeaderOUT->Flags = (TCP_FLAG_FIN | TCP_FLAG_ACK); | ||
| 435 | PacketResponse = true; | ||
| 436 | |||
| 437 | TCP_SetConnectionState(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, | ||
| 438 | TCPHeaderIN->SourcePort, TCP_Connection_CloseWait); | ||
| 439 | |||
| 440 | ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, | ||
| 441 | TCPHeaderIN->SourcePort); | ||
| 442 | |||
| 443 | ConnectionInfo->SequenceNumberIn++; | ||
| 444 | ConnectionInfo->SequenceNumberOut++; | ||
| 445 | } | ||
| 446 | else if ((TCPHeaderIN->Flags == TCP_FLAG_ACK) || (TCPHeaderIN->Flags == (TCP_FLAG_ACK | TCP_FLAG_PSH))) | ||
| 447 | { | ||
| 448 | ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, | ||
| 449 | TCPHeaderIN->SourcePort); | ||
| 450 | |||
| 451 | /* Check if the buffer is currently in use either by a buffered data to send, or receive */ | ||
| 452 | if ((ConnectionInfo->Buffer.InUse == false) && (ConnectionInfo->Buffer.Ready == false)) | ||
| 453 | { | ||
| 454 | ConnectionInfo->Buffer.Direction = TCP_PACKETDIR_IN; | ||
| 455 | ConnectionInfo->Buffer.InUse = true; | ||
| 456 | ConnectionInfo->Buffer.Length = 0; | ||
| 457 | } | ||
| 458 | |||
| 459 | /* Check if the buffer has been claimed by us to read in data from the peer */ | ||
| 460 | if ((ConnectionInfo->Buffer.Direction == TCP_PACKETDIR_IN) && | ||
| 461 | (ConnectionInfo->Buffer.Length != TCP_WINDOW_SIZE)) | ||
| 462 | { | ||
| 463 | uint16_t IPOffset = (IPHeaderIN->HeaderLength * sizeof(uint32_t)); | ||
| 464 | uint16_t TCPOffset = (TCPHeaderIN->DataOffset * sizeof(uint32_t)); | ||
| 465 | uint16_t DataLength = (SwapEndian_16(IPHeaderIN->TotalLength) - IPOffset - TCPOffset); | ||
| 466 | |||
| 467 | /* Copy the packet data into the buffer */ | ||
| 468 | memcpy(&ConnectionInfo->Buffer.Data[ConnectionInfo->Buffer.Length], | ||
| 469 | &((uint8_t*)TCPHeaderInStart)[TCPOffset], | ||
| 470 | DataLength); | ||
| 471 | |||
| 472 | ConnectionInfo->SequenceNumberIn += DataLength; | ||
| 473 | ConnectionInfo->Buffer.Length += DataLength; | ||
| 474 | |||
| 475 | /* Check if the buffer is full or if the PSH flag is set, if so indicate buffer ready */ | ||
| 476 | if ((!(TCP_WINDOW_SIZE - ConnectionInfo->Buffer.Length)) || (TCPHeaderIN->Flags & TCP_FLAG_PSH)) | ||
| 477 | { | ||
| 478 | ConnectionInfo->Buffer.InUse = false; | ||
| 479 | ConnectionInfo->Buffer.Ready = true; | ||
| 480 | |||
| 481 | TCPHeaderOUT->Flags = TCP_FLAG_ACK; | ||
| 482 | PacketResponse = true; | ||
| 483 | } | ||
| 484 | } | ||
| 485 | else | ||
| 486 | { | ||
| 487 | /* Buffer is currently in use by the application, defer processing of the incoming packet */ | ||
| 488 | return NO_PROCESS; | ||
| 489 | } | ||
| 490 | } | ||
| 491 | |||
| 492 | break; | ||
| 493 | case TCP_Connection_Closing: | ||
| 494 | ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, | ||
| 495 | TCPHeaderIN->SourcePort); | ||
| 496 | |||
| 497 | TCPHeaderOUT->Flags = (TCP_FLAG_ACK | TCP_FLAG_FIN); | ||
| 498 | PacketResponse = true; | ||
| 499 | |||
| 500 | ConnectionInfo->Buffer.InUse = false; | ||
| 501 | |||
| 502 | TCP_SetConnectionState(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, | ||
| 503 | TCPHeaderIN->SourcePort, TCP_Connection_FINWait1); | ||
| 504 | |||
| 505 | break; | ||
| 506 | case TCP_Connection_FINWait1: | ||
| 507 | if (TCPHeaderIN->Flags == (TCP_FLAG_FIN | TCP_FLAG_ACK)) | ||
| 508 | { | ||
| 509 | ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, | ||
| 510 | TCPHeaderIN->SourcePort); | ||
| 511 | |||
| 512 | TCPHeaderOUT->Flags = TCP_FLAG_ACK; | ||
| 513 | PacketResponse = true; | ||
| 514 | |||
| 515 | ConnectionInfo->SequenceNumberIn++; | ||
| 516 | ConnectionInfo->SequenceNumberOut++; | ||
| 517 | |||
| 518 | TCP_SetConnectionState(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, | ||
| 519 | TCPHeaderIN->SourcePort, TCP_Connection_Closed); | ||
| 520 | } | ||
| 521 | else if (TCPHeaderIN->Flags == TCP_FLAG_ACK) | ||
| 522 | { | ||
| 523 | TCP_SetConnectionState(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, | ||
| 524 | TCPHeaderIN->SourcePort, TCP_Connection_FINWait2); | ||
| 525 | } | ||
| 526 | |||
| 527 | break; | ||
| 528 | case TCP_Connection_FINWait2: | ||
| 529 | if (TCPHeaderIN->Flags == (TCP_FLAG_FIN | TCP_FLAG_ACK)) | ||
| 530 | { | ||
| 531 | ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, | ||
| 532 | TCPHeaderIN->SourcePort); | ||
| 533 | |||
| 534 | TCPHeaderOUT->Flags = TCP_FLAG_ACK; | ||
| 535 | PacketResponse = true; | ||
| 536 | |||
| 537 | ConnectionInfo->SequenceNumberIn++; | ||
| 538 | ConnectionInfo->SequenceNumberOut++; | ||
| 539 | |||
| 540 | TCP_SetConnectionState(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, | ||
| 541 | TCPHeaderIN->SourcePort, TCP_Connection_Closed); | ||
| 542 | } | ||
| 543 | |||
| 544 | break; | ||
| 545 | case TCP_Connection_CloseWait: | ||
| 546 | if (TCPHeaderIN->Flags == TCP_FLAG_ACK) | ||
| 547 | { | ||
| 548 | TCP_SetConnectionState(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, | ||
| 549 | TCPHeaderIN->SourcePort, TCP_Connection_Closed); | ||
| 550 | } | ||
| 551 | |||
| 552 | break; | ||
| 553 | } | ||
| 554 | } | ||
| 555 | } | ||
| 556 | else | ||
| 557 | { | ||
| 558 | /* Port is not open, indicate via a RST/ACK response to the sender */ | ||
| 559 | TCPHeaderOUT->Flags = (TCP_FLAG_RST | TCP_FLAG_ACK); | ||
| 560 | PacketResponse = true; | ||
| 561 | } | ||
| 562 | |||
| 563 | /* Check if we need to respond to the sent packet */ | ||
| 564 | if (PacketResponse) | ||
| 565 | { | ||
| 566 | ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, &IPHeaderIN->SourceAddress, | ||
| 567 | TCPHeaderIN->SourcePort); | ||
| 568 | |||
| 569 | TCPHeaderOUT->SourcePort = TCPHeaderIN->DestinationPort; | ||
| 570 | TCPHeaderOUT->DestinationPort = TCPHeaderIN->SourcePort; | ||
| 571 | TCPHeaderOUT->SequenceNumber = SwapEndian_32(ConnectionInfo->SequenceNumberOut); | ||
| 572 | TCPHeaderOUT->AcknowledgmentNumber = SwapEndian_32(ConnectionInfo->SequenceNumberIn); | ||
| 573 | TCPHeaderOUT->DataOffset = (sizeof(TCP_Header_t) / sizeof(uint32_t)); | ||
| 574 | |||
| 575 | if (!(ConnectionInfo->Buffer.InUse)) | ||
| 576 | TCPHeaderOUT->WindowSize = SwapEndian_16(TCP_WINDOW_SIZE); | ||
| 577 | else | ||
| 578 | TCPHeaderOUT->WindowSize = SwapEndian_16(TCP_WINDOW_SIZE - ConnectionInfo->Buffer.Length); | ||
| 579 | |||
| 580 | TCPHeaderOUT->UrgentPointer = 0; | ||
| 581 | TCPHeaderOUT->Checksum = 0; | ||
| 582 | TCPHeaderOUT->Reserved = 0; | ||
| 583 | |||
| 584 | TCPHeaderOUT->Checksum = TCP_Checksum16(TCPHeaderOUT, &IPHeaderIN->DestinationAddress, | ||
| 585 | &IPHeaderIN->SourceAddress, sizeof(TCP_Header_t)); | ||
| 586 | |||
| 587 | return sizeof(TCP_Header_t); | ||
| 588 | } | ||
| 589 | |||
| 590 | return NO_RESPONSE; | ||
| 591 | } | ||
| 592 | |||
| 593 | /** Calculates the appropriate TCP checksum, consisting of the addition of the one's compliment of each word, | ||
| 594 | * complimented. | ||
| 595 | * | ||
| 596 | * \param[in] TCPHeaderOutStart Pointer to the start of the packet's outgoing TCP header | ||
| 597 | * \param[in] SourceAddress Source protocol IP address of the outgoing IP header | ||
| 598 | * \param[in] DestinationAddress Destination protocol IP address of the outgoing IP header | ||
| 599 | * \param[in] TCPOutSize Size in bytes of the TCP data header and payload | ||
| 600 | * | ||
| 601 | * \return A 16-bit TCP checksum value | ||
| 602 | */ | ||
| 603 | static uint16_t TCP_Checksum16(void* TCPHeaderOutStart, | ||
| 604 | const IP_Address_t* SourceAddress, | ||
| 605 | const IP_Address_t* DestinationAddress, | ||
| 606 | uint16_t TCPOutSize) | ||
| 607 | { | ||
| 608 | uint32_t Checksum = 0; | ||
| 609 | |||
| 610 | /* TCP/IP checksums are the addition of the one's compliment of each word including the IP pseudo-header, | ||
| 611 | complimented */ | ||
| 612 | |||
| 613 | Checksum += ((uint16_t*)SourceAddress)[0]; | ||
| 614 | Checksum += ((uint16_t*)SourceAddress)[1]; | ||
| 615 | Checksum += ((uint16_t*)DestinationAddress)[0]; | ||
| 616 | Checksum += ((uint16_t*)DestinationAddress)[1]; | ||
| 617 | Checksum += SwapEndian_16(PROTOCOL_TCP); | ||
| 618 | Checksum += SwapEndian_16(TCPOutSize); | ||
| 619 | |||
| 620 | for (uint16_t CurrWord = 0; CurrWord < (TCPOutSize >> 1); CurrWord++) | ||
| 621 | Checksum += ((uint16_t*)TCPHeaderOutStart)[CurrWord]; | ||
| 622 | |||
| 623 | if (TCPOutSize & 0x01) | ||
| 624 | Checksum += (((uint16_t*)TCPHeaderOutStart)[TCPOutSize >> 1] & 0x00FF); | ||
| 625 | |||
| 626 | while (Checksum & 0xFFFF0000) | ||
| 627 | Checksum = ((Checksum & 0xFFFF) + (Checksum >> 16)); | ||
| 628 | |||
| 629 | return ~Checksum; | ||
| 630 | } | ||
| 631 | |||
