]> git.gir.st - tmk_keyboard.git/blob - protocol/lufa/LUFA-git/Demos/Device/LowLevel/RNDISEthernet/Lib/TCP.c
Squashed 'tmk_core/' changes from 8da1898..e5f9940
[tmk_keyboard.git] / protocol / lufa / LUFA-git / Demos / Device / LowLevel / RNDISEthernet / Lib / TCP.c
1 /*
2 LUFA Library
3 Copyright (C) Dean Camera, 2014.
4
5 dean [at] fourwalledcubicle [dot] com
6 www.lufa-lib.org
7 */
8
9 /*
10 Copyright 2014 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
Imprint / Impressum