1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** NetX Component */
16 /** */
17 /** Transmission Control Protocol (TCP) */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define NX_SOURCE_CODE
23
24
25 /* Include necessary system files. */
26
27 #include "nx_api.h"
28 #include "nx_tcp.h"
29 #include "nx_packet.h"
30 #include "nx_ip.h"
31
32 #ifdef FEATURE_NX_IPV6
33 #include "nx_ipv6.h"
34 #endif /* FEATURE_NX_IPV6 */
35
36 #ifdef NX_IPSEC_ENABLE
37 #include "nx_ipsec.h"
38 #endif /* NX_IPSEC_ENABLE */
39
40
41 /**************************************************************************/
42 /* */
43 /* FUNCTION RELEASE */
44 /* */
45 /* _nx_tcp_packet_process PORTABLE C */
46 /* 6.3.0 */
47 /* AUTHOR */
48 /* */
49 /* Yuxin Zhou, Microsoft Corporation */
50 /* */
51 /* DESCRIPTION */
52 /* */
53 /* This function processes an incoming TCP packet, which includes */
54 /* matching the packet to an existing connection and dispatching to */
55 /* the socket specific processing routine. If no connection is */
56 /* found, this routine checks for a new connection request and if */
57 /* found, processes it accordingly. If a reset packet is received, it */
58 /* checks the queue for a previous connection request which needs to be*/
59 /* removed. */
60 /* */
61 /* INPUT */
62 /* */
63 /* ip_ptr Pointer to IP control block */
64 /* packet_ptr Pointer to packet to send */
65 /* */
66 /* OUTPUT */
67 /* */
68 /* None */
69 /* */
70 /* CALLS */
71 /* */
72 /* _nx_packet_release Packet release function */
73 /* _nx_ip_checksum_compute Calculate TCP packet checksum */
74 /* _nx_tcp_mss_option_get Get peer MSS option */
75 /* _nx_tcp_no_connection_reset Reset on no connection */
76 /* _nx_tcp_packet_send_syn Send SYN message */
77 /* _nx_tcp_socket_packet_process Socket specific packet */
78 /* processing routine */
79 /* (nx_tcp_listen_callback) Application listen callback */
80 /* function */
81 /* */
82 /* CALLED BY */
83 /* */
84 /* _nx_tcp_queue_process Process TCP packet queue */
85 /* _nx_tcp_packet_receive Receive packet processing */
86 /* */
87 /* RELEASE HISTORY */
88 /* */
89 /* DATE NAME DESCRIPTION */
90 /* */
91 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
92 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
93 /* resulting in version 6.1 */
94 /* 10-31-2023 Tiejun Zhou Modified comment(s), */
95 /* validated TCP header buffer,*/
96 /* resulting in version 6.3.0 */
97 /* */
98 /**************************************************************************/
_nx_tcp_packet_process(NX_IP * ip_ptr,NX_PACKET * packet_ptr)99 VOID _nx_tcp_packet_process(NX_IP *ip_ptr, NX_PACKET *packet_ptr)
100 {
101
102 UINT index;
103 UINT port;
104 ULONG *source_ip = NX_NULL;
105 ULONG *dest_ip = NX_NULL;
106 UINT source_port;
107 NX_TCP_SOCKET *socket_ptr;
108 NX_TCP_HEADER *tcp_header_ptr;
109 struct NX_TCP_LISTEN_STRUCT *listen_ptr;
110 VOID (*listen_callback)(NX_TCP_SOCKET *socket_ptr, UINT port);
111 ULONG option_words;
112 ULONG mss = 0;
113 ULONG checksum;
114 NX_INTERFACE *interface_ptr = NX_NULL;
115 #if defined(NX_DISABLE_TCP_RX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
116 UINT compute_checksum = 1;
117 #endif /* defined(NX_DISABLE_TCP_RX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
118 ULONG queued_count;
119 NX_PACKET *queued_ptr;
120 NX_PACKET *queued_prev_ptr;
121 ULONG *queued_source_ip;
122 UINT queued_source_port;
123 UINT is_a_RST_request;
124 UINT is_valid_option_flag = NX_TRUE;
125 UINT status;
126 #ifdef NX_ENABLE_TCP_WINDOW_SCALING
127 ULONG rwin_scale = 0xFF;
128 #endif /* NX_ENABLE_TCP_WINDOW_SCALING */
129
130 #ifdef NX_DISABLE_TCP_RX_CHECKSUM
131 compute_checksum = 0;
132 #endif /* NX_DISABLE_TCP_RX_CHECKSUM */
133
134 /* Add debug information. */
135 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
136
137 /* Pickup the source IP address. */
138 #ifndef NX_DISABLE_IPV4
139 if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V4)
140 {
141
142 NX_IPV4_HEADER *ip_header_ptr;
143
144 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
145 ip_header_ptr = (NX_IPV4_HEADER *)packet_ptr -> nx_packet_ip_header;
146
147 source_ip = &ip_header_ptr -> nx_ip_header_source_ip;
148
149 dest_ip = &ip_header_ptr -> nx_ip_header_destination_ip;
150
151 mss = 536;
152
153 interface_ptr = packet_ptr -> nx_packet_address.nx_packet_interface_ptr;
154 }
155 #endif /* !NX_DISABLE_IPV4 */
156
157 #ifdef FEATURE_NX_IPV6
158 if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V6)
159 {
160
161 /* IPv6 */
162 NX_IPV6_HEADER *ipv6_header;
163
164 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
165 ipv6_header = (NX_IPV6_HEADER *)packet_ptr -> nx_packet_ip_header;
166
167 source_ip = &ipv6_header -> nx_ip_header_source_ip[0];
168
169 dest_ip = &ipv6_header -> nx_ip_header_destination_ip[0];
170
171 mss = 1220;
172
173 interface_ptr = packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address_attached;
174 }
175 #endif /* FEATURE_NX_IPV6 */
176
177 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
178 if (interface_ptr -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_TCP_RX_CHECKSUM)
179 {
180 compute_checksum = 0;
181 }
182 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
183
184 #ifdef NX_IPSEC_ENABLE
185 if ((packet_ptr -> nx_packet_ipsec_sa_ptr != NX_NULL) && (((NX_IPSEC_SA *)(packet_ptr -> nx_packet_ipsec_sa_ptr)) -> nx_ipsec_sa_encryption_method != NX_CRYPTO_NONE))
186 {
187 compute_checksum = 1;
188 }
189 #endif /* NX_IPSEC_ENABLE */
190
191 #if defined(NX_DISABLE_TCP_RX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
192 if (compute_checksum)
193 #endif /* defined(NX_DISABLE_TCP_RX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
194 {
195 checksum = _nx_ip_checksum_compute(packet_ptr, NX_PROTOCOL_TCP,
196 (UINT)packet_ptr -> nx_packet_length,
197 source_ip, dest_ip);
198
199 checksum = NX_LOWER_16_MASK & ~checksum;
200
201 /* Calculate the checksum. */
202 if (checksum != 0)
203 {
204
205 #ifndef NX_DISABLE_TCP_INFO
206
207 /* Increment the TCP invalid packet error count. */
208 ip_ptr -> nx_ip_tcp_invalid_packets++;
209
210 /* Increment the TCP packet checksum error count. */
211 ip_ptr -> nx_ip_tcp_checksum_errors++;
212 #endif
213
214 /* Checksum error, just release the packet. */
215 _nx_packet_release(packet_ptr);
216 return;
217 }
218 }
219
220 #ifndef NX_DISABLE_RX_SIZE_CHECKING
221 /* Make sure the TCP header is in the first packet. */
222 if ((UINT)(packet_ptr -> nx_packet_append_ptr - packet_ptr -> nx_packet_prepend_ptr) < sizeof(NX_TCP_HEADER))
223 {
224
225 #ifndef NX_DISABLE_TCP_INFO
226 /* Increment the TCP invalid packet error. */
227 ip_ptr -> nx_ip_tcp_invalid_packets++;
228 #endif
229
230 /* Not supported. */
231 _nx_packet_release(packet_ptr);
232 return;
233 }
234 #endif /* NX_DISABLE_RX_SIZE_CHECKING */
235
236 /* Pickup the pointer to the head of the TCP packet. */
237 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
238 tcp_header_ptr = (NX_TCP_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
239
240 /* Endian swapping logic. If NX_LITTLE_ENDIAN is specified, these macros will
241 swap the endian of the TCP header. */
242 NX_CHANGE_ULONG_ENDIAN(tcp_header_ptr -> nx_tcp_header_word_0);
243 NX_CHANGE_ULONG_ENDIAN(tcp_header_ptr -> nx_tcp_sequence_number);
244 NX_CHANGE_ULONG_ENDIAN(tcp_header_ptr -> nx_tcp_acknowledgment_number);
245 NX_CHANGE_ULONG_ENDIAN(tcp_header_ptr -> nx_tcp_header_word_3);
246 NX_CHANGE_ULONG_ENDIAN(tcp_header_ptr -> nx_tcp_header_word_4);
247
248 /* Determine if there are any option words... Note there are always 5 words in a TCP header. */
249 option_words = (tcp_header_ptr -> nx_tcp_header_word_3 >> 28) - 5;
250
251 #ifndef NX_DISABLE_RX_SIZE_CHECKING
252 /* Check for valid packet length. */
253 if (((INT)option_words < 0) ||
254 ((UINT)(packet_ptr -> nx_packet_append_ptr - packet_ptr -> nx_packet_prepend_ptr) <
255 (sizeof(NX_TCP_HEADER) + (option_words << 2))))
256 {
257
258 #ifndef NX_DISABLE_TCP_INFO
259 /* Increment the TCP invalid packet error. */
260 ip_ptr -> nx_ip_tcp_invalid_packets++;
261 #endif
262
263 /* Invalid packet length, just release it. */
264 _nx_packet_release(packet_ptr);
265
266 /* The function is complete, just return! */
267 return;
268 }
269 #endif
270
271 if (option_words)
272 {
273
274 /* Yes, there are one or more option words. */
275
276 /* Derive the Maximum Segment Size (MSS) in the option words. */
277 status = _nx_tcp_mss_option_get((packet_ptr -> nx_packet_prepend_ptr + sizeof(NX_TCP_HEADER)), option_words * (ULONG)sizeof(ULONG), &mss);
278
279 /* Check the status. if status is NX_FALSE, means Option Length is invalid. */
280 if (status == NX_FALSE)
281 {
282
283 /* The option is invalid. */
284 is_valid_option_flag = NX_FALSE;
285 }
286 else
287 {
288
289 /* Set the default MSS if the MSS value was not found. */
290 /*lint -e{644} suppress variable might not be initialized, since "mss" was initialized in _nx_tcp_mss_option_get. */
291 if (mss == 0)
292 {
293 #ifndef NX_DISABLE_IPV4
294 if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V4)
295 {
296 mss = 536;
297 }
298 #endif /* !NX_DISABLE_IPV4 */
299
300 #ifdef FEATURE_NX_IPV6
301 if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V6)
302 {
303 mss = 1220;
304 }
305 #endif /* FEATURE_NX_IPV6 */
306 }
307 }
308
309 #ifdef NX_ENABLE_TCP_WINDOW_SCALING
310 status = _nx_tcp_window_scaling_option_get((packet_ptr -> nx_packet_prepend_ptr + sizeof(NX_TCP_HEADER)), option_words * (ULONG)sizeof(ULONG), &rwin_scale);
311
312 /* Check the status. if status is NX_FALSE, means Option Length is invalid. */
313 if (status == NX_FALSE)
314 {
315 is_valid_option_flag = NX_FALSE;
316 }
317 #endif /* NX_ENABLE_TCP_WINDOW_SCALING */
318 }
319
320 /* Pickup the destination TCP port. */
321 port = (UINT)(tcp_header_ptr -> nx_tcp_header_word_0 & NX_LOWER_16_MASK);
322
323 /* Pickup the source TCP port. */
324 source_port = (UINT)(tcp_header_ptr -> nx_tcp_header_word_0 >> NX_SHIFT_BY_16);
325
326 /* Calculate the hash index in the TCP port array of the associated IP instance. */
327 index = (UINT)((port + (port >> 8)) & NX_TCP_PORT_TABLE_MASK);
328
329 /* Search the bound sockets in this index for the particular port. */
330 socket_ptr = ip_ptr -> nx_ip_tcp_port_table[index];
331
332 /* Determine if there are any sockets bound on this port index. */
333 if (socket_ptr)
334 {
335
336 INT find_a_match;
337
338 /* Yes, loop to examine the list of bound ports on this index. */
339 do
340 {
341
342 find_a_match = 0;
343
344 /* Determine if the port has been found. */
345 if ((socket_ptr -> nx_tcp_socket_port == port) &&
346 (socket_ptr -> nx_tcp_socket_connect_port == source_port))
347 {
348
349 /* Make sure they are the same IP protocol */
350 if (socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == packet_ptr -> nx_packet_ip_version)
351 {
352
353 #ifndef NX_DISABLE_IPV4
354 if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V4)
355 {
356
357 if (socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v4 == *source_ip)
358 {
359 find_a_match = 1;
360 }
361 }
362 #endif /* !NX_DISABLE_IPV4 */
363
364 #ifdef FEATURE_NX_IPV6
365 if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V6)
366 {
367 if (CHECK_IPV6_ADDRESSES_SAME(socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v6, source_ip))
368 {
369 find_a_match = 1;
370 }
371 }
372 #endif /* FEATURE_NX_IPV6 */
373 }
374
375 if (find_a_match)
376 {
377
378 /* Yes, we have a match! */
379
380 /* Determine if we need to update the tcp port head pointer. This should
381 only be done if the found socket pointer is not the head pointer and
382 the mutex for this IP instance is available. */
383
384 /* Move the port head pointer to this socket. */
385 ip_ptr -> nx_ip_tcp_port_table[index] = socket_ptr;
386
387 /* If this packet contains SYN */
388 if (tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_SYN_BIT)
389 {
390
391 /* Record the MSS value if it is present and the Otherwise use 536, as
392 outlined in RFC 1122 section 4.2.2.6. */
393 socket_ptr -> nx_tcp_socket_peer_mss = mss;
394
395 if ((mss > socket_ptr -> nx_tcp_socket_mss) && socket_ptr -> nx_tcp_socket_mss)
396 {
397 socket_ptr -> nx_tcp_socket_connect_mss = socket_ptr -> nx_tcp_socket_mss;
398 }
399 else if ((socket_ptr -> nx_tcp_socket_state != NX_TCP_SYN_SENT) ||
400 (socket_ptr -> nx_tcp_socket_connect_mss > mss))
401 {
402 socket_ptr -> nx_tcp_socket_connect_mss = mss;
403 }
404
405 /* Compute the SMSS * SMSS value, so later TCP module doesn't need to redo the multiplication. */
406 socket_ptr -> nx_tcp_socket_connect_mss2 =
407 socket_ptr -> nx_tcp_socket_connect_mss * socket_ptr -> nx_tcp_socket_connect_mss;
408 #ifdef NX_ENABLE_TCP_WINDOW_SCALING
409 /*
410 Simply record the peer's window scale value. When we move to the
411 ESTABLISHED state, we will set the peer window scale to 0 if the
412 peer does not support this feature.
413 */
414 socket_ptr -> nx_tcp_snd_win_scale_value = rwin_scale;
415 #endif /* NX_ENABLE_TCP_WINDOW_SCALING */
416 }
417
418 /* Process the packet within an existing TCP connection. */
419 _nx_tcp_socket_packet_process(socket_ptr, packet_ptr);
420
421 /* Get out of the search loop and this function! */
422 return;
423 }
424 }
425
426 /* Move to the next entry in the bound index. */
427 socket_ptr = socket_ptr -> nx_tcp_socket_bound_next;
428 } while (socket_ptr != ip_ptr -> nx_ip_tcp_port_table[index]);
429 }
430
431 /* At this point, we know there is not an existing TCP connection. */
432
433 /* If this packet contains the valid option. */
434 if (is_valid_option_flag == NX_FALSE)
435 {
436
437 /* Send RST message.
438 TCP MUST be prepared to handle an illegal option length (e.g., zero) without crashing;
439 a suggested procedure is to reset the connection and log the reason, outlined in RFC 1122, Section 4.2.2.5, Page85. */
440 _nx_tcp_no_connection_reset(ip_ptr, packet_ptr, tcp_header_ptr);
441
442 #ifndef NX_DISABLE_TCP_INFO
443 /* Increment the TCP invalid packet error count. */
444 ip_ptr -> nx_ip_tcp_invalid_packets++;
445 #endif /* NX_DISABLE_TCP_INFO */
446
447 /* Not a connection request, just release the packet. */
448 _nx_packet_release(packet_ptr);
449
450 return;
451 }
452
453 #ifdef NX_ENABLE_TCP_MSS_CHECK
454 /* Optionally check for a user specified minimum MSS. The user application may choose to
455 define a minimum MSS value, and reject a TCP connection if peer MSS value does not
456 meet the minimum. */
457 if (mss < NX_TCP_MSS_MINIMUM)
458 {
459
460 /* Send RST message. */
461 _nx_tcp_no_connection_reset(ip_ptr, packet_ptr, tcp_header_ptr);
462
463 #ifndef NX_DISABLE_TCP_INFO
464 /* Increment the TCP invalid packet error count. */
465 ip_ptr -> nx_ip_tcp_invalid_packets++;
466 #endif /* NX_DISABLE_TCP_INFO */
467
468 /* Handle this as an invalid connection request. */
469 _nx_packet_release(packet_ptr);
470
471 return;
472 }
473 #endif
474
475 /* Handle new connection requests without ACK bit in NX_TCP_SYN_RECEIVED state.
476 NX_TCP_SYN_RECEIVED state is equal of LISTEN state of RFC.
477 RFC793, Section3.9, Page65. */
478 if ((!(tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_ACK_BIT)) &&
479 (ip_ptr -> nx_ip_tcp_active_listen_requests))
480 {
481
482 #ifndef NX_DISABLE_IPV4
483 if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V4)
484 {
485
486 /* Check for LAND attack packet. This is an incoming packet with matching
487 Source and Destination IP address, and matching source and destination port. */
488 if ((*source_ip == *dest_ip) && (source_port == port))
489 {
490
491 /* Bogus packet. Drop it! */
492
493 #ifndef NX_DISABLE_TCP_INFO
494 /* Increment the TCP invalid packet error count. */
495 ip_ptr -> nx_ip_tcp_invalid_packets++;
496 #endif /* NX_DISABLE_TCP_INFO */
497
498 /* Release the packet we will not process any further. */
499 _nx_packet_release(packet_ptr);
500 return;
501 }
502
503 /* It shall not make connections if the source IP address
504 is broadcast or multicast. */
505 if (
506 /* Check for Multicast address */
507 ((*source_ip & NX_IP_CLASS_D_MASK) == NX_IP_CLASS_D_TYPE) ||
508 /* Check for subnet-directed broadcast */
509 (((*source_ip & interface_ptr -> nx_interface_ip_network_mask) == interface_ptr -> nx_interface_ip_network) &&
510 ((*source_ip & ~(interface_ptr -> nx_interface_ip_network_mask)) == ~(interface_ptr -> nx_interface_ip_network_mask))) ||
511 /* Check for local subnet address */
512 (*source_ip == interface_ptr -> nx_interface_ip_network) ||
513 /* Check for limited broadcast */
514 (*source_ip == NX_IP_LIMITED_BROADCAST)
515 )
516 {
517
518 #ifndef NX_DISABLE_TCP_INFO
519 /* Increment the TCP invalid packet error count. */
520 ip_ptr -> nx_ip_tcp_invalid_packets++;
521 #endif /* NX_DISABLE_TCP_INFO */
522
523 /* Release the packet. */
524 _nx_packet_release(packet_ptr);
525
526 /* Finished processing, simply return! */
527 return;
528 }
529 }
530 #endif /* !NX_DISABLE_IPV4 */
531
532 #ifdef FEATURE_NX_IPV6
533 if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V6)
534 {
535
536 /* Check for LAND attack packet. This is an incoming packet with matching
537 Source and Destination IP address, and matching source and destination port. */
538 if ((CHECK_IPV6_ADDRESSES_SAME(source_ip, dest_ip)) && (source_port == port))
539 {
540
541 /* Bogus packet. Drop it! */
542
543 #ifndef NX_DISABLE_TCP_INFO
544 /* Increment the TCP invalid packet error count. */
545 ip_ptr -> nx_ip_tcp_invalid_packets++;
546 #endif /* NX_DISABLE_TCP_INFO */
547
548 /* Release the packet we will not process any further. */
549 _nx_packet_release(packet_ptr);
550 return;
551 }
552
553 /* It shall not make connections if the source IP address
554 is broadcast or multicast. */
555 if (IPv6_Address_Type(source_ip) & IPV6_ADDRESS_MULTICAST)
556 {
557
558 #ifndef NX_DISABLE_TCP_INFO
559 /* Increment the TCP invalid packet error count. */
560 ip_ptr -> nx_ip_tcp_invalid_packets++;
561 #endif /* NX_DISABLE_TCP_INFO */
562
563 /* Release the packet. */
564 _nx_packet_release(packet_ptr);
565
566 /* Finished processing, simply return! */
567 return;
568 }
569 }
570 #endif /* FEATURE_NX_IPV6*/
571
572 /* Search all ports in listen mode for a match. */
573 listen_ptr = ip_ptr -> nx_ip_tcp_active_listen_requests;
574 do
575 {
576
577 /* Determine if this port is in a listen mode. */
578 if (listen_ptr -> nx_tcp_listen_port == port)
579 {
580
581 /* Determine if the packet is an initial connection request.
582 The incoming SYN packet is a connection request.
583 The incoming RST packet is related to a previous connection request.
584 Fourth other text or control. RFC793, Section3.9, Page66. */
585 if ((!(tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_SYN_BIT)) &&
586 (!(tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_RST_BIT)))
587 {
588
589 #ifndef NX_DISABLE_TCP_INFO
590 /* This is a duplicate connection request. Increment the TCP dropped packet count. */
591 ip_ptr -> nx_ip_tcp_receive_packets_dropped++;
592 #endif /* NX_DISABLE_TCP_INFO */
593
594 /* Release the packet. */
595 _nx_packet_release(packet_ptr);
596
597 return;
598 }
599
600 #ifndef NX_DISABLE_TCP_INFO
601
602 /* Check for a SYN bit set. */
603 if ((tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_SYN_BIT))
604 {
605
606 /* Increment the passive TCP connections count. */
607 ip_ptr -> nx_ip_tcp_passive_connections++;
608
609 /* Increment the TCP connections count. */
610 ip_ptr -> nx_ip_tcp_connections++;
611 }
612 #endif
613
614 /* Okay, this port is in a listen mode. We now need to see if
615 there is an available socket for the new connection request
616 present. */
617 if ((listen_ptr -> nx_tcp_listen_socket_ptr) &&
618 ((tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_RST_BIT) == NX_NULL))
619 {
620
621 /* Yes there is indeed a socket present. We now need to
622 fill in the appropriate info and call the server callback
623 routine. */
624
625 /* Allocate the supplied server socket. */
626 socket_ptr = listen_ptr -> nx_tcp_listen_socket_ptr;
627
628 #ifndef NX_DISABLE_EXTENDED_NOTIFY_SUPPORT
629 /* If extended notify is enabled, call the syn_received notify function.
630 This user-supplied function decides whether or not this SYN request
631 should be accepted. */
632 if (socket_ptr -> nx_tcp_socket_syn_received_notify)
633 {
634
635 /* Add debug information. */
636 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
637
638 if ((socket_ptr -> nx_tcp_socket_syn_received_notify)(socket_ptr, packet_ptr) != NX_TRUE)
639 {
640
641 /* Release the packet. */
642 _nx_packet_release(packet_ptr);
643
644 /* Finished processing, simply return! */
645 return;
646 }
647 }
648 #endif /* NX_DISABLE_EXTENDED_NOTIFY_SUPPORT */
649
650 /* If trace is enabled, insert this event into the trace buffer. */
651 NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_TCP_SYN_RECEIVE, ip_ptr, socket_ptr, packet_ptr, tcp_header_ptr -> nx_tcp_sequence_number, NX_TRACE_INTERNAL_EVENTS, 0, 0);
652
653 /* Clear the server socket pointer in the listen request. If the
654 application wishes to honor more server connections on this port,
655 the application must call relisten with a new server socket
656 pointer. */
657 listen_ptr -> nx_tcp_listen_socket_ptr = NX_NULL;
658
659 /* Fill the socket in with the appropriate information. */
660
661
662 #ifndef NX_DISABLE_IPV4
663 if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V4)
664 {
665
666 /* Assume the interface that receives the incoming packet is the best interface
667 for sending responses. */
668 socket_ptr -> nx_tcp_socket_connect_interface = interface_ptr;
669 socket_ptr -> nx_tcp_socket_next_hop_address = NX_NULL;
670
671 /* Set the next hop address. */
672 _nx_ip_route_find(ip_ptr, *source_ip, &socket_ptr -> nx_tcp_socket_connect_interface,
673 &socket_ptr -> nx_tcp_socket_next_hop_address);
674
675 socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version = NX_IP_VERSION_V4;
676 socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v4 = *source_ip;
677 }
678 #endif /* !NX_DISABLE_IPV4 */
679
680 #ifdef FEATURE_NX_IPV6
681 if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V6)
682 {
683
684 socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version = NX_IP_VERSION_V6;
685 COPY_IPV6_ADDRESS(source_ip, socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v6);
686
687 /* Also record the outgoing interface information. */
688 socket_ptr -> nx_tcp_socket_ipv6_addr = packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr;
689 socket_ptr -> nx_tcp_socket_connect_interface = interface_ptr;
690 }
691 #endif /* FEATURE_NX_IPV6 */
692
693 socket_ptr -> nx_tcp_socket_connect_port = source_port;
694 socket_ptr -> nx_tcp_socket_rx_sequence = tcp_header_ptr -> nx_tcp_sequence_number;
695
696
697 /* Yes, MSS was found, so store it! */
698 socket_ptr -> nx_tcp_socket_peer_mss = mss;
699
700 #ifdef NX_ENABLE_TCP_WINDOW_SCALING
701 /*
702 Simply record the peer's window scale value. When we move to the
703 ESTABLISHED state, we will set the peer window scale to 0 if the
704 peer does not support this feature.
705 */
706 socket_ptr -> nx_tcp_snd_win_scale_value = rwin_scale;
707 #endif /* NX_ENABLE_TCP_WINDOW_SCALING */
708
709 /* Set the initial slow start threshold to be the advertised window size. */
710 socket_ptr -> nx_tcp_socket_tx_slow_start_threshold = socket_ptr -> nx_tcp_socket_tx_window_advertised;
711
712 /* Slow start: setup initial window (IW) to be MSS, RFC 2581, 3.1 */
713 socket_ptr -> nx_tcp_socket_tx_window_congestion = mss;
714
715 /* Initialize the transmit outstanding byte count to zero. */
716 socket_ptr -> nx_tcp_socket_tx_outstanding_bytes = 0;
717
718 /* Calculate the hash index in the TCP port array of the associated IP instance. */
719 index = (UINT)((port + (port >> 8)) & NX_TCP_PORT_TABLE_MASK);
720
721 /* Determine if the list is NULL. */
722 if (ip_ptr -> nx_ip_tcp_port_table[index])
723 {
724
725 /* There are already sockets on this list... just add this one
726 to the end. */
727 socket_ptr -> nx_tcp_socket_bound_next =
728 ip_ptr -> nx_ip_tcp_port_table[index];
729 socket_ptr -> nx_tcp_socket_bound_previous =
730 (ip_ptr -> nx_ip_tcp_port_table[index]) -> nx_tcp_socket_bound_previous;
731 ((ip_ptr -> nx_ip_tcp_port_table[index]) -> nx_tcp_socket_bound_previous) -> nx_tcp_socket_bound_next =
732 socket_ptr;
733 (ip_ptr -> nx_ip_tcp_port_table[index]) -> nx_tcp_socket_bound_previous = socket_ptr;
734 }
735 else
736 {
737
738 /* Nothing is on the TCP port list. Add this TCP socket to an
739 empty list. */
740 socket_ptr -> nx_tcp_socket_bound_next = socket_ptr;
741 socket_ptr -> nx_tcp_socket_bound_previous = socket_ptr;
742 ip_ptr -> nx_ip_tcp_port_table[index] = socket_ptr;
743 }
744
745 /* Pickup the listen callback function. */
746 listen_callback = listen_ptr -> nx_tcp_listen_callback;
747
748 /* Release the incoming packet. */
749 _nx_packet_release(packet_ptr);
750
751 /* Determine if an accept call with suspension has already been made
752 for this socket. If so, the SYN message needs to be sent from
753 here. */
754 if (socket_ptr -> nx_tcp_socket_state == NX_TCP_SYN_RECEIVED)
755 {
756
757
758 /* If trace is enabled, insert this event into the trace buffer. */
759 NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_TCP_STATE_CHANGE, ip_ptr, socket_ptr, socket_ptr -> nx_tcp_socket_state, socket_ptr -> nx_tcp_socket_state, NX_TRACE_INTERNAL_EVENTS, 0, 0);
760
761
762 /* The application is suspended on an accept call for this socket.
763 Simply send the SYN now and keep the thread suspended until the
764 other side completes the connection. */
765
766 /* Send the SYN message, but increment the ACK first. */
767 socket_ptr -> nx_tcp_socket_rx_sequence++;
768
769 /* Increment the sequence number for the SYN message. */
770 socket_ptr -> nx_tcp_socket_tx_sequence++;
771
772 /* Setup a timeout so the connection attempt can be sent again. */
773 socket_ptr -> nx_tcp_socket_timeout = socket_ptr -> nx_tcp_socket_timeout_rate;
774 socket_ptr -> nx_tcp_socket_timeout_retries = 0;
775
776 /* Send the SYN+ACK message. */
777 _nx_tcp_packet_send_syn(socket_ptr, (socket_ptr -> nx_tcp_socket_tx_sequence - 1));
778 }
779
780 /* Determine if there is a listen callback function. */
781 if (listen_callback)
782 {
783 /* Call the user's listen callback function. */
784 (listen_callback)(socket_ptr, port);
785 }
786 }
787 else
788 {
789
790 /* There is no server socket available for the new connection. */
791
792 /* The application needs to call relisten with a new server request to process this queued
793 connection. */
794
795 /* Check for a RST (reset) bit set. */
796 if (!(tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_RST_BIT))
797 {
798
799 /* If trace is enabled, insert this event into the trace buffer. */
800 NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_TCP_SYN_RECEIVE, ip_ptr, NX_NULL, packet_ptr, tcp_header_ptr -> nx_tcp_sequence_number, NX_TRACE_INTERNAL_EVENTS, 0, 0);
801 }
802
803 /* Check for the same connection request already in the queue. */
804 queued_count = listen_ptr -> nx_tcp_listen_queue_current;
805 queued_ptr = listen_ptr -> nx_tcp_listen_queue_head;
806 queued_prev_ptr = queued_ptr;
807
808 /* Initialize the check for queued request to false.*/
809 is_a_RST_request = NX_FALSE;
810
811 /* Loop through the queued list in order to search for duplicate request. */
812 while (queued_count--)
813 {
814
815 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
816 queued_source_port = (UINT)(*((ULONG *)queued_ptr -> nx_packet_prepend_ptr) >> NX_SHIFT_BY_16);
817
818 #ifndef NX_DISABLE_IPV4
819 /* Pickup the queued source port and source IP address for comparison. */
820 if (queued_ptr -> nx_packet_ip_version == NX_IP_VERSION_V4)
821 {
822
823 /*lint -e{929} -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
824 queued_source_ip = (ULONG *)(((ULONG *)queued_ptr -> nx_packet_prepend_ptr) - 2);
825
826 /* Determine if this matches the current connection request. */
827 if ((*queued_source_ip == *source_ip) && (queued_source_port == source_port))
828 {
829
830 /* Possible duplicate connection request to one that is already queued. */
831
832 /* Check for a RST (reset) bit set. */
833 if (tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_RST_BIT)
834 {
835
836 /* RST packet matches a previously queued connection request. */
837 is_a_RST_request = NX_TRUE;
838 }
839 else
840 {
841 #ifndef NX_DISABLE_TCP_INFO
842 /* This is a duplicate connection request. Increment the TCP dropped packet count. */
843 ip_ptr -> nx_ip_tcp_receive_packets_dropped++;
844 #endif
845 /* Simply release the packet and return. */
846 _nx_packet_release(packet_ptr);
847
848 /* Return! */
849 return;
850 }
851 }
852 }
853 #endif /* !NX_DISABLE_IPV4 */
854
855 #ifdef FEATURE_NX_IPV6
856 if (queued_ptr -> nx_packet_ip_version == NX_IP_VERSION_V6)
857 {
858
859 /*lint -e{929} -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
860 queued_source_ip = (ULONG *)(((ULONG *)queued_ptr -> nx_packet_prepend_ptr) - 8);
861
862 /* Determine if this matches the current connection request. */
863 if ((CHECK_IPV6_ADDRESSES_SAME(queued_source_ip, source_ip)) && (queued_source_port == source_port))
864 {
865
866 /* Possible duplicate connection request to one that is already queued. */
867
868 /* Check for a RST (reset) bit set. */
869 if (tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_RST_BIT)
870 {
871
872 /* RST packet matches a previously queued connection request. */
873 is_a_RST_request = NX_TRUE;
874 }
875 else
876 {
877 #ifndef NX_DISABLE_TCP_INFO
878 /* This is a duplicate connection request. Increment the TCP dropped packet count. */
879 ip_ptr -> nx_ip_tcp_receive_packets_dropped++;
880 #endif
881 /* Simply release the packet and return. */
882 _nx_packet_release(packet_ptr);
883
884 /* Return! */
885 return;
886 }
887 }
888 }
889 #endif /* FEATURE_NX_IPV6 */
890
891 /* Handle the case of the RST packet which cancels a previously received
892 connection request. */
893 if (is_a_RST_request)
894 {
895
896 /* A previous connection request needs to be removed from the listen queue. */
897 if (queued_ptr == listen_ptr -> nx_tcp_listen_queue_head)
898 {
899
900 /* Reset the front (oldest) of the queue to the next request. */
901 listen_ptr -> nx_tcp_listen_queue_head = queued_ptr -> nx_packet_queue_next;
902 }
903 else
904 {
905
906 /* Link around the request we are removing. */
907 /*lint -e{613} suppress possible use of null pointer, since 'queued_prev_ptr' must not be NULL. */
908 queued_prev_ptr -> nx_packet_queue_next = queued_ptr -> nx_packet_queue_next;
909 }
910
911 /* Is the request being removed the tail (most recent connection?) */
912 if (queued_ptr == listen_ptr -> nx_tcp_listen_queue_tail)
913 {
914
915 /* Yes, set the previous connection request as the tail. */
916 listen_ptr -> nx_tcp_listen_queue_tail = queued_prev_ptr;
917 }
918
919 /* Release the connection request packet. */
920 _nx_packet_release(queued_ptr);
921
922 /* Update the listen queue. */
923 listen_ptr -> nx_tcp_listen_queue_current--;
924
925 #ifndef NX_DISABLE_TCP_INFO
926 /* Increment the TCP dropped packet count. */
927 ip_ptr -> nx_ip_tcp_receive_packets_dropped++;
928 #endif
929
930 /* Simply release the packet and return. */
931 _nx_packet_release(packet_ptr);
932
933 /* Return! */
934 return;
935 }
936
937 /* Move to next item in the queue. */
938 queued_prev_ptr = queued_ptr;
939 queued_ptr = queued_ptr -> nx_packet_queue_next;
940 }
941
942 /* Not a duplicate connection request, place this request on the listen queue. */
943
944 /* Is this a RST packet? */
945 if (tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_RST_BIT)
946 {
947
948 /* Yes, so not a connection request. Do not place on the listen queue. */
949 #ifndef NX_DISABLE_TCP_INFO
950 /* Increment the TCP dropped packet count. */
951 ip_ptr -> nx_ip_tcp_receive_packets_dropped++;
952 #endif
953
954 /* Release the packet. */
955 _nx_packet_release(packet_ptr);
956
957 /* Return! */
958 return;
959 }
960
961 /* Set the next pointer of the packet to NULL. */
962 packet_ptr -> nx_packet_queue_next = NX_NULL;
963
964 /* Queue the new connection request. */
965 if (listen_ptr -> nx_tcp_listen_queue_head)
966 {
967
968 /* There is a connection request already queued, just link packet to tail. */
969 (listen_ptr -> nx_tcp_listen_queue_tail) -> nx_packet_queue_next = packet_ptr;
970 }
971 else
972 {
973
974 /* The queue is empty. Setup head pointer to the new packet. */
975 listen_ptr -> nx_tcp_listen_queue_head = packet_ptr;
976 }
977
978 /* Setup the tail pointer to the new packet and increment the queue count. */
979 listen_ptr -> nx_tcp_listen_queue_tail = packet_ptr;
980 listen_ptr -> nx_tcp_listen_queue_current++;
981
982 /* Add debug information. */
983 NX_PACKET_DEBUG(NX_PACKET_TCP_LISTEN_QUEUE, __LINE__, packet_ptr);
984
985 /* Determine if the queue depth has been exceeded. */
986 if (listen_ptr -> nx_tcp_listen_queue_current > listen_ptr -> nx_tcp_listen_queue_maximum)
987 {
988
989 #ifndef NX_DISABLE_TCP_INFO
990
991 /* Increment the TCP connections dropped count. */
992 ip_ptr -> nx_ip_tcp_connections_dropped++;
993 ip_ptr -> nx_ip_tcp_connections--;
994
995 /* Increment the TCP dropped packet count. */
996 ip_ptr -> nx_ip_tcp_receive_packets_dropped++;
997 #endif
998
999 /* Save the head packet pointer, since this will be released below. */
1000 packet_ptr = listen_ptr -> nx_tcp_listen_queue_head;
1001
1002 /* Remove the oldest packet from the queue. */
1003 listen_ptr -> nx_tcp_listen_queue_head = (listen_ptr -> nx_tcp_listen_queue_head) -> nx_packet_queue_next;
1004
1005 /* Decrement the number of packets in the queue. */
1006 listen_ptr -> nx_tcp_listen_queue_current--;
1007
1008 /* We have exceeded the number of connections that can be
1009 queued for this port. */
1010
1011 /* Release the packet. */
1012 _nx_packet_release(packet_ptr);
1013 }
1014 }
1015
1016 /* Finished processing, just return. */
1017 return;
1018 }
1019
1020 /* Move to the next listen request. */
1021 listen_ptr = listen_ptr -> nx_tcp_listen_next;
1022 } while (listen_ptr != ip_ptr -> nx_ip_tcp_active_listen_requests);
1023 }
1024
1025 #ifndef NX_DISABLE_TCP_INFO
1026
1027 /* Determine if a connection request is present. */
1028 if (tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_SYN_BIT)
1029 {
1030
1031 /* Yes, increment the TCP connections dropped count. */
1032 ip_ptr -> nx_ip_tcp_connections_dropped++;
1033 }
1034
1035 /* Increment the TCP dropped packet count. */
1036 ip_ptr -> nx_ip_tcp_receive_packets_dropped++;
1037 #endif /* NX_DISABLE_TCP_INFO */
1038
1039 /* Determine if a RST is present. If so, don't send a RST in response. */
1040 if (!(tcp_header_ptr -> nx_tcp_header_word_3 & NX_TCP_RST_BIT))
1041 {
1042
1043 /* Non RST is present, send reset when no connection is present. */
1044 _nx_tcp_no_connection_reset(ip_ptr, packet_ptr, tcp_header_ptr);
1045 }
1046
1047 /* Not a connection request, just release the packet. */
1048 _nx_packet_release(packet_ptr);
1049
1050 return;
1051 }
1052
1053