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_icmp.h"
31 #include "nx_igmp.h"
32 #ifdef FEATURE_NX_IPV6
33 #include "nx_ipv6.h"
34 #include "nx_icmpv6.h"
35 #endif /* FEATURE_NX_IPV6 */
36 
37 #ifdef NX_IPSEC_ENABLE
38 #include "nx_ipsec.h"
39 #endif /* FEATURE_IPSEC_ENABLE */
40 
41 /**************************************************************************/
42 /*                                                                        */
43 /*  FUNCTION                                               RELEASE        */
44 /*                                                                        */
45 /*    _nx_ip_thread_entry                                 PORTABLE C      */
46 /*                                                           6.1.8        */
47 /*  AUTHOR                                                                */
48 /*                                                                        */
49 /*    Yuxin Zhou, Microsoft Corporation                                   */
50 /*                                                                        */
51 /*  DESCRIPTION                                                           */
52 /*                                                                        */
53 /*    This function is the entry point for each IP's helper thread.  The  */
54 /*    IP helper thread is responsible for periodic ARP requests,          */
55 /*    reassembling fragmented IP messages, and helping with TCP           */
56 /*    protocol.                                                           */
57 /*                                                                        */
58 /*    Note that the priority of this function is determined by the IP     */
59 /*    create service.                                                     */
60 /*                                                                        */
61 /*  INPUT                                                                 */
62 /*                                                                        */
63 /*    ip_ptr_value                          Pointer to IP control block   */
64 /*                                                                        */
65 /*  OUTPUT                                                                */
66 /*                                                                        */
67 /*    status                                Completion status             */
68 /*                                                                        */
69 /*  CALLS                                                                 */
70 /*                                                                        */
71 /*    tx_event_flags_get                    Suspend on event flags that   */
72 /*                                            are used to signal this     */
73 /*                                            thread what to do           */
74 /*    tx_mutex_get                          Obtain protection mutex       */
75 /*    tx_mutex_put                          Release protection mutex      */
76 /*    (nx_ip_driver_deferred_packet_handler)Optional deferred packet      */
77 /*                                            processing routine          */
78 /*    _nx_ip_packet_receive                 IP receive packet processing  */
79 /*    _nx_ipv6_multicast_join               Join IPv6 multicast group     */
80 /*    (nx_arp_queue_process)                ARP receive queue processing  */
81 /*    (nx_ip_arp_periodic_update)           ARP periodic update processing*/
82 /*    (nx_ip_rarp_periodic_update)          RARP periodic processing      */
83 /*    (nx_ip_fragment_assembly)             IP fragment processing        */
84 /*    (nx_ip_fragment_timeout_check)        Fragment timeout checking     */
85 /*    (nx_ip_icmp_queue_process)            ICMP message queue processing */
86 /*    (nx_ip_igmp_queue_process)            IGMP message queue processing */
87 /*    (nx_ip_igmp_periodic_processing)      IGMP periodic processing      */
88 /*    (nx_ip_tcp_queue_process)             TCP message queue processing  */
89 /*    (nx_ip_tcp_periodic_processing)       TCP periodic processing       */
90 /*    (nx_tcp_deferred_cleanup_check)       TCP deferred cleanup check    */
91 /*    _nx_ipsec_sa_lifetime_tick            IPsec lifetime tick update    */
92 /*    (ip_link_driver)                      User supplied link driver     */
93 /*    _nx_ipsec_hw_packet_process           IPsec HW packet process       */
94 /*    _nx_icmpv6_send_ns                    Send Neighbor Solicitation    */
95 /*                                            message                     */
96 /*    (nx_nd_cache_fast_periodic_update)    ND Cache fast periodic service*/
97 /*                                            routine                     */
98 /*    (nx_nd_cache_slow_periodic_update)    ND Cache slow periodic service*/
99 /*                                            routine                     */
100 /*    _nxd_ipv6_prefix_router_timer_tick    IPv6 Preifx service routine   */
101 /*    _nxd_ipv6_router_solicitation_check   IPv6 RS service routine       */
102 /*    (nx_destination_table_periodic_update)                              */
103 /*                                          Destination table service     */
104 /*                                            routine.                    */
105 /*                                                                        */
106 /*  CALLED BY                                                             */
107 /*                                                                        */
108 /*    ThreadX Scheduler                                                   */
109 /*                                                                        */
110 /*  RELEASE HISTORY                                                       */
111 /*                                                                        */
112 /*    DATE              NAME                      DESCRIPTION             */
113 /*                                                                        */
114 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
115 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
116 /*                                            resulting in version 6.1    */
117 /*  08-02-2021     Yuxin Zhou               Modified comment(s), and      */
118 /*                                            supported TCP/IP offload,   */
119 /*                                            resulting in version 6.1.8  */
120 /*                                                                        */
121 /**************************************************************************/
_nx_ip_thread_entry(ULONG ip_ptr_value)122 VOID  _nx_ip_thread_entry(ULONG ip_ptr_value)
123 {
124 
125 TX_INTERRUPT_SAVE_AREA
126 
127 NX_IP_DRIVER      driver_request;
128 NX_IP            *ip_ptr;
129 ULONG             ip_events;
130 NX_PACKET        *packet_ptr;
131 UINT              i;
132 UINT              index;
133 ULONG             foo;
134 #ifdef FEATURE_NX_IPV6
135 NXD_IPV6_ADDRESS *interface_ipv6_address;
136 #endif /* FEATURE_NX_IPV6 */
137 
138 
139     /* Setup IP pointer.  */
140     NX_THREAD_EXTENSION_PTR_GET(ip_ptr, NX_IP, ip_ptr_value)
141 
142     /* Obtain the IP internal mutex before calling the driver.  */
143     tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
144 
145     /* Set the IP initialization done flag to true.  */
146     ip_ptr -> nx_ip_initialize_done =  NX_TRUE;
147 
148     /* Loop through all physical interfaces to initialize and enable the hardware. */
149     for (i = 0; i < NX_MAX_PHYSICAL_INTERFACES; i++)
150     {
151 
152         /* Is this a valid interface with a link driver associated with it? */
153         if ((ip_ptr -> nx_ip_interface[i].nx_interface_valid) && (ip_ptr -> nx_ip_interface[i].nx_interface_link_driver_entry))
154         {
155 
156 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
157             /* Clear capability flag first.  */
158             ip_ptr -> nx_ip_interface[i].nx_interface_capability_flag = 0;
159 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
160 
161 
162             ip_ptr -> nx_ip_interface[i].nx_interface_link_up = NX_TRUE;
163 
164             /* Yes; attach the interface to the device. */
165             driver_request.nx_ip_driver_ptr        =  ip_ptr;
166             driver_request.nx_ip_driver_command    =  NX_LINK_INTERFACE_ATTACH;
167             driver_request.nx_ip_driver_interface  = &(ip_ptr -> nx_ip_interface[i]);
168             (ip_ptr -> nx_ip_interface[i].nx_interface_link_driver_entry)(&driver_request);
169 
170 #ifdef NX_ENABLE_TCPIP_OFFLOAD
171             if (ip_ptr -> nx_ip_interface[i].nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_TCPIP_OFFLOAD)
172             {
173 
174                 /* Set checksum capability for TCP/IP offload interface.  */
175                 ip_ptr -> nx_ip_interface[i].nx_interface_capability_flag |= NX_INTERFACE_CAPABILITY_CHECKSUM_ALL;
176             }
177 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
178 
179             /* Call the link driver to initialize the hardware. Among other
180                responsibilities, the driver is required to provide the
181                Maximum Transfer Unit (MTU) for the physical layer. The MTU
182                should represent the actual physical layer transfer size
183                less the physical layer headers and trailers.  */
184             driver_request.nx_ip_driver_ptr =      ip_ptr;
185             driver_request.nx_ip_driver_command =  NX_LINK_INITIALIZE;
186 
187             /* If trace is enabled, insert this event into the trace buffer.  */
188             NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_IO_DRIVER_INITIALIZE, ip_ptr, 0, 0, 0, NX_TRACE_INTERNAL_EVENTS, 0, 0);
189 
190             /*
191                When an IP instance is created, the first interface (nx_ip_interface[0]) is configured using parameters
192                provided in the IP create call.
193 
194                When IP thread runs, it invokes the first interface link driver for link initialization.
195              */
196             (ip_ptr -> nx_ip_interface[i].nx_interface_link_driver_entry)(&driver_request);
197 
198             /* Call the link driver again to enable the interface.  */
199             driver_request.nx_ip_driver_ptr =      ip_ptr;
200             driver_request.nx_ip_driver_command =  NX_LINK_ENABLE;
201 
202             /* If trace is enabled, insert this event into the trace buffer.  */
203             NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_IO_DRIVER_LINK_ENABLE, ip_ptr, 0, 0, 0, NX_TRACE_INTERNAL_EVENTS, 0, 0);
204 
205             (ip_ptr -> nx_ip_interface[i].nx_interface_link_driver_entry)(&driver_request);
206 
207 #ifdef FEATURE_NX_IPV6
208             /* For ever IPv6 address on this interface, set the Solicitated Multicast address. */
209             interface_ipv6_address = ip_ptr -> nx_ip_interface[i].nxd_interface_ipv6_address_list_head;
210 
211             while (interface_ipv6_address)
212             {
213             ULONG multicast_address[4];
214 
215                 SET_SOLICITED_NODE_MULTICAST_ADDRESS(multicast_address, interface_ipv6_address -> nxd_ipv6_address);
216                 _nx_ipv6_multicast_join(ip_ptr, multicast_address, &ip_ptr -> nx_ip_interface[i]);
217                 interface_ipv6_address = interface_ipv6_address -> nxd_ipv6_address_next;
218             }
219 #ifndef NX_DISABLE_ICMPV6_ROUTER_SOLICITATION
220             if (ip_ptr -> nx_ipv6_packet_receive)
221             {
222             ULONG address[4];
223 
224                 /* Create the all-node multicast group address, */
225                 address[0] = 0xFF020000;
226                 address[1] = 0;
227                 address[2] = 0;
228                 address[3] = 1;
229 
230 
231                 /* Join all-node multicast group. */
232                 _nx_ipv6_multicast_join(ip_ptr, address, &ip_ptr -> nx_ip_interface[i]);
233             }
234 #endif
235 #endif
236         }
237     }
238 
239     /* Loop to process events for this IP instance.  */
240     for (;;)
241     {
242 
243         /* Release the IP internal mutex.  */
244         tx_mutex_put(&(ip_ptr -> nx_ip_protection));
245 
246         /* Pickup IP event flags.  */
247         tx_event_flags_get(&(ip_ptr -> nx_ip_events), NX_IP_ALL_EVENTS, TX_OR_CLEAR, &ip_events, TX_WAIT_FOREVER);
248 
249         /* Obtain the IP internal mutex before processing the IP event.  */
250         tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
251 
252 #ifdef NX_DRIVER_DEFERRED_PROCESSING
253         /* Check for any packets deferred by the Driver.  */
254         /*lint -e{644} suppress variable might not be initialized, since "ip_events" was initialized in tx_event_flags_get. */
255         if (ip_events & NX_IP_DRIVER_PACKET_EVENT)
256         {
257 
258             /* Loop to process all deferred packet requests.  */
259             while (ip_ptr -> nx_ip_driver_deferred_packet_head)
260             {
261                 /* Remove the first packet and process it!  */
262 
263                 /* Disable interrupts.  */
264                 TX_DISABLE
265 
266                 /* Pickup the first packet.  */
267                 packet_ptr =  ip_ptr -> nx_ip_driver_deferred_packet_head;
268 
269                 /* Move the head pointer to the next packet.  */
270                 ip_ptr -> nx_ip_driver_deferred_packet_head =  packet_ptr -> nx_packet_queue_next;
271 
272                 /* Check for end of deferred processing queue.  */
273                 if (ip_ptr -> nx_ip_driver_deferred_packet_head == NX_NULL)
274                 {
275 
276                     /* Yes, the queue is empty.  Set the tail pointer to NULL.  */
277                     ip_ptr -> nx_ip_driver_deferred_packet_tail =  NX_NULL;
278                 }
279 
280                 /* Restore interrupts.  */
281                 TX_RESTORE
282 
283                 /* Add debug information. */
284                 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
285 
286                 /* make sure that there is a deferred processing function */
287                 if (ip_ptr ->  nx_ip_driver_deferred_packet_handler)
288                 {
289                     /* Call the actual Deferred packet processing function.  */
290                     (ip_ptr ->  nx_ip_driver_deferred_packet_handler)(ip_ptr, packet_ptr);
291                 }
292             }
293 
294             /* Determine if there is anything else to do in the loop.  */
295             ip_events =  ip_events & ~(NX_IP_DRIVER_PACKET_EVENT);
296             if (!ip_events)
297             {
298                 continue;
299             }
300         }
301 #endif
302 
303         /* Check for an IP receive packet event.  */
304         /*lint -e{644} suppress variable might not be initialized, since "ip_events" was initialized by tx_event_flags_get. */
305         if (ip_events & NX_IP_RECEIVE_EVENT)
306         {
307 
308             /* Loop to process all deferred packet requests.  */
309             while (ip_ptr -> nx_ip_deferred_received_packet_head)
310             {
311 
312                 /* Remove the first packet and process it!  */
313 
314                 /* Disable interrupts.  */
315                 TX_DISABLE
316 
317                 /* Pickup the first packet.  */
318                 packet_ptr =  ip_ptr -> nx_ip_deferred_received_packet_head;
319 
320                 /* Move the head pointer to the next packet.  */
321                 ip_ptr -> nx_ip_deferred_received_packet_head =  packet_ptr -> nx_packet_queue_next;
322 
323                 /* Check for end of deferred processing queue.  */
324                 if (ip_ptr -> nx_ip_deferred_received_packet_head == NX_NULL)
325                 {
326 
327                     /* Yes, the queue is empty.  Set the tail pointer to NULL.  */
328                     ip_ptr -> nx_ip_deferred_received_packet_tail =  NX_NULL;
329                 }
330 
331                 /* Restore interrupts.  */
332                 TX_RESTORE
333 
334                 /* Call the actual IP packet receive function.  */
335                 _nx_ip_packet_receive(ip_ptr, packet_ptr);
336             }
337 
338             /* Determine if there is anything else to do in the loop.  */
339             ip_events =  ip_events & ~(NX_IP_RECEIVE_EVENT);
340             if (!ip_events)
341             {
342                 continue;
343             }
344         }
345 
346         /* Check for a TCP message event.  */
347         if (ip_events & NX_IP_TCP_EVENT)
348         {
349 
350             /* Process the TCP packet queue.  */
351             (ip_ptr -> nx_ip_tcp_queue_process)(ip_ptr);
352 
353             /* Determine if there is anything else to do in the loop.  */
354             ip_events =  ip_events & ~(NX_IP_TCP_EVENT);
355             if (!ip_events)
356             {
357                 continue;
358             }
359         }
360 
361         /* Check for a fast TCP event.  */
362         if (ip_events & NX_IP_FAST_EVENT)
363         {
364 
365             /* Start DAD for the link local address by sending off the first solicitation immediately
366                while subsequent solicitations will occur on the next slow event. */
367 #ifdef FEATURE_NX_IPV6
368 #ifndef NX_DISABLE_IPV6_DAD
369 
370             if (ip_ptr -> nx_ip_icmpv6_packet_process)
371             {
372 
373                 /* Proceed with DAD check only if ICMPv6 is enabled. */
374                 for (i = 0; i < NX_MAX_PHYSICAL_INTERFACES; i++)
375                 {
376 
377                     interface_ipv6_address = ip_ptr -> nx_ip_interface[i].nxd_interface_ipv6_address_list_head;
378 
379                     if (interface_ipv6_address &&
380                         interface_ipv6_address -> nxd_ipv6_address_DupAddrDetectTransmit == NX_IPV6_DAD_TRANSMITS)
381                     {
382 
383 
384                         /* No. This address is still under DAD.  Transmit a NS */
385                         /* Note that the 2nd last parameter sendUnicast is set to Zero. In this case
386                            the last arg NDCacheEntry is not being used in _nx_icmpv6_send_ns. */
387                         _nx_icmpv6_send_ns(ip_ptr,
388                                            interface_ipv6_address -> nxd_ipv6_address,
389                                            0, interface_ipv6_address, 0, &ip_ptr -> nx_ipv6_nd_cache[0]);
390 
391                         interface_ipv6_address -> nxd_ipv6_address_DupAddrDetectTransmit--;
392                     }
393                 }
394             }
395 #endif
396 
397             if (ip_ptr -> nx_nd_cache_fast_periodic_update)
398             {
399                 /* Run the ND Cache update routine.  This is a 100 millisecond timer */
400                 ip_ptr -> nx_nd_cache_fast_periodic_update(ip_ptr);
401             }
402 
403 #endif /* FEATURE_NX_IPV6 */
404 
405             /* Process the fast TCP processing.  */
406             if (ip_ptr -> nx_ip_tcp_fast_periodic_processing)
407             {
408                 (ip_ptr -> nx_ip_tcp_fast_periodic_processing)(ip_ptr);
409             }
410 
411             /* Determine if there is anything else to do in the loop.  */
412             ip_events =  ip_events & ~(NX_IP_FAST_EVENT);
413             if (!ip_events)
414             {
415                 continue;
416             }
417         }
418 
419         /* Check for a periodic events.  */
420         if (ip_events & NX_IP_PERIODIC_EVENT)
421         {
422 
423 #ifndef NX_DISABLE_IPV4
424             /* Process the ARP periodic update, if ARP has been enabled.  */
425             if (ip_ptr -> nx_ip_arp_periodic_update)
426             {
427                 (ip_ptr -> nx_ip_arp_periodic_update)(ip_ptr);
428             }
429 
430             /* Process the RARP periodic update, if RARP has been enabled.  */
431             if (ip_ptr -> nx_ip_rarp_periodic_update)
432             {
433                 (ip_ptr -> nx_ip_rarp_periodic_update)(ip_ptr);
434             }
435 
436             /* Process IGMP periodic events, if IGMP has been enabled.  */
437             if (ip_ptr -> nx_ip_igmp_periodic_processing)
438             {
439                 (ip_ptr -> nx_ip_igmp_periodic_processing)(ip_ptr);
440             }
441 #endif /* !NX_DISABLE_IPV4  */
442 
443             /* Process IP fragmentation timeouts, if IP fragmenting has been
444                enabled.  */
445             if (ip_ptr -> nx_ip_fragment_timeout_check)
446             {
447                 (ip_ptr -> nx_ip_fragment_timeout_check)(ip_ptr);
448             }
449 
450             /* Process TCP periodic events, if TCP has been enabled.  */
451             if (ip_ptr -> nx_ip_tcp_periodic_processing)
452             {
453                 (ip_ptr -> nx_ip_tcp_periodic_processing)(ip_ptr);
454             }
455 #ifdef FEATURE_NX_IPV6
456             /* Process IPv6 events, such as DAD... */
457 #ifndef NX_DISABLE_IPV6_DAD
458             if (ip_ptr -> nx_ip_icmpv6_packet_process)
459             {
460                 _nx_icmpv6_perform_DAD(ip_ptr);
461             }
462 #endif /* NX_DISABLE_IPV6_DAD */
463             if (ip_ptr -> nx_nd_cache_slow_periodic_update)
464             {
465                 /* Run the ND Cache update routine.  This is a 1 second timer */
466                 ip_ptr -> nx_nd_cache_slow_periodic_update(ip_ptr);
467             }
468 
469             _nxd_ipv6_prefix_router_timer_tick(ip_ptr);
470 #ifndef NX_DISABLE_ICMPV6_ROUTER_SOLICITATION
471             _nxd_ipv6_router_solicitation_check(ip_ptr);
472 #endif
473 
474 #ifdef NX_IPSEC_ENABLE
475             _nx_ipsec_sa_lifetime_tick(ip_ptr);
476 #endif /* NX_IPSEC_ENABLE */
477 
478 #ifdef NX_ENABLE_IPV6_PATH_MTU_DISCOVERY
479 
480             if (ip_ptr -> nx_destination_table_periodic_update)
481             {
482 
483                 /* Run the Destination table update routine.  This will update the timers
484                    on destination table entries' MTU data, and if expired will initiate
485                    MTU path discovery.  */
486                 ip_ptr -> nx_destination_table_periodic_update(ip_ptr);
487             }
488 #endif /* NX_ENABLE_IPV6_PATH_MTU_DISCOVERY */
489 
490 #endif /* FEATURE_NX_IPV6 */
491             /* Determine if there is anything else to do in the loop.  */
492             ip_events =  ip_events & ~(NX_IP_PERIODIC_EVENT);
493             if (!ip_events)
494             {
495                 continue;
496             }
497         }
498 
499 #ifdef NX_IPSEC_ENABLE
500         if (ip_events & NX_IP_HW_DONE_EVENT)
501         {
502 
503             /* Process the hw_done_packet queue.  */
504             _nx_ipsec_hw_packet_process(ip_ptr);
505         }
506 #endif /* NX_IPSEC_ENABLE */
507 
508 #ifndef NX_DISABLE_IPV4
509         /* Check for an ARP receive packet event.  */
510         if ((ip_events & NX_IP_ARP_REC_EVENT) && (ip_ptr -> nx_ip_arp_queue_process))
511         {
512 
513             /* Process the ARP queue.  */
514             (ip_ptr -> nx_ip_arp_queue_process)(ip_ptr);
515         }
516 
517         /* Check for an RARP receive packet event.  */
518         if ((ip_events & NX_IP_RARP_REC_EVENT) && (ip_ptr -> nx_ip_rarp_queue_process))
519         {
520 
521             /* Process the RARP queue.  */
522             (ip_ptr -> nx_ip_rarp_queue_process)(ip_ptr);
523         }
524 
525         /* Check for an IGMP message event.  */
526         if (ip_events & NX_IP_IGMP_EVENT)
527         {
528 
529             /* Process the ICMP packet queue.  */
530             (ip_ptr -> nx_ip_igmp_queue_process)(ip_ptr);
531         }
532 
533         /* Check for an IGMP enable event.  */
534         if (ip_events & NX_IP_IGMP_ENABLE_EVENT)
535         {
536 
537             /* Call the associated driver for this IP instance to register the "all hosts"
538                multicast address.  */
539             for (i = 0; i < NX_MAX_PHYSICAL_INTERFACES; i++)
540             {
541                 /* Enable the hardware for IGMP for all valid interfaces. */
542                 if (ip_ptr -> nx_ip_interface[i].nx_interface_valid)
543                 {
544                     driver_request.nx_ip_driver_ptr =                    ip_ptr;
545                     driver_request.nx_ip_driver_command =                NX_LINK_MULTICAST_JOIN;
546                     driver_request.nx_ip_driver_physical_address_msw =   NX_IP_MULTICAST_UPPER;
547                     /*lint -e{835} -e{845} suppress operating on zero. */
548                     driver_request.nx_ip_driver_physical_address_lsw =   NX_IP_MULTICAST_LOWER | (NX_ALL_HOSTS_ADDRESS & NX_IP_MULTICAST_MASK);
549                     driver_request.nx_ip_driver_interface            =   &(ip_ptr -> nx_ip_interface[i]);
550 
551                     /* If trace is enabled, insert this event into the trace buffer.  */
552                     NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_IO_DRIVER_MULTICAST_JOIN, ip_ptr, 0, 0, 0, NX_TRACE_INTERNAL_EVENTS, 0, 0);
553 
554                     (ip_ptr -> nx_ip_interface[i].nx_interface_link_driver_entry)(&driver_request);
555                 }
556             }
557         }
558 #endif /* !NX_DISABLE_IPV4  */
559 
560         /* Check for an IP unfragment event.  */
561         if (ip_events & NX_IP_UNFRAG_EVENT)
562         {
563 
564             /* Process the IP fragment reassemble, if fragment has been enabled.  */
565             if (ip_ptr -> nx_ip_fragment_assembly)
566             {
567                 (ip_ptr -> nx_ip_fragment_assembly)(ip_ptr);
568             }
569         }
570 
571 #ifndef NX_DISABLE_IPV4
572         /* Check for an ICMP message event.  */
573         if (ip_events & NX_IP_ICMP_EVENT)
574         {
575 
576             /* Process the ICMP packet queue.  */
577             (ip_ptr -> nx_ip_icmp_queue_process)(ip_ptr);
578         }
579 #endif /* NX_DISABLE_IPV4 */
580 
581         /* Check for a deferred processing request from the driver.  */
582         if (ip_events & NX_IP_DRIVER_DEFERRED_EVENT)
583         {
584 
585             /* Go through each valid interface. */
586             for (index = 0; index < NX_MAX_PHYSICAL_INTERFACES; index++)
587             {
588                 if (ip_ptr -> nx_ip_interface[index].nx_interface_valid)
589                 {
590 
591                     /* Yes, there is a deferred processing event from the driver. The only valid information
592                        fields are the IP pointer and the command.  */
593                     driver_request.nx_ip_driver_ptr =        ip_ptr;
594                     driver_request.nx_ip_driver_command =    NX_LINK_DEFERRED_PROCESSING;
595                     driver_request.nx_ip_driver_interface  = &(ip_ptr -> nx_ip_interface[index]);
596                     driver_request.nx_ip_driver_return_ptr = &foo;
597 
598                     (ip_ptr -> nx_ip_interface[index].nx_interface_link_driver_entry)(&driver_request);
599                 }
600             }
601         }
602 
603         /* Check for a deferred TCP cleanup processing request from the driver.  */
604         if (ip_events & NX_IP_TCP_CLEANUP_DEFERRED)
605         {
606 
607             /* Yes, there is a deferred cleanup processing event. Call the TCP deferred cleanup
608                processing function.  */
609             (ip_ptr -> nx_tcp_deferred_cleanup_check)(ip_ptr);
610         }
611 
612         /* Check for a link status change request from the driver.  */
613         if (ip_events & NX_IP_LINK_STATUS_EVENT)
614         {
615 
616             /* Yes, there is a link status change  event. Call the deferred link status processing function. */
617             _nx_ip_deferred_link_status_process(ip_ptr);
618         }
619     }
620 }
621 
622