From 3061ecca3d0fdfb87dabbf5f63c9e06c2a30f53a Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Thu, 23 Aug 2018 17:08:59 +0200 Subject: o Initial import. --- .../components/iot/ipv6_stack/icmp6/icmp6.c | 1355 ++++++++++++++++++++ 1 file changed, 1355 insertions(+) create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.c (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.c') diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.c new file mode 100644 index 0000000..8c136b8 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.c @@ -0,0 +1,1355 @@ +/** + * Copyright (c) 2013 - 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 +#include +#include +#include + +#include "icmp6_api.h" +#include "ipv6_api.h" +#include "icmp6.h" +#include "iot_context_manager.h" +#include "ipv6_utils.h" +#include "iot_common.h" + +#if ICMP6_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME icmp6 + +#define NRF_LOG_LEVEL ICMP6_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR ICMP6_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR ICMP6_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define ICMP6_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define ICMP6_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define ICMP6_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define ICMP6_ENTRY() ICMP6_TRC(">> %s", __func__) +#define ICMP6_EXIT() ICMP6_TRC("<< %s", __func__) + +#else // ICMP6_CONFIG_LOG_ENABLED + +#define ICMP6_TRC(...) /**< Disables traces. */ +#define ICMP6_DUMP(...) /**< Disables dumping of octet streams. */ +#define ICMP6_ERR(...) /**< Disables error logs. */ + +#define ICMP6_ENTRY(...) +#define ICMP6_EXIT(...) + +#endif // ICMP6_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. + * ICMP6_DISABLE_API_PARAM_CHECK should be set to 1 to disable these checks. + * + * @{ + */ +#if (ICMP6_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_ICMP6_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_ICMP6_ERR_BASE); \ + } + +/** + * @brief Verify packet buffer is of ICMP6 Type. + */ +#define PACKET_TYPE_CHECK(PACKET) \ + if ((PACKET)->type != ICMP6_PACKET_TYPE) \ + { \ + return (NRF_ERROR_INVALID_PARAM | IOT_ICMP6_ERR_BASE); \ + } + + +#else // ICMP6_DISABLE_API_PARAM_CHECK + +#define VERIFY_MODULE_IS_INITIALIZED() +#define VERIFY_MODULE_IS_INITIALIZED_VOID() +#define NULL_PARAM_CHECK(PARAM) +#define PACKET_TYPE_CHECK(PACKET) + +#endif // ICMP6_DISABLE_API_PARAM_CHECK +/** @} */ + +/** + * @defgroup icmp6_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 ICMP6_MUTEX_LOCK() SDK_MUTEX_LOCK(m_icmp6_mutex) /**< Lock module using mutex */ +#define ICMP6_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_icmp6_mutex) /**< Unlock module using mutex */ +/** @} */ + +#define ND_NS_HEADER_SIZE 20 /**< Size of Neighbour Solicitation message. */ +#define ND_NA_HEADER_SIZE 20 /**< Size of Neighbour Advertisement message. */ +#define ND_RS_HEADER_SIZE 4 /**< Size of Router Solicitation message. */ +#define ND_RA_HEADER_SIZE 12 /**< Size of Router Advertisement message. */ +#define ND_PAYLOAD_ADJUST_OFFSET 4 /**< Adjusting ND related payload offset as the general ICMP structure is not upheld. */ + +#define ND_NA_R_FLAG 0x80 /**< Router flag. When set, the R-bit indicates that the sender is a router. */ +#define ND_NA_S_FLAG 0x40 /**< Solicited flag. When set, the S-bit indicates that the advertisement was sent in response + to a Neighbor Solicitation .*/ +#define ND_NA_O_FLAG 0x20 /**< Override flag. When set, the O-bit indicates that the advertisement should override + an existing cache entry and update the cached link-layer address .*/ + +#define ND_OPT_TYPE_SLLAO 1 /**< Source Link Layer Address Option. */ +#define ND_OPT_TYPE_TLLAO 2 /**< Target Link Layer Address Option. */ +#define ND_OPT_TYPE_PIO 3 /**< Prefix Information Option. */ +#define ND_OPT_TYPE_RHO 4 /**< Redirected Header Option. */ +#define ND_OPT_TYPE_MTU 5 /**< Maximum Transmit Unit Option. */ +#define ND_OPT_TYPE_ARO 33 /**< Address Registration Option. */ +#define ND_OPT_TYPE_6CO 34 /**< 6LoWPAN Context Option. */ +#define ND_OPT_TYPE_6ABRO 35 /**< Authoritative Border Router Option. */ + +#define ND_OPT_SLLAO_SIZE (8 * (((IPV6_LL_ADDR_SIZE) / 8) + 1)) /**< Size of SLLAO option. */ +#define ND_OPT_TLLAO_SIZE (8 * (((IPV6_LL_ADDR_SIZE) / 8) + 1)) /**< Size of TLLAO option. */ +#define ND_OPT_PIO_SIZE 32 /**< Size of PIO option. */ +#define ND_OPT_MTU_SIZE 8 /**< Size of MTU option. */ +#define ND_OPT_ARO_SIZE 16 /**< Size of ARO option. */ +#define ND_OPT_6CO_SIZE 24 /**< Size of 6CO option. */ +#define ND_OPT_6ABRO_SIZE 24 /**< Size of 6ABRO option. */ + +#define ND_OPT_SLLAO_LENGTH ((ND_OPT_SLLAO_SIZE) / 8) /**< Value of length field in SLLAO option. */ +#define ND_OPT_TLLAO_LENGTH ((ND_OPT_TLLAO_SIZE) / 8) /**< Value of length field in SLLAO option. */ +#define ND_OPT_ARO_LENGTH 2 /**< Value of length field in ARO option. */ + +#define ND_OPT_6CO_CID_MASK 0x0F +#define ND_OPT_6CO_CID_POS 0 +#define ND_OPT_6CO_C_MASK 0x10 +#define ND_OPT_6CO_C_POS 4 + +#define ND_OPT_PIO_L_MASK 0x80 +#define ND_OPT_PIO_L_POS 7 +#define ND_OPT_PIO_A_MASK 0x40 +#define ND_OPT_PIO_A_POS 6 + +#define ND_HOP_LIMIT 255 /**< Value of Hop Limit used in Neighbour Discovery procedure. */ + +#define ICMP6_OFFSET IPV6_IP_HEADER_SIZE + ICMP6_HEADER_SIZE /**< Offset of ICMPv6 packet type. */ + +#define ERROR_ADDITIONAL_HEADER_SIZE 4 /**< Additional 4 bytes of information every ICMP error message contains. */ +#define ERROR_MESSAGE_HEADER_SIZE (ICMP6_HEADER_SIZE + ERROR_ADDITIONAL_HEADER_SIZE) /**< Error message header size including type, code, checksum and 32-bit parameter. */ +#define ICMP6_ERROR_OFFSET IPV6_IP_HEADER_SIZE + ERROR_MESSAGE_HEADER_SIZE /**< Offset for ICMPv6 error message. */ + +/**@brief Neighbor Solicitation header. */ +typedef struct +{ + uint32_t reserved; /**< Reserved field. */ + ipv6_addr_t target_addr; /**< Target Address field. */ +} icmp6_ns_header_t; + +/**@brief Neighbor Advertisement header. */ +typedef struct +{ + uint8_t flags; /**< Flags (R,S and O). */ + uint8_t reserved; /**< Reserved field. */ + ipv6_addr_t target_addr; /**< Target Address field. */ +} icmp6_na_header_t; + +/**@brief Router Solicitation message's header. */ +typedef struct +{ + uint32_t reserved; /**< Reserved field. */ +} icmp6_rs_header_t; + +/**@brief Option header of ICMPv6 packet. */ +typedef struct +{ + uint8_t type; /**< Option type. */ + uint8_t length; /**< Length, in unit of 8 octets. */ +} nd_option_t; + +/**@brief Source Link Layer Address Option header format. */ +typedef struct +{ + uint8_t type; /**< Option type. */ + uint8_t length; /**< Length, units of 8 octets. */ + eui64_t addr; /**< Link-layer address. */ + uint8_t padding[6]; /**< Padding. */ +} nd_option_sllao_t; + +/**@brief Target Link Layer Address Option header format. */ +typedef struct +{ + uint8_t type; /**< Option type. */ + uint8_t length; /**< Length, units of 8 octets. */ + eui64_t addr; /**< Link-layer address. */ + uint8_t padding[6]; /**< Padding. */ +} nd_option_tllao_t; + +/**@brief Prefix Information Option header format. */ +typedef struct +{ + uint8_t type; /**< Option type. */ + uint8_t length; /**< Length, units of 8 octets. */ + uint8_t prefix_length; /**< Prefix length. */ + uint8_t flags; /**< Flags (L/A) and reserved. */ + uint32_t valid_lifetime; /**< Valid Lifetime. */ + uint32_t preferred_lifetime; /**< Preferred Lifetime. */ + uint32_t reserved; /**< Reserved field. */ + ipv6_addr_t prefix; /**< Prefix address. */ +} nd_option_pio_t; + +/**@brief Address Registration Option header format. */ +typedef struct +{ + uint8_t type; /**< Option type. */ + uint8_t length; /**< Length, units of 8 octets. */ + uint8_t status; /**< Status of ARO. */ + uint8_t reserved; /**< Reserved1, split to avoid alignment. */ + uint16_t reserved2; /**< Reserved2, split to avoid alignment. */ + uint16_t registration_lifetime; /**< Registration Lifetime. */ + eui64_t eui64; /**< EUI-64 source address. */ +} nd_option_aro_t; + +/**@brief 6LoWPAN Context Option header format. */ +typedef struct +{ + uint8_t type; /**< Option type. */ + uint8_t length; /**< Length, units of 8 octets. */ + uint8_t context_length; /**< Context Length. */ + uint8_t CID_C; /**< 4-bit Context and 1-bit context compression flag. */ + uint16_t reserved; /**< Reserved. */ + uint16_t valid_lifetime; /**< Valid Lifetime. */ + ipv6_addr_t context; /**< Context IPv6 Prefix. */ +} nd_option_6co_t; + +static bool m_initialization_state = false; /**< Variable to maintain module initialization state. */ +static uint16_t m_sequence_number = 0; /**< Sequence number from ICMPv6 packet. */ +static icmp6_receive_callback_t m_event_handler = NULL; /**< Application event handler. */ +SDK_MUTEX_DEFINE(m_icmp6_mutex) /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */ + +/**@brief Function for initializing default values of IP Header for ICMP. + * + * @param[in] p_ip_header Pointer to IPv6 header. + * @param[in] hoplimit Hop Limit in IPv6 header. + * + * @return None. + */ +static __INLINE void icmp_ip_header(ipv6_header_t * p_ip_header, uint8_t hoplimit) +{ + ipv6_header_init(p_ip_header); + p_ip_header->next_header = IPV6_NEXT_HEADER_ICMP6; + p_ip_header->hoplimit = hoplimit; +} + +/**@brief Function for adding SLLAO option to the packet. + * + * @param[in] p_interface Pointer to IoT interface. + * @param[in] p_data Pointer to the memory where SLLAO option should be added. + * + * @return None. + */ +static __INLINE void add_sllao_opt(const iot_interface_t * p_interface, nd_option_sllao_t * p_sllao) +{ + p_sllao->type = ND_OPT_TYPE_SLLAO; + p_sllao->length = ND_OPT_SLLAO_LENGTH; + +#if (IPV6_LL_ADDR_SIZE == 6) + memcpy(p_sllao->addr.identifier, p_interface->local_addr.identifier, 3); + memcpy(p_sllao->addr.identifier + 3, p_interface->local_addr.identifier + 5, 3); +#else + // Copy EUI-64 and add padding. + memcpy(p_sllao->addr.identifier, p_interface->local_addr.identifier, IPV6_LL_ADDR_SIZE); + memset(p_sllao->padding, 0, 6); +#endif +} + +/**@brief Function for adding TLLAO option to the packet. + * + * @param[in] p_interface Pointer to IoT interface. + * @param[in] p_data Pointer to the memory where TLLAO option should be added. + * + * @return None. + */ +static __INLINE void add_tllao_opt(const iot_interface_t * p_interface, nd_option_tllao_t * p_tllao) +{ + p_tllao->type = ND_OPT_TYPE_TLLAO; + p_tllao->length = ND_OPT_TLLAO_LENGTH; + +#if (IPV6_LL_ADDR_SIZE == 6) + memcpy(p_tllao->addr.identifier, p_interface->local_addr.identifier, 3); + memcpy(p_tllao->addr.identifier + 3, p_interface->local_addr.identifier + 5, 3); +#else + // Copy EUI-64 and add padding. + memcpy(p_tllao->addr.identifier, p_interface->local_addr.identifier, IPV6_LL_ADDR_SIZE); + memset(p_tllao->padding, 0, 6); +#endif +} + +/**@brief Function for adding ARO option to packet. + * + * @param[in] p_interface Pointer to IoT interface. + * @param[in] p_data Pointer to the memory where ARO option should be added. + * @param[in] aro_lifetime Lifetime of registration. + * + * @return None. + */ +static __INLINE void add_aro_opt(const iot_interface_t * p_interface, + nd_option_aro_t * p_aro, + uint16_t aro_lifetime) +{ + p_aro->type = ND_OPT_TYPE_ARO; + p_aro->length = ND_OPT_ARO_LENGTH; + p_aro->status = 0x00; + p_aro->reserved = 0x00; + p_aro->reserved2 = 0x00; + p_aro->registration_lifetime = HTONS(aro_lifetime); + + // Copy EUI-64 and add padding. + memcpy(p_aro->eui64.identifier, p_interface->local_addr.identifier, EUI_64_ADDR_SIZE); +} + +#if (ICMP6_ENABLE_ND6_MESSAGES_TO_APPLICATION == 1 || ICMP6_ENABLE_ALL_MESSAGES_TO_APPLICATION == 1) + +/**@brief Function for notifying application of the ICMPv6 received packet. + * + * @param[in] p_interface Pointer to external interface from which packet come. + * @param[in] p_pbuffer Pointer to packet buffer of ICMP6_PACKET_TYPE. + * @param[in] process_result Result of internal processing packet. + * + * @return NRF_SUCCESS after successful processing, error otherwise. + */ +static uint32_t app_notify_icmp_data(iot_interface_t * p_interface, + iot_pbuffer_t * p_pbuffer, + uint32_t process_result) +{ + uint32_t err_code = NRF_SUCCESS; + + if (m_event_handler != NULL) + { + + ipv6_header_t * p_ip_header = (ipv6_header_t *) + (p_pbuffer->p_payload - ICMP6_HEADER_SIZE - IPV6_IP_HEADER_SIZE); + icmp6_header_t * p_icmp_header = (icmp6_header_t *) + (p_pbuffer->p_payload - ICMP6_HEADER_SIZE); + + ICMP6_MUTEX_UNLOCK(); + + // Change byte order of ICMP header given to application. + p_icmp_header->checksum = NTOHS(p_icmp_header->checksum); + + err_code = m_event_handler(p_interface, + p_ip_header, + p_icmp_header, + process_result, + p_pbuffer); + + ICMP6_MUTEX_LOCK(); + } + + return err_code; +} + +#endif + +#if (ICMP6_ENABLE_HANDLE_ECHO_REQUEST_TO_APPLICATION == 0) + +/**@brief Function for responding on ECHO REQUEST message. + * + * @param[in] p_interface Pointer to external interface from which packet come. + * @param[in] p_ip_header Pointer to IPv6 Header. + * @param[in] p_icmp_header Pointer to ICMPv6 header. + * @param[in] p_packet Pointer to packet buffer. + * + * @return NRF_SUCCESS after successful processing, error otherwise. + */ +static void echo_reply_send(iot_interface_t * p_interface, + ipv6_header_t * p_ip_header, + icmp6_header_t * p_icmp_header, + iot_pbuffer_t * p_packet) +{ + uint32_t err_code; + uint16_t checksum; + iot_pbuffer_t * p_pbuffer; + iot_pbuffer_alloc_param_t pbuff_param; + + // Headers of new packet. + ipv6_header_t * p_reply_ip_header; + icmp6_header_t * p_reply_icmp_header; + + ICMP6_TRC("Sending reply on Echo Request."); + + // Requesting buffer for reply + pbuff_param.flags = PBUFFER_FLAG_DEFAULT; + pbuff_param.type = ICMP6_PACKET_TYPE; + pbuff_param.length = p_packet->length; + + err_code = iot_pbuffer_allocate(&pbuff_param, &p_pbuffer); + if (err_code == NRF_SUCCESS) + { + p_reply_ip_header = (ipv6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE - + IPV6_IP_HEADER_SIZE); + p_reply_icmp_header = (icmp6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE); + + // Change ICMP header. + p_reply_icmp_header->type = ICMP6_TYPE_ECHO_REPLY; + p_reply_icmp_header->code = 0; + p_reply_icmp_header->checksum = 0; + + // IPv6 Header initialization. + icmp_ip_header(p_reply_ip_header, IPV6_DEFAULT_HOP_LIMIT); + + p_reply_ip_header->destaddr = p_ip_header->srcaddr; + p_reply_ip_header->length = HTONS(p_pbuffer->length + ICMP6_HEADER_SIZE); + + if (IPV6_ADDRESS_IS_MULTICAST(&p_ip_header->destaddr)) + { + IPV6_CREATE_LINK_LOCAL_FROM_EUI64(&p_reply_ip_header->srcaddr, + p_interface->local_addr.identifier); + } + else + { + p_reply_ip_header->srcaddr = p_ip_header->destaddr; + } + + // Set echo reply parameters. + p_reply_icmp_header->sp.echo.id = p_icmp_header->sp.echo.id; + p_reply_icmp_header->sp.echo.sequence = p_icmp_header->sp.echo.sequence; + + // Copy user data. + memcpy(p_pbuffer->p_payload, + p_packet->p_payload, + p_packet->length); + + // Calculate checksum. + checksum = p_pbuffer->length + ICMP6_HEADER_SIZE + IPV6_NEXT_HEADER_ICMP6; + + ipv6_checksum_calculate(p_reply_ip_header->srcaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_reply_ip_header->destaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_pbuffer->p_payload - ICMP6_HEADER_SIZE, + p_pbuffer->length + ICMP6_HEADER_SIZE, + &checksum, + false); + + p_reply_icmp_header->checksum = HTONS((~checksum)); + + p_pbuffer->p_payload -= ICMP6_OFFSET; + p_pbuffer->length += ICMP6_OFFSET; + + // Send IPv6 packet. + err_code = ipv6_send(p_interface, p_pbuffer); + + if (err_code != NRF_SUCCESS) + { + ICMP6_ERR("Cannot send packet buffer!"); + } + } + else + { + ICMP6_ERR("Failed to allocate packet buffer!"); + } +} +#endif + + +/**@brief Function for responding on Neighbor Advertisement message. + * + * @param[in] p_interface Pointer to external interface from which packet come. + * @param[in] p_ip_header Pointer to IPv6 Header. + * @param[in] p_icmp_header Pointer to ICMPv6 header. + * @param[in] p_target_addr Pointer to the IPv6 address. + * + * @return NRF_SUCCESS after successful processing, error otherwise. + */ +static uint32_t na_send(iot_interface_t * p_interface, + ipv6_header_t * p_ip_header, + icmp6_header_t * p_icmp_header, + ipv6_addr_t * p_target_addr) +{ + uint32_t err_code; + uint16_t checksum; + iot_pbuffer_t * p_pbuffer; + iot_pbuffer_alloc_param_t pbuff_param; + + // Headers of new packet. + ipv6_header_t * p_reply_ip_header; + icmp6_header_t * p_reply_icmp_header; + icmp6_na_header_t * p_reply_na_header; + nd_option_tllao_t * p_reply_opt_tllao_header; + + ICMP6_TRC("Sending reply on Neighbor Solocitation."); + + // Requesting buffer for reply + pbuff_param.flags = PBUFFER_FLAG_DEFAULT; + pbuff_param.type = ICMP6_PACKET_TYPE; + pbuff_param.length = ND_NA_HEADER_SIZE + ND_OPT_TLLAO_SIZE - ND_PAYLOAD_ADJUST_OFFSET; + + err_code = iot_pbuffer_allocate(&pbuff_param, &p_pbuffer); + if (err_code == NRF_SUCCESS) + { + p_reply_ip_header = (ipv6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE - + IPV6_IP_HEADER_SIZE); + p_reply_icmp_header = (icmp6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE); + + p_pbuffer->p_payload -= ND_PAYLOAD_ADJUST_OFFSET; + + p_reply_na_header = (icmp6_na_header_t *)(p_pbuffer->p_payload); + p_reply_opt_tllao_header = (nd_option_tllao_t *)(p_pbuffer->p_payload + ND_NA_HEADER_SIZE); + + p_pbuffer->p_payload += ND_PAYLOAD_ADJUST_OFFSET; + + // Change ICMP header. + p_reply_icmp_header->type = ICMP6_TYPE_NEIGHBOR_ADVERTISEMENT; + p_reply_icmp_header->code = 0; + p_reply_icmp_header->checksum = 0; + + // IPv6 Header initialization. + icmp_ip_header(p_reply_ip_header, ND_HOP_LIMIT); + + p_reply_ip_header->srcaddr = *p_target_addr; + p_reply_ip_header->destaddr = p_ip_header->srcaddr; + p_reply_ip_header->length = HTONS(p_pbuffer->length + ICMP6_HEADER_SIZE); + + p_reply_na_header->flags = ND_NA_S_FLAG | ND_NA_O_FLAG ; + p_reply_na_header->reserved = 0; + p_reply_na_header->target_addr = *p_target_addr; + + // Add TLLAO option. + add_tllao_opt(p_interface, p_reply_opt_tllao_header); + + // Calculate checksum. + checksum = p_pbuffer->length + ICMP6_HEADER_SIZE + IPV6_NEXT_HEADER_ICMP6; + + ipv6_checksum_calculate(p_reply_ip_header->srcaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_reply_ip_header->destaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_pbuffer->p_payload - ICMP6_HEADER_SIZE, + p_pbuffer->length + ICMP6_HEADER_SIZE, + &checksum, + false); + + p_reply_icmp_header->checksum = HTONS((~checksum)); + + p_pbuffer->p_payload -= ICMP6_OFFSET; + p_pbuffer->length += ICMP6_OFFSET; + + // Send IPv6 packet. + err_code = ipv6_send(p_interface, p_pbuffer); + + if (err_code != NRF_SUCCESS) + { + ICMP6_ERR("Cannot send packet buffer!"); + } + } + else + { + ICMP6_ERR("Failed to allocate packet buffer!\r\n"); + } + + return err_code; +} + + +/**@brief Function for parsing Neighbor Solicitation message. + * + * @param[in] p_interface Pointer to external interface from which packet come. + * @param[in] p_ip_header Pointer to IPv6 Header. + * @param[in] p_icmp_header Pointer to ICMPv6 header. + * @param[in] p_packet Pointer to packet buffer. + * + * @return NRF_SUCCESS after successful processing, error otherwise. + */ +static uint32_t ns_input(iot_interface_t * p_interface, + ipv6_header_t * p_ip_header, + icmp6_header_t * p_icmp_header, + iot_pbuffer_t * p_packet) +{ + uint32_t err_code = NRF_SUCCESS; + + // Read target address. + icmp6_ns_header_t * p_ns_header = (icmp6_ns_header_t *)p_packet->p_payload; + + if (ipv6_address_check(p_interface, &p_ns_header->target_addr) == NRF_SUCCESS) + { + err_code = na_send(p_interface, p_ip_header, p_icmp_header, &p_ns_header->target_addr); + } + + return err_code; +} + + +/**@brief Function for parsing Router Advertisement message. + * Because stack gives all control to application, internal RA parsing take care + * only on Context Identifier. + * + * @param[in] p_interface Pointer to external interface from which packet come. + * @param[in] p_ip_header Pointer to IPv6 Header. + * @param[in] p_icmp_header Pointer to ICMPv6 header. + * @param[in] p_packet Pointer to packet buffer. + * + * @return NRF_SUCCESS after successful processing, error otherwise. + */ +static uint32_t ra_input(iot_interface_t * p_interface, + ipv6_header_t * p_ip_header, + icmp6_header_t * p_icmp_header, + iot_pbuffer_t * p_packet) +{ + uint32_t err_code; + iot_context_t context; + iot_context_t * p_context; + uint16_t curr_opt_offset = ND_RA_HEADER_SIZE; + nd_option_t * p_opt = NULL; + nd_option_6co_t * p_6co = NULL; + nd_option_pio_t * p_pio = NULL; + + if (!IPV6_ADDRESS_IS_LINK_LOCAL(&p_ip_header->srcaddr)) + { + return ICMP6_INVALID_PACKET_DATA; + } + + // Read all option we get. + while (curr_opt_offset < p_packet->length) + { + p_opt = (nd_option_t *)(p_packet->p_payload + curr_opt_offset); + + if (p_opt->length == 0) + { + ICMP6_ERR("Invalid zero length option!"); + return ICMP6_INVALID_PACKET_DATA; + } + + ICMP6_TRC("Option type = 0x%02x!", p_opt->type); + + // Searching for handling options. + switch (p_opt->type) + { + case ND_OPT_TYPE_PIO: + { + p_pio = (nd_option_pio_t *)p_opt; + + if (p_pio->prefix_length != 0 && + (p_pio->flags & ND_OPT_PIO_A_MASK) && + !(p_pio->flags & ND_OPT_PIO_L_MASK)) + { + // Ignore Link-Local address + if (IPV6_ADDRESS_IS_LINK_LOCAL(&p_pio->prefix)) + { + ICMP6_ERR("Ignore Link-Local prefix!"); + break; + } + + // For now address is automatically set as a preferred. + ipv6_addr_conf_t temp_address; + + // Set IPv6 EUI-64 + IPV6_CREATE_LINK_LOCAL_FROM_EUI64(&temp_address.addr, + p_interface->local_addr.identifier); + + // Add prefix + IPV6_ADDRESS_PREFIX_SET(temp_address.addr.u8, + p_pio->prefix.u8, + p_pio->prefix_length); + + if (p_pio->valid_lifetime != 0) + { + temp_address.state = IPV6_ADDR_STATE_PREFERRED; + + err_code = ipv6_address_set(p_interface, &temp_address); + + if (err_code != NRF_SUCCESS) + { + ICMP6_ERR("Cannot add new address! Address table full!"); + } + } + else + { + err_code = ipv6_address_remove(p_interface, &temp_address.addr); + + if (err_code != NRF_SUCCESS) + { + ICMP6_ERR("Cannot remove address!"); + } + } + } + else + { + ICMP6_ERR("Prefix option has incorrect parameters!"); + return ICMP6_INVALID_PACKET_DATA; + } + + break; + } + case ND_OPT_TYPE_6CO: + { + p_6co = (nd_option_6co_t *)p_opt; + + memset(context.prefix.u8, 0, IPV6_ADDR_SIZE); + + context.prefix = p_6co->context; + context.prefix_len = p_6co->context_length; + context.context_id = (p_6co->CID_C & ND_OPT_6CO_CID_MASK) >> + ND_OPT_6CO_CID_POS; + context.compression_flag = (p_6co->CID_C & ND_OPT_6CO_C_MASK) >> + ND_OPT_6CO_C_POS; + + if (p_6co->valid_lifetime == 0) + { + err_code = iot_context_manager_get_by_cid(p_interface, + context.context_id, + &p_context); + + if (err_code == NRF_SUCCESS) + { + err_code = iot_context_manager_remove(p_interface, p_context); + + if (err_code == NRF_SUCCESS) + { + ICMP6_TRC("Removed context! CID = 0x%02x", context.context_id); + } + } + + } + else + { + err_code = iot_context_manager_update(p_interface, &context); + + if (err_code == NRF_SUCCESS) + { + ICMP6_TRC("New context added! CID = 0x%02x", context.context_id); + } + } + + break; + } + } + + // Increment current offset option. + curr_opt_offset += 8 * p_opt->length; + } + + return NRF_SUCCESS; +} + +/**@brief Function for notifying application of the ICMPv6 received packet. + * + * @param[in] p_interface Pointer to external interface from which packet come. + * @param[in] p_ip_header Pointer to IPv6 Header. + * @param[in] p_icmp_header Pointer to ICMPv6 header. + * @param[in] p_packet Pointer to packet buffer. + * + * @return NRF_SUCCESS after successful processing, error otherwise. + */ +static uint32_t ndisc_input(iot_interface_t * p_interface, + ipv6_header_t * p_ip_header, + icmp6_header_t * p_icmp_header, + iot_pbuffer_t * p_packet) +{ + uint32_t process_result; + + switch (p_icmp_header->type) + { + case ICMP6_TYPE_ROUTER_SOLICITATION: + ICMP6_ERR("Got unsupported Router Solicitation message."); + process_result = ICMP6_UNHANDLED_PACKET_TYPE; + break; + + case ICMP6_TYPE_ROUTER_ADVERTISEMENT: + ICMP6_TRC("Got Router Advertisement message."); + process_result = ra_input(p_interface, p_ip_header, p_icmp_header, p_packet); + break; + + case ICMP6_TYPE_NEIGHBOR_SOLICITATION: + ICMP6_TRC("Got Neighbour Solicitation message."); + process_result = ns_input(p_interface, p_ip_header, p_icmp_header, p_packet); + break; + + case ICMP6_TYPE_NEIGHBOR_ADVERTISEMENT: + ICMP6_TRC("Got Neighbour Advertisement message."); + process_result = NRF_SUCCESS; + break; + + default: + process_result = ICMP6_UNHANDLED_PACKET_TYPE; + break; + } + + return process_result; +} + +uint32_t icmp6_error_message(const iot_interface_t * p_interface, + const ipv6_addr_t * p_src_addr, + const ipv6_addr_t * p_dest_addr, + const icmp6_error_message_param_t * p_param) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_interface); + NULL_PARAM_CHECK(p_src_addr); + NULL_PARAM_CHECK(p_dest_addr); + NULL_PARAM_CHECK(p_param); + NULL_PARAM_CHECK(p_param->p_packet); + + ICMP6_MUTEX_LOCK(); + + ICMP6_ENTRY(); + + iot_pbuffer_t * p_pbuffer; + ipv6_header_t * p_ip_header; + icmp6_header_t * p_icmp_header; + iot_pbuffer_alloc_param_t pbuff_param; + uint16_t checksum; + uint32_t err_code = NRF_SUCCESS; + const uint32_t error_packet_length = + (MIN(p_param->packet_len, + ICMP6_ERROR_MESSAGE_MAX_SIZE - ICMP6_HEADER_SIZE)); + + // Requesting buffer for error message. + pbuff_param.flags = PBUFFER_FLAG_DEFAULT; + pbuff_param.type = ICMP6_PACKET_TYPE; + pbuff_param.length = error_packet_length; + + err_code = iot_pbuffer_allocate(&pbuff_param, &p_pbuffer); + if (err_code == NRF_SUCCESS) + { + p_ip_header = (ipv6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE - + IPV6_IP_HEADER_SIZE); + p_icmp_header = (icmp6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE); + + // Change ICMP header. + p_icmp_header->type = p_param->type; + p_icmp_header->code = p_param->code; + p_icmp_header->checksum = 0; + + switch (p_param->type) + { + case ICMP6_TYPE_PACKET_TOO_LONG: + { + p_icmp_header->sp.mtu = HTONL(p_param->error_field.mtu); + break; + } + case ICMP6_TYPE_PARAMETER_PROBLEM: + { + p_icmp_header->sp.offset = HTONL(p_param->error_field.offset); + break; + } + default: + { + p_icmp_header->sp.unused = 0; + break; + } + } + + // IPv6 Header initialization. + icmp_ip_header(p_ip_header, IPV6_DEFAULT_HOP_LIMIT); + + p_ip_header->srcaddr = *p_src_addr; + p_ip_header->destaddr = *p_dest_addr; + p_ip_header->length = HTONS(p_pbuffer->length + ICMP6_HEADER_SIZE); + + memcpy(p_pbuffer->p_payload, p_param->p_packet, error_packet_length); + + // Calculate checksum. + checksum = error_packet_length + IPV6_NEXT_HEADER_ICMP6; + + 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_pbuffer->p_payload - ICMP6_HEADER_SIZE, + p_pbuffer->length + ICMP6_HEADER_SIZE, + &checksum, + false); + + // Update checksum in the packet. + p_icmp_header->checksum = HTONS((~checksum)); + + p_pbuffer->p_payload -= ICMP6_OFFSET; + p_pbuffer->length += ICMP6_OFFSET; + + // Send IPv6 packet. + err_code = ipv6_send(p_interface, p_pbuffer); + } + + ICMP6_EXIT(); + + ICMP6_MUTEX_UNLOCK(); + + return err_code; +} + +uint32_t icmp6_echo_request(const iot_interface_t * p_interface, + const ipv6_addr_t * p_src_addr, + const ipv6_addr_t * p_dest_addr, + iot_pbuffer_t * p_request) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_interface); + NULL_PARAM_CHECK(p_src_addr); + NULL_PARAM_CHECK(p_dest_addr); + NULL_PARAM_CHECK(p_request); + PACKET_TYPE_CHECK(p_request); + + uint32_t err_code = NRF_SUCCESS; + uint16_t checksum; + ipv6_header_t * p_ip_header; + icmp6_header_t * p_icmp_header; + + ICMP6_MUTEX_LOCK(); + + ICMP6_ENTRY(); + + // Headers of IPv6 packet. + p_ip_header = (ipv6_header_t *)(p_request->p_payload - ICMP6_HEADER_SIZE - + IPV6_IP_HEADER_SIZE); + p_icmp_header = (icmp6_header_t *)(p_request->p_payload - ICMP6_HEADER_SIZE); + + // Change ICMP header. + p_icmp_header->type = ICMP6_TYPE_ECHO_REQUEST; + p_icmp_header->code = 0; + p_icmp_header->checksum = 0; + + // IPv6 Header initialization. + icmp_ip_header(p_ip_header, IPV6_DEFAULT_HOP_LIMIT); + + p_ip_header->srcaddr = *p_src_addr; + p_ip_header->destaddr = *p_dest_addr; + p_ip_header->length = HTONS(p_request->length + ICMP6_HEADER_SIZE); + + // Set echo reply parameters. + p_icmp_header->sp.echo.id = 0; + p_icmp_header->sp.echo.sequence = HTONS(m_sequence_number); + + // Calculate checksum. + checksum = p_request->length + ICMP6_HEADER_SIZE + IPV6_NEXT_HEADER_ICMP6; + + 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_request->p_payload - ICMP6_HEADER_SIZE, + p_request->length + ICMP6_HEADER_SIZE, + &checksum, + false); + + p_icmp_header->checksum = HTONS((~checksum)); + + m_sequence_number++; + p_request->p_payload -= ICMP6_OFFSET; + p_request->length += ICMP6_OFFSET; + + // Send IPv6 packet. + err_code = ipv6_send(p_interface, p_request); + + ICMP6_EXIT(); + + ICMP6_MUTEX_UNLOCK(); + + return err_code; +} + +uint32_t icmp6_rs_send(const iot_interface_t * p_interface, + const ipv6_addr_t * p_src_addr, + const ipv6_addr_t * p_dest_addr) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_interface); + NULL_PARAM_CHECK(p_src_addr); + NULL_PARAM_CHECK(p_dest_addr); + + uint32_t err_code = NRF_SUCCESS; + uint16_t checksum; + iot_pbuffer_t * p_pbuffer; + iot_pbuffer_alloc_param_t pbuff_param; + + // IPv6 Headers. + ipv6_header_t * p_ip_header; + icmp6_header_t * p_icmp_header; + icmp6_rs_header_t * p_rs_header; + nd_option_sllao_t * p_sllao_opt; + + ICMP6_MUTEX_LOCK(); + + ICMP6_ENTRY(); + + // Requesting buffer for RS message + pbuff_param.flags = PBUFFER_FLAG_DEFAULT; + pbuff_param.type = ICMP6_PACKET_TYPE; + pbuff_param.length = ND_OPT_SLLAO_SIZE; + + err_code = iot_pbuffer_allocate(&pbuff_param, &p_pbuffer); + + if (err_code == NRF_SUCCESS) + { + p_ip_header = (ipv6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE - + IPV6_IP_HEADER_SIZE); + p_icmp_header = (icmp6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE); + p_rs_header = (icmp6_rs_header_t *)(&p_icmp_header->sp.unused); + p_sllao_opt = (nd_option_sllao_t *)(p_pbuffer->p_payload); + + // Change ICMP header. + p_icmp_header->type = ICMP6_TYPE_ROUTER_SOLICITATION; + p_icmp_header->code = 0; + p_icmp_header->checksum = 0; + + // IPv6 Header initialization. + icmp_ip_header(p_ip_header, ND_HOP_LIMIT); + + p_ip_header->srcaddr = *p_src_addr; + p_ip_header->destaddr = *p_dest_addr; + p_ip_header->length = HTONS(p_pbuffer->length + ICMP6_HEADER_SIZE); + + // Set Router Solicitation parameter. + p_rs_header->reserved = 0; + + // Add SLLAO option. + add_sllao_opt(p_interface, p_sllao_opt); + + // Calculate checksum. + checksum = p_pbuffer->length + ICMP6_HEADER_SIZE + IPV6_NEXT_HEADER_ICMP6; + + 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_pbuffer->p_payload - ICMP6_HEADER_SIZE, + p_pbuffer->length + ICMP6_HEADER_SIZE, + &checksum, + false); + + p_icmp_header->checksum = HTONS((~checksum)); + + p_pbuffer->p_payload -= ICMP6_OFFSET; + p_pbuffer->length += ICMP6_OFFSET; + + // Send IPv6 packet. + err_code = ipv6_send(p_interface, p_pbuffer); + } + else + { + ICMP6_ERR("Failed to allocate packet buffer!"); + } + + ICMP6_EXIT(); + + ICMP6_MUTEX_UNLOCK(); + + return err_code; +} + +uint32_t icmp6_ns_send(const iot_interface_t * p_interface, + const ipv6_addr_t * p_src_addr, + const ipv6_addr_t * p_dest_addr, + const icmp6_ns_param_t * p_param) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_interface); + NULL_PARAM_CHECK(p_src_addr); + NULL_PARAM_CHECK(p_dest_addr); + NULL_PARAM_CHECK(p_param); + + uint32_t err_code = NRF_SUCCESS; + uint16_t aro_size = 0; + uint16_t checksum; + iot_pbuffer_t * p_pbuffer; + iot_pbuffer_alloc_param_t pbuff_param; + + // IPv6 Headers. + ipv6_header_t * p_ip_header; + icmp6_header_t * p_icmp_header; + icmp6_ns_header_t * p_ns_header; + nd_option_sllao_t * p_sllao_opt; + nd_option_aro_t * p_aro_opt; + + ICMP6_MUTEX_LOCK(); + + ICMP6_ENTRY(); + + if (p_param->add_aro) + { + aro_size = ND_OPT_ARO_SIZE; + } + + // Requesting buffer for NS message + pbuff_param.flags = PBUFFER_FLAG_DEFAULT; + pbuff_param.type = ICMP6_PACKET_TYPE; + pbuff_param.length = ND_NS_HEADER_SIZE + ND_OPT_SLLAO_SIZE + \ + aro_size - ND_PAYLOAD_ADJUST_OFFSET; + + err_code = iot_pbuffer_allocate(&pbuff_param, &p_pbuffer); + + if (err_code == NRF_SUCCESS) + { + p_ip_header = (ipv6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE - + IPV6_IP_HEADER_SIZE); + p_icmp_header = (icmp6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE); + p_pbuffer->p_payload -= ND_PAYLOAD_ADJUST_OFFSET; + p_ns_header = (icmp6_ns_header_t *)(p_pbuffer->p_payload); + p_sllao_opt = (nd_option_sllao_t *)(p_pbuffer->p_payload + ND_NS_HEADER_SIZE); + p_aro_opt = (nd_option_aro_t *)(p_pbuffer->p_payload + ND_NS_HEADER_SIZE + + ND_OPT_SLLAO_SIZE); + + p_pbuffer->p_payload += ND_PAYLOAD_ADJUST_OFFSET; + + // Change ICMP header. + p_icmp_header->type = ICMP6_TYPE_NEIGHBOR_SOLICITATION; + p_icmp_header->code = 0; + p_icmp_header->checksum = 0; + + // IPv6 Header initialization. + icmp_ip_header(p_ip_header, ND_HOP_LIMIT); + + p_ip_header->srcaddr = *p_src_addr; + p_ip_header->destaddr = *p_dest_addr; + p_ip_header->length = HTONS(p_pbuffer->length + ICMP6_HEADER_SIZE); + + // Set Neighbour Solicitation parameter. + p_ns_header->reserved = 0; + p_ns_header->target_addr = p_param->target_addr; + + // Add SLLAO option. + add_sllao_opt(p_interface, p_sllao_opt); + + if (p_param->add_aro) + { + add_aro_opt(p_interface, p_aro_opt, p_param->aro_lifetime); + } + + // Calculate checksum. + checksum = p_pbuffer->length + ICMP6_HEADER_SIZE + IPV6_NEXT_HEADER_ICMP6; + + 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_pbuffer->p_payload - ICMP6_HEADER_SIZE, + p_pbuffer->length + ICMP6_HEADER_SIZE, + &checksum, + false); + + p_icmp_header->checksum = HTONS((~checksum)); + + p_pbuffer->p_payload -= ICMP6_OFFSET; + p_pbuffer->length += ICMP6_OFFSET; + + // Send IPv6 packet. + err_code = ipv6_send(p_interface, p_pbuffer); + } + else + { + ICMP6_ERR("Failed to allocate packet buffer!"); + } + + ICMP6_EXIT(); + + ICMP6_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t icmp6_receive_register(icmp6_receive_callback_t cb) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(cb); + UNUSED_VARIABLE(m_event_handler); + + ICMP6_MUTEX_LOCK(); + + ICMP6_ENTRY(); + + // Store application event handler. + m_event_handler = cb; + + ICMP6_EXIT(); + + ICMP6_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + + +uint32_t icmp6_init(void) +{ + SDK_MUTEX_INIT(m_icmp6_mutex); + ICMP6_MUTEX_LOCK(); + + ICMP6_ENTRY(); + + // Set application event handler. + m_event_handler = NULL; + + // Indicate initialization of module. + m_initialization_state = true; + + ICMP6_EXIT(); + + ICMP6_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + + +uint32_t icmp6_input(iot_interface_t * p_interface, + ipv6_header_t * p_ip_header, + iot_pbuffer_t * p_packet) +{ + VERIFY_MODULE_IS_INITIALIZED(); + + NULL_PARAM_CHECK(p_interface); + NULL_PARAM_CHECK(p_ip_header); + NULL_PARAM_CHECK(p_packet); + + uint16_t checksum; + uint32_t process_result = NRF_SUCCESS; + bool is_ndisc = false; + icmp6_header_t * p_icmp_header = (icmp6_header_t *)p_packet->p_payload; + uint32_t err_code = NRF_SUCCESS; + + ICMP6_MUTEX_LOCK(); + + ICMP6_ENTRY(); + + if (p_packet->length < ICMP6_HEADER_SIZE || p_ip_header->length < ICMP6_HEADER_SIZE) + { + ICMP6_ERR("Received malformed packet, which has 0x%08lX bytes.", p_packet->length); + process_result = ICMP6_MALFORMED_PACKET; + } + else + { + // Check checksum of packet. + checksum = p_packet->length + IPV6_NEXT_HEADER_ICMP6; + + 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); + checksum = (uint16_t)~checksum; + + // Change pbuffer type. + p_packet->type = ICMP6_PACKET_TYPE; + p_packet->p_payload = p_packet->p_payload + ICMP6_HEADER_SIZE; + p_packet->length -= ICMP6_HEADER_SIZE; + + if (checksum != 0) + { + ICMP6_ERR("Bad checksum detected. Got 0x%08x but expected 0x%08x, 0x%08lX", + NTOHS(p_icmp_header->checksum), checksum, p_packet->length); + process_result = ICMP6_BAD_CHECKSUM; + } + else + { + switch (p_icmp_header->type) + { + case ICMP6_TYPE_DESTINATION_UNREACHABLE: + case ICMP6_TYPE_PACKET_TOO_LONG: + case ICMP6_TYPE_TIME_EXCEED: + case ICMP6_TYPE_PARAMETER_PROBLEM: + { + ICMP6_TRC("Got ICMPv6 error message with type = 0x%08x", + p_icmp_header->type); + p_icmp_header->sp.unused = NTOHL(p_icmp_header->sp.unused); + break; + } + case ICMP6_TYPE_ECHO_REQUEST: + case ICMP6_TYPE_ECHO_REPLY: + { + ICMP6_TRC("Got ICMPv6 Echo message with type = 0x%x.", p_icmp_header->type); + ICMP6_TRC("From IPv6 Address:"); + ICMP6_DUMP(p_ip_header->srcaddr.u32, IPV6_ADDR_SIZE); + ICMP6_TRC("Identifier: 0x%04x, Sequence Number: 0x%04x", + NTOHS(p_icmp_header->sp.echo.id), + NTOHS(p_icmp_header->sp.echo.sequence)); + break; + } + case ICMP6_TYPE_ROUTER_SOLICITATION: + case ICMP6_TYPE_ROUTER_ADVERTISEMENT: + case ICMP6_TYPE_NEIGHBOR_SOLICITATION: + case ICMP6_TYPE_NEIGHBOR_ADVERTISEMENT: + { + p_packet->p_payload = p_packet->p_payload - ND_PAYLOAD_ADJUST_OFFSET; + p_packet->length += ND_PAYLOAD_ADJUST_OFFSET; + process_result = ndisc_input(p_interface, + p_ip_header, + p_icmp_header, + p_packet); + p_packet->p_payload = p_packet->p_payload + ND_PAYLOAD_ADJUST_OFFSET; + p_packet->length -= ND_PAYLOAD_ADJUST_OFFSET; + is_ndisc = true; + break; + } + default: + process_result = ICMP6_UNHANDLED_PACKET_TYPE; + break; + } + +#if (ICMP6_ENABLE_HANDLE_ECHO_REQUEST_TO_APPLICATION == 0) + if (p_icmp_header->type == ICMP6_TYPE_ECHO_REQUEST) + { + echo_reply_send(p_interface, p_ip_header, p_icmp_header, p_packet); + } +#endif + } + } + +#if (ICMP6_ENABLE_ALL_MESSAGES_TO_APPLICATION == 1) + err_code = app_notify_icmp_data(p_interface, p_packet, process_result); +#elif (ICMP6_ENABLE_ND6_MESSAGES_TO_APPLICATION == 1) + if (is_ndisc) + { + err_code = app_notify_icmp_data(p_interface, p_packet, process_result); + } +#endif + + ICMP6_EXIT(); + + UNUSED_VARIABLE(is_ndisc); + UNUSED_VARIABLE(process_result); + + ICMP6_MUTEX_UNLOCK(); + + return err_code; +} -- cgit v1.2.3