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