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_ipv6.h"
30 #include "nx_packet.h"
31 #include "nx_icmpv6.h"
32 
33 #ifdef FEATURE_NX_IPV6
34 
35 
36 /**************************************************************************/
37 /*                                                                        */
38 /*  FUNCTION                                               RELEASE        */
39 /*                                                                        */
40 /*    _nx_ipv6_packet_send                                PORTABLE C      */
41 /*                                                           6.1.8        */
42 /*  AUTHOR                                                                */
43 /*                                                                        */
44 /*    Yuxin Zhou, Microsoft Corporation                                   */
45 /*                                                                        */
46 /*  DESCRIPTION                                                           */
47 /*                                                                        */
48 /*    This function prepends an IP header and sends an IP packet to the   */
49 /*    appropriate link driver.  Caller needs to fill in the correct       */
50 /*    source and destination addresses into the packet source and         */
51 /*    destination address.  Caller also makes sure that the packet        */
52 /*    interface address is valid (not in tentative state), and source     */
53 /*    address is not unspecified e.g. NULL.                               */
54 /*                                                                        */
55 /*  INPUT                                                                 */
56 /*                                                                        */
57 /*    ip_ptr                                Pointer to IP control block   */
58 /*    packet_ptr                            Pointer to packet to send     */
59 /*    protocol                              Protocol being encapsulated   */
60 /*    payload_size                          Size of the payload           */
61 /*    hop_limit                             Hop limit value to set in IP  */
62 /*                                             header.                    */
63 /*    src_address                           Source address                */
64 /*    dest_address                          Destination address           */
65 /*                                                                        */
66 /*  OUTPUT                                                                */
67 /*                                                                        */
68 /*    None                                                                */
69 /*                                                                        */
70 /*  CALLS                                                                 */
71 /*                                                                        */
72 /*    _nx_ipv6_header_add                   Add IPv6 header               */
73 /*    _nx_packet_transmit_release           Release transmit packet       */
74 /*    _nx_nd_cache_add_entry                Add new entry to ND Cache     */
75 /*    IPv6_Address_Type                     Find IPv6 address type        */
76 /*    _nx_packet_copy                       Packet copy                   */
77 /*    _nx_ip_packet_deferred_receive        Places received packets in    */
78 /*                                            deferred packet queue       */
79 /*    _nx_icmpv6_send_ns                    Send neighbor solicitation    */
80 /*    _nxd_ipv6_search_onlink               Find onlink match             */
81 /*    _nx_ipv6_fragment_processing          Fragment processing           */
82 /*    (ip_link_driver)                      User supplied link driver     */
83 /*                                                                        */
84 /*  CALLED BY                                                             */
85 /*                                                                        */
86 /*    NetX Source Code                                                    */
87 /*                                                                        */
88 /*  RELEASE HISTORY                                                       */
89 /*                                                                        */
90 /*    DATE              NAME                      DESCRIPTION             */
91 /*                                                                        */
92 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
93 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
94 /*                                            resulting in version 6.1    */
95 /*  08-02-2021     Yuxin Zhou               Modified comment(s), and      */
96 /*                                            supported TCP/IP offload,   */
97 /*                                            resulting in version 6.1.8  */
98 /*                                                                        */
99 /**************************************************************************/
_nx_ipv6_packet_send(NX_IP * ip_ptr,NX_PACKET * packet_ptr,ULONG protocol,ULONG payload_size,ULONG hop_limit,ULONG * src_address,ULONG * dest_address)100 VOID _nx_ipv6_packet_send(NX_IP *ip_ptr, NX_PACKET *packet_ptr,
101                           ULONG protocol, ULONG payload_size, ULONG hop_limit,
102                           ULONG *src_address, ULONG *dest_address)
103 {
104 
105 UINT                       status = NX_SUCCESS;
106 ULONG                      address_type;
107 UINT                       next_hop_mtu;
108 ULONG                      fragment = NX_TRUE;
109 #ifdef NX_ENABLE_IPV6_PATH_MTU_DISCOVERY
110 NX_IPV6_DESTINATION_ENTRY *next_hop_dest_entry_ptr;
111 #endif /* NX_ENABLE_IPV6_PATH_MTU_DISCOVERY */
112 NX_IP_DRIVER               driver_request;
113 NX_PACKET                 *remove_packet;
114 NX_PACKET                 *packet_copy;
115 UINT                       same_address;
116 NX_INTERFACE              *if_ptr;
117 NX_IPV6_DESTINATION_ENTRY *dest_entry_ptr;
118 
119     /*lint -e{644} suppress variable might not be initialized, since "packet_ptr" was initialized. */
120     if_ptr = packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address_attached;
121 
122     /* Interface can not be NULL. */
123     NX_ASSERT(if_ptr != NX_NULL);
124 
125 #ifdef NX_ENABLE_TCPIP_OFFLOAD
126     if (if_ptr -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_TCPIP_OFFLOAD)
127     {
128 #ifndef NX_DISABLE_IP_INFO
129 
130         /* Increment the IP invalid packet error.  */
131         ip_ptr -> nx_ip_invalid_transmit_packets++;
132 #endif
133 
134         /* Ignore sending all packets for TCP/IP offload. Release the packet.  */
135         _nx_packet_transmit_release(packet_ptr);
136 
137         /* Return... nothing more can be done!  */
138         return;
139     }
140 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
141 
142     /* Add IPv6 header. */
143     if (_nx_ipv6_header_add(ip_ptr, &packet_ptr, protocol, payload_size,
144                             hop_limit, src_address, dest_address, &fragment) != NX_SUCCESS)
145     {
146 
147         /* Failed to add header. */
148         return;
149     }
150 
151 #ifdef NX_ENABLE_IP_PACKET_FILTER
152     /* Check if the IP packet filter is set. */
153     if (ip_ptr -> nx_ip_packet_filter)
154     {
155 
156         /* Yes, call the IP packet filter routine. */
157         if (ip_ptr -> nx_ip_packet_filter((VOID *)(packet_ptr -> nx_packet_prepend_ptr),
158                                           NX_IP_PACKET_OUT) != NX_SUCCESS)
159         {
160 
161             /* Drop the packet. */
162             _nx_packet_transmit_release(packet_ptr);
163             return;
164         }
165     }
166 
167     /* Check if the IP packet filter extended is set. */
168     if (ip_ptr -> nx_ip_packet_filter_extended)
169     {
170 
171         /* Yes, call the IP packet filter extended routine. */
172         if (ip_ptr -> nx_ip_packet_filter_extended(ip_ptr, packet_ptr, NX_IP_PACKET_OUT) != NX_SUCCESS)
173         {
174 
175             /* Drop the packet. */
176             _nx_packet_transmit_release(packet_ptr);
177             return;
178         }
179     }
180 #endif /* NX_ENABLE_IP_PACKET_FILTER */
181 
182     next_hop_mtu = if_ptr -> nx_interface_ip_mtu_size;
183     packet_ptr -> nx_packet_ip_header = packet_ptr -> nx_packet_prepend_ptr;
184 
185     /* Check if the host is sending itself a packet. */
186     same_address = (UINT)CHECK_IPV6_ADDRESSES_SAME(dest_address, src_address);
187 
188     /* If it is, consider this a loopback address. */
189     if (same_address == 1)
190     {
191 
192         address_type = IPV6_ADDRESS_LOOPBACK;
193     }
194     else
195     {
196 
197         /* Otherwise check if this packet sending to a known loopback address. */
198         address_type = IPv6_Address_Type(dest_address);
199     }
200 
201     /* Handle the internal loopback case. */
202     if (address_type == IPV6_ADDRESS_LOOPBACK)
203     {
204 
205         if (_nx_packet_copy(packet_ptr, &packet_copy, ip_ptr -> nx_ip_default_packet_pool, NX_NO_WAIT) == NX_SUCCESS)
206         {
207 
208 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
209 
210             /* Compute checksum for upper layer protocol. */
211             /*lint -e{644} suppress variable might not be initialized, since "packet_copy" was initialized as long as return value is NX_SUCCESS. */
212             if (packet_copy -> nx_packet_interface_capability_flag)
213             {
214                 _nx_ip_packet_checksum_compute(packet_copy);
215             }
216 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
217 
218             /* Get the interface of copied packet. */
219             /*lint --e{644} suppress variable might not be initialized, since "packet_copy" was initialized as long as return value is NX_SUCCESS. */
220             packet_copy -> nx_packet_address.nx_packet_interface_ptr = packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address_attached;
221 
222 #ifndef NX_DISABLE_IP_INFO
223 
224             /* Increment the IP packet sent count. */
225             ip_ptr -> nx_ip_total_packets_sent++;
226 
227             /* Increment the IP bytes sent count. */
228             ip_ptr -> nx_ip_total_bytes_sent += packet_ptr -> nx_packet_length - (ULONG)sizeof(NX_IPV6_HEADER);
229 #endif
230 
231             /* Send the packet to this IP's receive processing like it came in from the driver. */
232             _nx_ip_packet_deferred_receive(ip_ptr, packet_copy);
233         }
234 #ifndef NX_DISABLE_IP_INFO
235         else
236         {
237             /* Increment the IP send packets dropped count. */
238             ip_ptr -> nx_ip_send_packets_dropped++;
239 
240             /* Increment the IP transmit resource error count. */
241             ip_ptr -> nx_ip_transmit_resource_errors++;
242         }
243 #endif
244         /* Release the transmit packet. */
245         _nx_packet_transmit_release(packet_ptr);
246         return;
247     }
248 
249     /* Initial the driver request. */
250     driver_request.nx_ip_driver_ptr                  = ip_ptr;
251     driver_request.nx_ip_driver_command              = NX_LINK_PACKET_SEND;
252     driver_request.nx_ip_driver_packet               = packet_ptr;
253     driver_request.nx_ip_driver_interface            = NX_NULL;
254 
255     /* Determine if physical mapping is needed by this link driver. */
256     if (if_ptr -> nx_interface_address_mapping_needed)
257     {
258 
259         /* Is this packet a multicast ? */
260         if ((dest_address[0] & (ULONG)0xFF000000) == (ULONG)0xFF000000)
261         {
262 
263 
264             /* Set up the driver request. */
265             driver_request.nx_ip_driver_physical_address_msw = 0x00003333;
266             driver_request.nx_ip_driver_physical_address_lsw = dest_address[3];
267             driver_request.nx_ip_driver_interface            = if_ptr;
268 
269             /* It is; is path MTU enabled? */
270 #ifdef NX_ENABLE_IPV6_PATH_MTU_DISCOVERY
271 
272             /* It is. We will check the path MTU for the packet destination to
273                determine if we need to fragment the packet. */
274 
275             /* Lookup the multicast destination in the destination table.  */
276             status = _nx_icmpv6_dest_table_find(ip_ptr, dest_address, &dest_entry_ptr, 0, 0);
277 
278             /* Did we find it in the table? */
279             /*lint -e{644} suppress variable might not be initialized, since "dest_entry_ptr" was initialized as long as status is NX_SUCCESS. */
280             if (status == NX_SUCCESS)
281             {
282                 next_hop_mtu = dest_entry_ptr -> nx_ipv6_destination_entry_path_mtu;
283             }
284 
285 #endif  /* NX_ENABLE_IPV6_PATH_MTU_DISCOVERY */
286         }
287         else
288         {
289 
290         /* Obtain MAC address */
291         ND_CACHE_ENTRY *NDCacheEntry = NX_NULL;
292         ULONG           next_hop_address[4];
293 
294             SET_UNSPECIFIED_ADDRESS(next_hop_address);
295 
296             /* Lookup the packet destination in the destination table. */
297             status = _nx_icmpv6_dest_table_find(ip_ptr, dest_address, &dest_entry_ptr, 0, 0);
298 
299             /* Was a matching entry found? */
300             if (status != NX_SUCCESS)
301             {
302 
303                 /* No; If the packet is either onlink or there is no default router,
304                    just copy the packet destination address to the 'next hop' address.  */
305 
306                 if (_nxd_ipv6_search_onlink(ip_ptr, dest_address))
307                 {
308                     COPY_IPV6_ADDRESS(dest_address, next_hop_address);
309 
310                     /* Add the next_hop in destination table. */
311                     status = _nx_icmpv6_dest_table_add(ip_ptr, dest_address, &dest_entry_ptr,
312                                                        next_hop_address, if_ptr -> nx_interface_ip_mtu_size,
313                                                        NX_WAIT_FOREVER, packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr);
314 
315                     /* Get the NDCacheEntry. */
316                     if (status == NX_SUCCESS)
317                     {
318                         NDCacheEntry = dest_entry_ptr -> nx_ipv6_destination_entry_nd_entry;
319                     }
320                 }
321                 /* Check whether or not we have a default router. */
322                 /* Suppress cast of pointer to pointer, since it is necessary  */
323                 else if (_nxd_ipv6_router_lookup(ip_ptr, if_ptr, next_hop_address, /*lint -e{929}*/ (void **)&NDCacheEntry) == NX_SUCCESS)
324                 {
325                     /* Add the next_hop in destination table. */
326                     status = _nx_icmpv6_dest_table_add(ip_ptr, dest_address, &dest_entry_ptr,
327                                                        next_hop_address, if_ptr -> nx_interface_ip_mtu_size,
328                                                        NX_WAIT_FOREVER, packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr);
329 
330                     /* If the default router did not has a reachable ND_CACHE_ENTRY. Get the NDCacheEntry. */
331                     /*lint -e{644} suppress variable might not be initialized, since "NDCacheEntry" was initialized in _nxd_ipv6_route_lookup. */
332                     if ((status == NX_SUCCESS) && !NDCacheEntry)
333                     {
334                         NDCacheEntry = dest_entry_ptr -> nx_ipv6_destination_entry_nd_entry;
335                     }
336                 }
337 
338                 /* Destination table add failed. */
339                 if (status)
340                 {
341 
342                     /* Release the transmit packet. */
343                     _nx_packet_transmit_release(packet_ptr);
344 
345                     /* Can't send it. */
346                     return;
347                 }
348             }
349             /* Find a valid destination cache, set the nd cache and next hop address. */
350             else
351             {
352 
353                 /* Get the destination and next hop address. */
354                 NDCacheEntry = dest_entry_ptr -> nx_ipv6_destination_entry_nd_entry;
355                 COPY_IPV6_ADDRESS(dest_entry_ptr -> nx_ipv6_destination_entry_next_hop, next_hop_address);
356                 NX_ASSERT(NDCacheEntry -> nx_nd_cache_nd_status != ND_CACHE_STATE_INVALID);
357             }
358 
359             /* According RFC2461 ch 7.3.3, as long as the entry is valid and not in INCOMPLETE state,
360                the IP layer should use the cached link layer address.  */
361             if ((NDCacheEntry -> nx_nd_cache_nd_status >= ND_CACHE_STATE_REACHABLE) &&
362                 (NDCacheEntry -> nx_nd_cache_nd_status <= ND_CACHE_STATE_PROBE))
363             {
364 
365             UCHAR *mac_addr;
366 
367                 mac_addr = NDCacheEntry -> nx_nd_cache_mac_addr;
368 
369                 /* Assume we find the mac */
370                 driver_request.nx_ip_driver_physical_address_msw = ((ULONG)mac_addr[0] << 8) | mac_addr[1];
371                 driver_request.nx_ip_driver_physical_address_lsw =
372                     ((ULONG)mac_addr[2] << 24) | ((ULONG)mac_addr[3] << 16) | ((ULONG)mac_addr[4] << 8) | mac_addr[5];
373                 driver_request.nx_ip_driver_interface            = if_ptr;
374 
375                 /* Check if path MTU Discovery is enabled first. */
376 
377 #ifdef NX_ENABLE_IPV6_PATH_MTU_DISCOVERY
378 
379                 /* It is.  To know if we need to fragment this packet we need the path MTU for the packet
380                    destination.  */
381 
382                 /* If this destination has a non null next hop, we need to ascertain the next hop MTU.  */
383 
384                 /* Get the path MTU for the actual destination. */
385                 next_hop_mtu = dest_entry_ptr -> nx_ipv6_destination_entry_path_mtu;
386 
387 
388                 /* Find the next hop in the destination table. */
389                 status = _nx_icmpv6_dest_table_find(ip_ptr, next_hop_address, &next_hop_dest_entry_ptr, 0, 0);
390 
391                 if (status == NX_SUCCESS)
392                 {
393 
394                     /* Now compare the destination path MTU with the next hop path MTU*/
395                     /*lint -e{644} suppress variable might not be initialized, since "next_hop_dest_entry_ptr" was initialized as long as status is NX_SUCCESS. */
396                     if ((next_hop_dest_entry_ptr -> nx_ipv6_destination_entry_path_mtu > 0) &&
397                         (next_hop_mtu > next_hop_dest_entry_ptr -> nx_ipv6_destination_entry_path_mtu))
398                     {
399 
400                         /* Update the path mtu to reflect the next hop route. */
401                         next_hop_mtu = next_hop_dest_entry_ptr -> nx_ipv6_destination_entry_path_mtu;
402                     }
403                 }
404 
405 #endif  /* NX_ENABLE_IPV6_PATH_MTU_DISCOVERY */
406 
407                 /* If the entry is in STALE state, move it to DELAY state. */
408                 if (NDCacheEntry -> nx_nd_cache_nd_status == ND_CACHE_STATE_STALE)
409                 {
410                     NDCacheEntry -> nx_nd_cache_nd_status = ND_CACHE_STATE_DELAY;
411 
412                     /* Start the Delay first probe timer */
413                     NDCacheEntry -> nx_nd_cache_timer_tick = NX_DELAY_FIRST_PROBE_TIME;
414                 }
415             }
416             else
417             {
418 
419                 /* No MAC address was found in our cache table.  Start the Neighbor Discovery (ND)
420                    process to get it. */
421 
422                 /* Ensure the current packet's queue next pointer to NULL */
423                 packet_ptr -> nx_packet_queue_next = NX_NULL;
424 
425                 /* Determine if the queue is empty. */
426                 if (NDCacheEntry -> nx_nd_cache_packet_waiting_head == NX_NULL)
427                 {
428                     /* ICMPv6 is enabled */
429                     if (ip_ptr -> nx_ip_icmpv6_packet_process)
430                     {
431 
432                         /* Queue up this packet */
433                         NDCacheEntry -> nx_nd_cache_packet_waiting_head = packet_ptr;
434                         NDCacheEntry -> nx_nd_cache_packet_waiting_tail = packet_ptr;
435                         NDCacheEntry -> nx_nd_cache_packet_waiting_queue_length = 1;
436 
437                         /* Add debug information. */
438                         NX_PACKET_DEBUG(NX_PACKET_ND_WAITING_QUEUE, __LINE__, packet_ptr);
439 
440                         /* Set the outgoing address and interface to the cache entry.  */
441                         NDCacheEntry -> nx_nd_cache_outgoing_address = packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr;
442                         NDCacheEntry -> nx_nd_cache_interface_ptr = if_ptr;
443 
444                         /* Is this a new entry? */
445                         if (NDCacheEntry -> nx_nd_cache_nd_status == ND_CACHE_STATE_CREATED)
446                         {
447 
448                             /* Start Neighbor discovery process by advancing to the incomplete state. */
449                             NDCacheEntry -> nx_nd_cache_nd_status = ND_CACHE_STATE_INCOMPLETE;
450                         }
451 
452                         /* Note that the 2nd last parameter sendUnicast is set to Zero. In this case
453                            the last arg NDCacheEntry is not being used in _nx_icmpv6_send_ns. */
454                         _nx_icmpv6_send_ns(ip_ptr, next_hop_address,
455                                            1, packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr, 0, NDCacheEntry);
456 
457                         NDCacheEntry -> nx_nd_cache_num_solicit = NX_MAX_MULTICAST_SOLICIT - 1;
458                         NDCacheEntry -> nx_nd_cache_timer_tick = ip_ptr -> nx_ipv6_retrans_timer_ticks;
459                     }
460                     else
461                     {
462 
463                         _nx_packet_transmit_release(packet_ptr);
464 #ifndef NX_DISABLE_IP_INFO
465 
466                         /* Increment the IP transmit resource error count.  */
467                         ip_ptr -> nx_ip_transmit_resource_errors++;
468 
469                         /* Increment the IP send packets dropped count.  */
470                         ip_ptr -> nx_ip_send_packets_dropped++;
471 #endif
472                     }
473                     return;
474                 }
475 
476                 /* The ND process already started.  Simply queue up this packet */
477                 NDCacheEntry -> nx_nd_cache_packet_waiting_tail -> nx_packet_queue_next = packet_ptr;
478                 NDCacheEntry -> nx_nd_cache_packet_waiting_tail = packet_ptr;
479                 NDCacheEntry -> nx_nd_cache_packet_waiting_queue_length++;
480 
481                 /* Add debug information. */
482                 NX_PACKET_DEBUG(NX_PACKET_ND_WAITING_QUEUE, __LINE__, packet_ptr);
483 
484                 /* Check if the number of packets enqueued exceeds the allowed number. */
485                 if (NDCacheEntry -> nx_nd_cache_packet_waiting_queue_length > NX_ND_MAX_QUEUE_DEPTH)
486                 {
487 
488                     /* Yes, so delete the first packet. */
489                     remove_packet = NDCacheEntry -> nx_nd_cache_packet_waiting_head;
490 
491                     NDCacheEntry -> nx_nd_cache_packet_waiting_head = remove_packet -> nx_packet_queue_next;
492 
493                     /* Update the queued packet count for this cache entry. */
494                     NDCacheEntry -> nx_nd_cache_packet_waiting_queue_length--;
495 
496                     _nx_packet_transmit_release(remove_packet);
497 #ifndef NX_DISABLE_IP_INFO
498                     /* Increment the IP transmit resource error count.  */
499                     ip_ptr -> nx_ip_transmit_resource_errors++;
500 
501                     /* Increment the IP send packets dropped count.  */
502                     ip_ptr -> nx_ip_send_packets_dropped++;
503 #endif
504                 }
505 
506                 return;
507             }
508         }
509     }
510     else
511     {
512 
513         /* This IP instance does not require any IP-to-physical mapping.  */
514         /* Build the driver request.  */
515 
516         driver_request.nx_ip_driver_physical_address_msw = 0;
517         driver_request.nx_ip_driver_physical_address_lsw = 0;
518         driver_request.nx_ip_driver_interface            = if_ptr;
519     }
520 
521     /* Does the packet payload exceed next hop MTU?  */
522     if (packet_ptr -> nx_packet_length > next_hop_mtu)
523     {
524 #ifndef NX_DISABLE_FRAGMENTATION
525 #ifdef NX_IPSEC_ENABLE
526         /* Check the fragment status, transport mode SAs can not carry fragment, RFC 4301 page 66&88.  */
527         /*lint -e{774} suppress boolean always evaluates to True, since the value fragment can changed when NX_IPSEC_ENABLE is defined. */
528         if (fragment == NX_TRUE)
529         {
530 #endif  /* NX_IPSEC_ENABLE */
531 
532             /* Yes; ok to fragment the packet payload. */
533             _nx_ipv6_fragment_process(&driver_request, next_hop_mtu);
534 #ifdef NX_IPSEC_ENABLE
535         }
536         else
537         {
538 
539 #ifndef NX_DISABLE_IP_INFO
540 
541             /* Increment the IP send packets dropped count.  */
542             ip_ptr -> nx_ip_send_packets_dropped++;
543 #endif
544             /* Just release the packet.  */
545             _nx_packet_transmit_release(packet_ptr);
546         }
547 #endif  /* NX_IPSEC_ENABLE */
548 
549 #else   /* NX_DISABLE_FRAGMENTATION */
550 
551 #ifndef NX_DISABLE_IP_INFO
552         /* Increment the IP send packets dropped count.  */
553         ip_ptr -> nx_ip_send_packets_dropped++;
554 #endif
555         /* Just release the packet.  */
556         _nx_packet_transmit_release(packet_ptr);
557 #endif  /* NX_DISABLE_FRAGMENTATION */
558 
559         /* This packet send is complete, just return.  */
560         return;
561     }
562 
563     /* The packet requires no fragmentation. Proceed with sending the packet. */
564 
565 #ifndef NX_DISABLE_IP_INFO
566 
567     /* Increment the IP packet sent count.  */
568     ip_ptr -> nx_ip_total_packets_sent++;
569 
570     /* Increment the IP bytes sent count.  */
571     ip_ptr -> nx_ip_total_bytes_sent +=  packet_ptr -> nx_packet_length - (ULONG)sizeof(NX_IPV6_HEADER);
572 #endif
573 
574     /* Add debug information. */
575     NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
576 
577     /* Driver entry must not be NULL. */
578     NX_ASSERT(if_ptr -> nx_interface_link_driver_entry != NX_NULL);
579 
580     /* Send the IP packet out on the network via the attached driver.  */
581     (if_ptr -> nx_interface_link_driver_entry)(&driver_request);
582 }
583 
584 #endif /* FEATURE_NX_IPV6 */
585 
586