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