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