aboutsummaryrefslogtreecommitdiff
path: root/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.c
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.c')
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.c1355
1 files changed, 1355 insertions, 0 deletions
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 <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#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;
+}