/**************************************************************************/
/*                                                                        */
/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
/*                                                                        */
/*       This software is licensed under the Microsoft Software License   */
/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
/*       and in the root directory of this software.                      */
/*                                                                        */
/**************************************************************************/


/**************************************************************************/
/**************************************************************************/
/**                                                                       */
/** 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.3.0        */
/*  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                                                             */
/*                                                                        */
/*                                                                        */
/*  RELEASE HISTORY                                                       */
/*                                                                        */
/*    DATE              NAME                      DESCRIPTION             */
/*                                                                        */
/*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
/*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
/*                                            resulting in version 6.1    */
/*  10-31-2023     Bo Chen                  Modified comment(s), improved */
/*                                            packet length verification, */
/*                                            resulting in version 6.3.0  */
/*                                                                        */
/**************************************************************************/
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);
    }

    /* 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 */

