/*************************************************************************** * Copyright (c) 2024 Microsoft Corporation * Copyright (c) 2025-present Eclipse ThreadX Contributors * * This program and the accompanying materials are made available under the * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT **************************************************************************/ /**************************************************************************/ /**************************************************************************/ /** */ /** NetX Component */ /** */ /** Internet Control Message Protocol (ICMP) */ /** */ /**************************************************************************/ /**************************************************************************/ #define NX_SOURCE_CODE /* Include necessary system files. */ #include "nx_api.h" #include "nx_ipv6.h" #include "nx_icmpv6.h" #include "nx_packet.h" #ifdef NX_IPSEC_ENABLE #include "nx_ipsec.h" #endif /* NX_IPSEC_ENABLE */ #ifdef FEATURE_NX_IPV6 #ifdef NX_ENABLE_IPV6_PATH_MTU_DISCOVERY /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _nx_icmpv6_process_packet_too_big PORTABLE C */ /* 6.4.3 */ /* AUTHOR */ /* */ /* Yuxin Zhou, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This function handles an ICMPv6 error message indicating the next */ /* hop or beyond has a smaller MTU that the packet payload. It sets */ /* the IP task MTU to the next hop MTU in the option header. */ /* */ /* This function is called from IP thread periodic process routine. */ /* It starts the minimum Path MTU Discovery process if the interface */ /* address after the IP instance is initialized and its MTU established*/ /* by the Ethernet driver. */ /* */ /* INPUT */ /* */ /* ip_ptr Pointer to IP instance */ /* packet_ptr Pointer to Packet Too Big */ /* ICMPv6 packet received */ /* */ /* OUTPUT */ /* */ /* NX_SUCCESS Successful completion */ /* */ /* CALLS */ /* */ /* _nx_icmpv6_dest_table_find Find destination in table and */ /* update path MTU in table */ /* memset Set the memory */ /* */ /* CALLED BY */ /* */ /* */ /**************************************************************************/ UINT _nx_icmpv6_process_packet_too_big(NX_IP *ip_ptr, NX_PACKET *packet_ptr) { UINT status = NX_INVALID_MTU_DATA; NX_ICMPV6_OPTION_MTU *icmpv6_mtu_option_ptr; ULONG mtu; NX_IPV6_DESTINATION_ENTRY *dest_entry_ptr; ULONG *original_destination_ip; ULONG default_next_hop_address[4]; NX_IPV6_HEADER *ip_header_ptr, *original_ip_header_ptr; NX_INTERFACE *if_ptr; /* Add debug information. */ NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr); #ifndef NX_DISABLE_RX_SIZE_CHECKING if ((packet_ptr -> nx_packet_length < (sizeof(NX_ICMPV6_OPTION_MTU) + sizeof(NX_IPV6_HEADER))) #ifndef NX_DISABLE_PACKET_CHAIN || (packet_ptr -> nx_packet_next) /* Ignore chained packet. */ #endif /* NX_DISABLE_PACKET_CHAIN */ ) { #ifndef NX_DISABLE_ICMP_INFO /* Increment the ICMP invalid message count. */ ip_ptr -> nx_ip_icmp_invalid_packets++; #endif /* Invalid ICMP message, just release it. */ _nx_packet_release(packet_ptr); return(NX_NOT_SUCCESSFUL); } #endif /* NX_DISABLE_RX_SIZE_CHECKING */ memset(&default_next_hop_address[0], 0, (4 * sizeof(ULONG))); if_ptr = packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address_attached; /* Set a local pointer to the ICMPv6 Option data. */ /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */ icmpv6_mtu_option_ptr = (NX_ICMPV6_OPTION_MTU *)packet_ptr -> nx_packet_prepend_ptr; /* Parse the original sender data. */ /*lint -e{929} -e{826} -e{740} suppress cast of pointer to pointer, since it is necessary */ original_ip_header_ptr = (NX_IPV6_HEADER *)(packet_ptr -> nx_packet_prepend_ptr + sizeof(NX_ICMPV6_OPTION_MTU)); /* Extract the original sender from the IP header. */ packet_ptr -> nx_packet_prepend_ptr -= sizeof(NX_IPV6_HEADER); /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */ ip_header_ptr = (NX_IPV6_HEADER *)packet_ptr -> nx_packet_ip_header; /* Yes this looks like a valid IP header for the original offending packet. */ original_destination_ip = original_ip_header_ptr -> nx_ip_header_destination_ip; NX_IPV6_ADDRESS_CHANGE_ENDIAN(original_destination_ip); COPY_IPV6_ADDRESS(ip_header_ptr -> nx_ip_header_source_ip, &default_next_hop_address[0]); /* Handle endianness. */ NX_CHANGE_ULONG_ENDIAN(icmpv6_mtu_option_ptr -> nx_icmpv6_option_mtu_path_mtu); /* Create a local variable for convenience. */ mtu = icmpv6_mtu_option_ptr -> nx_icmpv6_option_mtu_path_mtu; /* MTU data is valid if it is non zero, and is no more than the driver MTU. */ if ((mtu > 0) && (mtu <= if_ptr -> nx_interface_ip_mtu_size)) { /* Add destination table. */ status = _nx_icmpv6_dest_table_add(ip_ptr, original_destination_ip, &dest_entry_ptr, &default_next_hop_address[0], mtu, 0, packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr); /* If a new ND cache is created, force the entry to be "INCOMPLETE" so that the timer logic will re-try, and then timeout if no responses are received. */ if(dest_entry_ptr -> nx_ipv6_destination_entry_nd_entry != NX_NULL) { ND_CACHE_ENTRY *nd_entry = dest_entry_ptr -> nx_ipv6_destination_entry_nd_entry; if(nd_entry -> nx_nd_cache_nd_status == ND_CACHE_STATE_CREATED) { _nx_icmpv6_send_ns(ip_ptr, &nd_entry -> nx_nd_cache_dest_ip[0], 1, nd_entry -> nx_nd_cache_outgoing_address, 0, nd_entry); nd_entry->nx_nd_cache_num_solicit = NX_MAX_MULTICAST_SOLICIT - 1; nd_entry->nx_nd_cache_timer_tick = ip_ptr -> nx_ipv6_retrans_timer_ticks; } } } /* Release the packet. */ _nx_packet_release(packet_ptr); /* Return the completion code. */ return(status); } #endif /* NX_ENABLE_IPV6_PATH_MTU_DISCOVERY */ #endif /* FEATURE_NX_IPV6 */