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 /**   Internet Protocol (IP)                                              */
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_ip.h"
30 #include "nx_packet.h"
31 #include "nx_ipv6.h"
32 #include "nx_icmpv6.h"
33 
34 #ifndef NX_DISABLE_FRAGMENTATION
35 /**************************************************************************/
36 /*                                                                        */
37 /*  FUNCTION                                               RELEASE        */
38 /*                                                                        */
39 /*    _nx_ip_fragment_assembly                            PORTABLE C      */
40 /*                                                           6.1          */
41 /*  AUTHOR                                                                */
42 /*                                                                        */
43 /*    Yuxin Zhou, Microsoft Corporation                                   */
44 /*                                                                        */
45 /*  DESCRIPTION                                                           */
46 /*                                                                        */
47 /*    This function processes the received fragment queue and attempts to */
48 /*    reassemble fragmented IP datagrams.  Once a datagram is reassembled */
49 /*    it is dispatched to the appropriate component.                      */
50 /*                                                                        */
51 /*  INPUT                                                                 */
52 /*                                                                        */
53 /*    ip_ptr                                Pointer to IP instance        */
54 /*                                                                        */
55 /*  OUTPUT                                                                */
56 /*                                                                        */
57 /*    None                                                                */
58 /*                                                                        */
59 /*  CALLS                                                                 */
60 /*                                                                        */
61 /*    _nx_packet_release                    Release packet                */
62 /*    _nx_ip_dispatch_process               The routine that examines     */
63 /*                                            other optional headers and  */
64 /*                                            upper layer protocols.      */
65 /*                                                                        */
66 /*  CALLED BY                                                             */
67 /*                                                                        */
68 /*    Application                                                         */
69 /*                                                                        */
70 /*  RELEASE HISTORY                                                       */
71 /*                                                                        */
72 /*    DATE              NAME                      DESCRIPTION             */
73 /*                                                                        */
74 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
75 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
76 /*                                            removed duplicated code,    */
77 /*                                            resulting in version 6.1    */
78 /*                                                                        */
79 /**************************************************************************/
80 
_nx_ip_fragment_assembly(NX_IP * ip_ptr)81 VOID  _nx_ip_fragment_assembly(NX_IP *ip_ptr)
82 {
83 TX_INTERRUPT_SAVE_AREA
84 
85 NX_PACKET                      *new_fragment_head;
86 NX_PACKET                      *current_fragment;
87 NX_PACKET                      *previous_fragment =  NX_NULL;
88 NX_PACKET                      *fragment_head;
89 NX_PACKET                      *search_ptr;
90 NX_PACKET                      *previous_ptr;
91 NX_PACKET                      *found_ptr;
92 NX_PACKET                      *old_ptr;
93 #ifndef NX_DISABLE_IPV4
94 NX_IPV4_HEADER                 *search_header = NX_NULL;
95 NX_IPV4_HEADER                 *current_header = NX_NULL;
96 ULONG                           current_ttl = 0;
97 #endif /* NX_DISABLE_IPV4 */
98 ULONG                           current_id = 0;
99 ULONG                           current_offset = 0;
100 ULONG                           protocol = NX_PROTOCOL_NO_NEXT_HEADER;
101 ULONG                           incomplete;
102 ULONG                           ip_version = NX_IP_VERSION_V4;
103 UCHAR                           copy_packet;
104 #ifdef FEATURE_NX_IPV6
105 NX_IPV6_HEADER_FRAGMENT_OPTION *search_v6_fragment_option = NX_NULL;
106 NX_IPV6_HEADER_FRAGMENT_OPTION *current_v6_fragment_option = NX_NULL;
107 NX_IPV6_HEADER                 *current_pkt_ip_header = NX_NULL;
108 #endif /* FEATURE_NX_IPV6 */
109 #ifdef NX_NAT_ENABLE
110 UINT                            packet_consumed;
111 #endif
112 
113 
114     /* Disable interrupts.  */
115     TX_DISABLE
116 
117     /* Remove the packets from the incoming IP fragment queue.  */
118     new_fragment_head =  ip_ptr -> nx_ip_received_fragment_head;
119     ip_ptr -> nx_ip_received_fragment_head =  NX_NULL;
120     ip_ptr -> nx_ip_received_fragment_tail =  NX_NULL;
121 
122     /* Restore interrupts.  */
123     TX_RESTORE
124 
125     /* Process each IP packet in the received IP fragment queue.  */
126     while (new_fragment_head)
127     {
128 
129         /* Setup the copy packet flag.  */
130         copy_packet = NX_FALSE;
131 
132         /* pick up the version number of this packet. */
133         ip_version = new_fragment_head -> nx_packet_ip_version;
134 
135         /* Setup the current fragment pointer.  */
136         current_fragment =  new_fragment_head;
137 
138         /* Move the head pointer.  */
139         new_fragment_head =  new_fragment_head -> nx_packet_queue_next;
140 
141 #ifndef NX_DISABLE_IPV4
142         if (ip_version == NX_IP_VERSION_V4)
143         {
144 
145             /* Setup header pointer for this packet.  */
146             /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
147             current_header =  (NX_IPV4_HEADER *)current_fragment -> nx_packet_prepend_ptr;
148 
149             /* Pickup the ID of this fragment.  */
150             current_id =  (current_header ->  nx_ip_header_word_1 >> NX_SHIFT_BY_16);
151 
152             /* Pickup the offset of the new IP fragment.  */
153             current_offset =  current_header -> nx_ip_header_word_1 & NX_IP_OFFSET_MASK;
154 
155             /* Pickup the time to live of this fragment.  */
156             current_ttl = (current_header -> nx_ip_header_word_2 & NX_IP_TIME_TO_LIVE_MASK) >> NX_IP_TIME_TO_LIVE_SHIFT;
157 
158             /* Set the IPv4 reassembly time. RFC791, Section3.2, Page27.  */
159             current_fragment -> nx_packet_reassembly_time = NX_IPV4_MAX_REASSEMBLY_TIME;
160             if (current_fragment -> nx_packet_reassembly_time < current_ttl)
161             {
162                 current_fragment -> nx_packet_reassembly_time = current_ttl;
163             }
164         }
165 #endif /* NX_DISABLE_IPV4 */
166 #ifdef FEATURE_NX_IPV6
167         if (ip_version == NX_IP_VERSION_V6)
168         {
169 
170             /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
171             current_pkt_ip_header = (NX_IPV6_HEADER *)current_fragment -> nx_packet_ip_header;
172 
173             /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
174             current_v6_fragment_option = (NX_IPV6_HEADER_FRAGMENT_OPTION *)current_fragment -> nx_packet_prepend_ptr;
175 
176             /* Pickup the ID of this fragment.  Note that the fragment ID is still in network byte order.
177                The ID is only used as a way to make comparisons.  We don't need to byte swap this field. */
178             current_id = current_v6_fragment_option -> nx_ipv6_header_fragment_option_packet_id;
179 
180             /* The offset field is left shifted by 3 bits.  However when we search through all the fragments
181                for the right location to insert this frame, we don't need get the accurate value of the offset
182                because the relative order of these offsets are preserved.  In other words, the lower 3 bits do
183                not affect the outcome of a simple comparison. */
184             current_offset = (current_v6_fragment_option -> nx_ipv6_header_fragment_option_offset_flag & 0xFFF8);
185 
186             /* Set the IPv6 reassembly time. RFC2460, Section4.5, Page22. */
187             current_fragment -> nx_packet_reassembly_time = NX_IPV6_MAX_REASSEMBLY_TIME;
188         }
189 #endif
190 
191         /* Set the found pointer to NULL.  */
192         found_ptr =  NX_NULL;
193 
194         /* Does the assembly list have anything in it?  */
195         if (ip_ptr -> nx_ip_fragment_assembly_head)
196         {
197 
198             /* Yes, we need to search the assembly queue to see if this fragment belongs
199                to another fragment.  */
200             search_ptr =    ip_ptr -> nx_ip_fragment_assembly_head;
201             previous_fragment =  NX_NULL;
202             while (search_ptr)
203             {
204 
205                 /* Check this packet only if the version number matches. */
206                 if (ip_version == search_ptr -> nx_packet_ip_version)
207                 {
208 #ifndef NX_DISABLE_IPV4
209                     if (ip_version == NX_IP_VERSION_V4)
210                     {
211                         /* Setup a pointer to the IP header of the packet in the assembly list.  */
212                         /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
213                         search_header =  (NX_IPV4_HEADER *)search_ptr -> nx_packet_prepend_ptr;
214 
215                         /* Determine if the IP header fields match. RFC 791 Section 3.2 recommends that packet
216                            fragments be compared for source IP, destination IP, protocol and IP header ID.  */
217                         /*lint -e{644} suppress variable might not be initialized, since "current_head" was initialized. */
218                         if ((current_id == (search_header -> nx_ip_header_word_1 >> NX_SHIFT_BY_16)) &&
219                             ((search_header -> nx_ip_header_word_2 & NX_IP_PROTOCOL_MASK) ==
220                              (current_header -> nx_ip_header_word_2 & NX_IP_PROTOCOL_MASK)) &&
221                             (search_header -> nx_ip_header_source_ip == current_header -> nx_ip_header_source_ip) &&
222                             (search_header -> nx_ip_header_destination_ip == current_header -> nx_ip_header_destination_ip))
223                         {
224 
225                             /* Yes, we found a match, just set the found_ptr and get out of
226                                this loop!  */
227                             found_ptr =  search_ptr;
228 
229                             /* The reassmebly timer should be MAX(reassembly time, Time To Live). RFC791, Section3.2, Page27.  */
230                             if (search_ptr -> nx_packet_reassembly_time < current_ttl)
231                             {
232                                 search_ptr -> nx_packet_reassembly_time = current_ttl;
233                             }
234 
235                             /* Updated the reassembly time.  */
236                             current_fragment -> nx_packet_reassembly_time = search_ptr -> nx_packet_reassembly_time;
237 
238                             break;
239                         }
240                     }
241 #endif /* NX_DISABLE_IPV4 */
242 
243 #ifdef FEATURE_NX_IPV6
244                     if (ip_version == NX_IP_VERSION_V6)
245                     {
246 
247                     NX_IPV6_HEADER *search_pkt_ip_header;
248 
249                         /* Set up a pointer to the IPv6 fragment option field.*/
250                         /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
251                         search_v6_fragment_option = (NX_IPV6_HEADER_FRAGMENT_OPTION *)search_ptr -> nx_packet_prepend_ptr;
252 
253                         /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
254                         search_pkt_ip_header = (NX_IPV6_HEADER *)search_ptr -> nx_packet_ip_header;
255 
256                         /* Determine if the IP packet IDs match. */
257                         /*lint -e{613} suppress possible use of null pointer, since "current_pkt_ip_header" was set to none NULL above. */
258                         if ((current_id == search_v6_fragment_option -> nx_ipv6_header_fragment_option_packet_id) &&
259                             (CHECK_IPV6_ADDRESSES_SAME(search_pkt_ip_header -> nx_ip_header_source_ip, current_pkt_ip_header -> nx_ip_header_source_ip)) &&
260                             (CHECK_IPV6_ADDRESSES_SAME(search_pkt_ip_header -> nx_ip_header_destination_ip, current_pkt_ip_header -> nx_ip_header_destination_ip)))
261                         {
262 
263                             /* Yes, we found a match, just set the found_ptr and get out of
264                                this loop!  */
265                             found_ptr =  search_ptr;
266                             current_fragment -> nx_packet_reassembly_time = search_ptr -> nx_packet_reassembly_time;
267                             break;
268                         }
269                     }
270 #endif /* FEATURE_NX_IPV6 */
271                 }
272 
273                 /* Remember the previous pointer.  */
274                 previous_fragment =  search_ptr;
275 
276                 /* Move to the next IP fragment in the re-assembly queue.  */
277                 search_ptr =  search_ptr -> nx_packet_queue_next;
278             }
279         }
280 
281         /* Was another IP packet fragment found?  */
282         if (found_ptr)
283         {
284 
285             /* Save the fragment head pointer.  */
286             fragment_head =  found_ptr;
287 
288             /* Another packet fragment was found...  find the proper place in the list
289                for this packet and check for complete re-assembly.  */
290 
291             /* Setup the previous pointer.  Note that the search pointer still points
292                to the first fragment in the list.  */
293             previous_ptr =  NX_NULL;
294             search_ptr =    found_ptr;
295 
296             /* Loop to walk through the fragment list.  */
297             do
298             {
299 
300 #ifndef NX_DISABLE_IPV4
301                 if (ip_version == NX_IP_VERSION_V4)
302                 {
303 
304                     /* Pickup a pointer to the IP header of the fragment.  */
305                     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
306                     search_header =  (NX_IPV4_HEADER *)search_ptr -> nx_packet_prepend_ptr;
307 
308                     /* Determine if the incoming IP fragment goes before this packet.  */
309                     if (current_offset <= (search_header -> nx_ip_header_word_1 & NX_IP_OFFSET_MASK))
310                     {
311 
312                         /* Determine if the incoming IP fragment is the copy packet. Link the new packet before old packet.  */
313                         if (current_offset == (search_header -> nx_ip_header_word_1 & NX_IP_OFFSET_MASK))
314                         {
315                             copy_packet = NX_TRUE;
316                         }
317 
318                         /* Yes, break out of the loop and insert the current packet.  */
319                         break;
320                     }
321                 }
322 #endif /* NX_DISABLE_IPV4 */
323 
324 #ifdef FEATURE_NX_IPV6
325                 if (ip_version == NX_IP_VERSION_V6)
326                 {
327 
328                     /* Build the IP header pointer.  */
329                     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
330                     search_v6_fragment_option = (NX_IPV6_HEADER_FRAGMENT_OPTION *)search_ptr -> nx_packet_prepend_ptr;
331 
332                     /* Determine if the incoming IP fragment goes before this packet.  */
333                     if (current_offset <= (ULONG)(search_v6_fragment_option -> nx_ipv6_header_fragment_option_offset_flag & 0xFFF8))
334                     {
335 
336                         /* Determine if the incoming IP fragment is copy packet. Link the new packet before old packet.  */
337                         if (current_offset == (ULONG)(search_v6_fragment_option -> nx_ipv6_header_fragment_option_offset_flag & 0xFFF8))
338                         {
339                             copy_packet = NX_TRUE;
340                         }
341 
342                         /* Yes, break out of the loop and insert the current packet.  */
343                         break;
344                     }
345                 }
346 #endif /* FEATURE_NX_IPV6 */
347 
348                 /* Otherwise, move the search and previous pointers to the next fragment in the
349                    chain.  */
350                 previous_ptr =  search_ptr;
351                 search_ptr   =  search_ptr -> nx_packet_union_next.nx_packet_fragment_next;
352             } while (search_ptr);
353 
354 
355             /* At this point, the previous pointer determines where to place the new fragment.  */
356             if (previous_ptr)
357             {
358 
359                 /* Add new fragment after the previous ptr.  */
360                 current_fragment -> nx_packet_union_next.nx_packet_fragment_next =  previous_ptr -> nx_packet_union_next.nx_packet_fragment_next;
361                 previous_ptr -> nx_packet_union_next.nx_packet_fragment_next =      current_fragment;
362             }
363             else
364             {
365 
366                 /* This packet needs to be inserted at the front of the fragment chain.  */
367                 current_fragment -> nx_packet_queue_next =     fragment_head -> nx_packet_queue_next;
368                 current_fragment -> nx_packet_union_next.nx_packet_fragment_next =  fragment_head;
369                 if (previous_fragment)
370                 {
371 
372                     /* We need to link up a different IP packet fragment chain that is in
373                        front of this one on the re-assembly queue.  */
374                     previous_fragment -> nx_packet_queue_next =  current_fragment;
375                 }
376                 else
377                 {
378 
379                     /* Nothing prior to this IP fragment chain, we need to just change the
380                        list header.  */
381                     ip_ptr -> nx_ip_fragment_assembly_head =  current_fragment;
382 
383                     /* Clear the timeout fragment pointer.  */
384                     ip_ptr -> nx_ip_timeout_fragment =  NX_NULL;
385                 }
386 
387                 /* Determine if we need to adjust the tail pointer.  */
388                 if (fragment_head == ip_ptr -> nx_ip_fragment_assembly_tail)
389                 {
390 
391                     /* Setup the new tail pointer.  */
392                     ip_ptr -> nx_ip_fragment_assembly_tail =  current_fragment;
393                 }
394 
395                 /* Setup the new fragment head.  */
396                 fragment_head =  current_fragment;
397             }
398 
399             /* Determine if the incoming IP fragment is the same packet.  */
400             if (copy_packet == NX_TRUE)
401             {
402 
403                 /* Fragments contain the same data, use the more recently arrived copy. RFC791, Section3.2, Page29.  */
404 
405                 /* Removed the old packet from the packet list, the old packet must be linked after new packet.  */
406                 old_ptr = current_fragment -> nx_packet_union_next.nx_packet_fragment_next;
407                 current_fragment -> nx_packet_union_next.nx_packet_fragment_next =  old_ptr -> nx_packet_union_next.nx_packet_fragment_next;
408 
409                 /* Reset tcp_queue_next before releasing. */
410                 /* Cast the ULONG into a packet pointer. Since this is exactly what we wish to do, disable the lint warning with the following comment:  */
411                 /*lint -e{923} suppress cast of ULONG to pointer.  */
412                 old_ptr -> nx_packet_union_next.nx_packet_tcp_queue_next = (NX_PACKET *)NX_PACKET_ALLOCATED;
413 
414                 /* Release the old packet.  */
415                 _nx_packet_release(old_ptr);
416             }
417 
418             /* At this point, the new IP fragment is in its proper place on the re-assembly
419                list.  We now need to walk the list and determine if all the fragments are
420                present.  */
421 
422             /* Setup the search pointer to the fragment head.  */
423             search_ptr =  fragment_head;
424 
425             /* Set the current expected offset to 0.  */
426             current_offset =  0;
427 
428             /* Loop through the packet chain to see if all the fragments have
429                arrived.  */
430             incomplete = 0;
431             do
432             {
433 
434 #ifndef NX_DISABLE_IPV4
435                 if (ip_version == NX_IP_VERSION_V4)
436                 {
437 
438                     /* Build the IP header pointer.  */
439                     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
440                     search_header =  (NX_IPV4_HEADER *)search_ptr -> nx_packet_prepend_ptr;
441 
442                     /* Check for the expected current offset.  */
443                     if (current_offset != (search_header -> nx_ip_header_word_1 & NX_IP_OFFSET_MASK))
444                     {
445 
446                         incomplete = 1;
447                         /* There are still more fragments necessary to reassemble this packet
448                            so just return.  */
449                         break;
450                     }
451 
452                     /* Calculate the next expected offset.  */
453                     current_offset =  current_offset +
454                         ((search_header -> nx_ip_header_word_0 & NX_LOWER_16_MASK) - (ULONG)sizeof(NX_IPV4_HEADER)) /
455                         NX_IP_ALIGN_FRAGS;
456                 }
457 #endif /* NX_DISABLE_IPV4 */
458 
459 #ifdef FEATURE_NX_IPV6
460                 if (ip_version == NX_IP_VERSION_V6)
461                 {
462 
463                     /* Build the IP header pointer.  */
464                     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
465                     search_v6_fragment_option = (NX_IPV6_HEADER_FRAGMENT_OPTION *)search_ptr -> nx_packet_prepend_ptr;
466 
467                     /* Check for the expected current offset.  */
468                     if (current_offset != (ULONG)(search_v6_fragment_option -> nx_ipv6_header_fragment_option_offset_flag & 0xFFF8))
469                     {
470 
471                         incomplete = 1;
472                         /* There are still more fragments necessary to reassemble this packet
473                            so just return.  */
474                         break;
475                     }
476 
477                     /* Calculate the next expected offset.  */
478                     current_offset =  current_offset +
479                         (search_ptr -> nx_packet_length - (ULONG)sizeof(NX_IPV6_HEADER_FRAGMENT_OPTION));
480                 }
481 #endif /* FEATURE_NX_IPV6 */
482 
483                 /* Move the search pointer forward to the next fragment.  */
484                 search_ptr =    search_ptr -> nx_packet_union_next.nx_packet_fragment_next;
485             } while (search_ptr);
486 
487             if (incomplete)
488             {
489                 continue;
490             }
491 
492             /* At this point the search header points to the last fragment in the chain.  In
493                order for the packet to be complete, the "more fragments" bit in its IP header
494                must be clear.  */
495             /*lint -e{613} suppress possible use of null pointer, since "search_header" and "search_v6_fragment_option" were set to none NULL in the while loop above. */
496             if (
497 #ifndef NX_DISABLE_IPV4
498                 ((ip_version == NX_IP_VERSION_V4) && (search_header -> nx_ip_header_word_1 & NX_IP_MORE_FRAGMENT))
499 #ifdef FEATURE_NX_IPV6
500                 ||
501 #endif /* FEATURE_NX_IPV6 */
502 #endif /* NX_DISABLE_IPV4 */
503 #ifdef FEATURE_NX_IPV6
504                 ((ip_version == NX_IP_VERSION_V6) && (search_v6_fragment_option -> nx_ipv6_header_fragment_option_offset_flag & 1))
505 #endif /* FEATURE_NX_IPV6 */
506                )
507             {
508 
509                 /* There are still more fragments necessary to re-assembly this packet
510                    so just return.  */
511                 continue;
512             }
513 
514             /* The packet can be reassembled under the fragment head pointer now.  It must now
515                be removed from the re-assembly list.  */
516             if (previous_fragment)
517             {
518 
519                 /* Remove the fragment from a position other than the head of the assembly list.  */
520                 previous_fragment -> nx_packet_queue_next =  fragment_head -> nx_packet_queue_next;
521             }
522             else
523             {
524 
525                 /* Modify the head of the re-assembly list.  */
526                 ip_ptr -> nx_ip_fragment_assembly_head =  fragment_head -> nx_packet_queue_next;
527 
528                 /* Clear the timeout fragment pointer since we are removing the first
529                    fragment (the oldest) on the assembly list.  */
530                 ip_ptr -> nx_ip_timeout_fragment =  NX_NULL;
531             }
532 
533             /* Determine if we need to adjust the tail pointer.  */
534             if (fragment_head == ip_ptr -> nx_ip_fragment_assembly_tail)
535             {
536 
537                 /* Setup the new tail pointer.  */
538                 ip_ptr -> nx_ip_fragment_assembly_tail =  previous_fragment;
539             }
540 
541             /* If we get here, the necessary fragments to reassemble the packet
542                are indeed available.  We now need to loop through the packet and reassemble
543                it.  */
544             search_ptr =       fragment_head -> nx_packet_union_next.nx_packet_fragment_next;
545             previous_fragment = fragment_head;
546 
547             /* Loop through the fragments and assemble the IP fragment.  */
548             while (search_ptr)
549             {
550 
551                 /* Reset tcp_queue_next before releasing. */
552                 /*lint -e{923} suppress cast of ULONG to pointer.  */
553                 previous_fragment -> nx_packet_union_next.nx_packet_tcp_queue_next = (NX_PACKET *)NX_PACKET_ALLOCATED;
554 
555 #ifndef NX_DISABLE_IPV4
556                 if (ip_version == NX_IP_VERSION_V4)
557                 {
558                     /* Accumulate the new length into the head packet.  */
559                     fragment_head -> nx_packet_length =  fragment_head -> nx_packet_length +
560                         search_ptr -> nx_packet_length - (ULONG)sizeof(NX_IPV4_HEADER);
561 
562                     /* Position past the IP header in the subsequent packets.  */
563                     search_ptr -> nx_packet_prepend_ptr =  search_ptr -> nx_packet_prepend_ptr +
564                         sizeof(NX_IPV4_HEADER);
565                 }
566 #endif /* NX_DISABLE_IPV4 */
567 #ifdef FEATURE_NX_IPV6
568                 if (ip_version == NX_IP_VERSION_V6)
569                 {
570 
571                     /* For IPv6, we move the prepend ptr to the next option .*/
572                     search_ptr -> nx_packet_prepend_ptr += sizeof(NX_IPV6_HEADER_FRAGMENT_OPTION);
573                     search_ptr -> nx_packet_length -= (ULONG)sizeof(NX_IPV6_HEADER_FRAGMENT_OPTION);
574 
575                     /* Accumulate the new length into the head packet. */
576                     fragment_head -> nx_packet_length += search_ptr -> nx_packet_length;
577                 }
578 
579 #endif /* FEATURE_NX_IPV6 */
580 
581                 /* Link the addition fragment to the head fragment.  */
582                 if (fragment_head -> nx_packet_last)
583                 {
584                     (fragment_head -> nx_packet_last) -> nx_packet_next =  search_ptr;
585                 }
586                 else
587                 {
588                     fragment_head -> nx_packet_next =  search_ptr;
589                 }
590                 if (search_ptr -> nx_packet_last)
591                 {
592                     fragment_head -> nx_packet_last =  search_ptr -> nx_packet_last;
593                 }
594                 else
595                 {
596                     fragment_head -> nx_packet_last =  search_ptr;
597                 }
598 
599                 /* Set the previous fragment. */
600                 previous_fragment = search_ptr;
601 
602                 /* Move to the next fragment in the chain.  */
603                 search_ptr =  search_ptr -> nx_packet_union_next.nx_packet_fragment_next;
604             }
605 
606             /* Reset tcp_queue_next before releasing. */
607             /*lint -e{923} suppress cast of ULONG to pointer.  */
608             previous_fragment -> nx_packet_union_next.nx_packet_tcp_queue_next = (NX_PACKET *)NX_PACKET_ALLOCATED;
609 
610             /* We are now ready to dispatch this packet just like the normal IP receive packet
611                processing.  */
612 
613 #ifndef NX_DISABLE_IP_INFO
614 
615             /* Increment the number of packets reassembled.  */
616             ip_ptr -> nx_ip_packets_reassembled++;
617 
618             /* Increment the number of packets delivered.  */
619             ip_ptr -> nx_ip_total_packets_delivered++;
620 
621             /* Increment the IP packet bytes received (not including the header).  */
622             ip_ptr -> nx_ip_total_bytes_received +=  fragment_head -> nx_packet_length;
623 #endif
624 
625             /* Build a pointer to the IP header.  */
626 #ifndef NX_DISABLE_IPV4
627             if (ip_version == NX_IP_VERSION_V4)
628             {
629 
630                 /* The packet is now reassembled. */
631 
632                 /* Check if this IP interface has a NAT forwarding service. If so, let NAT get the
633                    packet first and if it is not a packet that should be forwarded by NAT, then
634                    let NetX process the packet in the normal way.  */
635 
636 #ifdef NX_NAT_ENABLE
637 
638                 /* Check if this IP interface has a NAT forwarding service. */
639                 if (ip_ptr -> nx_ip_nat_packet_process)
640                 {
641 
642                     /* Yes, so forward this packet to the NAT handler.  If NAT does not 'consume' this
643                        packet, allow NetX to process the packet.  */
644                     packet_consumed = (ip_ptr -> nx_ip_nat_packet_process)(ip_ptr, fragment_head, NX_TRUE);
645 
646                     /* Check to see if the packet has been consumed by NAT.  */
647                     if (packet_consumed)
648                     {
649 
650 #ifndef NX_DISABLE_IP_INFO
651 
652                         /* Increment the IP packets forwarded counter.  */
653                         ip_ptr -> nx_ip_packets_forwarded++;
654 #endif /* NX_DISABLE_IP_INFO */
655 
656                         continue;
657                     }
658 
659                     /* (NetX will process all packets that drop through here.) */
660                 }
661 #endif
662 
663                 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
664                 current_header = (NX_IPV4_HEADER *)fragment_head -> nx_packet_ip_header;
665 
666                 /* Determine what protocol the current IP datagram is.  */
667                 protocol = (current_header -> nx_ip_header_word_2 >> 16) & 0xFF;
668 
669                 /* Remove the IP header from the packet.  */
670                 fragment_head -> nx_packet_prepend_ptr = fragment_head -> nx_packet_prepend_ptr + sizeof(NX_IPV4_HEADER);
671 
672                 /* Adjust the length.  */
673                 fragment_head -> nx_packet_length = fragment_head -> nx_packet_length - (ULONG)sizeof(NX_IPV4_HEADER);
674             }
675 #endif /* NX_DISABLE_IPV4 */
676 #ifdef FEATURE_NX_IPV6
677             if (ip_version == NX_IP_VERSION_V6)
678             {
679                 fragment_head -> nx_packet_prepend_ptr += sizeof(NX_IPV6_HEADER_FRAGMENT_OPTION);
680                 fragment_head -> nx_packet_length -= (ULONG)sizeof(NX_IPV6_HEADER_FRAGMENT_OPTION);
681 
682                 /*lint -e{613} suppress possible use of null pointer, since "current_pkt_ip_header" was set to none NULL above. */
683                 protocol = current_v6_fragment_option -> nx_ipv6_header_fragment_option_next_header;
684             }
685 #endif
686 
687             /* Call the dispatch function go to process the packet. */
688             if (_nx_ip_dispatch_process(ip_ptr, fragment_head, (UINT)protocol))
689             {
690 
691                 /* Toss the IP packet since we don't know what to do with it!  */
692                 _nx_packet_release(fragment_head);
693             }
694         }
695         else
696         {
697 
698             /* No other packet was found on the re-assembly list so this packet must be the
699                first one of a new IP packet.  Just add it to the end of the assembly queue.  */
700             if (ip_ptr -> nx_ip_fragment_assembly_head)
701             {
702 
703                 /* Re-assembly list is not empty.  Just place this IP packet at the
704                    end of the IP fragment assembly list.  */
705                 ip_ptr -> nx_ip_fragment_assembly_tail -> nx_packet_queue_next =   current_fragment;
706                 ip_ptr -> nx_ip_fragment_assembly_tail =                           current_fragment;
707                 current_fragment -> nx_packet_queue_next =                         NX_NULL;
708                 current_fragment -> nx_packet_union_next.nx_packet_fragment_next = NX_NULL;
709             }
710             else
711             {
712 
713                 /* First IP fragment on the assembly list.  Setup the head and tail pointers to
714                    this packet.  */
715                 ip_ptr -> nx_ip_fragment_assembly_head =                           current_fragment;
716                 ip_ptr -> nx_ip_fragment_assembly_tail =                           current_fragment;
717                 current_fragment -> nx_packet_queue_next =                         NX_NULL;
718                 current_fragment -> nx_packet_union_next.nx_packet_fragment_next = NX_NULL;
719             }
720         }
721     }
722 }
723 #endif /* NX_DISABLE_FRAGMENTATION */
724 
725