/**
 * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#include "nordic_common.h"
#include "sdk_common.h"
#include "sdk_config.h"
#include "iot_common.h"
#include "iot_pbuffer.h"
#include "udp_api.h"
#include "udp.h"
#include "ipv6_utils.h"

#if UDP6_CONFIG_LOG_ENABLED

#define NRF_LOG_MODULE_NAME udp6

#define NRF_LOG_LEVEL       UDP6_CONFIG_LOG_LEVEL
#define NRF_LOG_INFO_COLOR  UDP6_CONFIG_INFO_COLOR
#define NRF_LOG_DEBUG_COLOR UDP6_CONFIG_DEBUG_COLOR

#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();

#define UDP6_TRC     NRF_LOG_DEBUG                                                                  /**< Used for getting trace of execution in the module. */
#define UDP6_ERR     NRF_LOG_ERROR                                                                  /**< Used for logging errors in the module. */
#define UDP6_DUMP    NRF_LOG_HEXDUMP_DEBUG                                                          /**< Used for dumping octet information to get details of bond information etc. */

#define UDP6_ENTRY() UDP6_TRC(">> %s", __func__)
#define UDP6_EXIT()  UDP6_TRC("<< %s", __func__)

#else // UDP6_CONFIG_LOG_ENABLED

#define UDP6_TRC(...)                                                                               /**< Disables traces. */
#define UDP6_DUMP(...)                                                                              /**< Disables dumping of octet streams. */
#define UDP6_ERR(...)                                                                               /**< Disables error logs. */

#define UDP6_ENTRY(...)
#define UDP6_EXIT(...)

#endif // UDP6_CONFIG_LOG_ENABLED

/**
 * @defgroup api_param_check API Parameters check macros.
 *
 * @details Macros that verify parameters passed to the module in the APIs. These macros
 *          could be mapped to nothing in final versions of code to save execution and size.
 *          UDP_DISABLE_API_PARAM_CHECK should be set to 1 to disable these checks.
 *
 * @{
 */
#if (UDP6_DISABLE_API_PARAM_CHECK == 0)

/**@brief Macro to check is module is initialized before requesting one of the module procedures. */
#define VERIFY_MODULE_IS_INITIALIZED()                                                             \
        if (m_initialization_state == false)                                                       \
        {                                                                                          \
            return (SDK_ERR_MODULE_NOT_INITIALIZED | IOT_UDP6_ERR_BASE);                           \
        }

/**@brief Macro to check is module is initialized before requesting one of the module
         procedures but does not use any return code. */
#define VERIFY_MODULE_IS_INITIALIZED_VOID()                                                        \
        if (m_initialization_state == false)                                                       \
        {                                                                                          \
            return;                                                                                \
        }

/**
 * @brief Verify NULL parameters are not passed to API by application.
 */
#define NULL_PARAM_CHECK(PARAM)                                                                    \
        if ((PARAM) == NULL)                                                                       \
        {                                                                                          \
            return (NRF_ERROR_NULL | IOT_UDP6_ERR_BASE);                                           \
        }

/**
 * @brief Verify socket id passed on the API by application is valid.
 */
#define VERIFY_SOCKET_ID(ID)                                                                       \
        if (((ID) >=  UDP6_MAX_SOCKET_COUNT))                                                      \
        {                                                                                          \
            return (NRF_ERROR_INVALID_ADDR | IOT_UDP6_ERR_BASE);                                   \
        }

/**
 * @brief Verify socket id passed on the API by application is valid.
 */
#define VERIFY_PORT_NUMBER(PORT)                                                                   \
        if ((PORT) ==  0)                                                                          \
        {                                                                                          \
            return (NRF_ERROR_INVALID_PARAM | IOT_UDP6_ERR_BASE);                                  \
        }

/**
 * @brief Verify socket id passed on the API by application is valid.
 */
#define VERIFY_NON_ZERO_LENGTH(LEN)                                                                \
        if ((LEN) ==  0)                                                                           \
        {                                                                                          \
            return (NRF_ERROR_INVALID_LENGTH | IOT_UDP6_ERR_BASE);                                 \
        }

#else // UDP6_DISABLE_API_PARAM_CHECK

#define VERIFY_MODULE_IS_INITIALIZED()
#define VERIFY_MODULE_IS_INITIALIZED_VOID()
#define NULL_PARAM_CHECK(PARAM)
#define VERIFY_SOCKET_ID(ID)
#endif //UDP6_DISABLE_API_PARAM_CHECK

/**
 * @defgroup ble_ipsp_mutex_lock_unlock Module's Mutex Lock/Unlock Macros.
 *
 * @details Macros used to lock and unlock modules. Currently, SDK does not use mutexes but
 *          framework is provided in case need arises to use an alternative architecture.
 * @{
 */
#define UDP_MUTEX_LOCK()   SDK_MUTEX_LOCK(m_udp_mutex)                                   /**< Lock module using mutex */
#define UDP_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_udp_mutex)                                 /**< Unlock module using mutex */
/** @} */

#define UDP_PORT_FREE  0                                                                 /**< Reserved port of the socket, indicates that port is free. */

/**@brief UDP Socket Data needed by the module to manage it. */
typedef struct
{
    uint16_t           local_port;                                                       /**< Local Port of the socket. */
    uint16_t           remote_port;                                                      /**< Remote port of the socket. */
    ipv6_addr_t        local_addr;                                                       /**< Local IPv6 Address of the socket. */
    ipv6_addr_t        remote_addr;                                                      /**< Remote IPv6 Address of the socket. */
    udp6_handler_t     rx_cb;                                                            /**< Callback registered by application to receive data on the socket. */
    void             * p_app_data;                                                       /**< Application data mapped to the socket using the udp6_app_data_set. */
} udp_socket_entry_t;


SDK_MUTEX_DEFINE(m_udp_mutex)                                                            /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */
static bool      m_initialization_state  = false;                                        /**< Variable to maintain module initialization state. */
static udp_socket_entry_t  m_socket[UDP6_MAX_SOCKET_COUNT];                              /**< Table of sockets managed by the module. */


/** @brief Initializes socket managed by the module. */
static void udp_socket_init(udp_socket_entry_t * p_socket)
{
    p_socket->local_port  = UDP_PORT_FREE;
    p_socket->remote_port = UDP_PORT_FREE;
    p_socket->rx_cb       = NULL;
    p_socket->p_app_data  = NULL;
    IPV6_ADDRESS_INITIALIZE(&p_socket->local_addr);
    IPV6_ADDRESS_INITIALIZE(&p_socket->remote_addr);
}

/**
 * @brief Find UDP socket based on local port. If found its index to m_socket table is returned.
 *        else  UDP6_MAX_SOCKET_COUNT is returned.
 */
static uint32_t socket_find(uint16_t port)
{
    uint32_t index;

    for (index = 0; index < UDP6_MAX_SOCKET_COUNT; index++)
    {
        if (m_socket[index].local_port == port)
        {
            break;
        }
    }

    return index;
}


uint32_t udp_init(void)
{
    uint32_t index;

    UDP6_ENTRY();

    SDK_MUTEX_INIT(m_udp_mutex);

    UDP_MUTEX_LOCK();

    for (index = 0; index < UDP6_MAX_SOCKET_COUNT; index++)
    {
        udp_socket_init(&m_socket[index]);
    }

    m_initialization_state = true;

    UDP6_EXIT();

    UDP_MUTEX_UNLOCK();

    return NRF_SUCCESS;
}


uint32_t udp6_socket_allocate(udp6_socket_t  * p_socket)
{
    VERIFY_MODULE_IS_INITIALIZED();
    NULL_PARAM_CHECK(p_socket);

    UDP6_ENTRY();

    UDP_MUTEX_LOCK();

    //Search for an unassigned socket.
    const uint32_t socket_id = socket_find(UDP_PORT_FREE);
    uint32_t       err_code  = NRF_SUCCESS;

    if (socket_id != UDP6_MAX_SOCKET_COUNT)
    {
        UDP6_TRC("Assigned socket 0x%08lX", socket_id);

        // Found a free socket. Assign.
        p_socket->socket_id = socket_id;
    }
    else
    {
        // No free socket found.
        UDP6_ERR("No room for new socket.");
        err_code = (NRF_ERROR_NO_MEM | IOT_UDP6_ERR_BASE);
    }

    UDP_MUTEX_UNLOCK();

    UDP6_EXIT();

    return err_code;
}


uint32_t udp6_socket_free(const udp6_socket_t * p_socket)
{
    VERIFY_MODULE_IS_INITIALIZED();
    NULL_PARAM_CHECK(p_socket);
    VERIFY_SOCKET_ID(p_socket->socket_id);

    UDP6_ENTRY();

    UDP_MUTEX_LOCK();

    udp_socket_init(&m_socket[p_socket->socket_id]);

    UDP_MUTEX_UNLOCK();

    UDP6_EXIT();

    return NRF_SUCCESS;
}


uint32_t udp6_socket_recv(const udp6_socket_t  * p_socket,
                          const udp6_handler_t   callback)
{
    VERIFY_MODULE_IS_INITIALIZED();
    NULL_PARAM_CHECK(p_socket);
    NULL_PARAM_CHECK(callback);
    VERIFY_SOCKET_ID(p_socket->socket_id);
    VERIFY_PORT_NUMBER(m_socket[p_socket->socket_id].local_port);

    UDP6_ENTRY();

    UDP_MUTEX_LOCK();

    m_socket[p_socket->socket_id].rx_cb = callback;

    UDP_MUTEX_UNLOCK();

    UDP6_EXIT();

    return NRF_SUCCESS;
}


uint32_t udp6_socket_bind(const udp6_socket_t * p_socket,
                          const ipv6_addr_t   * p_src_addr,
                          uint16_t              src_port)
{
    VERIFY_MODULE_IS_INITIALIZED();
    NULL_PARAM_CHECK(p_socket);
    NULL_PARAM_CHECK(p_src_addr);
    VERIFY_SOCKET_ID(p_socket->socket_id);
    VERIFY_PORT_NUMBER(src_port);

    UDP6_ENTRY();

    UDP_MUTEX_LOCK();

    uint32_t err_code = NRF_SUCCESS;

    // Change Host Byte Order to Network Byte Order.
    src_port = HTONS(src_port);

    //Check if port is already registered.
    for (uint32_t index = 0; index < UDP6_MAX_SOCKET_COUNT; index ++)
    {
        if (m_socket[index].local_port == src_port)
        {
            err_code = UDP_PORT_IN_USE;
        }
    }

    if (err_code == NRF_SUCCESS)
    {
        m_socket[p_socket->socket_id].local_port = src_port;
        m_socket[p_socket->socket_id].local_addr = (*p_src_addr);

    }
    UDP_MUTEX_UNLOCK();

    UDP6_EXIT();

    return err_code;
}


uint32_t udp6_socket_connect(const udp6_socket_t * p_socket,
                             const ipv6_addr_t   * p_dest_addr,
                             uint16_t              dest_port)
{
    VERIFY_MODULE_IS_INITIALIZED();
    NULL_PARAM_CHECK(p_socket);
    NULL_PARAM_CHECK(p_dest_addr);
    VERIFY_SOCKET_ID(p_socket->socket_id);
    VERIFY_PORT_NUMBER(dest_port);
    VERIFY_PORT_NUMBER(m_socket[p_socket->socket_id].local_port);

    UDP6_ENTRY();

    UDP_MUTEX_LOCK();

    m_socket[p_socket->socket_id].remote_port = HTONS(dest_port);
    m_socket[p_socket->socket_id].remote_addr = (*p_dest_addr);

    UDP_MUTEX_UNLOCK();

    UDP6_EXIT();

    return NRF_SUCCESS;
}


uint32_t udp6_socket_send(const udp6_socket_t * p_socket,
                          iot_pbuffer_t       * p_packet)
{
    VERIFY_MODULE_IS_INITIALIZED();
    NULL_PARAM_CHECK(p_socket);
    NULL_PARAM_CHECK(p_packet);
    NULL_PARAM_CHECK(p_packet->p_payload);
    VERIFY_NON_ZERO_LENGTH(p_packet->length);
    VERIFY_SOCKET_ID(p_socket->socket_id);
    VERIFY_PORT_NUMBER(m_socket[p_socket->socket_id].local_port);
    VERIFY_PORT_NUMBER(m_socket[p_socket->socket_id].remote_port);

    UDP6_ENTRY();

    UDP_MUTEX_LOCK();

    uint32_t err_code;
    const udp_socket_entry_t  * p_skt       = &m_socket[p_socket->socket_id];
    const uint32_t    header_size = UDP_HEADER_SIZE + IPV6_IP_HEADER_SIZE;
    udp6_header_t   * p_header    = (udp6_header_t *)(p_packet->p_payload - UDP_HEADER_SIZE);
    ipv6_header_t   * p_ip_header = (ipv6_header_t *)(p_packet->p_payload - header_size);
    iot_interface_t * p_interface = NULL;
    uint16_t          checksum;

    p_header->srcport  = p_skt->local_port;
    p_header->destport = p_skt->remote_port;
    p_header->checksum = 0;

    p_header->length = HTONS(p_packet->length  + UDP_HEADER_SIZE);

    // Pack destination address.
    p_ip_header->destaddr = p_skt->remote_addr;

    // Pack source address.
    if ((0 == IPV6_ADDRESS_CMP(&p_skt->local_addr, IPV6_ADDR_ANY)))
    {
        err_code = ipv6_address_find_best_match(&p_interface,
                                                &p_ip_header->srcaddr,
                                                &p_ip_header->destaddr);
    }
    else
    {
        err_code = ipv6_address_find_best_match(&p_interface,
                                                NULL,
                                                &p_ip_header->destaddr);

        p_ip_header->srcaddr = p_skt->local_addr;
    }

    if (err_code == NRF_SUCCESS)
    {
        // Pack next header.
        p_ip_header->next_header             = IPV6_NEXT_HEADER_UDP;

        //Pack HOP Limit.
        p_ip_header->hoplimit                = IPV6_DEFAULT_HOP_LIMIT;

        //Traffic class and flow label.
        p_ip_header->version_traffic_class   = 0x60;
        p_ip_header->traffic_class_flowlabel = 0x00;
        p_ip_header->flowlabel               = 0x0000;

        // Length.
        p_ip_header->length                  = HTONS(p_packet->length + UDP_HEADER_SIZE);

        checksum = p_packet->length + UDP_HEADER_SIZE + IPV6_NEXT_HEADER_UDP;

        ipv6_checksum_calculate(p_ip_header->srcaddr.u8, IPV6_ADDR_SIZE, &checksum, false);
        ipv6_checksum_calculate(p_ip_header->destaddr.u8, IPV6_ADDR_SIZE, &checksum, false);
        ipv6_checksum_calculate(p_packet->p_payload - UDP_HEADER_SIZE,
                                p_packet->length + UDP_HEADER_SIZE,
                                &checksum,
                                true);

        p_header->checksum = HTONS((~checksum));

        p_packet->p_payload -= header_size;
        p_packet->length    += header_size;

        err_code = ipv6_send(p_interface, p_packet);
    }
    else
    {
        err_code = UDP_INTERFACE_NOT_READY;
    }

    UDP_MUTEX_UNLOCK();

    UDP6_EXIT();

    return err_code;
}


uint32_t udp6_socket_sendto(const udp6_socket_t    * p_socket,
                            const ipv6_addr_t      * p_dest_addr,
                            uint16_t                 dest_port,
                            iot_pbuffer_t          * p_packet)
{
    VERIFY_MODULE_IS_INITIALIZED();
    NULL_PARAM_CHECK(p_socket);
    NULL_PARAM_CHECK(p_dest_addr);
    NULL_PARAM_CHECK(p_packet);
    NULL_PARAM_CHECK(p_packet->p_payload);
    VERIFY_NON_ZERO_LENGTH(p_packet->length);
    VERIFY_SOCKET_ID(p_socket->socket_id);
    VERIFY_PORT_NUMBER(dest_port);

    UDP6_ENTRY();

    UDP_MUTEX_LOCK();

    uint32_t err_code;
    const udp_socket_entry_t  * p_skt       = &m_socket[p_socket->socket_id];
    const uint32_t    header_size = UDP_HEADER_SIZE + IPV6_IP_HEADER_SIZE;
    udp6_header_t   * p_header    = (udp6_header_t *)(p_packet->p_payload - UDP_HEADER_SIZE);
    ipv6_header_t   * p_ip_header = (ipv6_header_t *)(p_packet->p_payload - header_size);
    iot_interface_t * p_interface = NULL;
    uint16_t          checksum;

    p_header->srcport  = p_skt->local_port;
    p_header->destport = HTONS(dest_port);
    p_header->checksum = 0;

    checksum = p_packet->length + UDP_HEADER_SIZE + IPV6_NEXT_HEADER_UDP;

    p_header->length = HTONS(p_packet->length + UDP_HEADER_SIZE);

    //Pack destination address.
    p_ip_header->destaddr = *p_dest_addr;

    // Pack source address.
    if ((0 == IPV6_ADDRESS_CMP(&p_skt->local_addr, IPV6_ADDR_ANY)))
    {
        err_code = ipv6_address_find_best_match(&p_interface,
                                                &p_ip_header->srcaddr,
                                                &p_ip_header->destaddr);
    }
    else
    {
        err_code = ipv6_address_find_best_match(&p_interface,
                                                NULL,
                                                &p_ip_header->destaddr);

        p_ip_header->srcaddr = p_skt->local_addr;
    }

    if (err_code == NRF_SUCCESS)
    {
        //Pack next header.
        p_ip_header->next_header             = IPV6_NEXT_HEADER_UDP;

        //Pack HOP Limit.
        p_ip_header->hoplimit                = IPV6_DEFAULT_HOP_LIMIT;

        //Traffic class and flow label.
        p_ip_header->version_traffic_class   = 0x60;
        p_ip_header->traffic_class_flowlabel = 0x00;
        p_ip_header->flowlabel               = 0x0000;

        // Length.
        p_ip_header->length                  = HTONS(p_packet->length + UDP_HEADER_SIZE);

        ipv6_checksum_calculate(p_ip_header->srcaddr.u8,  IPV6_ADDR_SIZE, &checksum, false);
        ipv6_checksum_calculate(p_ip_header->destaddr.u8, IPV6_ADDR_SIZE, &checksum, false);
        ipv6_checksum_calculate(p_packet->p_payload - UDP_HEADER_SIZE,
                                p_packet->length + UDP_HEADER_SIZE,
                                &checksum,
                                true);

        p_header->checksum = HTONS((~checksum));

        p_packet->p_payload -= header_size;
        p_packet->length    += header_size;

        err_code = ipv6_send(p_interface, p_packet);
    }
    else
    {
        err_code = UDP_INTERFACE_NOT_READY;
    }

    UDP_MUTEX_UNLOCK();

    UDP6_EXIT();

    return err_code;
}


uint32_t udp6_socket_app_data_set(const udp6_socket_t * p_socket)
{
    VERIFY_MODULE_IS_INITIALIZED();
    NULL_PARAM_CHECK(p_socket);
    VERIFY_SOCKET_ID(p_socket->socket_id);

    //Note: no null check is performed on the p_app_data as it is permissible
    //to pass on a NULL value if need be.

    UDP6_ENTRY();

    UDP_MUTEX_LOCK();

    m_socket[p_socket->socket_id].p_app_data = p_socket->p_app_data;

    UDP_MUTEX_UNLOCK();

    UDP6_EXIT();

    return NRF_SUCCESS;
}


uint32_t udp_input(const iot_interface_t  * p_interface,
                   const ipv6_header_t    * p_ip_header,
                   iot_pbuffer_t          * p_packet)
{
    NULL_PARAM_CHECK(p_interface);
    NULL_PARAM_CHECK(p_ip_header);
    NULL_PARAM_CHECK(p_packet);

    UNUSED_VARIABLE(p_interface);

    uint32_t err_code = (NRF_ERROR_NOT_FOUND | IOT_UDP6_ERR_BASE);

    if ((p_packet->length > UDP_HEADER_SIZE) && (p_ip_header->length > UDP_HEADER_SIZE))
    {
        UDP_MUTEX_LOCK();

        UDP6_ENTRY();

        uint32_t index;
        udp6_header_t * p_udp_header = (udp6_header_t *)(p_packet->p_payload);

        // Check to which UDP socket, port and address was bind.
        for (index = 0; index < UDP6_MAX_SOCKET_COUNT; index ++)
        {
            if (m_socket[index].local_port == p_udp_header->destport)
            {
                if ((0 == IPV6_ADDRESS_CMP(&m_socket[index].local_addr, IPV6_ADDR_ANY)) ||
                   (0 == IPV6_ADDRESS_CMP(&m_socket[index].local_addr, &p_ip_header->destaddr)))
                {
                    // Check if connection was established.
                    if (m_socket[index].remote_port == 0 || m_socket[index].remote_port == p_udp_header->srcport)
                    {
                        if ((0 == IPV6_ADDRESS_CMP(&m_socket[index].remote_addr, IPV6_ADDR_ANY)) ||
                           (0 == IPV6_ADDRESS_CMP(&m_socket[index].remote_addr, &p_ip_header->srcaddr)))
                           {
                                err_code = NRF_SUCCESS;
                                break;
                           }
                    }
                }
            }
        }

        if (index < UDP6_MAX_SOCKET_COUNT)
        {
            uint16_t checksum = p_packet->length +  IPV6_NEXT_HEADER_UDP;
            uint32_t process_result = NRF_SUCCESS;
            uint16_t udp_hdr_length = NTOHS(p_udp_header->length);

            if (udp_hdr_length > p_packet->length)
            {
                UDP6_ERR("Received truncated packet, "
                         "payload length 0x%08lX, length in header 0x%08X.",
                         p_packet->length, NTOHS(p_udp_header->length));
                process_result = UDP_TRUNCATED_PACKET;
            }
            else if (udp_hdr_length < p_packet->length)
            {
                UDP6_ERR("Received malformed packet, "
                         "payload length 0x%08lX, length in header 0x%08X.",
                         p_packet->length, NTOHS(p_udp_header->length));

                process_result = UDP_MALFORMED_PACKET;
            }
            else
            {
                ipv6_checksum_calculate(p_ip_header->srcaddr.u8, IPV6_ADDR_SIZE, &checksum, false);
                ipv6_checksum_calculate(p_ip_header->destaddr.u8, IPV6_ADDR_SIZE, &checksum, false);
                ipv6_checksum_calculate(p_packet->p_payload, p_packet->length, &checksum, false);

                if (checksum != 0 && checksum != 0xFFFF)
                {
                    UDP6_ERR("Bad checksum detected.");
                    process_result = UDP_BAD_CHECKSUM;
                }
            }

            p_packet->p_payload  = p_packet->p_payload + UDP_HEADER_SIZE;
            p_packet->length    -= UDP_HEADER_SIZE;

            //Found port for which data is intended.
            const  udp6_socket_t sock  = {index, m_socket[index].p_app_data};

            //Give application a callback if callback is registered.
            if (m_socket[index].rx_cb != NULL)
            {
                UDP_MUTEX_UNLOCK();

                // Change byte ordering given to application.
                p_udp_header->destport = NTOHS(p_udp_header->destport);
                p_udp_header->srcport  = NTOHS(p_udp_header->srcport);
                p_udp_header->length   = NTOHS(p_udp_header->length);
                p_udp_header->checksum = NTOHS(p_udp_header->checksum);

                err_code = m_socket[index].rx_cb(&sock, p_ip_header, p_udp_header, process_result, p_packet);

                UDP_MUTEX_LOCK();
            }
        }
        else
        {
            UDP6_ERR("Packet received on unknown port, dropping!");
        }

        UDP_MUTEX_UNLOCK();

        UDP6_EXIT();
    }
    else
    {
        UDP6_ERR("Packet of length less than UDP header size received!");
        err_code = (IOT_UDP6_ERR_BASE | NRF_ERROR_INVALID_LENGTH);
    }

    return err_code;
}
