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