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