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_igmp.h"
30 #include "nx_packet.h"
31 #include "nx_udp.h"
32 
33 #ifndef NX_DISABLE_IPV4
34 /**************************************************************************/
35 /*                                                                        */
36 /*  FUNCTION                                               RELEASE        */
37 /*                                                                        */
38 /*    _nx_ipv4_packet_receive                             PORTABLE C      */
39 /*                                                           6.3.0        */
40 /*  AUTHOR                                                                */
41 /*                                                                        */
42 /*    Yuxin Zhou, Microsoft Corporation                                   */
43 /*                                                                        */
44 /*  DESCRIPTION                                                           */
45 /*                                                                        */
46 /*    This function receives a packet from the nx_ip_packet_receive.      */
47 /*    nx_ip_packet_receive forwards only IPv4 packet to this function.    */
48 /*    Here it is either processes it or places it in a deferred           */
49 /*    processing queue, depending on the complexity of the packet.        */
50 /*                                                                        */
51 /*  INPUT                                                                 */
52 /*                                                                        */
53 /*    ip_ptr                                Pointer to IP control block   */
54 /*    packet_ptr                            Pointer to packet to send     */
55 /*                                                                        */
56 /*  OUTPUT                                                                */
57 /*                                                                        */
58 /*    None                                                                */
59 /*                                                                        */
60 /*  CALLS                                                                 */
61 /*                                                                        */
62 /*    _nx_ip_checksum_compute               Compute IP checksum           */
63 /*    _nx_igmp_multicast_check              Check for Multicast match     */
64 /*    _nx_packet_release                    Release packet to packet pool */
65 /*    tx_event_flags_set                    Set events for IP thread      */
66 /*    _nx_ip_dispatch_process               The routine that examines     */
67 /*                                            other optional headers and  */
68 /*                                            upper layer protocols.      */
69 /*                                                                        */
70 /*  CALLED BY                                                             */
71 /*                                                                        */
72 /*    Application I/O Driver                                              */
73 /*                                                                        */
74 /*  RELEASE HISTORY                                                       */
75 /*                                                                        */
76 /*    DATE              NAME                      DESCRIPTION             */
77 /*                                                                        */
78 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
79 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
80 /*                                            resulting in version 6.1    */
81 /*  10-31-2023     Tiejun Zhou              Modified comment(s),          */
82 /*                                            validated packet length for */
83 /*                                            fragments,                  */
84 /*                                            resulting in version 6.3.0  */
85 /*                                                                        */
86 /**************************************************************************/
_nx_ipv4_packet_receive(NX_IP * ip_ptr,NX_PACKET * packet_ptr)87 VOID  _nx_ipv4_packet_receive(NX_IP *ip_ptr, NX_PACKET *packet_ptr)
88 {
89 
90 TX_INTERRUPT_SAVE_AREA
91 #ifndef NX_DISABLE_PACKET_CHAIN
92 NX_PACKET      *before_last_packet;
93 NX_PACKET      *last_packet;
94 #endif /* NX_DISABLE_PACKET_CHAIN */
95 NX_IPV4_HEADER *ip_header_ptr;
96 ULONG          *word_ptr;
97 ULONG           ip_header_length;
98 ULONG           protocol;
99 ULONG           delta;
100 ULONG           val;
101 ULONG           pkt_length;
102 ULONG           checksum;
103 NX_INTERFACE   *if_ptr;
104 NX_UDP_HEADER  *udp_header_ptr;
105 UINT            dest_port;
106 UINT            option_processed;
107 #if defined(NX_DISABLE_IP_RX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY)
108 UINT            compute_checksum = 1;
109 #endif /* defined(NX_DISABLE_IP_RX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) */
110 #ifdef NX_NAT_ENABLE
111 UINT            packet_consumed;
112 #endif
113 
114 #ifdef NX_DISABLE_IP_RX_CHECKSUM
115     compute_checksum = 0;
116 #endif /* NX_DISABLE_IP_RX_CHECKSUM */
117 
118     /* It's assumed that the IP link driver has positioned the top pointer in the
119        packet to the start of the IP address... so that's where we will start.  */
120     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
121     ip_header_ptr = (NX_IPV4_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
122 
123     /* Add debug information. */
124     NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
125 
126     /* If trace is enabled, insert this event into the trace buffer.  */
127     NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_IP_RECEIVE, ip_ptr, ip_header_ptr -> nx_ip_header_source_ip, packet_ptr, packet_ptr -> nx_packet_length, NX_TRACE_INTERNAL_EVENTS, 0, 0);
128 
129 
130     /* Pick up the first word in the IP header. */
131     val = ip_header_ptr -> nx_ip_header_word_0;
132 
133     /* Convert to host byte order. */
134     NX_CHANGE_ULONG_ENDIAN(val);
135 
136     /* Obtain packet length. */
137     pkt_length = val & NX_LOWER_16_MASK;
138 
139     /* Make sure the IP length matches the packet length.  Some Ethernet devices
140        add padding to small packets, which results in a discrepancy between the
141        packet length and the IP header length.  */
142     if (packet_ptr -> nx_packet_length != pkt_length)
143     {
144 
145         /* Determine if the packet length is less than the size reported in the IP header.  */
146         if (packet_ptr -> nx_packet_length < pkt_length)
147         {
148 
149             /* Packet is too small!  */
150 
151 #ifndef NX_DISABLE_IP_INFO
152 
153             /* Increment the IP invalid packet error.  */
154             ip_ptr -> nx_ip_invalid_packets++;
155 
156             /* Increment the IP receive packets dropped count.  */
157             ip_ptr -> nx_ip_receive_packets_dropped++;
158 #endif
159 
160             /* Invalid packet length, just release it.  */
161             _nx_packet_release(packet_ptr);
162 
163             /* The function is complete, just return!  */
164             return;
165         }
166 
167         /* Calculate the difference in the length.  */
168         delta =  packet_ptr -> nx_packet_length - pkt_length;
169 
170         /* Adjust the packet length.  */
171         packet_ptr -> nx_packet_length =  packet_ptr -> nx_packet_length - delta;
172 
173         /* Adjust the append pointer.  */
174 
175 #ifndef NX_DISABLE_PACKET_CHAIN
176         /* Loop to process adjustment that spans multiple packets.  */
177         while (delta)
178         {
179 
180             /* Determine if the packet is chained (or still chained after the adjustment).  */
181             if (packet_ptr -> nx_packet_last == NX_NULL)
182             {
183 
184                 /* No, packet is not chained, simply adjust the append pointer in the packet.  */
185                 packet_ptr -> nx_packet_append_ptr =  packet_ptr -> nx_packet_append_ptr - delta;
186 
187                 /* Break out of the loop, since the adjustment is complete.  */
188                 break;
189             }
190 
191             /* Pickup the pointer to the last packet.  */
192             last_packet =  packet_ptr -> nx_packet_last;
193 
194             /* Determine if the amount to adjust is less than the payload in the last packet.  */
195             /*lint -e{946} -e{947} suppress pointer subtraction, since it is necessary. */
196             if (((ULONG)(last_packet -> nx_packet_append_ptr - last_packet -> nx_packet_prepend_ptr)) > delta)
197             {
198 
199                 /* Yes, simply adjust the append pointer of the last packet in the chain.  */
200                 /*lint -e{946} -e{947} suppress pointer subtraction, since it is necessary. */
201                 last_packet -> nx_packet_append_ptr =  last_packet -> nx_packet_append_ptr - delta;
202 
203                 /* Get out of the loop, since the adjustment is complete.  */
204                 break;
205             }
206             else
207             {
208 
209                 /* Adjust the delta by the amount in the last packet.  */
210                 delta =  delta - ((ULONG)(last_packet -> nx_packet_append_ptr - last_packet -> nx_packet_prepend_ptr));
211 
212                 /* Find the packet before the last packet.  */
213                 before_last_packet =  packet_ptr;
214                 while (before_last_packet -> nx_packet_next != last_packet)
215                 {
216 
217                     /* Move to the next packet in the chain.  */
218                     before_last_packet =  before_last_packet -> nx_packet_next;
219                 }
220 
221                 /* At this point, we need to release the last packet and adjust the other packet
222                    pointers.  */
223 
224                 /* Ensure the next packet pointer is NULL in what is now the last packet.  */
225                 before_last_packet -> nx_packet_next =  NX_NULL;
226 
227                 /* Determine if the packet is still chained.  */
228                 if (packet_ptr != before_last_packet)
229                 {
230 
231                     /* Yes, the packet is still chained, setup the last packet pointer.  */
232                     packet_ptr -> nx_packet_last =  before_last_packet;
233                 }
234                 else
235                 {
236 
237                     /* The packet is no longer chained, set the last packet pointer to NULL.  */
238                     packet_ptr -> nx_packet_last =  NX_NULL;
239                 }
240 
241                 /* Release the last packet.   */
242                 _nx_packet_release(last_packet);
243             }
244         }
245 #else
246 
247         /* Simply adjust the append pointer in the packet.  */
248         packet_ptr -> nx_packet_append_ptr =  packet_ptr -> nx_packet_append_ptr - delta;
249 #endif /* NX_DISABLE_PACKET_CHAIN */
250     }
251 
252     /* Get the incoming interface. */
253     if_ptr = packet_ptr -> nx_packet_address.nx_packet_interface_ptr;
254 
255     /* Obtain IP header length. */
256     ip_header_length =  (val & NX_IP_LENGTH_MASK) >> 24;
257 
258 #ifndef NX_DISABLE_RX_SIZE_CHECKING
259 
260     /* Check for minimal packet length. The check is done after the endian swapping
261        since the compiler may possibly be able to optimize the lookup of
262        "nx_packet_length" and therefore reduce the amount of work performing these
263        size checks. The endian logic is okay since packets must always have
264        payloads greater than the IP header in size.  */
265     if ((packet_ptr -> nx_packet_length <= (ip_header_length << 2)) ||
266         (ip_header_length < NX_IP_NORMAL_LENGTH))
267     {
268 
269         /* Packet is too small!  */
270 
271 #ifndef NX_DISABLE_IP_INFO
272 
273         /* Increment the IP invalid packet error.  */
274         ip_ptr -> nx_ip_invalid_packets++;
275 
276         /* Increment the IP receive packets dropped count.  */
277         ip_ptr -> nx_ip_receive_packets_dropped++;
278 #endif
279 
280         /* Invalid packet length, just release it.  */
281         _nx_packet_release(packet_ptr);
282 
283         /* The function is complete, just return!  */
284         return;
285     }
286 #endif
287 
288 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
289     if (if_ptr -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_IPV4_RX_CHECKSUM)
290     {
291         compute_checksum = 0;
292     }
293 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
294 #if defined(NX_DISABLE_IP_RX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY)
295     if (compute_checksum == 1)
296 #endif /* defined(NX_DISABLE_IP_RX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) */
297     {
298 
299 
300         checksum = _nx_ip_checksum_compute(packet_ptr, NX_IP_VERSION_V4,
301                                            /* length is the size of IP header, including options */
302                                            (UINT)(ip_header_length << 2),
303                                            /* IPv4 header checksum doesn't care src/dest addresses */
304                                            NULL, NULL);
305         checksum =  ~checksum & NX_LOWER_16_MASK;
306 
307         /* Check the checksum again.  */
308         if (checksum)
309         {
310 
311 #ifndef NX_DISABLE_IP_INFO
312 
313             /* Increment the IP invalid packet error.  */
314             ip_ptr -> nx_ip_invalid_packets++;
315 
316             /* Increment the IP checksum error.  */
317             ip_ptr -> nx_ip_receive_checksum_errors++;
318 
319             /* Increment the IP receive packets dropped count.  */
320             ip_ptr -> nx_ip_receive_packets_dropped++;
321 #endif
322 
323             /* Checksum error, just release it.  */
324             _nx_packet_release(packet_ptr);
325 
326             /* The function is complete, just return!  */
327             return;
328         }
329     }
330 
331     /* IP receive checksum processing is disabled... just check for and remove if
332        necessary the IP option words.  */
333 
334     /* Endian swapping logic.  If NX_LITTLE_ENDIAN is specified, these macros will
335        swap the endian of the IP header.  */
336     NX_CHANGE_ULONG_ENDIAN(ip_header_ptr -> nx_ip_header_word_0);
337     NX_CHANGE_ULONG_ENDIAN(ip_header_ptr -> nx_ip_header_word_1);
338     NX_CHANGE_ULONG_ENDIAN(ip_header_ptr -> nx_ip_header_word_2);
339     NX_CHANGE_ULONG_ENDIAN(ip_header_ptr -> nx_ip_header_source_ip);
340     NX_CHANGE_ULONG_ENDIAN(ip_header_ptr -> nx_ip_header_destination_ip);
341 
342 #ifdef NX_ENABLE_SOURCE_ADDRESS_CHECK
343     /* Check whether source address is valid. */
344     /* Section 3.2.1.3, page 30, RFC 1122. */
345     if (if_ptr -> nx_interface_address_mapping_needed == NX_TRUE)
346     {
347         if (((ip_header_ptr -> nx_ip_header_source_ip & ~(if_ptr -> nx_interface_ip_network_mask)) == ~(if_ptr -> nx_interface_ip_network_mask)) ||
348             (((ip_header_ptr -> nx_ip_header_source_ip & ~(if_ptr -> nx_interface_ip_network_mask)) == 0) &&
349              (ip_header_ptr -> nx_ip_header_source_ip != 0)) ||
350             ((ip_header_ptr -> nx_ip_header_source_ip & NX_IP_CLASS_D_MASK) == NX_IP_CLASS_D_TYPE))
351         {
352 
353 #ifndef NX_DISABLE_IP_INFO
354 
355             /* Increment the IP invalid address error.  */
356             ip_ptr -> nx_ip_invalid_receive_address++;
357 
358             /* Increment the IP receive packets dropped count.  */
359             ip_ptr -> nx_ip_receive_packets_dropped++;
360 #endif
361 
362             /* Toss the IP packet since we don't know what to do with it!  */
363             _nx_packet_release(packet_ptr);
364 
365             /* Return to caller.  */
366             return;
367         }
368     }
369 #endif /* NX_ENABLE_SOURCE_ADDRESS_CHECK */
370 
371     /* Determine if there are options in the IP header that make the length greater
372        than the default length.  */
373     if (ip_header_length > NX_IP_NORMAL_LENGTH)
374     {
375 
376         /* Process the IPv4 option.  */
377         option_processed = _nx_ipv4_option_process(ip_ptr, packet_ptr);
378 
379         /* Check the status.  */
380         if (option_processed == NX_FALSE)
381         {
382 
383 #ifndef NX_DISABLE_IP_INFO
384 
385             /* Increment the IP receive packets dropped count.  */
386             ip_ptr -> nx_ip_receive_packets_dropped++;
387 #endif
388 
389             /* IPv4 option error, toss the packet!  */
390             _nx_packet_release(packet_ptr);
391 
392             /* In all cases, receive processing is finished.  Return to caller.  */
393             return;
394         }
395 
396         /* Setup a pointer to the last option word.  */
397         word_ptr = ((ULONG *)((VOID *)ip_header_ptr)) + ip_header_length - 1;
398 
399         /* Remove the option words prior to handling the IP header.  */
400         *word_ptr-- = ip_header_ptr -> nx_ip_header_destination_ip;
401         *word_ptr-- = ip_header_ptr -> nx_ip_header_source_ip;
402         *word_ptr-- = ip_header_ptr -> nx_ip_header_word_2;
403         *word_ptr-- = ip_header_ptr -> nx_ip_header_word_1;
404         *word_ptr = (ULONG)(((ip_header_ptr -> nx_ip_header_word_0) & (~NX_IP_LENGTH_MASK)) | NX_IP_VERSION);
405 
406         /* Update the ip_header_ptr and the packet and the packet prepend pointer, ip header pointer and length.  */
407         /*lint -e{929} -e{740} -e{826} suppress cast from pointer to pointer, since it is necessary  */
408         ip_header_ptr =  (NX_IPV4_HEADER *)word_ptr;
409 
410         /*lint -e{928} suppress cast from pointer to pointer, since it is necessary  */
411         packet_ptr -> nx_packet_prepend_ptr = (UCHAR *)word_ptr;
412         packet_ptr -> nx_packet_ip_header = packet_ptr -> nx_packet_prepend_ptr;
413         packet_ptr -> nx_packet_length = packet_ptr -> nx_packet_length - ((ip_header_length -  NX_IP_NORMAL_LENGTH) * (ULONG)sizeof(ULONG));
414     }
415 
416     /* Check if this IP interface has a NAT forwarding service.  If so, let NAT get the
417        packet first and if it is not a packet that should be forwarded by NAT, then
418        let NetX process the packet in the normal way.  */
419 
420 #ifdef NX_NAT_ENABLE
421     /* Check if this IP interface has a NAT forwarding service. */
422     if (ip_ptr -> nx_ip_nat_packet_process)
423     {
424 
425         /* Call NAT preprocess hanlder to check if NAT module can process this packet.  */
426         if ((ip_ptr -> nx_ip_nat_packet_process)(ip_ptr, packet_ptr, NX_FALSE) == NX_TRUE)
427         {
428 
429             /* NAT router would need to assemble the fragments together first
430                and then translate prior to forwarding. RFC2663, RFC2766. */
431 
432             /* Determine if this packet is fragmented.  If so, place it on the deferred processing
433                queue. The input packet will then be processed by an IP system thread.  */
434             if (ip_header_ptr -> nx_ip_header_word_1 & NX_IP_FRAGMENT_MASK)
435             {
436 
437 #ifndef NX_DISABLE_IP_INFO
438 
439                 /* Increment the IP receive fragments count.  */
440                 ip_ptr -> nx_ip_total_fragments_received++;
441 #endif
442 
443                 /* Yes, the incoming IP header is fragmented.  Check to see if IP fragmenting
444                    has been enabled.  */
445                 if (ip_ptr -> nx_ip_fragment_assembly)
446                 {
447 
448                     /* Yes, fragmenting is available.  Place the packet on the incoming
449                        fragment queue.  */
450 
451                     /* Disable interrupts.  */
452                     TX_DISABLE
453 
454                     /* Determine if the queue is empty.  */
455                     if (ip_ptr -> nx_ip_received_fragment_head)
456                     {
457 
458                         /* Reassembly queue is not empty, add this packet to the end of
459                            the queue.  */
460                         (ip_ptr -> nx_ip_received_fragment_tail) -> nx_packet_queue_next =  packet_ptr;
461                         packet_ptr -> nx_packet_queue_next =  NX_NULL;
462                         ip_ptr -> nx_ip_received_fragment_tail =  packet_ptr;
463                     }
464                     else
465                     {
466 
467                         /* Reassembly queue is empty.  Just setup the head and tail pointers
468                            to point to this packet.  */
469                         ip_ptr -> nx_ip_received_fragment_head =  packet_ptr;
470                         ip_ptr -> nx_ip_received_fragment_tail =  packet_ptr;
471                         packet_ptr -> nx_packet_queue_next =      NX_NULL;
472                     }
473 
474                     /* Add debug information. */
475                     NX_PACKET_DEBUG(NX_PACKET_IP_FRAGMENT_QUEUE, __LINE__, packet_ptr);
476 
477                     /* Restore interrupts.  */
478                     TX_RESTORE
479 
480 #ifndef NX_FRAGMENT_IMMEDIATE_ASSEMBLY
481                     /* Wakeup IP helper thread to process the IP fragment re-assembly.  */
482                     tx_event_flags_set(&(ip_ptr -> nx_ip_events), NX_IP_UNFRAG_EVENT, TX_OR);
483 #else
484                     /* Process the IP fragment reassemble.  */
485                     (ip_ptr -> nx_ip_fragment_assembly)(ip_ptr);
486 #endif /* NX_FRAGMENT_IMMEDIATE_ASSEMBLY */
487                 }
488                 else
489                 {
490 
491 #ifndef NX_DISABLE_IP_INFO
492 
493                     /* Increment the IP receive packets dropped count.  */
494                     ip_ptr -> nx_ip_receive_packets_dropped++;
495 #endif
496 
497                     /* Fragmentation has not been enabled, toss the packet!  */
498                     _nx_packet_release(packet_ptr);
499                 }
500 
501                 /* In all cases, receive processing is finished.  Return to caller.  */
502                 return;
503             }
504 
505             /* Normal packet, so forward this packet to the NAT handler. If NAT does not 'consume' this
506                packet, allow NetX to process the packet.  */
507             packet_consumed = (ip_ptr -> nx_ip_nat_packet_process)(ip_ptr, packet_ptr, NX_TRUE);
508 
509             /* Check to see if the packet has been consumed by NAT.  */
510             if (packet_consumed)
511             {
512 
513 #ifndef NX_DISABLE_IP_INFO
514 
515                 /* Increment the IP packets forwarded counter.  */
516                 ip_ptr -> nx_ip_packets_forwarded++;
517 #endif /* NX_DISABLE_IP_INFO */
518 
519                 return;
520             }
521         }
522 
523         /* (NetX will process all packets that drop through here.) */
524     }
525 #endif
526 
527     /* Determine if the IP datagram is for this IP address or a broadcast IP on this
528        network.  */
529     if ((ip_header_ptr -> nx_ip_header_destination_ip == if_ptr -> nx_interface_ip_address) ||
530 
531         /* Check for incoming IP address of zero.  Incoming IP address of zero should
532            be received regardless of our current IP address.  */
533         (ip_header_ptr -> nx_ip_header_destination_ip == 0) ||
534 
535         /* Check for IP broadcast.  */
536         (((ip_header_ptr -> nx_ip_header_destination_ip & if_ptr -> nx_interface_ip_network_mask) ==
537           if_ptr -> nx_interface_ip_network) &&
538          ((ip_header_ptr -> nx_ip_header_destination_ip & ~(if_ptr -> nx_interface_ip_network_mask)) ==
539           ~(if_ptr -> nx_interface_ip_network_mask))) ||
540 
541         /* Check for limited broadcast.  */
542         (ip_header_ptr -> nx_ip_header_destination_ip == NX_IP_LIMITED_BROADCAST) ||
543 
544         /* Check for loopback address.  */
545         ((ip_header_ptr -> nx_ip_header_destination_ip >= NX_IP_LOOPBACK_FIRST) &&
546          (ip_header_ptr -> nx_ip_header_destination_ip <= NX_IP_LOOPBACK_LAST)) ||
547 
548         /* Check for valid Multicast address.  */
549         (_nx_igmp_multicast_check(ip_ptr, ip_header_ptr -> nx_ip_header_destination_ip, if_ptr)))
550     {
551 
552         /* Determine if this packet is fragmented.  If so, place it on the deferred processing
553            queue.  The input packet will then be processed by an IP system thread.  */
554         if (ip_header_ptr -> nx_ip_header_word_1 & NX_IP_FRAGMENT_MASK)
555         {
556 
557 #ifndef NX_DISABLE_IP_INFO
558 
559             /* Increment the IP receive fragments count.  */
560             ip_ptr -> nx_ip_total_fragments_received++;
561 #endif
562 
563             /* Yes, the incoming IP header is fragmented.  Check to see if IP fragmenting
564                has been enabled.  */
565 #ifdef NX_ENABLE_LOW_WATERMARK
566             if (ip_ptr -> nx_ip_fragment_assembly &&
567                 (packet_ptr -> nx_packet_pool_owner -> nx_packet_pool_available >=
568                  packet_ptr -> nx_packet_pool_owner -> nx_packet_pool_low_watermark))
569 #else
570             if (ip_ptr -> nx_ip_fragment_assembly)
571 #endif
572             {
573 
574                 /* Yes, fragmenting is available.  Place the packet on the incoming
575                    fragment queue.  */
576 
577                 /* Check packet length with more fragment bit. If not multiple of 8 bytes...  */
578                 if ((ip_header_ptr -> nx_ip_header_word_1 & NX_IP_MORE_FRAGMENT) &&
579                     (((pkt_length  - (ULONG)sizeof(NX_IPV4_HEADER))  & 0x7) != 0))
580                 {
581 
582                     /* Invalid length.  Drop the packet.  */
583 #ifndef NX_DISABLE_IP_INFO
584 
585                     /* Increment the IP receive packets dropped count.  */
586                     ip_ptr -> nx_ip_receive_packets_dropped++;
587 #endif
588                     _nx_packet_release(packet_ptr);
589                     return;
590                 }
591 
592                 /* Disable interrupts.  */
593                 TX_DISABLE
594 
595                 /* Determine if the queue is empty.  */
596                 if (ip_ptr -> nx_ip_received_fragment_head)
597                 {
598 
599                     /* Reassembly queue is not empty, add this packet to the end of
600                        the queue.  */
601                     (ip_ptr -> nx_ip_received_fragment_tail) -> nx_packet_queue_next =  packet_ptr;
602                     packet_ptr -> nx_packet_queue_next =  NX_NULL;
603                     ip_ptr -> nx_ip_received_fragment_tail =  packet_ptr;
604                 }
605                 else
606                 {
607 
608                     /* Reassembly queue is empty.  Just setup the head and tail pointers
609                        to point to this packet.  */
610                     ip_ptr -> nx_ip_received_fragment_head =  packet_ptr;
611                     ip_ptr -> nx_ip_received_fragment_tail =  packet_ptr;
612                     packet_ptr -> nx_packet_queue_next =      NX_NULL;
613                 }
614 
615                 /* Add debug information. */
616                 NX_PACKET_DEBUG(NX_PACKET_IP_FRAGMENT_QUEUE, __LINE__, packet_ptr);
617 
618                 /* Restore interrupts.  */
619                 TX_RESTORE
620 
621 #ifndef NX_FRAGMENT_IMMEDIATE_ASSEMBLY
622                 /* Wakeup IP helper thread to process the IP fragment re-assembly.  */
623                 tx_event_flags_set(&(ip_ptr -> nx_ip_events), NX_IP_UNFRAG_EVENT, TX_OR);
624 #else
625                 /* Process the IP fragment reassemble.  */
626                 (ip_ptr -> nx_ip_fragment_assembly)(ip_ptr);
627 #endif /* NX_FRAGMENT_IMMEDIATE_ASSEMBLY */
628             }
629             else
630             {
631 
632 #ifndef NX_DISABLE_IP_INFO
633 
634                 /* Increment the IP receive packets dropped count.  */
635                 ip_ptr -> nx_ip_receive_packets_dropped++;
636 #endif
637 
638                 /* Fragmentation has not been enabled, toss the packet!  */
639                 _nx_packet_release(packet_ptr);
640             }
641 
642             /* In all cases, receive processing is finished.  Return to caller.  */
643             return;
644         }
645 
646         /* Determine what protocol the current IP datagram is.  */
647         protocol =  (ip_header_ptr -> nx_ip_header_word_2 >> 16) & 0xFF;
648 
649         /* Remove the IP header from the packet.  */
650         packet_ptr -> nx_packet_prepend_ptr =  packet_ptr -> nx_packet_prepend_ptr + sizeof(NX_IPV4_HEADER);
651 
652         /* Adjust the length.  */
653         packet_ptr -> nx_packet_length =  packet_ptr -> nx_packet_length - (ULONG)sizeof(NX_IPV4_HEADER);
654 
655 #ifndef NX_DISABLE_IP_INFO
656 
657         /* Increment the number of packets delivered.  */
658         ip_ptr -> nx_ip_total_packets_delivered++;
659 
660         /* Increment the IP packet bytes received (not including the header).  */
661         ip_ptr -> nx_ip_total_bytes_received +=  packet_ptr -> nx_packet_length;
662 #endif
663         if (_nx_ip_dispatch_process(ip_ptr, packet_ptr, (UINT)protocol))
664         {
665             _nx_packet_release(packet_ptr);
666         }
667     }
668     /* Try to receive the DHCP message before release this packet.
669        NetX should receive the unicast DHCP message when interface IP address is zero.  */
670 
671     /* Check if this IP interface has IP address.  */
672     else if (if_ptr -> nx_interface_ip_address == 0)
673     {
674 
675         /* Determine what protocol the current IP datagram is.  */
676         protocol =  ip_header_ptr -> nx_ip_header_word_2 & NX_IP_PROTOCOL_MASK;
677 
678         /* Check if this packet is UDP message.  */
679         if (protocol == NX_IP_UDP)
680         {
681 
682             /* Remove the IP header from the packet.  */
683             packet_ptr -> nx_packet_prepend_ptr =  packet_ptr -> nx_packet_prepend_ptr + sizeof(NX_IPV4_HEADER);
684 
685             /* Adjust the length.  */
686             packet_ptr -> nx_packet_length =  packet_ptr -> nx_packet_length - (ULONG)sizeof(NX_IPV4_HEADER);
687 
688 #ifndef NX_DISABLE_IP_INFO
689 
690             /* Increment the number of packets delivered.  */
691             ip_ptr -> nx_ip_total_packets_delivered++;
692 
693             /* Increment the IP packet bytes received (not including the header).  */
694             ip_ptr -> nx_ip_total_bytes_received +=  packet_ptr -> nx_packet_length;
695 #endif
696 
697             /* Pickup the pointer to the head of the UDP packet.  */
698             /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
699             udp_header_ptr =  (NX_UDP_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
700 
701             /* Endian swapping logic.  If NX_LITTLE_ENDIAN is specified, these macros will
702                swap the endian of the UDP header.  */
703             NX_CHANGE_ULONG_ENDIAN(udp_header_ptr -> nx_udp_header_word_0);
704 
705             /* Pickup the destination UDP port.  */
706             dest_port =  (UINT)(udp_header_ptr -> nx_udp_header_word_0 & NX_LOWER_16_MASK);
707 
708             /* Endian swapping logic.  If NX_LITTLE_ENDIAN is specified, these macros will
709                swap the endian of the UDP header.  */
710             NX_CHANGE_ULONG_ENDIAN(udp_header_ptr -> nx_udp_header_word_0);
711 
712             /* Check if this packet is DHCP message.  */
713             if (dest_port == 68)
714             {
715                 if (ip_ptr -> nx_ip_udp_packet_receive)
716                 {
717 
718                     /* Yes, dispatch it to the appropriate UDP handler if present.  */
719                     (ip_ptr -> nx_ip_udp_packet_receive)(ip_ptr, packet_ptr);
720 
721                     return;
722                 }
723             }
724         }
725 
726 #ifndef NX_DISABLE_IP_INFO
727 
728         /* Decrement the number of packets delivered.  */
729         ip_ptr -> nx_ip_total_packets_delivered--;
730 
731         /* Decrement the IP packet bytes received (not including the header).  */
732         ip_ptr -> nx_ip_total_bytes_received -=  packet_ptr -> nx_packet_length;
733 
734         /* Increment the IP invalid address error.  */
735         ip_ptr -> nx_ip_invalid_receive_address++;
736 
737         /* Increment the IP receive packets dropped count.  */
738         ip_ptr -> nx_ip_receive_packets_dropped++;
739 #endif
740 
741         /* Toss the IP packet since we don't know what to do with it!  */
742         _nx_packet_release(packet_ptr);
743 
744         /* Return to caller.  */
745         return;
746     }
747     else if (ip_ptr -> nx_ip_forward_packet_process)
748     {
749 
750 #ifndef NX_DISABLE_IP_INFO
751 
752         /* Increment the IP packets forwarded counter.  */
753         ip_ptr -> nx_ip_packets_forwarded++;
754 #endif
755 
756         /* The packet is not for this IP instance so call the
757            forward IP packet processing routine.  */
758         (ip_ptr -> nx_ip_forward_packet_process)(ip_ptr, packet_ptr);
759     }
760     else
761     {
762 
763 #ifndef NX_DISABLE_IP_INFO
764 
765         /* Increment the IP invalid address error.  */
766         ip_ptr -> nx_ip_invalid_receive_address++;
767 
768         /* Increment the IP receive packets dropped count.  */
769         ip_ptr -> nx_ip_receive_packets_dropped++;
770 #endif
771 
772         /* Toss the IP packet since we don't know what to do with it!  */
773         _nx_packet_release(packet_ptr);
774 
775         /* Return to caller.  */
776         return;
777     }
778 }
779 #endif /* !NX_DISABLE_IPV4  */
780 
781