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