aboutsummaryrefslogtreecommitdiff
path: root/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack')
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/dns6/dns6.c903
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.c1355
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.h101
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/dns6_api.h173
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/icmp6_api.h253
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/ipv6_api.h207
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/udp_api.h255
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/ipv6/ipv6.c1088
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/pbuffer/iot_pbuffer.c467
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/pbuffer/iot_pbuffer.h175
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/sntp_client/sntp_client.c607
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/sntp_client/sntp_client.h202
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/tftp/iot_tftp.c2455
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/tftp/iot_tftp.h245
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/udp/udp.h98
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/udp/udp6.c708
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/utils/ipv6_utils.c101
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/utils/ipv6_utils.h93
18 files changed, 9486 insertions, 0 deletions
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/dns6/dns6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/dns6/dns6.c
new file mode 100644
index 0000000..6c90ce3
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/dns6/dns6.c
@@ -0,0 +1,903 @@
+/**
+ * Copyright (c) 2015 - 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 <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include "sdk_errors.h"
+#include "sdk_os.h"
+#include "sdk_config.h"
+#include "iot_common.h"
+#include "iot_pbuffer.h"
+#include "mem_manager.h"
+#include "ipv6_api.h"
+#include "udp_api.h"
+#include "dns6_api.h"
+
+#if DNS6_CONFIG_LOG_ENABLED
+
+#define NRF_LOG_MODULE_NAME dns6
+
+#define NRF_LOG_LEVEL DNS6_CONFIG_LOG_LEVEL
+#define NRF_LOG_INFO_COLOR DNS6_CONFIG_INFO_COLOR
+#define NRF_LOG_DEBUG_COLOR DNS6_CONFIG_DEBUG_COLOR
+
+#include "nrf_log.h"
+NRF_LOG_MODULE_REGISTER();
+
+#define DNS6_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */
+#define DNS6_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */
+#define DNS6_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */
+
+#define DNS6_ENTRY() DNS6_TRC(">> %s", __func__)
+#define DNS6_EXIT() DNS6_TRC("<< %s", __func__)
+
+#else // DNS6_CONFIG_LOG_ENABLED
+
+#define DNS6_TRC(...) /**< Disables traces. */
+#define DNS6_DUMP(...) /**< Disables dumping of octet streams. */
+#define DNS6_ERR(...) /**< Disables error logs. */
+
+#define DNS6_ENTRY(...)
+#define DNS6_EXIT(...)
+
+#endif // DNS6_CONFIG_LOG_ENABLED
+
+/**
+ * @defgroup dns6_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 DNS6_MUTEX_LOCK() SDK_MUTEX_LOCK(m_dns6_mutex) /**< Lock module using mutex */
+#define DNS6_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_dns6_mutex) /**< Unlock module using mutex */
+/** @} */
+
+/**
+ * @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.
+ * DNS6_DISABLE_API_PARAM_CHECK should be set to 0 to enable these checks.
+ *
+ * @{
+ */
+
+#if (DNS6_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_DNS6_ERR_BASE);\
+ }
+
+/**@brief Verify NULL parameters are not passed to API by application. */
+#define NULL_PARAM_CHECK(PARAM) \
+ if ((PARAM) == NULL) \
+ { \
+ return (NRF_ERROR_NULL | IOT_DNS6_ERR_BASE); \
+ }
+
+/**@brief Verify that empty parameters are not passed to API by application. */
+#define EMPTY_PARAM_CHECK(PARAM) \
+ if (*PARAM == 0) \
+ { \
+ return (NRF_ERROR_INVALID_DATA | IOT_DNS6_ERR_BASE); \
+ }
+
+#else // DNS6_DISABLE_API_PARAM_CHECK
+
+#define VERIFY_MODULE_IS_INITIALIZED()
+#define NULL_PARAM_CHECK(PARAM)
+#define EMPTY_PARAM_CHECK(PARAM)
+
+#endif // DNS6_DISABLE_API_PARAM_CHECK
+/** @} */
+
+/**@brief RFC1035 - DNS Header Fields and Values. */
+#define DNS_HEADER_FLAG1_QR_QUERY 0x00 /**< Bit specifies that message is a query. */
+#define DNS_HEADER_FLAG1_QR_RESPONSE 0x80 /**< Bit specifies that message is a response. */
+#define DNS_HEADER_FLAG1_OPCODE_STANDARD 0x00 /**< A standard type of query. */
+#define DNS_HEADER_FLAG1_OPCODE_INVERSE 0x08 /**< An inverse type of query. */
+#define DNS_HEADER_FLAG1_OPCODE_STATUS 0x10 /**< A server status request. */
+#define DNS_HEADER_FLAG1_AA 0x04 /**< Bit specifies that the responding name server is an authority for the domain name in question section. */
+#define DNS_HEADER_FLAG1_TC 0x02 /**< Bit specifies that message is truncated. */
+#define DNS_HEADER_FLAG1_RD 0x01 /**< Bit specifies that recursion is desired. */
+
+#define DNS_HEADER_FLAG2_RA 0x80 /**< Bit specifies if recursive query support is available in the name server. */
+#define DNS_HEADER_FLAG2_RCODE_NONE 0x00 /**< No error condition. */
+#define DNS_HEADER_FLAG2_RCODE_FORMAT_ERROR 0x01 /**< Error indicates that dns server is unable o interpret the query. */
+#define DNS_HEADER_FLAG2_RCODE_SERVER_FAILURE 0x02 /**< Error indicates that dns server has internal problem. */
+#define DNS_HEADER_FLAG2_RCODE_NAME_ERROR 0x03 /**< Error indicates that domain name referenced in the query does not exist. */
+#define DNS_HEADER_FLAG2_RCODE_NOT_IMPLEMENTED 0x04 /**< Error indicates that dns server does not support previously sent query. */
+#define DNS_HEADER_FLAG2_RCODE_REFUSED 0x05 /**< Error indicates that dns server refuses to perform operation. */
+#define DNS_HEADER_FLAG2_RCODE_MASK 0x0F /**< Bit mask of RCODE field. */
+
+#define DNS_QTYPE_A 0x0001 /**< QTYPE indicates IPv4 address. */
+#define DNS_QTYPE_CNAME 0x0005 /**< QTYPE indicates CNAME record. */
+#define DNS_QTYPE_AAAA 0x001C /**< QTYPE indicates IPv6 address. */
+
+#define DNS_QCLASS_IN 0x0001 /**< QCLASS indicates Internet type. */
+
+/**@brief DNS6 client module's defines. */
+#define DNS_LABEL_SEPARATOR '.' /**< Separator of hostname string. */
+#define DNS_LABEL_OFFSET 0xc0 /**< Byte indicates that offset is used to determine hostname. */
+
+#define DNS_HEADER_SIZE 12 /**< Size of DNS Header. */
+#define DNS_QUESTION_FOOTER_SIZE 4 /**< Size of DNS Question footer. */
+#define DNS_RR_BODY_SIZE 10 /**< Size of DNS Resource Record Body. */
+
+#define MESSAGE_ID_UNUSED 0 /**< Value indicates that record is unused and no request was performed yet. */
+#define MESSAGE_ID_INITIAL 0x0001 /**< Initial value of message id counter. */
+
+
+/**@brief DNS Header Format. */
+typedef struct
+{
+ uint16_t msg_id; /**< Query/Response message identifier. */
+ uint8_t flags_1; /**< Flags ( QR | Opcode | AA | TC | RD ). */
+ uint8_t flags_2; /**< Flags ( RA | Z | RCODE ). */
+ uint16_t qdcount; /**< The number of entries in the question section. */
+ uint16_t ancount; /**< The number of resource records in the answer section. */
+ uint16_t nscount; /**< The number of name server resource records in the authority records section. */
+ uint16_t arcount; /**< The number of resource records in the additional records section. */
+} dns_header_t;
+
+/**@brief DNS Question Footer Format. */
+typedef struct
+{
+ uint16_t qtype; /**< Type of the query. */
+ uint16_t qclass; /**< Class of the query. */
+} dns_question_footer_t;
+
+/**@brief DNS Resource AAAA Record Body Format. */
+typedef struct
+{
+ uint16_t rtype; /**< Type of the response. */
+ uint16_t rclass; /**< Class of the response. */
+ uint32_t rttl; /**< Time to Life field of the response. */
+ uint16_t rdlength; /**< Length of data in octets. */
+} dns_rr_body_t;
+
+/**@brief Structure holds pending query. */
+typedef struct
+{
+ uint16_t message_id; /**< Message id for DNS Query. */
+ uint8_t retries; /**< Number of already performed retries. */
+ uint8_t * p_hostname; /**< Pointer to hostname string in memory menager.*/
+ iot_timer_time_in_ms_t next_retransmission; /**< Time when next retransmission should be invoked. */
+ dns6_evt_handler_t evt_handler; /**< User registered callback. */
+} pending_query_t;
+
+SDK_MUTEX_DEFINE(m_dns6_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 pending_query_t m_pending_queries[DNS6_MAX_PENDING_QUERIES]; /**< Queue contains pending queries. */
+static uint16_t m_message_id_counter; /**< Message ID counter, used to generate unique message IDs. */
+static udp6_socket_t m_socket; /**< Socket information provided by UDP. */
+
+
+/**@brief Function for freeing query entry in pending queue.
+ *
+ * @param[in] index Index of query.
+ *
+ * @retval None.
+ */
+static void query_init(uint32_t index)
+{
+ if (m_pending_queries[index].p_hostname)
+ {
+ UNUSED_VARIABLE(nrf_free(m_pending_queries[index].p_hostname));
+ }
+
+ m_pending_queries[index].message_id = MESSAGE_ID_UNUSED;
+ m_pending_queries[index].retries = 0;
+ m_pending_queries[index].p_hostname = NULL;
+ m_pending_queries[index].evt_handler = NULL;
+ m_pending_queries[index].next_retransmission = 0;
+}
+
+
+/**@brief Function for adding new query to pending queue.
+ *
+ * @param[in] p_hostname Pointer to hostname string.
+ * @param[in] evt_handler User defined event to handle given query.
+ *
+ * @retval Index of element in pending queries' table or DNS6_MAX_PENDING_QUERIES if no memory.
+ */
+static uint32_t query_add(uint8_t * p_hostname, dns6_evt_handler_t evt_handler)
+{
+ uint32_t index;
+
+ for (index = 0; index < DNS6_MAX_PENDING_QUERIES; index++)
+ {
+ if (m_pending_queries[index].message_id == MESSAGE_ID_UNUSED)
+ {
+ m_pending_queries[index].message_id = m_message_id_counter++;
+ m_pending_queries[index].retries = 0;
+ m_pending_queries[index].p_hostname = p_hostname;
+ m_pending_queries[index].evt_handler = evt_handler;
+ m_pending_queries[index].next_retransmission = 0;
+
+ break;
+ }
+ }
+
+ return index;
+}
+
+
+/**@brief Function for finding element in pending queue with specific message_id.
+ *
+ * @param[in] message_id Message identifier to find.
+ *
+ * @retval Index of element in pending queue or DNS6_MAX_PENDING_QUERIES if nothing found.
+ */
+static uint32_t query_find(uint32_t message_id)
+{
+ uint32_t index;
+
+ for (index = 0; index < DNS6_MAX_PENDING_QUERIES; index++)
+ {
+ if (m_pending_queries[index].message_id == message_id)
+ {
+ break;
+ }
+ }
+
+ return index;
+}
+
+
+/**@brief Function for checking if retransmission time of DNS query has been expired.
+ *
+ * @param[in] index Index of pending query.
+ *
+ * @retval True if timer has been expired, False otherwise.
+ */
+static bool query_timer_is_expired(uint32_t index)
+{
+ uint32_t err_code;
+ iot_timer_time_in_ms_t wall_clock_value;
+
+ // Get wall clock time.
+ err_code = iot_timer_wall_clock_get(&wall_clock_value);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ if (wall_clock_value >= m_pending_queries[index].next_retransmission)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/**@brief Function for setting retransmissions time of DNS query has been expired.
+ *
+ * @param[in] index Index of pending query.
+ *
+ * @retval None.
+ */
+static void query_timer_set(uint32_t index)
+{
+ uint32_t err_code;
+ iot_timer_time_in_ms_t wall_clock_value;
+
+ // Get wall clock time.
+ err_code = iot_timer_wall_clock_get(&wall_clock_value);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ m_pending_queries[index].next_retransmission =
+ wall_clock_value + (DNS6_RETRANSMISSION_INTERVAL * 1000);
+ }
+}
+
+
+/**@brief Function for creating compressed hostname from string.
+ *
+ * @param[inout] p_dest Pointer to place where hostname will be compressed.
+ * @param[in] p_hostname Pointer to hostname string.
+ *
+ * @retval Number of used bytes to compress a hostname.
+ */
+static uint32_t compress_hostname(uint8_t * p_dest, const uint8_t * p_hostname)
+{
+ uint32_t index = 0;
+ uint32_t label_pos = 0;
+ uint8_t * p_original = p_dest;
+
+ // Elide first byte in destination buffer to put label.
+ p_dest++;
+
+ // Parse until string termination is found.
+ for (index = 0; p_hostname[index] != 0; index++)
+ {
+ // Look for string separator.
+ if (p_hostname[index] == DNS_LABEL_SEPARATOR)
+ {
+ // Put number of subsequent string to last label.
+ p_original[label_pos] = index - label_pos;
+
+ // Protection to stop compressing after getting incorrect sequence.
+ if (index == label_pos)
+ {
+ return index + 1;
+ }
+
+ label_pos = index + 1;
+ }
+ else
+ {
+ // Copy character of hostname to destination buffer.
+ *p_dest = p_hostname[index];
+ }
+
+ p_dest++;
+ }
+
+ // Set last label.
+ p_original[label_pos] = index - label_pos;
+
+ // Terminate compressed hostname with 0.
+ *p_dest = 0;
+
+ // Return length of compressed string.
+ return index + 2;
+}
+
+
+/**@brief Function for finding end of compressed hostname.
+ *
+ * @param[in] p_hostname Pointer to compressed hostname string.
+ *
+ * @retval Pointer to the end of compressed hostname.
+ */
+static uint8_t * skip_compressed_hostname(uint8_t * p_hostname)
+{
+ while (*p_hostname != 0)
+ {
+ if ((*p_hostname & DNS_LABEL_OFFSET) == DNS_LABEL_OFFSET)
+ {
+ return p_hostname + 2;
+ }
+ else
+ {
+ p_hostname += *p_hostname + 1;
+ }
+ }
+
+ return p_hostname + 1;
+}
+
+
+/**@brief Function for sending DNS query.
+ *
+ * @param[in] index Index of query.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static uint32_t query_send(uint32_t index)
+{
+ uint32_t length;
+ uint32_t err_code;
+ iot_pbuffer_t * p_buffer;
+ iot_pbuffer_alloc_param_t buffer_param;
+
+ buffer_param.type = UDP6_PACKET_TYPE;
+ buffer_param.flags = PBUFFER_FLAG_DEFAULT;
+ buffer_param.length = DNS_HEADER_SIZE + DNS_QUESTION_FOOTER_SIZE +
+ strlen((const char *)m_pending_queries[index].p_hostname) + 2;
+
+ // Allocate packet buffer.
+ err_code = iot_pbuffer_allocate(&buffer_param, &p_buffer);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ const dns_question_footer_t question_footer =
+ {
+ .qtype = HTONS(DNS_QTYPE_AAAA),
+ .qclass = HTONS(DNS_QCLASS_IN)
+ };
+
+ dns_header_t * p_dns_header = (dns_header_t *)p_buffer->p_payload;
+
+ // Fill DNS header fields.
+ p_dns_header->msg_id = HTONS(m_pending_queries[index].message_id);
+ p_dns_header->flags_1 = DNS_HEADER_FLAG1_QR_QUERY | DNS_HEADER_FLAG1_RD;
+ p_dns_header->flags_2 = DNS_HEADER_FLAG2_RCODE_NONE;
+
+ // Send only one question.
+ p_dns_header->qdcount = HTONS(1);
+ p_dns_header->ancount = HTONS(0);
+ p_dns_header->nscount = HTONS(0);
+ p_dns_header->arcount = HTONS(0);
+
+ // Start indexing from the end of the DNS header.
+ length = DNS_HEADER_SIZE;
+
+ // Compress and put hostname.
+ length += compress_hostname(&p_buffer->p_payload[length],
+ m_pending_queries[index].p_hostname);
+
+ // Add question footer.
+ memcpy(&p_buffer->p_payload[length], (uint8_t *)&question_footer, DNS_QUESTION_FOOTER_SIZE);
+
+ length += DNS_QUESTION_FOOTER_SIZE;
+
+ // Update packet buffer's data length.
+ p_buffer->length = length;
+
+ // Set retransmission timer.
+ query_timer_set(index);
+
+ // Send DNS query using UDP socket.
+ err_code = udp6_socket_send(&m_socket, p_buffer);
+
+ if (err_code != NRF_SUCCESS)
+ {
+ DNS6_ERR("Unable to send query on UDP socket. Reason %08lx.", err_code);
+
+ // Free the allocated buffer as send procedure has failed.
+ UNUSED_VARIABLE(iot_pbuffer_free(p_buffer, true));
+ }
+ }
+ else
+ {
+ DNS6_ERR("No memory to allocate packet buffer.");
+ }
+
+ return err_code;
+}
+
+
+/**@brief Function for notifying application of the DNS6 query status.
+ *
+ * @param[in] index Index of query.
+ * @param[in] process_result Variable indicates result of DNS query.
+ * @param[in] p_addr Pointer to memory that holds IPv6 addresses.
+ * @param[in] addr_count Number of found addresses.
+ *
+ * @retval None.
+ */
+static void app_notify(uint32_t index,
+ uint32_t process_result,
+ ipv6_addr_t * p_addr,
+ uint16_t addr_count)
+{
+ if (m_pending_queries[index].evt_handler)
+ {
+ DNS6_MUTEX_UNLOCK();
+
+ // Call handler of user request.
+ m_pending_queries[index].evt_handler(process_result,
+ (const char *)m_pending_queries[index].p_hostname,
+ p_addr,
+ addr_count);
+
+ DNS6_MUTEX_LOCK();
+ }
+}
+
+
+/**@brief Callback handler to receive data on the UDP port.
+ *
+ * @param[in] p_socket Socket identifier.
+ * @param[in] p_ip_header IPv6 header containing source and destination addresses.
+ * @param[in] p_udp_header UDP header identifying local and remote endpoints.
+ * @param[in] process_result Result of data reception, there could be possible errors like
+ * invalid checksum etc.
+ * @param[in] p_rx_packet Packet buffer containing the received data packet.
+ *
+ * @retval NRF_SUCCESS Indicates received data was handled successfully, else an an
+ * error code indicating reason for failure..
+ */
+static uint32_t server_response(const udp6_socket_t * p_socket,
+ const ipv6_header_t * p_ip_header,
+ const udp6_header_t * p_udp_header,
+ uint32_t process_result,
+ iot_pbuffer_t * p_rx_packet)
+{
+ uint32_t index;
+ uint32_t rr_index;
+ uint32_t err_code = NRF_SUCCESS;
+ ipv6_addr_t * p_addresses = NULL;
+ uint16_t addr_length = 0;
+
+ DNS6_MUTEX_LOCK();
+
+ DNS6_ENTRY();
+
+ // Check UDP process result and data length.
+ if ((process_result != NRF_SUCCESS) || p_rx_packet->length < DNS_HEADER_SIZE)
+ {
+ DNS6_ERR("Received erroneous response.");
+ err_code = (NRF_ERROR_INVALID_DATA | IOT_DNS6_ERR_BASE);
+ }
+ else
+ {
+ dns_header_t * p_dns_header = (dns_header_t *)p_rx_packet->p_payload;
+ uint8_t * p_data = &p_rx_packet->p_payload[DNS_HEADER_SIZE];
+ uint16_t qdcount = NTOHS(p_dns_header->qdcount);
+ uint16_t ancount = NTOHS(p_dns_header->ancount);
+
+ // Try to find a proper query for this response, else discard.
+ index = query_find(NTOHS(p_dns_header->msg_id));
+
+ if (index != DNS6_MAX_PENDING_QUERIES)
+ {
+ DNS6_TRC("Received response for hostname %s with %d answers.",
+ m_pending_queries[index].p_hostname, ancount);
+
+ // Check truncation error.
+ if (p_dns_header->flags_1 & DNS_HEADER_FLAG1_TC)
+ {
+ err_code = DNS6_RESPONSE_TRUNCATED;
+ }
+ else if (!(p_dns_header->flags_1 & DNS_HEADER_FLAG1_QR_RESPONSE))
+ {
+ err_code = (NRF_ERROR_INVALID_DATA | IOT_DNS6_ERR_BASE);
+ }
+ // Check response code.
+ else if (p_dns_header->flags_2 & DNS_HEADER_FLAG2_RCODE_MASK)
+ {
+ switch (p_dns_header->flags_2 & DNS_HEADER_FLAG2_RCODE_MASK)
+ {
+ case DNS_HEADER_FLAG2_RCODE_FORMAT_ERROR:
+ err_code = DNS6_FORMAT_ERROR;
+ break;
+
+ case DNS_HEADER_FLAG2_RCODE_SERVER_FAILURE:
+ err_code = DNS6_SERVER_FAILURE;
+ break;
+
+ case DNS_HEADER_FLAG2_RCODE_NAME_ERROR:
+ err_code = DNS6_HOSTNAME_NOT_FOUND;
+ break;
+
+ case DNS_HEADER_FLAG2_RCODE_NOT_IMPLEMENTED:
+ err_code = DNS6_NOT_IMPLEMENTED;
+ break;
+
+ case DNS_HEADER_FLAG2_RCODE_REFUSED:
+ err_code = DNS6_REFUSED_ERROR;
+ break;
+
+ default:
+ err_code = (NRF_ERROR_INVALID_DATA | IOT_DNS6_ERR_BASE);
+ break;
+ }
+ }
+ else if (ancount == 0)
+ {
+ // No answer found.
+ err_code = DNS6_HOSTNAME_NOT_FOUND;
+ }
+ else
+ {
+ dns_rr_body_t rr;
+
+ // Skip questions section.
+ for (rr_index = 0; rr_index < qdcount; rr_index++)
+ {
+ p_data = skip_compressed_hostname(p_data) + DNS_QUESTION_FOOTER_SIZE;
+ }
+
+ // Addresses are moved to beginning of the packet to ensure alignment is correct.
+ p_addresses = (ipv6_addr_t *)p_rx_packet->p_payload;
+
+ // Parse responses section.
+ for (rr_index = 0; rr_index < ancount; rr_index++)
+ {
+ p_data = skip_compressed_hostname(p_data);
+
+ // Fill resource record structure to fit alignment.
+ memcpy((uint8_t *)&rr, p_data, DNS_RR_BODY_SIZE);
+
+ if (NTOHS(rr.rtype) == DNS_QTYPE_AAAA && NTOHS(rr.rclass) == DNS_QCLASS_IN)
+ {
+ if (NTOHS(rr.rdlength) == IPV6_ADDR_SIZE)
+ {
+ DNS6_TRC("Found AAAA record with IPv6 address:");
+ DNS6_DUMP(p_data + DNS_RR_BODY_SIZE, IPV6_ADDR_SIZE);
+
+ // Move all addresses next to each other.
+ memmove(p_addresses[addr_length].u8,
+ p_data + DNS_RR_BODY_SIZE,
+ IPV6_ADDR_SIZE);
+
+ addr_length++;
+ }
+ }
+
+ p_data += DNS_RR_BODY_SIZE + NTOHS(rr.rdlength);
+ }
+
+ if (addr_length == 0)
+ {
+ DNS6_ERR("No IPv6 addresses was found.");
+
+ err_code = DNS6_HOSTNAME_NOT_FOUND;
+ }
+ }
+
+ // Notify application.
+ app_notify(index, err_code, p_addresses, addr_length);
+
+ // Initialize query entry.
+ query_init(index);
+ }
+ else
+ {
+ DNS6_ERR("Response with unknown message id.");
+ err_code = (NRF_ERROR_NOT_FOUND | IOT_DNS6_ERR_BASE);
+ }
+ }
+
+ DNS6_EXIT();
+
+ DNS6_MUTEX_UNLOCK();
+
+ return err_code;
+}
+
+
+uint32_t dns6_init(const dns6_init_t * p_dns_init)
+{
+ NULL_PARAM_CHECK(p_dns_init);
+
+ uint32_t index;
+ uint32_t err_code;
+
+ DNS6_ENTRY();
+
+ SDK_MUTEX_INIT(m_dns6_mutex);
+
+ DNS6_MUTEX_LOCK();
+
+ for (index = 0; index < DNS6_MAX_PENDING_QUERIES; index++)
+ {
+ query_init(index);
+ }
+
+ // Request new socket creation.
+ err_code = udp6_socket_allocate(&m_socket);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ // Bind the socket to the local port.
+ err_code = udp6_socket_bind(&m_socket, IPV6_ADDR_ANY, p_dns_init->local_src_port);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ // Connect to DNS server.
+ err_code = udp6_socket_connect(&m_socket,
+ &p_dns_init->dns_server.addr,
+ p_dns_init->dns_server.port);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ // Register data receive callback.
+ err_code = udp6_socket_recv(&m_socket, server_response);
+ }
+ }
+
+ if (err_code == NRF_SUCCESS)
+ {
+ DNS6_TRC("Module initialization is complete.");
+
+ // Set initialization state flag if all procedures succeeded.
+ m_initialization_state = true;
+ m_message_id_counter = 0x0001;
+ }
+ else
+ {
+ DNS6_ERR("UDP socket initialization failed. Reason %08lx.", err_code);
+
+ // Not all procedures succeeded with allocated socket, hence free it.
+ UNUSED_VARIABLE(udp6_socket_free(&m_socket));
+ }
+ }
+
+ DNS6_EXIT();
+
+ DNS6_MUTEX_UNLOCK();
+
+ return err_code;
+}
+
+
+uint32_t dns6_uninit(void)
+{
+ VERIFY_MODULE_IS_INITIALIZED();
+
+ uint32_t index;
+
+ DNS6_ENTRY();
+
+ DNS6_MUTEX_LOCK();
+
+ for (index = 0; index < DNS6_MAX_PENDING_QUERIES; index++)
+ {
+ query_init(index);
+ }
+
+ // Free UDP socket.
+ UNUSED_VARIABLE(udp6_socket_free(&m_socket));
+
+ // Clear initialization state flag.
+ m_initialization_state = false;
+
+ DNS6_EXIT();
+
+ DNS6_MUTEX_UNLOCK();
+
+ return NRF_SUCCESS;
+}
+
+
+uint32_t dns6_query(const char * p_hostname, dns6_evt_handler_t evt_handler)
+{
+ VERIFY_MODULE_IS_INITIALIZED();
+ NULL_PARAM_CHECK(evt_handler);
+ NULL_PARAM_CHECK(p_hostname);
+ EMPTY_PARAM_CHECK(p_hostname);
+
+ uint32_t index;
+ uint32_t err_code;
+ uint32_t hostname_length;
+ uint8_t * p_hostname_buff = NULL;
+
+ DNS6_ENTRY();
+
+ DNS6_MUTEX_LOCK();
+
+ // Calculate hostname length.
+ hostname_length = strlen(p_hostname) + 1;
+
+ // Allocate memory to make copy of hostname string.
+ err_code = nrf_mem_reserve(&p_hostname_buff, &hostname_length);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ // Copy hostname to cache buffer.
+ strcpy((char *)p_hostname_buff, p_hostname);
+
+ // Add query to pending queue.
+ index = query_add(p_hostname_buff, evt_handler);
+
+ if (index != DNS6_MAX_PENDING_QUERIES)
+ {
+ // Create and send DNS Query.
+ err_code = query_send(index);
+
+ if (err_code != NRF_SUCCESS)
+ {
+ // Remove query from pending queue immediately.
+ query_init(index);
+ }
+ }
+ else
+ {
+ DNS6_ERR("No place in pending queue.");
+
+ // No place in pending queue.
+ err_code = (NRF_ERROR_NO_MEM | IOT_DNS6_ERR_BASE);
+ }
+
+ // Not all procedures succeeded with sending query, hence free buffer for hostname.
+ if (err_code != NRF_SUCCESS)
+ {
+ UNUSED_VARIABLE(nrf_free(p_hostname_buff));
+ }
+ }
+ else
+ {
+ DNS6_ERR("No memory to allocate buffer for hostname.");
+ }
+
+ DNS6_EXIT();
+
+ DNS6_MUTEX_UNLOCK();
+
+ return err_code;
+}
+
+
+void dns6_timeout_process(iot_timer_time_in_ms_t wall_clock_value)
+{
+ uint32_t index;
+ uint32_t err_code;
+
+ UNUSED_PARAMETER(wall_clock_value);
+
+ DNS6_ENTRY();
+
+ DNS6_MUTEX_LOCK();
+
+ for (index = 0; index < DNS6_MAX_PENDING_QUERIES; index++)
+ {
+ if (m_pending_queries[index].message_id != MESSAGE_ID_UNUSED)
+ {
+ if (query_timer_is_expired(index))
+ {
+ err_code = NRF_SUCCESS;
+
+ if (m_pending_queries[index].retries < DNS6_MAX_RETRANSMISSION_COUNT)
+ {
+ DNS6_TRC("Query retransmission [%d] for hostname %s.",
+ m_pending_queries[index].retries, m_pending_queries[index].p_hostname);
+
+ // Increase retransmission number.
+ m_pending_queries[index].retries++;
+
+ // Send query again.
+ err_code = query_send(index);
+ }
+ else
+ {
+ DNS6_ERR("DNS server did not response on query for hostname %s.",
+ m_pending_queries[index].p_hostname);
+
+ // No response from server.
+ err_code = DNS6_SERVER_UNREACHABLE;
+ }
+
+ if (err_code != NRF_SUCCESS)
+ {
+ // Inform application that timeout occurs.
+ app_notify(index, err_code, NULL, 0);
+
+ // Remove query from pending queue.
+ query_init(index);
+ }
+ }
+ break;
+ }
+ }
+
+ DNS6_EXIT();
+
+ DNS6_MUTEX_UNLOCK();
+}
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;
+}
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.h
new file mode 100644
index 0000000..7ce517b
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.h
@@ -0,0 +1,101 @@
+/**
+ * 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.
+ *
+ */
+/** @cond */
+ /** @file icmp6.h
+ *
+ * @defgroup iot_icmp6 ICMP Module Header.
+ * @ingroup iot_sdk
+ * @{
+ * @brief Internet Control Message Protocol module header defining interface between the ICMP6
+ * module and other IP stack layers. This interface is internal to the IPv6 stack and not
+ * available to the application. The application shall not explicitly use this interface.
+ *
+ */
+
+#ifndef ICMP6_H__
+#define ICMP6_H__
+
+#include "sdk_config.h"
+#include "sdk_common.h"
+#include "ipv6_api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Module initialization function called from ipv6_init(). This shall not be called by the
+ * application explicitly.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+uint32_t icmp6_init(void);
+
+
+/**
+ * @brief Function to feed incoming ICMP packets to the module. To be called by the IPv6
+ * stack only and never by the application.
+ *
+ * @param[in] p_interface Identifies network interface on which the packet is received.
+ * @param[in] p_ip_header IP Header of the ICMP packet being fed to the module.
+ * @param[in] p_packet ICMP packet being notified to the module. p_packet->p_payload points the
+ * IPv6 payload and p_packet->length indicates total length of the payload.
+ *
+ * @note This routine is called by the stack with next header field value is set to transport
+ * protocol value of 58.
+ *
+ * @retval NRF_SUCCESS on successful handling of the packet, else an error code indicating reason
+ * for failure.
+ */
+uint32_t icmp6_input(iot_interface_t * p_interface,
+ ipv6_header_t * p_ip_header,
+ iot_pbuffer_t * p_packet);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //ICMP6_H__
+
+/**@} */
+
+/** @endcond */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/dns6_api.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/dns6_api.h
new file mode 100644
index 0000000..0f6ecd9
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/dns6_api.h
@@ -0,0 +1,173 @@
+/**
+ * Copyright (c) 2015 - 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.
+ *
+ */
+/** @file dns6_api.h
+ *
+ * @defgroup iot_dns6 DNS Application Interface for Nordic's IPv6 stack
+ * @ingroup iot_sdk_stack
+ * @{
+ * @brief Domain Name System module provides implementation of DNS6 service.
+ *
+ */
+
+#ifndef DNS6_H__
+#define DNS6_H__
+
+#include "sdk_config.h"
+#include "sdk_common.h"
+#include "ipv6_api.h"
+#include "iot_timer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @brief DNS Server parameter. */
+typedef struct
+{
+ ipv6_addr_t addr; /**< The IPv6 address of the DNS Server. */
+ uint16_t port; /**< The default UDP port of the DNS Server. */
+} dns6_server_param_t;
+
+
+/**@brief Initialization parameters type. */
+typedef struct
+{
+ uint16_t local_src_port; /**< The local UDP port for reception the DNS responses. */
+ dns6_server_param_t dns_server; /**< Parameters of the DNS Server. */
+} dns6_init_t;
+
+
+/**
+ * @brief DNS event receive callback.
+ *
+ * @details API used to notify the application of DNS Response on specific hostname or of an error
+ * during resolving process. The process_result parameter indicates whether the DNS module
+ * was successfully processed. If the received DNS Response is malformed in a way that
+ * allow to assign response with specific callback (e.g. timeout occurs or hostname is not
+ * found), information about error is still notified to the application. The application
+ * should check process_result and number of IPv6 address before reading them.
+ *
+ * @param[in] process_result Notifies the application if the DNS module was processed successfully
+ * or if an error occurred, for example DNS server is unreachable.
+ * @param[in] p_hostname Identifies hostname (URL string) that was requested to bee resolved,
+ * e.g. "example.com".
+ * @param[in] p_addr Pointer to the IPv6 addresses being resolved for given hostname. In
+ * case addr_count variable is 0, p_addr gets NULL value and should not
+ * be used.
+ * @param[in] addr_count Number of IPv6 addresses being successfully resolved.
+ *
+ * @retval None.
+ */
+typedef void (* dns6_evt_handler_t) (uint32_t process_result,
+ const char * p_hostname,
+ ipv6_addr_t * p_addr,
+ uint16_t addr_count);
+
+/**
+ * @brief Function for initializing DNS6 module.
+ *
+ * @param[in] p_dns_init Initialization structure for DNS client. Should not be NULL.
+ *
+ * @note DNS protocol module uses UDP transport layer, therefore one UDP socket is allocated
+ * inside function and uses for further communication.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+uint32_t dns6_init(const dns6_init_t * p_dns_init);
+
+
+/**
+ * @brief Function for uninitializing DNS6 module.
+ *
+ * @note Apart of DNS specific functionality, function frees previously allocated UDP socket.
+ * Function removes any pending queries from the sending queue, so registered user
+ * callbacks will not be executed.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+uint32_t dns6_uninit(void);
+
+
+/**
+ * @brief Function for querying given URL string to DNS server, in order to get IPv6 address of
+ * given hostname.
+ *
+ * @param[in] p_hostname Identifies hostname (URL string) to be find, e.g. "example.com". Should
+ * not be NULL.
+ * @param[in] evt_handler Callback that is called once response is received, timeout occurred or
+ * internal error was detected. Should not be NULL.
+ *
+ * @note Function sends DNS Query to DNS Server to obtain all AAAA records (with IPv6 address)
+ * assigned to given hostname. In case DNS Server replies with more that one AAAA records
+ * DNS module call user defined evt_handler with addr_count indicates number of addresses.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure.
+ * @retval IOT_DNS6_ERR_BASE | NRF_ERROR_NO_MEM if there is no place in pending queries' queue.
+ * @retval IOT_PBUFFER_ERR_BASE | NRF_ERROR_NO_MEM if there is no memory for hostname allocation.
+ * @retval NRF_ERROR_MEMORY_MANAGER_ERR_BASE | NRF_ERROR_NO_MEM if there is no memory for packet allocation.
+ * @retval UDP_INTERFACE_NOT_READY if interface is not ready for sending packets e.g. interface is
+ * down.
+ * @retval Other errors indicates reason of failure.
+ */
+uint32_t dns6_query(const char * p_hostname, dns6_evt_handler_t evt_handler);
+
+
+/**@brief Function for performing retransmissions of DNS queries.
+ *
+ * @note DNS module implements the retransmission mechanism by invoking this function periodically.
+ * So that method has to be added to IoT Timer client list and has to be called with minimum of
+ * DNS6_RETRANSMISSION_INTERVAL resolution.
+ *
+ * @param[in] wall_clock_value The value of the wall clock that triggered the callback.
+ *
+ * @retval None.
+ */
+void dns6_timeout_process(iot_timer_time_in_ms_t wall_clock_value);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //DNS6_H__
+
+/**@} */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/icmp6_api.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/icmp6_api.h
new file mode 100644
index 0000000..b13b4b6
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/icmp6_api.h
@@ -0,0 +1,253 @@
+/**
+ * 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.
+ *
+ */
+/** @file icmp6_api.h
+ *
+ * @defgroup iot_icmp6 ICMP6 Application Interface for Nordic's IPv6 stack
+ * @ingroup iot_sdk_stack
+ * @{
+ * @brief Nordic Internet Control Message Protocol Application Interface for Nordic's IPv6 stack.
+ *
+ * @details This module provides basic features related to ICMPv6 support.
+ */
+
+#ifndef ICMP6_API_H__
+#define ICMP6_API_H__
+
+#include "sdk_config.h"
+#include "sdk_common.h"
+#include "iot_defines.h"
+#include "ipv6_api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ICMP6_ECHO_REQUEST_PAYLOAD_OFFSET 8 /**< Offset of echo request payload from ICMPv6 header. */
+
+
+/**@defgroup icmp6_code ICMPv6 codes per message type as defined in RFC 4443.
+ * @ingroup iot_icmp6
+ * @{
+ */
+#define ICMP6_DU_CODE_NO_ROUTE_TO_DESTINATION 0 /**< Code for Destination Unreachable Message when there is no route to destination. */
+#define ICMP6_DU_CODE_ADMINISTRATIVELY_PROHIBITED 1 /**< Code for Destination Unreachable Message when the communication to destination administratively prohibited. */
+#define ICMP6_DU_CODE_BEYOND_SCOPE_OF_SOURCE 2 /**< Code for Destination Unreachable Message when the destination is beyond the scope of source address. */
+#define ICMP6_DU_CODE_ADDRESS_UNREACHABLE 3 /**< Code for Destination Unreachable Message when the destination address is unreachable. */
+#define ICMP6_DU_CODE_PORT_UNREACHABLE 4 /**< Code for Destination Unreachable Message when the destination port is unreachable. */
+#define ICMP6_DU_CODE_FAILED_INGRESS_EGRESS_POLICY 5 /**< Code for Destination Unreachable Message when the source address failed on ingress/egress policy. */
+#define ICMP6_DU_CODE_REJECT_ROUTE_TO_DESTINATION 6 /**< Code for Destination Unreachable Message when the route to destination is rejected. */
+
+#define ICMP6_TE_CODE_EXCEEDED_HOP_LIMIT_TRANSIT 0 /**< Code for Time Exceeded Message when the packet received had hop limit of zero. */
+#define ICMP6_TE_CODE_FAR_TIME_EXCEEDED 1 /**< Code for Time Exceeded Message to report fragmentation and reassembly timeout. */
+
+#define ICMP6_PP_CODE_INVALID_HEADER 0 /**< Code for Parameter Problem Message when the header of the incoming packet was erroneous. */
+#define ICMP6_PP_CODE_UNKNOWN_NEXT_HEADER 1 /**< Code for Parameter Problem Message when the next header of the incoming packet was unrecognized. */
+#define ICMP6_PP_CODE_UNKNOWN_IPV6_OPTION 2 /**< Code for Parameter Problem Message when the option of the incoming packet was unrecognized. */
+
+#define ICMP6_ERROR_MESSAGE_INVOKING_PKT_OFFSET 8 /**< Offset in the received ICMPv6 payload where the packet (partial or complete) that invoked the error message is found. */
+/* @} */
+
+/** Neighbor solicitation parameters. */
+typedef struct
+{
+ ipv6_addr_t target_addr; /**< The IPv6 address of the target of the solicitation. MUST NOT be a multi-cast address. */
+ bool add_aro; /**< Indicates if ARO option should be added. */
+ uint16_t aro_lifetime; /**< The amount of time in units of 60 seconds that the router should retain the NCE for the sender of the NS. */
+} icmp6_ns_param_t;
+
+/**@brief Parameters associated with error message in receive and transmit paths. */
+typedef struct
+{
+ uint8_t type; /**< Identifies error message type, valid values as described in RFC 4443. See @ref icmp6_error_type for possible values. */
+ uint8_t code; /**< Identifies code, if any associated with the error. See \ref icmp6_code and RFC 4443 for details. */
+ union /**< Additional field like MTU, pointer or unused based on message type. See RFC 4443 for more details. If unused, application may ignore this field. */
+ {
+ uint32_t mtu; /**< MTU of next hop limit, used only with ICMP6_TYPE_PACKET_TOO_LONG type. */
+ uint32_t offset; /**< Offset pointing to the parameter that resulted in the ICMP6_TYPE_PARAMETER_PROBLEM error. Used only with ICMP6_TYPE_PARAMETER_PROBLEM. */
+ uint32_t unused; /**< Any other error message. Is always zero. */
+ } error_field;
+ uint8_t * p_packet; /**< Points to the start of IPv6 packet that has resulted in the error message. */
+ uint16_t packet_len; /**< Length of the packet that resulted in error. The module may truncate the packet and pack only partially the packet based on configuration of ICMP6_ERROR_MESSAGE_MAX_SIZE. */
+} icmp6_error_message_param_t;
+
+
+/**@brief ICMPv6 data RX callback.
+ *
+ * @details Asynchronous callback used to notify the application of ICMP packets received.
+ * By default, the application is not notified through ICMP of messages related to ECHO
+ * requests or any errors. However, these notifications can easily be enabled by defining
+ * either the ICMP6_ENABLE_ND6_MESSAGES_TO_APPLICATION or the
+* ICMP6_ENABLE_ALL_MESSAGES_TO_APPLICATION if the application should handle them.
+ *
+ * @param[in] p_interface Pointer to the IPv6 interface from where the ICMP packet was received.
+ * @param[in] p_ip_header Pointer to the IP header of the ICMP packet received.
+ * @param[in] p_icmp_header Pointer to the ICMP header of the received packet.
+ * @param[in] process_result Notifies the application if the ICMP packet was processed successfully or if
+ * an error occurred, for example, if the packet was malformed.
+ * @param[in] p_rx_packet Packet buffer containing the packet received. p_rx_packet->p_payload
+ * contains the ICMP payload.
+ *
+ * @retval A provision for the application to notify the module of whether the received packet was
+ * processed successfully by application. The application may take ownership of the received
+ * packet by returning IOT_IPV6_ERR_PENDING, in which case the application must take care to
+ * free it using @ref iot_pbuffer_free.
+ */
+typedef uint32_t (*icmp6_receive_callback_t)(iot_interface_t * p_interface,
+ ipv6_header_t * p_ip_header,
+ icmp6_header_t * p_icmp_header,
+ uint32_t process_result,
+ iot_pbuffer_t * p_rx_packet);
+
+
+/**@brief Sends ICMPv6 Error Message as defined in RFC 4443.
+ *
+ * @details API to send messages categorized under error messages. See @ref icmp6_error_type and
+ * RFC 4443 for valid types.
+ *
+ * @param[in] p_interface Identifies the interface on which the procedure was requested.
+ * Shall not be NULL.
+ * @param[in] p_src_addr Source IPv6 address to be used for the request. Shall not be NULL.
+ * @param[in] p_dest_addr Destination IPv6 address to which the message send is requested.
+ * Shall not be NULL.
+ * @param[in] p_param Parameters describing Type, code, invoking packet information any
+ * additional details associated with the error message.
+ *
+ * @retval NRF_SUCCESS If the send request was successful, else, an error code indicating reason for
+ * failure.
+ */
+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);
+
+
+/**@brief Sends ICMPv6 echo request as defined in RFC4443.
+ *
+ * @details API used to send an ICMPv6 echo request packet to a specific destination address.
+ * The user can decide how much additional data must be sent.
+ *
+ * The application calling the function should allocate a packet before, with the type set to
+ * ICMP6_PACKET_TYPE in the allocation parameter.
+ *
+ * The application should pack the payload at ICMP6_ECHO_REQUEST_PAYLOAD_OFFSET. ID,
+ * Sequence number, ICMP Code, type checksum, etc. are filled in by the module.
+ *
+ * The application shall not free the allocated packet buffer if the procedure was successful,
+ * to ensure that no data copies are needed when transmitting a packet.
+ *
+ * @param[in] p_interface Pointer to the IPv6 interface to send the ICMP packet.
+ * @param[in] p_src_addr IPv6 source address from where the echo request is sent.
+ * @param[in] p_dest_addr IPv6 destination address to where the echo request is sent.
+ * @param[in] p_request Packet buffer containing the echo request.
+ *
+ * @retval NRF_SUCCESS If the send request was successful.
+ */
+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);
+
+
+/**@brief Sends router solicitation message defined in RFC6775.
+ *
+ * @details API used to send a neighbor discovery message of type Router Solicitation to a specific
+ * destination address. If no address is known, the user should send the message to all
+ * routers' address (FF02::1).
+ *
+ * The function internally tries to allocate a packet buffer. EUI-64 used in the SLLAO option is
+ * taken from the interface parameter defined in the @ref ipv6_init() function.
+ *
+ * @param[in] p_interface Pointer to the IPv6 interface to send the ICMP packet.
+ * @param[in] p_src_addr IPv6 source address from where the router solicitation message is
+ * sent.
+ * @param[in] p_dest_addr IPv6 destination address to where the router solicitation message is
+ * sent.
+ *
+ * @retval NRF_SUCCESS If the send request was successful.
+ */
+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);
+
+
+/**@brief Sends neighbour solicitation message defined in RFC6775.
+ *
+ * @details API used to send a neighbor discovery message of type Neighbor Solicitation to a
+ * specific destination address.
+ *
+ * The function internally tries to allocate a packet buffer. EUI-64 used in the SLLAO and ARO
+ * options is taken from the interface parameter defined in the @ref ipv6_init() function.
+ *
+ * @param[in] p_interface Pointer to the IPv6 interface to send the ICMP packet.
+ * @param[in] p_src_addr IPv6 source address from where the neighbor solicitation message is
+ * sent.
+ * @param[in] p_dest_addr IPv6 destination address to where the neighbor solicitation message
+ * is sent.
+ * @param[in] p_ns_param Neighbor discovery parameters.
+ *
+ * @retval NRF_SUCCESS If the send request was successful.
+ */
+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_ns_param);
+
+
+/**@brief Registers the callback function for echo reply.
+ *
+ * @details API used to register callback to indicate the ICMP echo reply packet. Could be not used.
+ *
+ * Neighbor discovery related messages are not relayed to the application by default.
+ * However, this can be enabled by using the ICMP6_ENABLE_ND6_MESSAGES_TO_APPLICATION
+ * configuration parameter.
+ *
+ * @param[in] cb Handler called when an ICMP packet is received.
+ *
+ * @retval NRF_SUCCESS If the registration was successful.
+ */
+uint32_t icmp6_receive_register(icmp6_receive_callback_t cb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //ICMP6_API_H__
+
+/**@} */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/ipv6_api.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/ipv6_api.h
new file mode 100644
index 0000000..33b8ec9
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/ipv6_api.h
@@ -0,0 +1,207 @@
+/**
+ * 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.
+ *
+ */
+/** @file ipv6_api.h
+ *
+ * @defgroup iot_ipv6 IPv6 Core Application Interface for Nordic's IPv6 stack
+ * @ingroup iot_sdk_stack
+ * @{
+ * @brief Nordic's IPv6 stack. Currently, only a Host role is supported.
+ *
+ * @details Nordic's IPv6 stack provides minimal implementations of ICMP, UDP for a Host, and
+ * IPv6 Neighbor Discovery for Host.
+ * Router, neighbor, and prefix cache are not maintained across BLE link disconnections or
+ * power cycles.
+ */
+
+#ifndef IPV6_API_H_
+#define IPV6_API_H_
+
+#include <stdint.h>
+#include "sdk_config.h"
+#include "iot_common.h"
+#include "iot_pbuffer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**@brief Asynchronous event identifiers type. */
+typedef enum
+{
+ IPV6_EVT_INTERFACE_ADD, /**< Notification of a new IPv6 interface added. */
+ IPV6_EVT_INTERFACE_DELETE, /**< Notification of IPv6 interface deleted. */
+ IPV6_EVT_INTERFACE_RX_DATA /**< Notification of IPv6 data, depending on configuration. For example, IPV6_ENABLE_USNUPORTED_PROTOCOLS_TO_APPLICATION. */
+} ipv6_event_id_t;
+
+/**@brief IPv6 address configuration. */
+typedef struct
+{
+ ipv6_addr_t addr;
+ ipv6_addr_state_t state;
+} ipv6_addr_conf_t;
+
+/**@brief Event parameters associated with the IPV6_EVT_INTERFACE_RX_DATA event. */
+typedef struct
+{
+ ipv6_header_t * p_ip_header; /**< IPv6 header of the packet. */
+ iot_pbuffer_t * p_rx_packet; /**< Packet buffer contains received data. */
+} ipv6_data_rx_t;
+
+/**@brief Asynchronous event parameter type. */
+typedef union
+{
+ ipv6_data_rx_t rx_event_param; /**< Parameters notified with the received IPv6 packet. */
+} ipv6_event_param_t;
+
+/**@brief Asynchronous event type. */
+typedef struct
+{
+ ipv6_event_id_t event_id; /**< Event identifier. */
+ ipv6_event_param_t event_param; /**< Event parameters. */
+} ipv6_event_t;
+
+/**@brief Asynchronous event notification callback type. */
+typedef void (* ipv6_evt_handler_t)(iot_interface_t * p_interface,
+ ipv6_event_t * p_event);
+
+/**@brief Initialization parameters type. */
+typedef struct
+{
+ eui64_t * p_eui64; /**< Global identifiers EUI-64 address of device. */
+ ipv6_evt_handler_t event_handler; /**< Asynchronous event notification callback registered to receive IPv6 events. */
+} ipv6_init_t;
+
+
+/**@brief Initializes the IPv6 stack module.
+ *
+ * @param[in] p_init Initialization parameters.
+ *
+ * @retval NRF_SUCCESS If initialization was successful. Otherwise, an error code is returned.
+ */
+uint32_t ipv6_init(const ipv6_init_t * p_init);
+
+/**@brief Sets address to specific interface.
+ *
+ * @details API used to add or update an IPv6 address on an interface. The address can have three specific
+ * states that determine transmitting capabilities.
+ *
+ * @param[in] p_interface The interface on which the address must be assigned.
+ * @param[in] p_addr IPv6 address and state to be assigned/updated.
+ *
+ * @retval NRF_SUCCESS If the operation was successful.
+ * @retval NRF_ERROR_NO_MEM If no memory was available.
+ */
+uint32_t ipv6_address_set(const iot_interface_t * p_interface,
+ const ipv6_addr_conf_t * p_addr);
+
+
+/**@brief Removes address from specific interface.
+ *
+ * @details API used to remove an IPv6 address from an interface.
+ *
+ * @param[in] p_interface The interface from which the address must be removed.
+ * @param[in] p_addr IPv6 address to remove.
+ *
+ * @retval NRF_SUCCESS If the operation was successful.
+ * @retval NRF_ERROR_NOT_FOUND If no address was found.
+ */
+uint32_t ipv6_address_remove(const iot_interface_t * p_interface,
+ const ipv6_addr_t * p_addr);
+
+
+/**@brief Checks if given unicast address has been registered.
+ *
+ * @param[in] p_interface The interface on which IPv6 address wil be checked.
+ * @param[in] p_addr IPv6 address to be checked.
+ *
+ * @retval NRF_SUCCESS If the operation was successful.
+ * @retval NRF_ERROR_NOT_FOUND If no address was found.
+ */
+uint32_t ipv6_address_check(const iot_interface_t * p_interface,
+ const ipv6_addr_t * p_addr);
+
+
+/**@brief Finds the best matched address and interface.
+ *
+ * @details API used to find the most suitable interface and address to a given destination address.
+ *
+ * To look only for the interface, set p_addr_r to NULL.
+ *
+ * To find the best matched address, IPV6_ADDR_STATE_PREFERRED state of address is required.
+ *
+ * @param[out] pp_interface Interface to be found.
+ * @param[out] p_addr_r Best matching address if procedure succeeded and this value was not NULL.
+ * @param[inout] p_addr_f IPv6 address for which best matching interface and/or address are requested.
+ *
+ * @retval NRF_SUCCESS If the operation was successful.
+ * @retval NRF_ERROR_NOT_FOUND If no interface was found.
+ * @retval NRF_ERROR_NOT_SUPPORTED If the operation was not supported.
+ */
+uint32_t ipv6_address_find_best_match(iot_interface_t ** pp_interface,
+ ipv6_addr_t * p_addr_r,
+ const ipv6_addr_t * p_addr_f);
+
+
+/**@brief Sends IPv6 packet.
+ *
+ * @details API used to send an IPv6 packet. Which interface that packet must be sent to is determined
+ * by analyzing the destination address.
+ *
+ * @param[in] p_interface The interface to which the packet is to be sent.
+ * @param[in] p_packet IPv6 packet to send. The packet should be allocated using
+ * @ref iot_pbuffer_allocate, to give stack control and to release
+ * the memory buffer.
+ *
+ * @retval NRF_SUCCESS If the send request was successful.
+ * @retval NRF_ERROR_NOT_FOUND If there was a failure while looking for the interface.
+ * @retval NRF_ERROR_INVALID_PARAM If there was an error in processing the IPv6 packet.
+ * @retval NRF_ERROR_NO_MEM If no memory was available in the transport
+ * interface.
+ */
+uint32_t ipv6_send(const iot_interface_t * p_interface, iot_pbuffer_t * p_packet);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //IPV6_API_H_
+
+/** @} */
+
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/udp_api.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/udp_api.h
new file mode 100644
index 0000000..8efa378
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/udp_api.h
@@ -0,0 +1,255 @@
+/**
+ * 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.
+ *
+ */
+/** @file udp_api.h
+ *
+ * @defgroup iot_udp UDP Application Interface for Nordic's IPv6 stack
+ * @ingroup iot_sdk_stack
+ * @{
+ * @brief Nordic User Datagram Protocol Application Interface for Nordic's IPv6 stack.
+ *
+ * @details This module provides basic features related to User Datagram Protocol (UDP) support.
+ */
+
+#ifndef UDP_API_H__
+#define UDP_API_H__
+
+#include "sdk_config.h"
+#include "sdk_common.h"
+#include "iot_defines.h"
+#include "ipv6_api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief UDP socket reference.
+ */
+typedef struct
+{
+ uint32_t socket_id; /**< UDP socket identifier. */
+ void * p_app_data; /**< Pointer to application data mapped by the application to the socket. If no mapping is provided by the application using the @ref udp6_socket_app_data_set API, this pointer is NULL. */
+} udp6_socket_t;
+
+/**
+ * @brief UDP data receive callback.
+ *
+ * @details API used to notify the application of UDP packets received. If the received data is
+ * malformed (for example, a checksum error), the packet is still notified to the application.
+ * The process_result parameter indicates whether the packet was successfully processed by UDP.
+ * The application should check process_result before
+ * consuming the packet.
+ *
+ * @param[in] p_socket Reference to the socket on which the data is received.
+ * @param[in] p_ip6_header Pointer to the IP header of the received ICMP packet.
+ * @param[in] p_udp_header Pointer to the UDP header of the received packet.
+ * @param[in] process_result Notifies the application if the UDP packet was processed successfully success or
+ * if an error occurred, for example, the packet was malformed.
+ * @param[in] p_rx_packet Packet buffer containing the received packed. p_rx_packet->p_payload
+ * contains the UDP payload.
+ *
+ * @returns A provision for the application to notify the module of whether the received packet was
+ * processed successfully by application. The application may take ownership of the received
+ * packet by returning IOT_IPV6_ERR_PENDING, in which case the application must take care to
+ * free it using @ref iot_pbuffer_free.
+ */
+typedef uint32_t (* udp6_handler_t)(const udp6_socket_t * p_socket,
+ const ipv6_header_t * p_ip_header,
+ const udp6_header_t * p_udp_header,
+ uint32_t process_result,
+ iot_pbuffer_t * p_rx_packet);
+
+
+/**
+ * @brief Allocates a UDP socket.
+ *
+ * @details This API should be called to be assigned a UDP socket. The maximum number of sockets that can
+ * be allocated using the API is determined by the define UDP6_MAX_SOCKET_COUNT.
+ *
+ * @param[out] p_socket Reference to the allocated socket is provided in the pointer if the procedure
+ * was successful. Should not be NULL.
+ *
+ * @retval NRF_SUCCESS If the socket was allocated successfully. Otherwise, an
+ * error code that indicates the reason for the failure is returned.
+ */
+uint32_t udp6_socket_allocate(udp6_socket_t * p_socket);
+
+
+/**
+ * @brief Frees an allocated UDP socket.
+ *
+ * @details API used to free a socket allocated using @ref udp6_socket_allocate.
+ *
+ * @param[in] p_socket Handle reference to the socket. Should not be NULL.
+ *
+ * @retval NRF_SUCCESS If the socket was freed successfully. Otherwise, an
+ * error code that indicates the reason for the failure is returned.
+ *
+ */
+uint32_t udp6_socket_free(const udp6_socket_t * p_socket);
+
+
+/**
+ * @brief Registers callback to be notified of data received on a socket.
+ *
+ * @details API to register a callback to be notified of data received on a socket.
+ *
+ * @param[in] p_socket Handle reference to the socket. Should not be NULL.
+ * @param[in] callback Callback being registered to receive data. Should not be NULL.
+ *
+ * @retval NRF_SUCCESS If the procedure was executed successfully. Otherwise, an
+ * error code that indicates the reason for the failure is returned.
+ *
+ */
+uint32_t udp6_socket_recv(const udp6_socket_t * p_socket,
+ const udp6_handler_t callback);
+
+
+/**
+ * @brief Binds a UDP socket to a specific port and address.
+ *
+ * @details API used to bind a UDP socket to a local port and an address.
+ *
+ * @param[in] p_socket Handle reference to the socket. Should not be NULL.
+ * @param[in] p_src_addr Local IPv6 address to be bound on specific socket.
+ * @param[in] src_port Local UDP port to be bound on specific socket.
+ *
+ * @retval NRF_SUCCESS If the procedure was executed successfully. Otherwise, an
+ * error code that indicates the reason for the failure is returned.
+ *
+ */
+uint32_t udp6_socket_bind(const udp6_socket_t * p_socket,
+ const ipv6_addr_t * p_src_addr,
+ uint16_t src_port);
+
+
+/**
+ * @brief Connects a UDP socket to aspecific port and address.
+ *
+ * @details API used to connect a UDP socket to a remote port and remote address.
+ *
+ * @param[in] p_socket Handle reference to the socket. Should not be NULL.
+ * @param[in] p_dest_addr IPv6 address of the remote destination.
+ * @param[in] dest_port Remote USP port to connect the socket to.
+ *
+ * @retval NRF_SUCCESS If the connection was established successfully.
+ */
+uint32_t udp6_socket_connect(const udp6_socket_t * p_socket,
+ const ipv6_addr_t * p_dest_addr,
+ uint16_t dest_port);
+
+
+/**
+ * @brief Sends a UDP packet on a specific socket.
+ *
+ * @details API used to send UDP data over a UDP socket. Remote port and address must be set with
+ * \ref udp6_socket_connect() before using this API.
+ *
+ * Applications that call this function should allocate a packet with type
+ * UDP6_PACKET_TYPE (set in the allocation
+ * parameter) before calling the function.
+ *
+ * The application shall not free the allocated packet buffer if the procedure was
+ * successful, to ensure that no data copies are needed when transmitting a packet.
+ *
+ * @param[in] p_socket Handle reference to the socket. Should not be NULL.
+ * @param[in] p_packet Data to be transmitted on the socket. The application should allocate a packet
+ * buffer with type UDP6_PACKET_TYPE using \ref iot_pbuffer_allocate.
+ * p_packet->p_payload and p_packet->length should be appropriately
+ * populated by the application to contain the payload and length of the UDP
+ * packet, respectively.
+ *
+ * @retval NRF_SUCCESS If the procedure was executed successfully. Otherwise, an
+ * error code that indicates the reason for the failure is returned.
+ *
+ */
+uint32_t udp6_socket_send(const udp6_socket_t * p_socket,
+ iot_pbuffer_t * p_packet);
+
+
+/**
+ * @brief Sends a UDP packet on a specific socket to a remote address and port.
+ *
+ * @details API used to send UDP data over a UDP socket.
+ *
+ * @param[in] p_socket Handle reference to the socket. Should not be NULL.
+ * @param[in] p_dest_addr IPv6 address of the remote destination.
+ * @param[in] dest_port Remote UDP port to which data transmission is requested.
+ * @param[in] p_packet Data to be transmitted on the socket. Application should allocate a
+ * packet buffer with type UDP6_PACKET_TYPE using \ref
+ * iot_pbuffer_allocate. p_packet->p_payload and p_packet->length should
+ * be appropriately populated by the application to contain the payload and
+ * length of the UDP packet, respectively.
+ *
+ * @retval NRF_SUCCESS If the procedure was executed successfully. Otherwise, an
+ * error code that indicates the reason for the failure is returned.
+ *
+ */
+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);
+
+
+/**
+ * @brief Sets application data for a socket.
+ *
+ * @details A utility API that allows the application to set any application specific mapping with the
+ * UDP Socket. The UDP module remembers the pointer provided by the application as long as the
+ * socket is not freed and if receive data callback is called each time as part of
+ * udp_socket_t.
+ *
+ * @param[in] p_socket Pointer to the socket for which the application data mapping is being set.
+ * A pointer to the application data should be provided by setting the
+ * p_socket->p_app_data field.
+ *
+ * @retval NRF_SUCCESS If the procedure was executed successfully. Otherwise, an
+ * error code that indicates the reason for the failure is returned.
+ *
+ */
+uint32_t udp6_socket_app_data_set(const udp6_socket_t * p_socket);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //UDP_API_H__
+
+/**@} */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/ipv6/ipv6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/ipv6/ipv6.c
new file mode 100644
index 0000000..6d7b641
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/ipv6/ipv6.c
@@ -0,0 +1,1088 @@
+/**
+ * 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 <string.h>
+
+#include "ble_6lowpan.h"
+#include "mem_manager.h"
+#include "sdk_os.h"
+#include "sdk_config.h"
+#include "iot_common.h"
+#include "iot_context_manager.h"
+#include "ipv6_api.h"
+#include "icmp6_api.h"
+#include "udp_api.h"
+#include "icmp6.h"
+#include "udp.h"
+
+#if IPV6_CONFIG_LOG_ENABLED
+
+#define NRF_LOG_MODULE_NAME ipv6
+
+#define NRF_LOG_LEVEL IPV6_CONFIG_LOG_LEVEL
+#define NRF_LOG_INFO_COLOR IPV6_CONFIG_INFO_COLOR
+#define NRF_LOG_DEBUG_COLOR IPV6_CONFIG_DEBUG_COLOR
+
+#include "nrf_log.h"
+NRF_LOG_MODULE_REGISTER();
+
+#define IPV6_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */
+#define IPV6_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */
+#define IPV6_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */
+
+#define IPV6_ENTRY() IPV6_TRC(">> %s", __func__)
+#define IPV6_EXIT() IPV6_TRC("<< %s", __func__)
+
+#else // IPV6_CONFIG_LOG_ENABLED
+
+#define IPV6_TRC(...) /**< Disables traces. */
+#define IPV6_DUMP(...) /**< Disables dumping of octet streams. */
+#define IPV6_ERR(...) /**< Disables error logs. */
+
+#define IPV6_ENTRY(...)
+#define IPV6_EXIT(...)
+
+#endif // IPV6_CONFIG_LOG_ENABLED
+
+/**
+ * @defgroup ipv6_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 IPV6_MUTEX_LOCK() SDK_MUTEX_LOCK(m_ipv6_mutex) /**< Lock module using mutex */
+#define IPV6_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_ipv6_mutex) /**< Unlock module using mutex */
+/** @} */
+
+/**
+ * @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.
+ * IPV6_DISABLE_API_PARAM_CHECK should be set to 0 to enable these checks.
+ *
+ * @{
+ */
+
+#if (IPV6_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_event_handler == NULL) \
+ { \
+ return (SDK_ERR_MODULE_NOT_INITIALIZED | IOT_IPV6_ERR_BASE); \
+ }
+
+/**@brief Verify NULL parameters are not passed to API by application. */
+#define NULL_PARAM_CHECK(PARAM) \
+ if ((PARAM) == NULL) \
+ { \
+ return (NRF_ERROR_NULL | IOT_IPV6_ERR_BASE); \
+ }
+
+#else // IPV6_DISABLE_API_PARAM_CHECK
+
+#define VERIFY_MODULE_IS_INITIALIZED()
+#define NULL_PARAM_CHECK(PARAM)
+
+#endif // IPV6_DISABLE_API_PARAM_CHECK
+/** @} */
+
+#define PBUFFER_ICMP_PAYLOAD_OFFSET IPV6_IP_HEADER_SIZE + ICMP6_HEADER_SIZE /**< ICMP payload offset. */
+#define PBUFFER_UDP_PAYLOAD_OFFSET IPV6_IP_HEADER_SIZE + UDP_HEADER_SIZE /**< UDP payload offset. */
+#define PBUFFER_OTHER_PAYLOAD_OFFSET IPV6_IP_HEADER_SIZE /**< Raw IPv6 payload offset. */
+
+#define IPV6_MAX_ADDRESS_COUNT (IPV6_MAX_ADDRESS_PER_INTERFACE * IPV6_MAX_INTERFACE) /**< Maximum number of addresses. */
+#define IPV6_INVALID_ADDR_INDEX 0xFF /**< Invalid address representation. */
+
+#define DEST_ADDR_OFFSET 24 /**< Offset of destination address in IPv6 packet. */
+
+/**@brief Internal interface structure. */
+typedef struct
+{
+ iot_interface_t * p_interface; /**< Pointer to driver interface */
+ uint8_t addr_range[IPV6_MAX_ADDRESS_PER_INTERFACE]; /**< Indexes to m_address_table indicating the address. If an index is IPV6_INVALID_ADDR_INDEX, it means there is no address entry. */
+} ipv6_interface_t;
+
+/**@brief Application Event Handler. */
+static ipv6_evt_handler_t m_event_handler = NULL;
+
+/**@brief Table of addresses */
+static ipv6_addr_conf_t m_address_table[IPV6_MAX_ADDRESS_COUNT];
+
+/**@brief Network interfaces table. */
+static ipv6_interface_t m_interfaces[IPV6_MAX_INTERFACE];
+
+/**@brief Number of network interfaces. */
+static uint32_t m_interfaces_count = 0;
+
+/**@brief Global address for IPv6 any. */
+ipv6_addr_t ipv6_addr_any;
+
+/**@brief Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */
+SDK_MUTEX_DEFINE(m_ipv6_mutex)
+
+
+/**@brief Function for finding specific address in address table.
+ *
+ * @param[in] p_addr Checked address.
+ * @param[out] p_index Index of address.
+ *
+ * @return NRF_SUCCESS if success, NRF_ERROR_NOT_FOUND otherwise.
+ */
+static uint32_t addr_find(const ipv6_addr_t * p_addr, uint32_t * p_index)
+{
+ uint32_t index;
+ uint32_t err_code = (IOT_IPV6_ERR_BASE | NRF_ERROR_NOT_FOUND);
+
+ for (index = 0; index < IPV6_MAX_ADDRESS_COUNT; index++)
+ {
+ if ((m_address_table[index].state != IPV6_ADDR_STATE_UNUSED) &&
+ (0 == IPV6_ADDRESS_CMP(&m_address_table[index].addr, p_addr)))
+ {
+ *p_index = index;
+ err_code = NRF_SUCCESS;
+ break;
+ }
+ }
+
+ return err_code;
+}
+
+/**@brief Function for finding free place in address table.
+ *
+ * @param[out] p_index Index of address.
+ *
+ * @return NRF_SUCCESS if success, NRF_ERROR_NOT_FOUND otherwise.
+ */
+static uint32_t addr_find_free(uint32_t * p_index)
+{
+ uint32_t index;
+ uint32_t err_code = (IOT_IPV6_ERR_BASE | NRF_ERROR_NO_MEM);
+
+ for (index = 0; index < IPV6_MAX_ADDRESS_COUNT; index++)
+ {
+ if (m_address_table[index].state == IPV6_ADDR_STATE_UNUSED)
+ {
+ *p_index = index;
+ err_code = NRF_SUCCESS;
+ break;
+ }
+ }
+
+ return err_code;
+}
+
+
+/**@brief Function for freeing an address configuration entry in m_address_table.
+ *
+ * @param[in] index Index of address.
+ * @param[in] check_references Indicate that before remove references should be counted.
+ *
+ * @return None.
+ */
+static void addr_free(uint32_t addr_index, bool check_references)
+{
+ uint32_t if_index;
+ uint32_t index;
+
+ if (check_references)
+ {
+ for (if_index = 0; if_index < IPV6_MAX_INTERFACE; if_index++)
+ {
+ for (index = 0; index < IPV6_MAX_ADDRESS_PER_INTERFACE; index++)
+ {
+ if (m_interfaces[if_index].addr_range[index] == addr_index)
+ {
+ return;
+ }
+ }
+ }
+ }
+
+ m_address_table[addr_index].state = IPV6_ADDR_STATE_UNUSED;
+ IPV6_ADDRESS_INITIALIZE(&m_address_table[addr_index].addr);
+}
+
+
+/**@brief Function for checking if received packet is for us.
+ * Currently only all-node, MLDv2 and solicited-node
+* multicast addresses are accepted.
+ *
+ * @param[in] interface_id Index of the interface.
+ * @param[in] p_addr Checked address.
+ * @param[in] check_multicast Define if multicast addresses have to be checked.
+ *
+ * @return NRF_SUCCESS if packet can be processing to IPv6 multiplexer.
+ */
+static uint32_t addr_check(uint32_t interface_id, const ipv6_addr_t * p_addr, bool check_multicast)
+{
+ ipv6_addr_conf_t * p_addr_conf;
+ uint32_t index;
+ uint32_t err_code = NRF_ERROR_NOT_FOUND;
+
+ // Check basic Multicast addresses.
+ if (check_multicast && (IPV6_ADDRESS_IS_MLDV2_MCAST(p_addr) || IPV6_ADDRESS_IS_ALL_NODE(p_addr)))
+ {
+ return NRF_SUCCESS;
+ }
+
+ for (index = 0; m_interfaces[interface_id].addr_range[index] != IPV6_INVALID_ADDR_INDEX; index++)
+ {
+ p_addr_conf = &m_address_table[m_interfaces[interface_id].addr_range[index]];
+
+ if (check_multicast && IPV6_ADDRESS_IS_MULTICAST_SOLICITED_NODE(p_addr))
+ {
+ // Solicited-node multicast address is formed by taking the low-order 24 bits of an address (unicast or anycast).
+ if (0 == memcmp(&p_addr_conf->addr.u8[13], &p_addr->u8[13], 3))
+ {
+ err_code = NRF_SUCCESS;
+ break;
+ }
+ }
+ else if (0 == IPV6_ADDRESS_CMP(&p_addr_conf->addr, p_addr))
+ {
+ err_code = NRF_SUCCESS;
+ break;
+ }
+ }
+
+ return err_code;
+}
+
+
+/**@brief Function for adding/updating IPv6 address in table.
+ *
+ * @param[in] interface_id Index of interface.
+ * @param[in] p_addr Given address.
+ *
+ * @return NRF_SUCCESS if operation successful, NRF_ERROR_NO_MEM otherwise.
+ */
+static uint32_t addr_set(const iot_interface_t * p_interface,
+ const ipv6_addr_conf_t * p_addr)
+{
+ uint32_t index;
+ uint32_t addr_index;
+ uint32_t err_code;
+
+ uint32_t interface_id = (uint32_t)p_interface->p_upper_stack;
+
+ // Try to find address.
+ err_code = addr_find(&p_addr->addr, &addr_index);
+
+ if (err_code != NRF_SUCCESS)
+ {
+ // Find first empty one.
+ err_code = addr_find_free(&addr_index);
+ }
+
+ if (err_code == NRF_SUCCESS)
+ {
+ err_code = IOT_IPV6_ERR_ADDR_IF_MISMATCH;
+
+ // Check if this index entry exists in the p_interface for which API is requested.
+ for (index = 0; index < IPV6_MAX_ADDRESS_PER_INTERFACE; index++)
+ {
+ if (m_interfaces[interface_id].addr_range[index] == addr_index)
+ {
+ m_address_table[index].state = p_addr->state;
+
+ err_code = NRF_SUCCESS;
+ break;
+ }
+ }
+
+ if (err_code == IOT_IPV6_ERR_ADDR_IF_MISMATCH)
+ {
+ err_code = (IOT_IPV6_ERR_BASE | NRF_ERROR_NO_MEM);
+
+ for (index = 0; index < IPV6_MAX_ADDRESS_PER_INTERFACE; index++)
+ {
+ if (m_interfaces[interface_id].addr_range[index] == IPV6_INVALID_ADDR_INDEX)
+ {
+ m_address_table[index].state = p_addr->state;
+ memcpy(&m_address_table[index].addr, p_addr, IPV6_ADDR_SIZE);
+ m_interfaces[interface_id].addr_range[index] = addr_index;
+
+ err_code = NRF_SUCCESS;
+ break;
+ }
+ }
+ }
+ }
+
+ return err_code;
+}
+
+
+/**@brief Function for calculating how many bits of addresses are equal.
+ *
+ * @param[in] p_addr1 Base address.
+ * @param[in] p_addr2 Base address.
+ *
+ * @return Number of same bits.
+ */
+static uint32_t addr_bit_equal(const ipv6_addr_t * p_addr1,
+ const ipv6_addr_t * p_addr2)
+{
+ uint32_t index;
+ uint32_t match = 0;
+ uint8_t temp;
+ uint32_t index_tab;
+
+ for (index = 0; index < IPV6_ADDR_SIZE; index++)
+ {
+ if (p_addr1->u8[index] == p_addr2->u8[index])
+ {
+ // Add full 8bits to match
+ match += 8;
+ }
+ else
+ {
+ // Operation of XOR to detect differences
+ temp = p_addr1->u8[index] ^ p_addr2->u8[index];
+
+ // Check all single bits
+ for (index_tab = 0; index_tab < 8; index_tab++)
+ {
+ if ((temp & 0x80) == 0)
+ {
+ // If the oldest bits matched, add one more.
+ match++;
+
+ // Check next bit.
+ temp = temp << 1;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ break;
+ }
+ }
+
+ return match;
+}
+
+/**@brief Function for searching specific network interface by given address.
+ *
+ * @param[in] p_interface Pointer to IPv6 network interface.
+ * @param[in] p_dest_addr IPv6 address to be matched.
+ *
+ * @return NRF_SUCCESS if operation successful, NRF_ERROR_NOT_FOUND otherwise.
+ */
+static uint32_t interface_find(iot_interface_t ** pp_interface, const ipv6_addr_t * p_dest_addr)
+{
+ // Currently only host role is implemented, though no need to match addresses.
+ UNUSED_VARIABLE(p_dest_addr);
+
+ uint32_t index;
+ uint32_t err_code = (IOT_IPV6_ERR_BASE | NRF_ERROR_NOT_FOUND);
+
+ if (m_interfaces_count == 1)
+ {
+ for (index = 0; index < IPV6_MAX_INTERFACE; index++)
+ {
+ if (m_interfaces[index].p_interface != NULL)
+ {
+ *pp_interface = m_interfaces[index].p_interface;
+ err_code = NRF_SUCCESS;
+ break;
+ }
+ }
+ }
+ else if (m_interfaces_count == 0)
+ {
+ err_code = (IOT_IPV6_ERR_BASE | NRF_ERROR_NOT_FOUND);
+ }
+ else
+ {
+ // Not supported now.
+ err_code = (IOT_IPV6_ERR_BASE | NRF_ERROR_NOT_SUPPORTED);
+ }
+
+ return err_code;
+}
+
+
+/**@brief Function for resetting specific network interface.
+ *
+ * @param[in] p_interface Pointer to IPv6 network interface.
+ *
+ * @return None.
+ */
+static void interface_reset(ipv6_interface_t * p_interface)
+{
+ uint32_t index;
+ uint8_t addr_index;
+
+ p_interface->p_interface = NULL;
+
+ for (index = 0; index < IPV6_MAX_ADDRESS_PER_INTERFACE; index++)
+ {
+ addr_index = p_interface->addr_range[index];
+
+ if (addr_index != IPV6_INVALID_ADDR_INDEX)
+ {
+ p_interface->addr_range[index] = IPV6_INVALID_ADDR_INDEX;
+ addr_free(index, true);
+ }
+ }
+}
+
+
+/**@brief Function for getting specific network interface by 6LoWPAN interface.
+ *
+ * @param[in] p_6lo_interface Pointer to 6LoWPAN interface.
+ *
+ * @return Pointer to internal network interface on success, otherwise NULL.
+ */
+static uint32_t interface_get_by_6lo(iot_interface_t * p_6lo_interface)
+{
+ return (uint32_t)(p_6lo_interface->p_upper_stack);
+}
+
+
+/**@brief Function for adding new 6lowpan interface to interface table.
+ *
+ * @param[in] p_6lo_interface Pointer to 6LoWPAN interface.
+ * @param[out] p_index Pointer to index of internal network interface.
+ *
+ * @return NRF_SUCCESS on success, otherwise NRF_ERROR_NO_MEM error.
+ */
+static uint32_t interface_add(iot_interface_t * p_interface,
+ uint32_t * p_index )
+{
+ uint32_t index;
+ uint32_t err_code;
+ ipv6_addr_conf_t linklocal_addr;
+
+ for (index = 0; index < IPV6_MAX_INTERFACE; index++)
+ {
+ if (m_interfaces[index].p_interface == NULL)
+ {
+ m_interfaces[index].p_interface = p_interface;
+ p_interface->p_upper_stack = (void *) index;
+ (*p_index) = index;
+
+ // Add link local address.
+ IPV6_CREATE_LINK_LOCAL_FROM_EUI64(&linklocal_addr.addr, p_interface->local_addr.identifier);
+ linklocal_addr.state = IPV6_ADDR_STATE_PREFERRED;
+
+ err_code = addr_set(p_interface, &linklocal_addr);
+ if (err_code != NRF_SUCCESS)
+ {
+ IPV6_ERR("Cannot add link-local address to interface!");
+ }
+
+ return NRF_SUCCESS;
+ }
+ }
+
+ return NRF_ERROR_NO_MEM;
+}
+
+
+/**@brief Function for removing 6lowpan interface from interface table.
+ *
+ * @param[in] p_interface Pointer to internal network interface.
+ *
+ * @return None.
+ */
+static void interface_delete(uint32_t index)
+{
+ interface_reset(&m_interfaces[index]);
+}
+
+/**@brief Function for notifying application of the new interface established.
+ *
+ * @param[in] p_interface Pointer to internal network interface.
+ *
+ * @return None.
+ */
+static void app_notify_interface_add(iot_interface_t * p_interface)
+{
+ ipv6_event_t event;
+
+ event.event_id = IPV6_EVT_INTERFACE_ADD;
+
+ IPV6_MUTEX_UNLOCK();
+
+ m_event_handler(p_interface, &event);
+
+ IPV6_MUTEX_LOCK();
+}
+
+
+/**@brief Function for notifying application of the interface disconnection.
+ *
+ * @param[in] p_interface Pointer to internal network interface.
+ *
+ * @return None.
+ */
+static void app_notify_interface_delete(iot_interface_t * p_interface)
+{
+ ipv6_event_t event;
+
+ event.event_id = IPV6_EVT_INTERFACE_DELETE;
+
+ IPV6_MUTEX_UNLOCK();
+
+ m_event_handler(p_interface, &event);
+
+ IPV6_MUTEX_LOCK();
+}
+
+
+#if (IPV6_ENABLE_USNUPORTED_PROTOCOLS_TO_APPLICATION == 1)
+/**@brief Function for notifying application of the received packet (e.g. with unsupported protocol).
+ *
+ * @param[in] p_interface Pointer to external interface from which packet come.
+ * @param[in] p_pbuffer Pointer to packet buffer.
+ *
+ * @return None.
+ */
+static void app_notify_rx_data(iot_interface_t * p_interface, iot_pbuffer_t * p_pbuffer)
+{
+ ipv6_event_t event;
+
+ event.event_id = IPV6_EVT_INTERFACE_RX_DATA;
+
+ // RX Event parameter.
+ event.event_param.rx_event_param.p_rx_packet = p_pbuffer;
+ event.event_param.rx_event_param.p_ip_header = (ipv6_header_t *)p_pbuffer->p_memory;
+
+ IPV6_MUTEX_UNLOCK();
+
+ m_event_handler(p_interface, &event);
+
+ IPV6_MUTEX_LOCK();
+}
+#endif
+
+
+/**@brief Function for multiplexing transport protocol to different modules.
+ *
+ * @param[in] p_interface Pointer to external interface from which packet come.
+ * @param[in] p_pbuffer Pointer to packet buffer.
+ *
+ * @return NRF_SUCCESS if success, otherwise an error code.
+ */
+static uint32_t ipv6_input(iot_interface_t * p_interface, iot_pbuffer_t * p_pbuffer)
+{
+ uint32_t err_code = NRF_SUCCESS;
+ ipv6_header_t * p_iphdr = (ipv6_header_t *)(p_pbuffer->p_payload - IPV6_IP_HEADER_SIZE);
+
+ // Change byte order of IP header given to application.
+ p_iphdr->length = NTOHS(p_iphdr->length);
+ p_iphdr->flowlabel = NTOHS(p_iphdr->flowlabel);
+
+ switch (p_iphdr->next_header)
+ {
+ case IPV6_NEXT_HEADER_ICMP6:
+ IPV6_TRC("Got ICMPv6 packet.");
+
+ IPV6_MUTEX_UNLOCK();
+ err_code = icmp6_input(p_interface, p_iphdr, p_pbuffer);
+ IPV6_MUTEX_LOCK();
+
+ break;
+
+ case IPV6_NEXT_HEADER_UDP:
+ IPV6_TRC("Got UDP packet.");
+
+ IPV6_MUTEX_UNLOCK();
+ err_code = udp_input(p_interface, p_iphdr, p_pbuffer);
+ IPV6_MUTEX_LOCK();
+
+ break;
+
+ default:
+ IPV6_ERR("Got unsupported protocol packet. Protocol ID = 0x%x!",
+ p_iphdr->next_header);
+
+#if (IPV6_ENABLE_USNUPORTED_PROTOCOLS_TO_APPLICATION == 1)
+ app_notify_rx_data(p_interface, p_pbuffer);
+#endif
+ break;
+ }
+
+ // Free packet buffer unless marked explicitly as pending
+ if (err_code != IOT_IPV6_ERR_PENDING)
+ {
+ UNUSED_VARIABLE(iot_pbuffer_free(p_pbuffer, true));
+ }
+
+ return err_code;
+}
+
+
+/**@brief Function for receiving 6LoWPAN module events.
+ *
+ * @param[in] p_6lo_interface Pointer to 6LoWPAN interface.
+ * @param[in] p_6lo_event Pointer to 6LoWPAN related event.
+ *
+ * @return None.
+ */
+static void ble_6lowpan_evt_handler(iot_interface_t * p_interface,
+ ble_6lowpan_event_t * p_6lo_event)
+{
+ bool rx_failure = false;
+ uint32_t err_code;
+ uint32_t interface_id;
+ iot_pbuffer_t * p_pbuffer;
+ iot_pbuffer_alloc_param_t pbuff_param;
+
+ IPV6_MUTEX_LOCK();
+
+ IPV6_ENTRY();
+ IPV6_TRC("In 6LoWPAN Handler:");
+
+ interface_id = interface_get_by_6lo(p_interface);
+
+ switch (p_6lo_event->event_id)
+ {
+ case BLE_6LO_EVT_ERROR:
+ {
+ IPV6_ERR("Got error, with result %08lx", p_6lo_event->event_result);
+ break;
+ }
+ case BLE_6LO_EVT_INTERFACE_ADD:
+ {
+ IPV6_TRC("New interface established!");
+
+ // Add interface to internal table.
+ err_code = interface_add(p_interface, &interface_id);
+
+ if (NRF_SUCCESS == err_code)
+ {
+ IPV6_TRC("Added new network interface to internal table.");
+
+ err_code = iot_context_manager_table_alloc(p_interface);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ IPV6_TRC("Successfully allocated context table!");
+ }
+ else
+ {
+ IPV6_ERR("Failed to allocate context table!");
+ }
+
+ // Increase number of up interfaces.
+ m_interfaces_count++;
+
+ // Notify application.
+ app_notify_interface_add(p_interface);
+ }
+ else
+ {
+ IPV6_ERR("Cannot add new interface. Table is full.");
+ }
+
+ break;
+ }
+ case BLE_6LO_EVT_INTERFACE_DELETE:
+ {
+ IPV6_TRC("Interface disconnected!");
+
+ if (interface_id < IPV6_MAX_INTERFACE)
+ {
+ IPV6_TRC("Removed network interface.");
+
+ // Notify application.
+ app_notify_interface_delete(p_interface);
+
+ err_code = iot_context_manager_table_free(p_interface);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ IPV6_TRC("Successfully freed context table!");
+ }
+
+ // Decrease number of up interfaces.
+ m_interfaces_count--;
+
+ // Remove interface from internal table.
+ interface_delete(interface_id);
+ }
+ break;
+ }
+ case BLE_6LO_EVT_INTERFACE_DATA_RX:
+ {
+ IPV6_TRC("Got data with size = %d!",
+ p_6lo_event->event_param.rx_event_param.packet_len);
+ IPV6_TRC("Data: ");
+ IPV6_DUMP(p_6lo_event->event_param.rx_event_param.p_packet,
+ p_6lo_event->event_param.rx_event_param.packet_len);
+
+ if (interface_id < IPV6_MAX_INTERFACE)
+ {
+ if (p_6lo_event->event_result == NRF_ERROR_NOT_FOUND)
+ {
+ IPV6_ERR("Cannot restore IPv6 addresses!");
+ IPV6_ERR("Source CID = 0x%x, Destination CID = 0x%x",
+ p_6lo_event->event_param.rx_event_param.rx_contexts.src_cntxt_id,
+ p_6lo_event->event_param.rx_event_param.rx_contexts.dest_cntxt_id);
+
+ // Indicates failure.
+ rx_failure = true;
+ break;
+ }
+
+ // Check if packet is for us.
+ ipv6_addr_t * p_addr =
+ (ipv6_addr_t *)&p_6lo_event->event_param.rx_event_param.p_packet[DEST_ADDR_OFFSET];
+
+ err_code = addr_check(interface_id, p_addr, true);
+
+ // If no address found - drop message.
+ if (err_code != NRF_SUCCESS)
+ {
+ IPV6_ERR("Packet received on unknown address!");
+ rx_failure = true;
+ break;
+ }
+
+ // Try to allocate pbuffer, with no memory.
+ pbuff_param.flags = PBUFFER_FLAG_NO_MEM_ALLOCATION;
+ pbuff_param.type = RAW_PACKET_TYPE;
+ pbuff_param.length = p_6lo_event->event_param.rx_event_param.packet_len;
+
+ // Try to allocate pbuffer for receiving data.
+ err_code = iot_pbuffer_allocate(&pbuff_param, &p_pbuffer);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ p_pbuffer->p_memory = p_6lo_event->event_param.rx_event_param.p_packet;
+ p_pbuffer->p_payload = p_pbuffer->p_memory + IPV6_IP_HEADER_SIZE;
+ p_pbuffer->length -= IPV6_IP_HEADER_SIZE;
+
+ // Execute multiplexer.
+ err_code = ipv6_input(p_interface, p_pbuffer);
+
+ if (err_code != NRF_SUCCESS)
+ {
+ IPV6_ERR("Failed while processing packet, error = 0x%08lX!", err_code);
+ }
+ }
+ else
+ {
+ IPV6_ERR("Failed to allocate packet buffer!");
+ rx_failure = true;
+ }
+ }
+ else
+ {
+ IPV6_ERR("[6LOWPAN]: Got data to unknown interface!");
+ rx_failure = true;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (rx_failure == true)
+ {
+ UNUSED_VARIABLE(nrf_free(p_6lo_event->event_param.rx_event_param.p_packet));
+ }
+
+ IPV6_EXIT();
+
+ IPV6_MUTEX_UNLOCK();
+}
+
+
+uint32_t ipv6_init(const ipv6_init_t * p_init)
+{
+ uint32_t index;
+ uint32_t err_code;
+ ble_6lowpan_init_t init_params;
+
+ NULL_PARAM_CHECK(p_init);
+ NULL_PARAM_CHECK(p_init->p_eui64);
+ NULL_PARAM_CHECK(p_init->event_handler);
+
+ SDK_MUTEX_INIT(m_ipv6_mutex);
+ IPV6_MUTEX_LOCK();
+
+ IPV6_ENTRY();
+
+ // Initialize related modules.
+ UNUSED_VARIABLE(nrf_mem_init());
+ UNUSED_VARIABLE(iot_pbuffer_init());
+
+ // Initialize submodules of IPv6 stack.
+ UNUSED_VARIABLE(udp_init());
+ UNUSED_VARIABLE(icmp6_init());
+
+ // Initialize context manager.
+ UNUSED_VARIABLE(iot_context_manager_init());
+
+ IPV6_ADDRESS_INITIALIZE(IPV6_ADDR_ANY);
+
+ // Set application event handler.
+ m_event_handler = p_init->event_handler;
+
+ // Clear number of interfaces.
+ m_interfaces_count = 0;
+
+ // Clear network interfaces.
+ for (index = 0; index < IPV6_MAX_INTERFACE; index++)
+ {
+ interface_reset(&m_interfaces[index]);
+ }
+
+ // Clear all addresses.
+ for (index = 0; index < IPV6_MAX_ADDRESS_COUNT; index++)
+ {
+ addr_free(index, false);
+ }
+
+ // 6LoWPAN module initialization.
+ init_params.p_eui64 = p_init->p_eui64;
+ init_params.event_handler = ble_6lowpan_evt_handler;
+
+ err_code = ble_6lowpan_init(&init_params);
+
+ IPV6_EXIT();
+
+ IPV6_MUTEX_UNLOCK();
+
+ return err_code;
+}
+
+
+uint32_t ipv6_address_set(const iot_interface_t * p_interface,
+ const ipv6_addr_conf_t * p_addr)
+{
+ VERIFY_MODULE_IS_INITIALIZED();
+
+ NULL_PARAM_CHECK(p_addr);
+ NULL_PARAM_CHECK(p_interface);
+
+ uint32_t err_code;
+
+ IPV6_MUTEX_LOCK();
+
+ IPV6_ENTRY();
+
+ err_code = addr_set(p_interface, p_addr);
+
+ IPV6_EXIT();
+
+ IPV6_MUTEX_UNLOCK();
+
+ return err_code;
+}
+
+
+uint32_t ipv6_address_check(const iot_interface_t * p_interface,
+ const ipv6_addr_t * p_addr)
+{
+ VERIFY_MODULE_IS_INITIALIZED();
+
+ NULL_PARAM_CHECK(p_addr);
+ NULL_PARAM_CHECK(p_interface);
+
+ uint32_t err_code;
+
+ IPV6_MUTEX_LOCK();
+
+ IPV6_ENTRY();
+
+ uint32_t interface_id = (uint32_t)p_interface->p_upper_stack;
+
+ err_code = addr_check(interface_id, p_addr, false);
+
+ IPV6_EXIT();
+
+ IPV6_MUTEX_UNLOCK();
+
+ return err_code;
+}
+
+
+uint32_t ipv6_address_find_best_match(iot_interface_t ** pp_interface,
+ ipv6_addr_t * p_addr_r,
+ const ipv6_addr_t * p_addr_f)
+{
+ VERIFY_MODULE_IS_INITIALIZED();
+
+ NULL_PARAM_CHECK(p_addr_f);
+ NULL_PARAM_CHECK(pp_interface);
+
+ uint32_t index;
+ uint32_t err_code;
+ uint32_t addr_index;
+ uint32_t match_temp = 0;
+ uint32_t match_best = 0;
+ ipv6_addr_t * p_best_addr = NULL;
+
+ IPV6_MUTEX_LOCK();
+
+ err_code = interface_find(pp_interface, p_addr_f);
+
+ if (err_code == NRF_SUCCESS && p_addr_r)
+ {
+ uint32_t interface_id = (uint32_t)(*pp_interface)->p_upper_stack;
+
+ for (index = 0; index < IPV6_MAX_ADDRESS_PER_INTERFACE; index++)
+ {
+ addr_index = m_interfaces[interface_id].addr_range[index];
+
+ if (addr_index != IPV6_INVALID_ADDR_INDEX)
+ {
+ if (m_address_table[addr_index].state == IPV6_ADDR_STATE_PREFERRED)
+ {
+ match_temp = addr_bit_equal(p_addr_f, &m_address_table[addr_index].addr);
+
+ if (match_temp >= match_best)
+ {
+ match_best = match_temp;
+ p_best_addr = &m_address_table[addr_index].addr;
+ }
+ }
+ }
+ }
+
+ // No address found.
+ if (p_best_addr == NULL)
+ {
+ // Set undefined :: address.
+ IPV6_ADDRESS_INITIALIZE(p_addr_r);
+ }
+ else
+ {
+ memcpy(p_addr_r->u8, p_best_addr->u8, IPV6_ADDR_SIZE);
+ }
+ }
+
+ IPV6_MUTEX_UNLOCK();
+
+ return err_code;
+}
+
+
+uint32_t ipv6_address_remove(const iot_interface_t * p_interface,
+ const ipv6_addr_t * p_addr)
+{
+ VERIFY_MODULE_IS_INITIALIZED();
+
+ NULL_PARAM_CHECK(p_addr);
+ NULL_PARAM_CHECK(p_interface);
+
+ uint32_t index;
+ uint32_t err_code;
+ uint32_t addr_index;
+
+ IPV6_MUTEX_LOCK();
+
+ IPV6_ENTRY();
+
+ uint32_t interface_id = (uint32_t)p_interface->p_upper_stack;
+
+ err_code = (IOT_IPV6_ERR_BASE | NRF_ERROR_NOT_FOUND);
+
+ for (index = 0; index < IPV6_MAX_ADDRESS_PER_INTERFACE; index++)
+ {
+ addr_index = m_interfaces[interface_id].addr_range[index];
+
+ if (addr_index != IPV6_INVALID_ADDR_INDEX)
+ {
+ if (0 == IPV6_ADDRESS_CMP(&m_address_table[addr_index].addr, p_addr))
+ {
+ m_interfaces[interface_id].addr_range[index] = IPV6_INVALID_ADDR_INDEX;
+
+ // Remove address if no reference to interface found.
+ addr_free(index, true);
+
+ err_code = NRF_SUCCESS;
+
+ break;
+ }
+ }
+ }
+
+ IPV6_EXIT();
+
+ IPV6_MUTEX_UNLOCK();
+
+ return err_code;
+}
+
+
+uint32_t ipv6_send(const iot_interface_t * p_interface, iot_pbuffer_t * p_packet)
+{
+ VERIFY_MODULE_IS_INITIALIZED();
+
+ NULL_PARAM_CHECK(p_packet);
+ NULL_PARAM_CHECK(p_interface);
+
+ uint32_t err_code;
+
+ IPV6_MUTEX_LOCK();
+
+ IPV6_ENTRY();
+
+ err_code = ble_6lowpan_interface_send(p_interface,
+ p_packet->p_payload,
+ p_packet->length);
+
+ if (err_code != NRF_SUCCESS)
+ {
+ IPV6_ERR("Cannot send packet!");
+ }
+
+ // Free pbuffer, without freeing memory.
+ UNUSED_VARIABLE(iot_pbuffer_free(p_packet, false));
+
+ IPV6_EXIT();
+
+ IPV6_MUTEX_UNLOCK();
+
+ return err_code;
+}
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/pbuffer/iot_pbuffer.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/pbuffer/iot_pbuffer.c
new file mode 100644
index 0000000..6611396
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/pbuffer/iot_pbuffer.c
@@ -0,0 +1,467 @@
+/**
+ * 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 "nrf_soc.h"
+#include "nordic_common.h"
+#include "sdk_common.h"
+#include "sdk_config.h"
+#include "iot_common.h"
+#include "iot_pbuffer.h"
+#include "mem_manager.h"
+
+#if IOT_PBUFFER_CONFIG_LOG_ENABLED
+
+#define NRF_LOG_MODULE_NAME pbuffer
+
+#define NRF_LOG_LEVEL IOT_PBUFFER_CONFIG_LOG_LEVEL
+#define NRF_LOG_INFO_COLOR IOT_PBUFFER_CONFIG_INFO_COLOR
+#define NRF_LOG_DEBUG_COLOR IOT_PBUFFER_CONFIG_DEBUG_COLOR
+
+#include "nrf_log.h"
+NRF_LOG_MODULE_REGISTER();
+
+#define PBUFFER_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */
+#define PBUFFER_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */
+#define PBUFFER_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */
+
+#define PBUFFER_ENTRY() PBUFFER_TRC(">> %s", __func__)
+#define PBUFFER_EXIT() PBUFFER_TRC("<< %s", __func__)
+
+#else // IOT_PBUFFER_CONFIG_LOG_ENABLED
+
+#define PBUFFER_TRC(...) /**< Disables traces. */
+#define PBUFFER_DUMP(...) /**< Disables dumping of octet streams. */
+#define PBUFFER_ERR(...) /**< Disables error logs. */
+
+#define PBUFFER_ENTRY(...)
+#define PBUFFER_EXIT(...)
+
+#endif // IOT_PBUFFER_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.
+ * BLE_HPS_DISABLE_API_PARAM_CHECK should be defined to disable these checks.
+ *
+ * @{
+ */
+#if (IOT_PBUFFER_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_PBUFFER_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_PBUFFER_ERR_BASE); \
+ }
+
+/**
+ * @brief Verify Type field has valid value.
+ */
+#define VERIFY_PBUFFER_TYPE(TYPE) \
+ if (((TYPE) == 0) || ((TYPE) > COAP_PACKET_TYPE)) \
+ { \
+ return (NRF_ERROR_INVALID_PARAM | IOT_PBUFFER_ERR_BASE); \
+ }
+
+
+/**
+ * @brief Verify flags field has valid value.
+ */
+#define VERIFY_PBUFFER_FLAGS(FLAG) \
+ if ((uint32_t)(FLAG) > PBUFFER_FLAG_NO_MEM_ALLOCATION) \
+ { \
+ return (NRF_ERROR_INVALID_PARAM | IOT_PBUFFER_ERR_BASE); \
+ }
+
+/**
+ * @brief Verify flags field has valid value.
+ */
+#define VERIFY_NON_ZERO_LENGTH(LEN) \
+ if ((LEN) ==0) \
+ { \
+ return (NRF_ERROR_INVALID_LENGTH | IOT_PBUFFER_ERR_BASE); \
+ }
+
+#else //IOT_PBUFFER_DISABLE_API_PARAM_CHECK
+
+#define VERIFY_MODULE_IS_INITIALIZED()
+#define VERIFY_MODULE_IS_INITIALIZED_VOID()
+#define NULL_PARAM_CHECK(PARAM)
+#define VERIFY_PBUFFER_TYPE(TYPE)
+#define VERIFY_PBUFFER_FLAGS(FLAG)
+#define VERIFY_NON_ZERO_LENGTH(LEN)
+
+#endif //IOT_PBUFFER_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 PBUFFER_MUTEX_LOCK() SDK_MUTEX_LOCK(m_pbuffer_mutex) /**< Lock module using mutex */
+#define PBUFFER_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_pbuffer_mutex) /**< Unlock module using mutex */
+/** @} */
+
+/** @brief Packet buffer type managed by the module. */
+typedef struct
+{
+ iot_pbuffer_t buffer; /**< Packet buffer being managed. */
+ uint32_t allocated_length; /**< Length allocated for the buffer. */
+}pbuffer_t;
+
+SDK_MUTEX_DEFINE(m_pbuffer_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 pbuffer_t m_pbuffer[IOT_PBUFFER_MAX_COUNT]; /**< Table of packet buffers managed by the module. */
+
+
+/**@brief Initializes packet buffer. */
+static void pbuffer_init(pbuffer_t * p_buffer)
+{
+ p_buffer->buffer.p_memory = NULL;
+ p_buffer->buffer.p_payload = NULL;
+ p_buffer->buffer.length = 0;
+ p_buffer->buffer.type = UNASSIGNED_TYPE;
+ p_buffer->allocated_length = 0;
+}
+
+
+/**@brief Get size of offset to be used based on the requested type. */
+static uint32_t type_offset_get(iot_pbuffer_type_t type)
+{
+ uint32_t offset = 0;
+
+ switch (type)
+ {
+ case RAW_PACKET_TYPE:
+ {
+ offset = 0;
+ break;
+ }
+ case IPV6_PACKET_TYPE:
+ {
+ offset = IPV6_IP_HEADER_SIZE;
+ break;
+ }
+ case ICMP6_PACKET_TYPE:
+ {
+ offset = IPV6_IP_HEADER_SIZE + ICMP6_HEADER_SIZE;
+ break;
+ }
+ case UDP6_PACKET_TYPE: // Fall through.
+ {
+ offset = IPV6_IP_HEADER_SIZE + UDP_HEADER_SIZE;
+ break;
+ }
+ case COAP_PACKET_TYPE:
+ {
+ offset = IPV6_IP_HEADER_SIZE + UDP_HEADER_SIZE + COAP_HEADER_SIZE;
+ break;
+ }
+ default:
+ {
+ // Should never happen.
+ break;
+ }
+ }
+
+ return offset;
+}
+
+
+/**@brief Allocates 'length' sized packet buffer. */
+static uint32_t pbuffer_allocate(pbuffer_t ** pp_buffer, uint32_t length, iot_pbuffer_flags_t flags)
+{
+ uint32_t index;
+ uint32_t err_code = (NRF_ERROR_NO_MEM | IOT_PBUFFER_ERR_BASE);
+
+
+ for (index = 0; index < IOT_PBUFFER_MAX_COUNT; index ++)
+ {
+ if (m_pbuffer[index].allocated_length == 0)
+ {
+ // Found a free buffer, allocate.
+ PBUFFER_TRC("Found free buffer. Requesting memory allocation.");
+
+ m_pbuffer[index].allocated_length = length;
+
+ if (flags == PBUFFER_FLAG_DEFAULT)
+ {
+ err_code = nrf_mem_reserve(&m_pbuffer[index].buffer.p_memory, &m_pbuffer[index].allocated_length);
+ if (err_code == NRF_SUCCESS)
+ {
+ PBUFFER_TRC("Allocated pbuffer at index 0x%08lX", index);
+ (*pp_buffer) = &m_pbuffer[index];
+ }
+ else
+ {
+ PBUFFER_ERR("Failed to allocate memory for packet buffer of size %ld", length);
+ m_pbuffer[index].allocated_length = 0;
+ }
+ }
+ else
+ {
+ PBUFFER_TRC("Allocated pbuffer at index 0x%08lX without any memory allocation.", index);
+ (*pp_buffer) = &m_pbuffer[index];
+ err_code = NRF_SUCCESS;
+ }
+ break;
+ }
+ }
+
+ return err_code;
+}
+
+
+/**@brief Finds the internal buffer based on the external iot_pbuffer_t. */
+static uint32_t pbuffer_find(pbuffer_t ** p_internal_buffer, iot_pbuffer_t * p_buffer)
+{
+ const uint32_t size = sizeof (pbuffer_t);
+ const uint32_t diff = (((uint32_t)p_buffer) - ((uint32_t)m_pbuffer));
+
+ if ((diff > (size * IOT_PBUFFER_MAX_COUNT)) ||
+ ((diff % size) != 0))
+ {
+ return (NRF_ERROR_INVALID_ADDR | IOT_PBUFFER_ERR_BASE);
+ }
+
+ (*p_internal_buffer) = (pbuffer_t *) p_buffer;
+
+ return NRF_SUCCESS;
+}
+
+
+uint32_t iot_pbuffer_init(void)
+{
+ uint32_t index;
+
+ PBUFFER_ENTRY();
+
+ SDK_MUTEX_INIT(m_pbuffer_mutex);
+
+ PBUFFER_MUTEX_LOCK();
+
+ for (index = 0; index < IOT_PBUFFER_MAX_COUNT; index++)
+ {
+ pbuffer_init(&m_pbuffer[index]);
+ }
+
+ m_initialization_state = true;
+
+ PBUFFER_EXIT();
+
+ PBUFFER_MUTEX_UNLOCK();
+
+ return NRF_SUCCESS;
+}
+
+
+uint32_t iot_pbuffer_allocate(iot_pbuffer_alloc_param_t * p_param,
+ iot_pbuffer_t ** pp_pbuffer)
+{
+ VERIFY_MODULE_IS_INITIALIZED();
+ NULL_PARAM_CHECK(p_param);
+ NULL_PARAM_CHECK(pp_pbuffer);
+ VERIFY_PBUFFER_TYPE(p_param->type);
+ VERIFY_PBUFFER_FLAGS(p_param->flags);
+ VERIFY_NON_ZERO_LENGTH(p_param->length);
+
+ PBUFFER_ENTRY();
+
+ PBUFFER_MUTEX_LOCK();
+
+ uint32_t err_code;
+ uint32_t offset;
+ pbuffer_t * p_alloc_buffer;
+
+ // Get offset to be added to length.
+ offset = type_offset_get(p_param->type);
+
+ err_code = pbuffer_allocate(&p_alloc_buffer, ((p_param->length) + offset), p_param->flags);
+ if (err_code == NRF_SUCCESS)
+ {
+ p_alloc_buffer->buffer.length = p_param->length;
+ p_alloc_buffer->buffer.type = p_param->type;
+
+ if (p_param->flags != PBUFFER_FLAG_NO_MEM_ALLOCATION)
+ {
+ p_alloc_buffer->buffer.p_payload = ((p_alloc_buffer->buffer.p_memory) + offset);
+ }
+
+ (*pp_pbuffer) = &p_alloc_buffer->buffer;
+ }
+
+ PBUFFER_MUTEX_UNLOCK();
+
+ PBUFFER_EXIT();
+
+ return err_code;
+}
+
+
+uint32_t iot_pbuffer_reallocate(iot_pbuffer_alloc_param_t * p_param,
+ iot_pbuffer_t * p_pbuffer)
+{
+ VERIFY_MODULE_IS_INITIALIZED();
+ NULL_PARAM_CHECK(p_param);
+ NULL_PARAM_CHECK(p_pbuffer);
+ VERIFY_PBUFFER_TYPE(p_param->type);
+ VERIFY_PBUFFER_FLAGS(p_param->flags);
+ VERIFY_NON_ZERO_LENGTH(p_param->length);
+
+ PBUFFER_ENTRY();
+
+ PBUFFER_MUTEX_LOCK();
+
+ uint32_t err_code;
+ uint32_t realloc_len;
+ pbuffer_t * p_alloc_buffer;
+
+ // Ensure pointer provided is in the ranged managed by the module.
+ err_code = pbuffer_find(&p_alloc_buffer, p_pbuffer);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ // Get realloc_len to be added to length.
+ const uint32_t offset = type_offset_get(p_param->type);
+ realloc_len = p_param->length + offset;
+
+ // Check if requested length cannot be accommodated in the allocated buffer.
+ if (realloc_len > p_alloc_buffer->allocated_length)
+ {
+ // No, it cannot be, request a new buffer.
+ uint8_t * p_new_mem;
+
+ if (p_param->flags != PBUFFER_FLAG_NO_MEM_ALLOCATION)
+ {
+ err_code = nrf_mem_reserve(&p_new_mem, &realloc_len);
+ if (err_code == NRF_SUCCESS)
+ {
+ // Copy data into the new buffer.
+ memcpy (p_new_mem,
+ p_pbuffer->p_memory,
+ p_alloc_buffer->allocated_length);
+
+ // Now free the old buffer. Perform the free
+ nrf_free(p_alloc_buffer->buffer.p_memory);
+
+ p_alloc_buffer->allocated_length = realloc_len;
+ p_alloc_buffer->buffer.p_memory = p_new_mem;
+ p_alloc_buffer->buffer.length = p_param->length;
+ }
+ }
+ }
+
+ if (err_code == NRF_SUCCESS)
+ {
+ p_alloc_buffer->buffer.length = p_param->length;
+ p_alloc_buffer->buffer.type = p_param->type;
+
+ if (p_param->flags == PBUFFER_FLAG_DEFAULT)
+ {
+ p_alloc_buffer->buffer.p_payload = (p_alloc_buffer->buffer.p_memory + offset);
+ }
+ }
+ }
+ else
+ {
+ PBUFFER_ERR("Cannot find buffer to be freed.");
+ }
+
+ PBUFFER_MUTEX_UNLOCK();
+
+ PBUFFER_EXIT();
+
+ return err_code;
+}
+
+
+uint32_t iot_pbuffer_free(iot_pbuffer_t * p_pbuffer, bool free_flag)
+{
+ VERIFY_MODULE_IS_INITIALIZED();
+ NULL_PARAM_CHECK(p_pbuffer);
+
+ PBUFFER_ENTRY();
+
+ PBUFFER_MUTEX_LOCK();
+
+ uint32_t err_code;
+ pbuffer_t * p_alloc_buffer;
+
+ // Ensure pointer provided is in the ranged managed by the module.
+ err_code = pbuffer_find(&p_alloc_buffer, p_pbuffer);
+ if (err_code == NRF_SUCCESS)
+ {
+ if (free_flag == true)
+ {
+ nrf_free(p_alloc_buffer->buffer.p_memory);
+ }
+ pbuffer_init(p_alloc_buffer);
+ }
+ else
+ {
+ PBUFFER_ERR("Cannot find buffer to be freed.");
+ }
+
+ PBUFFER_MUTEX_UNLOCK();
+
+ PBUFFER_EXIT();
+
+ return err_code;
+}
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/pbuffer/iot_pbuffer.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/pbuffer/iot_pbuffer.h
new file mode 100644
index 0000000..e13da3a
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/pbuffer/iot_pbuffer.h
@@ -0,0 +1,175 @@
+/**
+ * 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.
+ *
+ */
+/** @file
+ *
+ * @defgroup iot_pbuffer Packet Buffer
+ * @{
+ * @ingroup iot_sdk_stack
+ * @brief Packet buffer management for IPv6 stack layers to minimize data copy across stack layers.
+ *
+ * @details This module interfaces with the Memory Manager to allocate packet buffers
+ * for the IPv6 stack layers, without each layer having to ensure
+ * sufficient header space for layers below.
+ */
+#ifndef IOT_PBUFFER__
+#define IOT_PBUFFER__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**@brief IPv6 packet type identifiers that are needed to ensure that enough
+ * space is reserved for headers from layers below during memory allocation.
+ */
+typedef enum
+{
+ UNASSIGNED_TYPE = 0, /**< Indicates that the packet buffer is unassigned and not in use. */
+ RAW_PACKET_TYPE = 1, /**< Raw packet, with no room made for headers of any lower layer. */
+ IPV6_PACKET_TYPE = 2, /**< Indicates that the packet buffer is requested for an entire IPv6 packet; pbuffer provisions 40 bytes of IPv6 header. */
+ ICMP6_PACKET_TYPE = 3, /**< Indicates that the packet buffer is requested for an ICMPv6 packet, and provision for 40 bytes of IPv6 header is made by pbuffer. */
+ UDP6_PACKET_TYPE = 4, /**< Indicates that the packet buffer is requested for a UDP packet, and provision for 40 bytes of IPv6 header and UDP header is made by pbuffer. */
+ COAP_PACKET_TYPE = 5 /**< Indicates that the packet buffer is requested for a CoAP packet, and provision for 4 bytes of CoAP header, 8 bytes of UDP header, and 40 bytes of IPv6 header is made. */
+}iot_pbuffer_type_t;
+
+/**@brief Additional information that must be provided to the module during allocation
+ * or reallocation to ensure optimal utilization of memory and avoid unnecessary data
+ * copies.
+ */
+typedef enum
+{
+ PBUFFER_FLAG_DEFAULT = 0, /**< Default behavior with respect to memory allocation when allocating packet buffer. Memory will be allocated for the length indicated by this default.*/
+ PBUFFER_FLAG_NO_MEM_ALLOCATION = 1, /**< Only allocate packet buffer, not memory. This is needed when a packet already exists and the packet buffer is needed only to feed it to the IPv6 stack.*/
+}iot_pbuffer_flags_t;
+
+/**@brief Packet buffer used for exchanging IPv6 payload across layers in both receive and transmit
+ * paths.
+ */
+typedef struct
+{
+ iot_pbuffer_type_t type; /**< Determines if any offset for lower layers must be provisioned for in the stack. */
+ uint8_t * p_memory; /**< Pointer to actual memory allocated for the buffer. */
+ uint8_t * p_payload; /**< Pointer to memory where the payload for the layer that allocates the packet buffer should be contained. */
+ uint32_t length; /**< Length of the payload of the layer processing it. This value can be modified by each layer of the IPv6 stack based on the required header information added by each.*/
+}iot_pbuffer_t;
+
+
+/**@brief Parameters required to allocate the packet buffer. */
+typedef struct
+{
+ iot_pbuffer_type_t type; /**< Payload type for which the packet buffer is requested to be allocated or reallocated. */
+ iot_pbuffer_flags_t flags; /**< Flags that indicate if memory allocation is needed or not. */
+ uint32_t length; /**< Length of payload for which the packet buffer is requested. */
+}iot_pbuffer_alloc_param_t;
+
+
+/**@brief Function for initializing the module.
+ *
+ * @retval NRF_SUCCESS If the module was successfully initialized. Otherwise, an error code that indicates the reason for the failure is returned.
+ */
+uint32_t iot_pbuffer_init(void);
+
+
+/**@brief Function for allocating a packet buffer.
+ *
+ * @param[in] p_param Pointer to allocation parameters that indicate the length of the payload requested,
+ * the type of payload, and additional information using the flags. This
+ * parameter cannot be NULL.
+ * @param[out] pp_pbuffer Pointer to allocated packet buffer. This parameter shall
+ * not be NULL.
+ *
+ * @retval NRF_SUCCESS If the packet buffer was successfully allocated. Otherwise, an error code that indicates the reason for the failure is returned.
+ */
+uint32_t iot_pbuffer_allocate(iot_pbuffer_alloc_param_t * p_param,
+ iot_pbuffer_t ** pp_pbuffer);
+
+
+/**@brief Function for reallocating a packet buffer.
+ *
+ * Reallocation requests are treated as follows:
+ * - If the requested reallocation is less than or equal to the allocated size,
+ * no data is moved, and the function returns NRF_SUCCESS.
+ * - If the requested reallocation is more than what is allocated, the function
+ * requests new memory, backs up existing data, and then frees the previously
+ * allocated memory.
+ * - If reallocation is requested with the PBUFFER_FLAG_NO_MEM_ALLOCATION flag,
+ * the function does not free previously allocated memory or copy it to the
+ * new location. In this case, the application that uses the pbuffer must
+ * decide when to move previously allocated memory and when to free it and
+ * handle this.
+ *
+ * @param[in] p_param Pointer to reallocation parameters that indicate the length of the payload requested,
+ * the type of payload, and additional information using the flags. This
+ * parameter cannot be NULL.
+ * @param[in] p_pbuffer Pointer to the packet buffer being reallocated. This parameter shall
+ * not be NULL.
+ *
+ * @retval NRF_SUCCESS If the packet buffer was successfully reallocated. Otherwise, an error code that indicates the reason for the failure is returned.
+ */
+uint32_t iot_pbuffer_reallocate(iot_pbuffer_alloc_param_t * p_param,
+ iot_pbuffer_t * p_pbuffer);
+
+
+/**@brief Function for freeing a packet buffer.
+ *
+ * This function frees the packet buffer. If the parameter free_flag is set, the
+ * function tries to free the memory allocated as well. This action is performed
+ * irrespective of whether the memory was allocated using the PBUFFER_FLAG_DEFAULT or
+ * the PBUFFER_FLAG_NO_MEM_ALLOCATION flag.
+ *
+ * @param[in] p_pbuffer Pointer to the packet buffer requested to be freed. This parameter shall
+ * not be NULL.
+ * @param[in] free_flag Indicates if the allocated memory should be freed or not when freeing the
+ * packet buffer.
+ *
+ * @retval NRF_SUCCESS If the packet buffer was successfully freed. Otherwise, an error code that indicates the reason for the failure is returned.
+ *
+ */
+uint32_t iot_pbuffer_free(iot_pbuffer_t * p_pbuffer, bool free_flag);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_PBUFFER__
+
+/**@} */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/sntp_client/sntp_client.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/sntp_client/sntp_client.c
new file mode 100644
index 0000000..2b79462
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/sntp_client/sntp_client.c
@@ -0,0 +1,607 @@
+/**
+ * Copyright (c) 2015 - 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 "udp_api.h"
+#include "ipv6_api.h"
+#include "app_error.h"
+#include "sdk_common.h"
+#include "sntp_client.h"
+
+#if SNTP_CLIENT_CONFIG_LOG_ENABLED
+
+#define NRF_LOG_MODULE_NAME sntp
+
+#define NRF_LOG_LEVEL SNTP_CLIENT_CONFIG_LOG_LEVEL
+#define NRF_LOG_INFO_COLOR SNTP_CLIENT_CONFIG_INFO_COLOR
+#define NRF_LOG_DEBUG_COLOR SNTP_CLIENT_CONFIG_DEBUG_COLOR
+
+#include "nrf_log.h"
+NRF_LOG_MODULE_REGISTER();
+
+#define SNTP_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */
+#define SNTP_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */
+#define SNTP_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */
+
+#define SNTP_ENTRY() SNTP_TRC(">> %s", __func__)
+#define SNTP_EXIT() SNTP_TRC("<< %s", __func__)
+
+#else // SNTP_CLIENT_CONFIG_LOG_ENABLED
+
+#define SNTP_TRC(...) /**< Disables traces. */
+#define SNTP_DUMP(...) /**< Disables dumping of octet streams. */
+#define SNTP_ERR(...) /**< Disables error logs. */
+
+#define SNTP_ENTRY(...)
+#define SNTP_EXIT(...)
+
+#endif // SNTP_CLIENT_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.
+ * SNTP_CLIENT_DISABLE_API_PARAM_CHECK should be defined to disable these checks.
+ *
+ * @{
+ */
+#if (SNTP_CLIENT_DISABLE_API_PARAM_CHECK == 0)
+
+/**
+ * @brief Verify NULL parameters are not passed to an API by application.
+ */
+#define NULL_PARAM_CHECK(PARAM) \
+ if ((PARAM) == NULL) \
+ { \
+ return (NRF_ERROR_NULL | IOT_NTP_ERR_BASE); \
+ }
+
+/**
+ * @brief Verify that not zero is passed to an API by application.
+ */
+#define ZERO_PARAM_CHECK(PARAM) \
+ if ((PARAM) == 0x00) \
+ { \
+ return (NRF_ERROR_NULL | IOT_NTP_ERR_BASE); \
+ }
+
+/**
+ * @brief Macro to check if module is initialized.
+ */
+#define VERIFY_MODULE_IS_INITIALIZED() \
+ if (m_sntp_client_state == SNTP_CLIENT_STATE_UNINITIALIZED) \
+ { \
+ return (SDK_ERR_MODULE_NOT_INITIALIZED | IOT_NTP_ERR_BASE); \
+ }
+
+#else // SNTP_CLIENT_DISABLE_API_PARAM_CHECK
+
+#define NULL_PARAM_CHECK(PARAM)
+#define ZERO_PARAM_CHECK(PARAM)
+#define VERIFY_MODULE_IS_INITIALIZED()
+
+#endif //SNTP_CLIENT_DISABLE_API_PARAM_CHECK
+/** @} */
+
+/**
+ * @defgroup ble_sntp_c_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 SNTP_C_MUTEX_LOCK() SDK_MUTEX_LOCK(m_sntp_c_mutex) /**< Lock module using mutex */
+#define SNTP_C_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_sntp_c_mutex) /**< Unlock module using mutex */
+/** @} */
+
+#define TIME_AT_1970 2208988800UL // Number of seconds between 1st Jan 1900 and 1st Jan 1970, for NTP<->Unix time conversion.
+#define PROTOCOL_MODE_SERVER 4
+
+/**@brief NTP Header Format. */
+typedef struct
+{
+ uint8_t flags; /**< Please see RFC 4330. */
+ uint8_t stratum; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */
+ uint8_t poll_interval; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */
+ uint8_t precision; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */
+ uint32_t root_delay; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */
+ uint32_t root_dispersion; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */
+ uint32_t reference_id; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */
+ uint32_t reference_timestamp[2]; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */
+ uint32_t originate_timestamp[2]; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */
+ uint32_t receive_timestamp[2]; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */
+ uint32_t transmit_timestamp[2]; /**< Please see RFC 4330. */
+} ntp_header_t;
+
+typedef enum
+{
+ SNTP_CLIENT_STATE_UNINITIALIZED = 1,
+ SNTP_CLIENT_STATE_IDLE,
+ SNTP_CLIENT_STATE_BUSY
+} sntp_client_state_t;
+
+typedef struct
+{
+ time_t unix_time;
+ iot_timer_time_in_ms_t wall_clock_value;
+} local_timestamp_t;
+
+SDK_MUTEX_DEFINE(m_sntp_c_mutex) /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */
+static sntp_client_state_t m_sntp_client_state = SNTP_CLIENT_STATE_UNINITIALIZED;
+static ipv6_addr_t * m_p_ntp_server_address;
+static uint16_t m_ntp_server_port;
+static bool m_do_sync_local_time;
+static iot_timer_time_in_ms_t m_time_of_last_transmission;
+static uint8_t m_retransmission_count;
+static sntp_evt_handler_t m_app_evt_handler;
+static udp6_socket_t m_udp_socket;
+static local_timestamp_t m_local_time;
+
+/**@brief Function for checking if a received NTP packet is valid.
+ *
+ * @param[in] p_ntp_response Pointer to the NTP packet header.
+ *
+ */
+static bool is_response_valid(ntp_header_t * p_ntp_response)
+{
+ if (((p_ntp_response->transmit_timestamp[0] == 0x00) && \
+ (p_ntp_response->transmit_timestamp[1] == 0x00)) || \
+ ((p_ntp_response->flags & 0x38) == 0x00) || \
+ ((p_ntp_response->flags & 0x07) != PROTOCOL_MODE_SERVER))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+/**@brief Callback handler to receive data on the UDP port.
+ *
+ * @param[in] p_socket Socket identifier.
+ * @param[in] p_ip_header IPv6 header containing source and destination addresses.
+ * @param[in] p_udp_header UDP header identifying local and remote endpoints.
+ * @param[in] process_result Result of data reception, there could be possible errors like
+ * invalid checksum etc.
+ * @param[in] p_rx_packet Packet buffer containing the received data packet.
+ *
+ * @retval NRF_SUCCESS Indicates received data was handled successfully, else an an
+ * error code indicating reason for failure..
+ */
+static uint32_t port_data_callback(const udp6_socket_t * p_socket,
+ const ipv6_header_t * p_ip_header,
+ const udp6_header_t * p_udp_header,
+ uint32_t process_result,
+ iot_pbuffer_t * p_rx_packet)
+{
+ SNTP_C_MUTEX_LOCK();
+
+ SNTP_ENTRY();
+
+ uint32_t err_code = NRF_SUCCESS;
+ ntp_header_t * p_ntp_header = (ntp_header_t *)p_rx_packet->p_payload;
+
+ if (m_sntp_client_state != SNTP_CLIENT_STATE_BUSY)
+ {
+ SNTP_ERR("Unexpected NTP response received.");
+
+ SNTP_C_MUTEX_UNLOCK();
+
+ SNTP_EXIT();
+ return (NRF_ERROR_INVALID_STATE | IOT_NTP_ERR_BASE);
+ }
+ else
+ {
+ // Check UDP process result and data length.
+ if ((process_result != NRF_SUCCESS) || p_rx_packet->length < sizeof(ntp_header_t))
+ {
+ SNTP_ERR("Received erroneous NTP response.");
+
+ m_sntp_client_state = SNTP_CLIENT_STATE_IDLE;
+ m_retransmission_count = 0;
+ m_do_sync_local_time = false;
+ err_code = (NRF_ERROR_INVALID_DATA | IOT_NTP_ERR_BASE);
+
+ SNTP_C_MUTEX_UNLOCK();
+
+ if (m_app_evt_handler != NULL)
+ {
+ m_app_evt_handler(&(p_ip_header->srcaddr), p_udp_header->srcport, err_code, \
+ (sntp_client_cb_param_t){ .callback_data = 0x00 });
+ }
+
+ SNTP_EXIT();
+ return err_code;
+ }
+ else
+ {
+ if (!is_response_valid(p_ntp_header))
+ {
+ SNTP_ERR("Received bad NTP response.");
+
+ m_sntp_client_state = SNTP_CLIENT_STATE_IDLE;
+ m_retransmission_count = 0;
+ m_do_sync_local_time = false;
+ err_code = NTP_SERVER_BAD_RESPONSE;
+
+ SNTP_C_MUTEX_UNLOCK();
+
+ if (m_app_evt_handler != NULL)
+ {
+ m_app_evt_handler(&(p_ip_header->srcaddr), \
+ p_udp_header->srcport, \
+ err_code, \
+ (sntp_client_cb_param_t){ .callback_data = 0x00 });
+ }
+
+ SNTP_EXIT();
+ return err_code;
+ }
+ else
+ {
+ // Check if Kiss-o'-Death packet.
+ if (p_ntp_header->stratum == 0x00)
+ {
+ SNTP_TRC("Received Kiss-o'-Death packet.");
+
+ m_sntp_client_state = SNTP_CLIENT_STATE_IDLE;
+ m_retransmission_count = 0;
+ m_do_sync_local_time = false;
+
+ SNTP_C_MUTEX_UNLOCK();
+
+ if (m_app_evt_handler != NULL)
+ {
+ m_app_evt_handler(&(p_ip_header->srcaddr), p_udp_header->srcport, \
+ NTP_SERVER_KOD_PACKET_RECEIVED, \
+ (sntp_client_cb_param_t){ .callback_data \
+ = p_ntp_header->reference_id });
+ }
+
+ SNTP_EXIT();
+ return NRF_SUCCESS;
+ }
+ else
+ {
+ // Process decent NTP response.
+ time_t time_from_response = (HTONL(p_ntp_header->transmit_timestamp[0])) - \
+ TIME_AT_1970;
+
+ if (m_do_sync_local_time)
+ {
+ iot_timer_time_in_ms_t wall_clock_value;
+ UNUSED_VARIABLE(iot_timer_wall_clock_get(&wall_clock_value));
+ m_local_time.unix_time = time_from_response;
+ m_local_time.wall_clock_value = wall_clock_value;
+ m_do_sync_local_time = false;
+ }
+
+ m_sntp_client_state = SNTP_CLIENT_STATE_IDLE;
+ m_retransmission_count = 0;
+
+ SNTP_C_MUTEX_UNLOCK();
+
+ if (m_app_evt_handler != NULL)
+ {
+ m_app_evt_handler(&(p_ip_header->srcaddr), \
+ p_udp_header->srcport, \
+ NRF_SUCCESS, \
+ (sntp_client_cb_param_t){ .time_from_server = \
+ time_from_response });
+ }
+
+ SNTP_EXIT();
+ return NRF_SUCCESS;
+ }
+ }
+ }
+ }
+}
+
+
+uint32_t sntp_client_init(const sntp_client_init_param_t * p_sntp_client_init_param)
+{
+ NULL_PARAM_CHECK(p_sntp_client_init_param);
+ ZERO_PARAM_CHECK(p_sntp_client_init_param->local_udp_port);
+
+ SNTP_ENTRY();
+
+ SDK_MUTEX_INIT(m_sntp_c_mutex);
+ SNTP_C_MUTEX_LOCK();
+
+ uint32_t err_code;
+
+ memset(&m_local_time, 0x00, sizeof(m_local_time));
+ m_app_evt_handler = p_sntp_client_init_param->app_evt_handler;
+
+ //Request new socket creation.
+ err_code = udp6_socket_allocate(&m_udp_socket);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ // Bind the socket to the local port.
+ err_code = udp6_socket_bind(&m_udp_socket, \
+ IPV6_ADDR_ANY, \
+ p_sntp_client_init_param->local_udp_port);
+ if (err_code == NRF_SUCCESS)
+ {
+ //Register data receive callback.
+ err_code = udp6_socket_recv(&m_udp_socket, port_data_callback);
+ }
+ if (err_code != NRF_SUCCESS)
+ {
+ //Not all procedures succeeded with allocated socket, hence free it.
+ UNUSED_VARIABLE(udp6_socket_free(&m_udp_socket));
+ }
+ }
+
+ if (err_code == NRF_SUCCESS)
+ {
+ m_sntp_client_state = SNTP_CLIENT_STATE_IDLE;
+ }
+
+ SNTP_EXIT();
+
+ SNTP_C_MUTEX_UNLOCK();
+
+ return err_code;
+}
+
+
+static uint32_t local_time_get(time_t * p_local_time)
+{
+ uint32_t err_code = NRF_SUCCESS;
+ iot_timer_time_in_ms_t delta_ms;
+ err_code = iot_timer_wall_clock_delta_get(&m_local_time.wall_clock_value, &delta_ms);
+ if (err_code != NRF_SUCCESS)
+ {
+ return err_code;
+ }
+
+ *p_local_time = m_local_time.unix_time + (delta_ms / 1000);
+
+ return err_code;
+}
+
+
+uint32_t sntp_client_local_time_get(time_t * p_current_time)
+{
+ VERIFY_MODULE_IS_INITIALIZED();
+ NULL_PARAM_CHECK(p_current_time);
+
+ uint32_t err_code = NRF_SUCCESS;
+
+ SNTP_ENTRY();
+
+ SNTP_C_MUTEX_LOCK();
+
+ err_code = local_time_get(p_current_time);
+
+ SNTP_EXIT();
+
+ SNTP_C_MUTEX_UNLOCK();
+
+ return err_code;
+}
+
+
+/**@brief Function for sending SNTP query.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, otherwise an error code indicating reason
+ * for failure.
+ */
+static uint32_t sntp_query_send()
+{
+ uint32_t err_code;
+ iot_pbuffer_t * p_buffer;
+ iot_pbuffer_alloc_param_t buffer_param;
+ time_t current_local_time;
+
+ err_code = local_time_get(&current_local_time);
+ if (err_code != NRF_SUCCESS)
+ {
+ SNTP_ERR("An error occurred while getting local time value!");
+ return err_code;
+ }
+
+ buffer_param.type = UDP6_PACKET_TYPE;
+ buffer_param.flags = PBUFFER_FLAG_DEFAULT;
+ buffer_param.length = sizeof(ntp_header_t);
+
+ UNUSED_VARIABLE(iot_timer_wall_clock_get(&m_time_of_last_transmission));
+
+ // Allocate packet buffer.
+ err_code = iot_pbuffer_allocate(&buffer_param, &p_buffer);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ ntp_header_t * p_ntp_header = (ntp_header_t *)p_buffer->p_payload;
+ memset(p_ntp_header, 0x00, sizeof(ntp_header_t));
+
+ // Fill NTP header fields.
+ p_ntp_header->flags = 0x1B; // LI = 0; VN = 3; Mode = 3
+ p_ntp_header->transmit_timestamp[0] = HTONL((uint32_t)(current_local_time + TIME_AT_1970));
+
+ // Send NTP query using UDP socket.
+ err_code = udp6_socket_sendto(&m_udp_socket, \
+ m_p_ntp_server_address, \
+ m_ntp_server_port, \
+ p_buffer);
+ if (err_code != NRF_SUCCESS)
+ {
+ SNTP_ERR("Unable to send query on UDP socket. Reason %08lx.", err_code);
+
+ // Free the allocated buffer as send procedure has failed.
+ UNUSED_VARIABLE(iot_pbuffer_free(p_buffer, true));
+ }
+ }
+ else
+ {
+ SNTP_ERR("No memory to allocate packet buffer.");
+ }
+
+ return err_code;
+}
+
+
+uint32_t sntp_client_server_query(ipv6_addr_t * p_ntp_server_address, \
+ uint16_t ntp_server_udp_port, \
+ bool sync_local_time)
+{
+ VERIFY_MODULE_IS_INITIALIZED();
+ NULL_PARAM_CHECK(p_ntp_server_address);
+ ZERO_PARAM_CHECK(ntp_server_udp_port);
+
+ uint32_t err_code = NRF_SUCCESS;
+ SNTP_ENTRY();
+
+ SNTP_C_MUTEX_LOCK();
+
+ if (m_sntp_client_state != SNTP_CLIENT_STATE_IDLE)
+ {
+ SNTP_EXIT();
+ return (NRF_ERROR_BUSY | IOT_NTP_ERR_BASE);
+ }
+
+ m_p_ntp_server_address = p_ntp_server_address;
+ m_ntp_server_port = ntp_server_udp_port;
+ m_do_sync_local_time = sync_local_time;
+
+ err_code = sntp_query_send();
+ if (err_code == NRF_SUCCESS)
+ {
+ m_sntp_client_state = SNTP_CLIENT_STATE_BUSY;
+ }
+
+ SNTP_EXIT();
+
+ SNTP_C_MUTEX_UNLOCK();
+
+ return err_code;
+}
+
+
+/**@brief Function for determining whether it is time to retransmit a query.
+ *
+ */
+static bool is_it_time_to_retransmit()
+{
+ uint32_t err_code = NRF_SUCCESS;
+ iot_timer_time_in_ms_t delta_ms = 0;
+
+ err_code = iot_timer_wall_clock_delta_get(&m_time_of_last_transmission, &delta_ms);
+ if (err_code != NRF_SUCCESS)
+ {
+ return true;
+ }
+ if (delta_ms >= (SNTP_RETRANSMISSION_INTERVAL * 1000))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+void sntp_client_timeout_process(iot_timer_time_in_ms_t wall_clock_value)
+{
+ SNTP_C_MUTEX_LOCK();
+
+ UNUSED_PARAMETER(wall_clock_value);
+
+ if (m_sntp_client_state == SNTP_CLIENT_STATE_BUSY)
+ {
+ if (is_it_time_to_retransmit())
+ {
+ m_retransmission_count++;
+ if (m_retransmission_count > SNTP_MAX_RETRANSMISSION_COUNT)
+ {
+ m_sntp_client_state = SNTP_CLIENT_STATE_IDLE;
+ m_retransmission_count = 0;
+ m_do_sync_local_time = false;
+
+ SNTP_C_MUTEX_UNLOCK();
+
+ if (m_app_evt_handler != NULL)
+ {
+ m_app_evt_handler(m_p_ntp_server_address, \
+ m_ntp_server_port, \
+ NTP_SERVER_UNREACHABLE, \
+ (sntp_client_cb_param_t){ .callback_data = 0x00 });
+ }
+
+ SNTP_TRC("NTP server did not respond to query.");
+ return;
+ }
+ else
+ {
+ SNTP_TRC("Query retransmission [%d].", m_retransmission_count);
+ UNUSED_VARIABLE(sntp_query_send());
+ }
+ }
+ }
+
+ SNTP_C_MUTEX_UNLOCK();
+ return;
+}
+
+
+uint32_t sntp_client_uninitialize()
+{
+ VERIFY_MODULE_IS_INITIALIZED();
+
+ SNTP_ENTRY();
+
+ SNTP_C_MUTEX_LOCK();
+
+ // Free UDP socket.
+ UNUSED_VARIABLE(udp6_socket_free(&m_udp_socket));
+
+ m_sntp_client_state = SNTP_CLIENT_STATE_UNINITIALIZED;
+ m_retransmission_count = 0;
+ m_do_sync_local_time = false;
+
+ SNTP_EXIT();
+
+ SNTP_C_MUTEX_UNLOCK();
+
+ return NRF_SUCCESS;
+}
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/sntp_client/sntp_client.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/sntp_client/sntp_client.h
new file mode 100644
index 0000000..d9cb7eb
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/sntp_client/sntp_client.h
@@ -0,0 +1,202 @@
+/**
+ * Copyright (c) 2015 - 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.
+ *
+ */
+/** @file
+ *
+ * @defgroup sntp_client SNTP Client
+ * @{
+ * @ingroup iot_sdk_stack
+ * @brief Simple Network Time Protocol (SNTP) client for obtaining and storing local unix time.
+ *
+ * @details Concurrent queries are not supported. Exponential-backoff algorithm for
+ * retransmissions is not implemented, retransmissions are triggered at regular intervals.
+ *
+ */
+
+#ifndef SNTP_CLIENT_H__
+#define SNTP_CLIENT_H__
+
+#include <stdint.h>
+/*lint -save -e43 -e1504 */
+#include <time.h>
+/*lint -restore */
+#include "sdk_config.h"
+#include "nrf_error.h"
+#include "ipv6_api.h"
+#include "iot_timer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define KISS_CODE_LEN 4 /**< Kiss-o'-Death packets convey kiss codes as 4 character long @c ASCII messages. */
+
+/**@brief SNTP client callback parameter.
+ */
+typedef union
+{
+ time_t time_from_server; /**< Unix time if a proper proper response is received from an NTP server. */
+ uint32_t callback_data; /**< Data pertaining to the event triggering the callback. The kiss code from any Kiss-o'-Death packets. */
+} sntp_client_cb_param_t;
+
+/**@brief SNTP client callback type.
+ *
+ * @details Execution of the callback function marks the completion of an SNTP query.
+ * The callback will be executed if a response is received from the NTP server,
+ * or if the server remains unresponsive even after @ref SNTP_MAX_RETRANSMISSION_COUNT
+ * is reached.
+ *
+ * @param[in] p_ntp_srv_addr Pointer to the source IPv6 address of the NTP response, or to
+ * the IPv6 address of the NTP server targeted by the unsuccessful
+ * query.
+ * @param[in] ntp_srv_udp_port The source UDP port of the NTP response, or the UDP port of
+ * the NTP server targeted by the unsuccessful query.
+ * @param[in] process_result The value of this parameter reveals whether a response from the
+ * NTP server or a timeout triggered the callback.
+ * @param[in] callback_parameter This parameter holds the unix time from the server after a
+ * successful query, or the kiss code if a Kiss-o'-Death packet
+ * was received. Otherwise NULL.
+ *
+ * @retval None.
+ *
+ */
+typedef void (*sntp_evt_handler_t)(const ipv6_addr_t * p_ntp_srv_addr, \
+ uint16_t ntp_srv_udp_port, \
+ uint32_t process_result, \
+ sntp_client_cb_param_t callback_parameter);
+
+/**@brief SNTP client initialization structure.
+ *
+ * @note @ref app_evt_handler can be set to zero to disable callbacks.
+ */
+typedef struct
+{
+ sntp_evt_handler_t app_evt_handler; /**< Pointer to the event handler callback function. Triggered by a response from an NTP server to an SNTP query, or a retransmission timeout after @ref SNTP_MAX_RETRANSMISSION_COUNT is reached. */
+ uint16_t local_udp_port; /**< Local port used by the UDP socket allocated for the SNTP client module. Cannot be NULL. */
+} sntp_client_init_param_t;
+
+/**
+ * @brief Function for initializing the SNTP client module.
+ *
+ * @details The SNTP client uses UDP as transport layer, therefore, one UDP socket is allocated
+ * for the module and is used to transmit any future queries.
+ *
+ * @param[in] p_sntp_client_init_param Pointer to the initialization structure for the SNTP client.
+ * Should not be NULL.
+ *
+ * @note The event handler in the initialization structure can be set to NULL to disable callbacks
+ * from the module.
+ *
+ * @retval NRF_SUCCESS Module successfully initialized.
+ * @retval NRF_ERROR_NULL If @b p_sntp_client_init_param is NULL, or if it points to a local UDP
+ * port that is NULL.
+ *
+ */
+uint32_t sntp_client_init(const sntp_client_init_param_t * p_sntp_client_init_param);
+
+/**
+ * @brief Function for uninitializing the SNTP client module.
+ *
+ * @details This procedure frees up the UDP socket previously allocated to the module.
+ * Any pending retransmissions are cleared and no more callbacks will be executed.
+ *
+ * @retval NRF_SUCCESS Module successfully uninitialized.
+ * @retval SDK_ERR_MODULE_NOT_INITIALIZED The module was not initialized.
+ *
+ */
+uint32_t sntp_client_uninitialize(void);
+
+/**@brief Function for sending an SNTP query to the specified NTP server.
+ *
+ * @details The local unix time is set to zero (1-Jan-70) when the module is initialized. It can
+ * be updated by using the @ref sntp_client_server_query procedure. The accuracy of the
+ * output is depending on the wall clock of the IoT Timer module.
+ *
+ * @param[in] p_ntp_server_address Pointer to the IPv6 address of the NTP server. This memory must
+ * be resident until the query is completed.
+ * @param[in] ntp_server_udp_port Destination port of the NTP server. The UDP port number
+ * assigned by the IANA to NTP is 123.
+ * @param[in] sync_local_time A boolean value telling the module whether to synchronize its
+ * local clock with any response received from the NTP server.
+ *
+ * @retval NRF_SUCCESS SNTP query successfully sent.
+ * @retval SDK_ERR_MODULE_NOT_INITIALIZED The module was not initialized.
+ * @retval NRF_ERROR_NULL If @b p_ntp_server_address or @b ntp_server_udp_port
+ * is a NULL pointer.
+ *
+ */
+uint32_t sntp_client_server_query(ipv6_addr_t * p_ntp_server_address, \
+ uint16_t ntp_server_udp_port, \
+ bool sync_local_time);
+
+/**@brief Function for getting the local unix time from the module.
+ *
+ * @details The local unix time is set to zero (1-Jan-70) when the module is initialized. It can
+ * be updated by using @ref sntp_client_server_query procedure. The accuracy of the
+ * output is depending on the wall clock of the IoT Timer module.
+ *
+ * @param[out] p_current_time Local unix time.
+ *
+ * @retval NRF_SUCCESS Getting locally stored unix time successful.
+ * @retval SDK_ERR_MODULE_NOT_INITIALIZED The module was not initialized.
+ * @retval NRF_ERROR_NULL If @b p_current_time is a NULL pointer.
+ *
+ */
+uint32_t sntp_client_local_time_get(time_t * p_current_time);
+
+/**@brief Function for performing retransmissions of SNTP queries.
+ *
+ * @details The SNTP client module implements the retransmission mechanism by invoking this
+ * function periodically. This procedure is to be added to the IoT Timer client list
+ * and has to be called repeatedly with a minimum period of SNTP_RETRANSMISSION_INTERVAL.
+ *
+ * @param[in] wall_clock_value The value of the wall clock that triggered the callback.
+ *
+ * @retval None.
+ *
+ */
+void sntp_client_timeout_process(iot_timer_time_in_ms_t wall_clock_value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // SNTP_CLIENT_H__
+
+/**@} */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/tftp/iot_tftp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/tftp/iot_tftp.c
new file mode 100644
index 0000000..65f0ec1
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/tftp/iot_tftp.c
@@ -0,0 +1,2455 @@
+/**
+ * Copyright (c) 2015 - 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 "sdk_config.h"
+#include "iot_tftp.h"
+#include "iot_common.h"
+#include "udp_api.h"
+#include "app_util.h"
+
+#if TFTP_CONFIG_LOG_ENABLED
+
+#define NRF_LOG_MODULE_NAME tftp
+
+#define NRF_LOG_LEVEL TFTP_CONFIG_LOG_LEVEL
+#define NRF_LOG_INFO_COLOR TFTP_CONFIG_INFO_COLOR
+#define NRF_LOG_DEBUG_COLOR TFTP_CONFIG_DEBUG_COLOR
+
+#include "nrf_log.h"
+NRF_LOG_MODULE_REGISTER();
+
+#define TFTP_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */
+#define TFTP_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */
+#define TFTP_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */
+
+#define TFTP_ENTRY() TFTP_TRC(">> %s", __func__)
+#define TFTP_EXIT() TFTP_TRC("<< %s", __func__)
+
+#else // TFTP_CONFIG_LOG_ENABLED
+
+#define TFTP_TRC(...) /**< Disables traces. */
+#define TFTP_DUMP(...) /**< Disables dumping of octet streams. */
+#define TFTP_ERR(...) /**< Disables error logs. */
+
+#define TFTP_ENTRY(...)
+#define TFTP_EXIT(...)
+
+#endif // TFTP_CONFIG_LOG_ENABLED
+/**
+ * @defgroup tftp_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 TFTP_MUTEX_LOCK() SDK_MUTEX_LOCK(m_tftp_mutex) /**< Lock module using mutex. */
+#define TFTP_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_tftp_mutex) /**< Unlock module using mutex. */
+/** @} */
+
+#define TFTP_HEADER_SIZE 2 /**< uint16_t opcode number. */
+#define TFTP_BLOCK_ID_SIZE 2 /**< uint16_t block id number. */
+#define TFTP_ERR_CODE_SIZE 2 /**< uint16_t error code. */
+#define TFTP_DEFAULT_BLOCK_SIZE 512 /**< uint16_t default data block size. */
+#define TFTP_DEFAULT_PORT 69 /**< uint16_t default TFTP server port number. */
+
+/**@brief Supported TFTP options. */
+#define OPTION_MODE_ASCII "netascii" /**< NETASCII mode string defined inside RFC1350. */
+#define OPTION_MODE_OCTET "octet" /**< OCTET mode string defined inside RFC1350. */
+#define OPTION_BLKSIZE "blksize" /**< Block Size option string defined inside RFC2348. */
+#define OPTION_TIMEOUT "timeout" /**< Timeout option string defined inside RFC2349. */
+#define OPTION_SIZE "tsize" /**< Transfer Size option string defined inside RFC2348. */
+
+#define NEXT_RETR_MAX_LENGTH 4 /**< Maximum length of TFTP "timeout" option value. */
+#define BLKSIZE_MAX_LENGTH 10 /**< Maximum length of TFTP "blksize" option value. */
+#define FILE_SIZE_MAX_LENGTH 10 /**< Maximum length of TFTP "tsize" option value. */
+
+#define OPTION_ERROR_MESSAGE "Unsupported option(s) requested"
+#define UDP_ERROR_MSG "UDP Error!"
+#define LENGTH_ERROR_MSG "Invalid packet length!"
+#define UNINT_ERROR_MSG "Connection reset by peer"
+#define ACCESS_ERROR_MSG "Access denied (cannot read/write from file)"
+#define OPTION_SIZE_REQUEST_VALUE "0"
+
+/**@brief TFTP Error codes. */
+#define ERR_UNDEFINED 0 /**< Not defined, see error message (if any). */
+#define ERR_FILE_NOT_FOUND 1 /**< File not found. */
+#define ERR_ACCESS_ERROR 2 /**< Access violation. */
+#define ERR_STORAGE_FULL 3 /**< Disk full or allocation exceeded. */
+#define ERR_INVALID_OP 4 /**< Illegal TFTP operation. */
+#define ERR_INVALID_TID 5 /**< Unknown transfer ID. */
+#define ERR_FILE_EXISTS 6 /**< File already exists. */
+#define ERR_BAD_USER 7 /**< No such user. */
+#define ERR_OPTION_REJECT 8 /**< Reject proposed options. */
+
+/**@brief TFTP opcode's. This field specifies type of packet. */
+#define TYPE_RRQ 1 /**< Read request (RRQ). */
+#define TYPE_WRQ 2 /**< Write request (WRQ). */
+#define TYPE_DATA 3 /**< Data (DATA). */
+#define TYPE_ACK 4 /**< Acknowledgment (ACK). */
+#define TYPE_ERR 5 /**< Error (ERROR). */
+#define TYPE_OACK 6 /**< Option Acknowledgment (RRQ/WRQ ACK). */
+
+/**
+ * @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.
+ * DNS6_DISABLE_API_PARAM_CHECK should be set to 0 to enable these checks.
+ *
+ * @{
+ */
+
+#if (TFTP_DISABLE_API_PARAM_CHECK == 0)
+
+/**@brief Verify NULL parameters are not passed to API by application. */
+#define NULL_PARAM_CHECK(PARAM) \
+ if ((PARAM) == NULL) \
+ { \
+ return (NRF_ERROR_NULL | IOT_TFTP_ERR_BASE); \
+ }
+
+#else // TFTP_DISABLE_API_PARAM_CHECK
+
+#define NULL_PARAM_CHECK(PARAM)
+
+#endif // DNS6_DISABLE_API_PARAM_CHECK
+
+/**@brief Check err_code, free p_buffer and return on error. */
+#define PBUFFER_FREE_IF_ERROR(err_code) \
+ if (err_code != NRF_SUCCESS) \
+ { \
+ (void)iot_pbuffer_free(p_buffer, true); \
+ return err_code; \
+ }
+
+/**@brief Convert TFTP error code into IOT error with appropriate base. */
+#define CONVERT_TO_IOT_ERROR(error_code) \
+ ((IOT_TFTP_ERR_BASE+0x0040) + NTOHS(error_code))
+
+/**@brief Convert IOT error into TFTP error code by removing TFTP error base. */
+#define CONVERT_TO_TFTP_ERROR(error_code) \
+ (HTONS(err_code - (IOT_TFTP_ERR_BASE+0x0040)))
+
+/**@brief Iterator for string list delimited with '\0'. */
+typedef struct
+{
+ char * p_start; /**< Pointer to the beginning of a string. */
+ char * p_end; /**< Pointer to the end of a string. */
+ struct curr_struct
+ {
+ char * p_key; /**< Pointer to the last, found key string. */
+ char * p_value; /**< Pointer to the last, found value string. */
+ } curr;
+} option_iter_t;
+
+/**@brief Allowed states of a single TFTP instance. */
+typedef enum
+{
+ STATE_FREE = 0, /**< Start state, after calling UDP to allocate socket. */
+ STATE_IDLE, /**< Socket is allocated, but not used. */
+ STATE_CONNECTING_RRQ, /**< RRQ packet sent. Waiting for response. */
+ STATE_CONNECTING_WRQ, /**< WRQ packet sent. Waiting for response. */
+ STATE_SENDING, /**< Sending file and receiving ACK. */
+ STATE_SEND_HOLD, /**< Sending held. Waiting for resume call. */
+ STATE_RECEIVING, /**< Receiving file and sending ACK. */
+ STATE_RECV_HOLD, /**< Receiving held. Waiting for resume call. */
+ STATE_RECV_COMPLETE /**< State after receiving last DATA, before sending last ACK packet. There won't be another UDP event to emit IOT_TFTP_EVT_TRANSFER_GET_COMPLETE event, so next resume() should emit that event. */
+} tftp_state_t;
+
+/**@brief Internal TFTP instance structure. */
+typedef struct
+{
+ iot_tftp_trans_params_t init_params; /**< Connection parameters set during initialization. */
+ iot_tftp_trans_params_t connect_params; /**< Negotiated Connection parameters. */
+ udp6_socket_t socket; /**< UDP socket assigned to single instance. */
+ tftp_state_t state; /**< Integer representing current state of an instance. */
+ iot_tftp_callback_t callback; /**< User defined callback (passed inside initial parameters structure). */
+ iot_file_t * p_file; /**< Pointer to destination/source file assigned in get/put call. */
+ const char * p_path; /**< Path of the file on the remote node. */
+ uint16_t block_id; /**< ID of last received/sent data block. */
+ uint16_t src_tid; /**< UDP port used for sending information to the server. */
+ uint16_t dst_tid; /**< UDP port on which all packets will be sent. At first - dst_port (see below), then reassigned. */
+ uint16_t dst_port; /**< UDP port on which request packets will be sent. Usually DEFAULT_PORT. */
+ const char * p_password; /**< Pointer to a constant string containing password passed inside Read/Write Requests. */
+ ipv6_addr_t addr; /**< IPv6 server address. */
+ iot_pbuffer_t * p_packet; /**< Reference to the temporary packet buffer. */
+ uint8_t retries; /**< Number of already performed retries. */
+ volatile iot_timer_time_in_ms_t request_timeout; /**< Number of milliseconds on which last request should be retransmitted. */
+} tftp_instance_t;
+
+SDK_MUTEX_DEFINE(m_tftp_mutex) /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */
+static tftp_instance_t m_instances[TFTP_MAX_INSTANCES]; /**< Array of allowed TFTP instances. */
+
+/**@brief Function for finding free TFTP instance index.
+ *
+ * @param[out] p_index Index being found.
+ *
+ * @retval NRF_SUCCESS if passed instance was found, else NRF_ERROR_NO_MEM error code will
+ * be returned.
+ */
+static uint32_t find_free_instance(uint32_t * p_index)
+{
+ uint32_t index = 0;
+
+ for (index = 0; index < TFTP_MAX_INSTANCES; index++)
+ {
+ if (m_instances[index].state == STATE_FREE)
+ {
+ *p_index = index;
+
+ return NRF_SUCCESS;
+ }
+ }
+
+ return (NRF_ERROR_NO_MEM | IOT_TFTP_ERR_BASE);
+}
+
+/**@brief Function for resolving instance index by passed pointer.
+ *
+ * @param[in] p_tftp Pointer representing TFTP instance in user space.
+ * @param[out] p_index Index of passed TFTP instance.
+ *
+ * @retval NRF_SUCCESS if passed instance was found, else NRF_ERROR_INVALID_PARAM error code
+ * will be returned.
+ */
+static uint32_t find_instance(iot_tftp_t * p_tftp, uint32_t * p_index)
+{
+ if (*p_tftp > TFTP_MAX_INSTANCES)
+ {
+ return (NRF_ERROR_INVALID_PARAM | IOT_TFTP_ERR_BASE);
+ }
+
+ *p_index = *p_tftp;
+
+ return NRF_SUCCESS;
+}
+
+/**@brief Function for notifying application of the TFTP events.
+ *
+ * @param[in] p_tftp TFTP instance.
+ * @param[in] p_evt Event description.
+ *
+ * @retval None.
+ */
+static void app_notify(iot_tftp_t * p_tftp, iot_tftp_evt_t * p_evt)
+{
+ uint32_t index;
+ uint32_t err_code;
+
+ err_code = find_instance(p_tftp, &index);
+ if (err_code != NRF_SUCCESS)
+ {
+ return;
+ }
+
+ if (m_instances[index].callback)
+ {
+ TFTP_MUTEX_UNLOCK();
+
+
+ // Call handler of user request.
+ m_instances[index].callback(p_tftp, p_evt);
+
+ TFTP_MUTEX_LOCK();
+ }
+}
+
+/**@brief Increment option iterator.
+ *
+ * @details The iterator will point to the next option or to p_end if it reaches the end.
+ *
+ * @param[in] p_iter Pointer to option iterator.
+ *
+ * @retval NRF_SUCCESS if iterator successfully moved to next option, else an error code indicating reason
+ * for failure.
+ */
+static uint32_t op_get_next(option_iter_t * p_iter)
+{
+ uint32_t key_length;
+ uint32_t value_length;
+
+ NULL_PARAM_CHECK(p_iter->p_start);
+ NULL_PARAM_CHECK(p_iter->p_end);
+ NULL_PARAM_CHECK(p_iter->curr.p_key);
+ NULL_PARAM_CHECK(p_iter->curr.p_value);
+
+ // If reached end.
+ if ((p_iter->curr.p_value == p_iter->p_end) || (p_iter->curr.p_key == p_iter->p_end))
+ {
+ return (NRF_ERROR_DATA_SIZE | IOT_TFTP_ERR_BASE);
+ }
+
+ key_length = strlen(p_iter->curr.p_key);
+ value_length = strlen(p_iter->curr.p_value);
+
+ if ((p_iter->curr.p_value == p_iter->p_start) && (p_iter->curr.p_key == p_iter->p_start))
+ {
+ // First call. Check if [start] + [string] fits before [end] reached.
+ // This statement just checks if there is '\0' between start and end (passing single string as input).
+ if (p_iter->curr.p_key + key_length < p_iter->p_end)
+ {
+ p_iter->curr.p_value = p_iter->curr.p_key + key_length + 1;
+
+ return NRF_SUCCESS;
+ }
+ else
+ {
+ return (NRF_ERROR_DATA_SIZE | IOT_TFTP_ERR_BASE);
+ }
+ }
+ else if (p_iter->curr.p_value + value_length < p_iter->p_end)
+ {
+ p_iter->curr.p_key = p_iter->curr.p_value + value_length + 1;
+ p_iter->curr.p_value = p_iter->curr.p_key + strlen(p_iter->curr.p_key) + 1;
+
+ if ((*p_iter->curr.p_key == '\0') || (*p_iter->curr.p_value == '\0')) // If string list finishes before the end of the buffer.
+ {
+ p_iter->curr.p_key = p_iter->p_end;
+ p_iter->curr.p_value = p_iter->p_end;
+
+ return (NRF_ERROR_DATA_SIZE | IOT_TFTP_ERR_BASE);
+ }
+
+ return NRF_SUCCESS;
+ }
+ else
+ {
+ p_iter->curr.p_key = p_iter->p_end;
+ p_iter->curr.p_value = p_iter->p_end;
+
+ return (NRF_ERROR_DATA_SIZE | IOT_TFTP_ERR_BASE);
+ }
+}
+
+/**@brief Set new (key, value) pair at the end of a string.
+ *
+ * @param[out] p_iter Pointer to iterator, which will be used to add (key, value) pair.
+ * @param[in] p_inp_key Pointer to the new key string. If p_key is NULL, then this function will insert just value.
+ * @param[in] p_inp_value Pointer to the new value string.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static __INLINE uint32_t op_set(option_iter_t * p_iter, const char * p_inp_key, const char * p_inp_value)
+{
+ char * p_last_key;
+ char * p_last_value;
+
+ NULL_PARAM_CHECK(p_iter->p_start);
+ NULL_PARAM_CHECK(p_iter->p_end);
+ NULL_PARAM_CHECK(p_iter->curr.p_key);
+ NULL_PARAM_CHECK(p_iter->curr.p_value);
+
+ p_last_key = p_iter->curr.p_key;
+ p_last_value = p_iter->curr.p_value;
+
+ // Print appropriate trace log.
+ if (p_inp_key != NULL)
+ {
+ TFTP_TRC("Set option: %s with value: %s.", p_inp_key, p_inp_value);
+ }
+ else
+ {
+ TFTP_TRC("Set value: %s.", p_inp_value);
+ }
+
+ // Set key & value pointers.
+ if ((p_iter->curr.p_key == p_iter->p_start) && (p_iter->curr.p_value == p_iter->p_start)) // Start condition.
+ {
+ if (p_inp_key != NULL)
+ {
+ p_iter->curr.p_value = p_iter->curr.p_key + strlen(p_inp_key) + 1;
+ }
+ else
+ {
+ p_iter->curr.p_value = p_iter->curr.p_key; // Insert only passed value.
+ p_iter->curr.p_key = p_iter->curr.p_value + strlen(p_iter->curr.p_value) + 1; // Just assign anything different that p_start and inside buffer.
+ }
+ }
+ else
+ {
+ p_iter->curr.p_key = p_iter->curr.p_value + strlen(p_iter->curr.p_value) + 1; // New key starts where last value ends.
+
+ if (p_inp_key != NULL)
+ {
+ p_iter->curr.p_value = p_iter->curr.p_key + strlen(p_inp_key) + 1; // If key not null - new value starts where new key ends.
+ }
+ else
+ {
+ p_iter->curr.p_value = p_iter->curr.p_key; // Otherwise - value is placed at the key position.
+ }
+ }
+
+ // Copy strings into set pointers.
+ if ((p_iter->curr.p_value + strlen(p_inp_value)) < p_iter->p_end)
+ {
+ if (p_inp_key != NULL)
+ {
+ memcpy(p_iter->curr.p_key, p_inp_key, strlen(p_inp_key) + 1);
+ }
+ memcpy(p_iter->curr.p_value, p_inp_value, strlen(p_inp_value) + 1);
+ }
+ else // If it is not possible to insert new key & value pair.
+ {
+ p_iter->curr.p_key = p_last_key;
+ p_iter->curr.p_value = p_last_value;
+
+ TFTP_ERR("Unable to set option (size error)!");
+
+ return (NRF_ERROR_DATA_SIZE | IOT_TFTP_ERR_BASE);
+ }
+
+ return NRF_SUCCESS;
+}
+
+/**@brief Initializes new option iterator.
+ *
+ * @param[out] p_iter Pointer to iterator, which will be configured.
+ * @param[in] p_buf Pointer to the new string buffer which iterator will be modifying.
+ * @param[in] buf_len Length of passed buffer.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static __INLINE void op_init(option_iter_t * p_iter, char * p_buf, uint32_t buf_len)
+{
+ p_iter->p_start = p_buf;
+ p_iter->p_end = p_buf + buf_len;
+ p_iter->curr.p_key = p_buf;
+ p_iter->curr.p_value = p_buf;
+}
+
+/**@brief: Converts string containing unsigned number into uint32_t.
+ *
+ * @param[in] p_str Input string.
+ *
+ * @retval Integer number equal to read value. Reading process skips all non-digit characters.
+ */
+static uint32_t str_to_uint(char * p_str)
+{
+ uint32_t len;
+ uint32_t ret_val = 0;
+ uint32_t mul = 1;
+
+ if (p_str == NULL)
+ {
+ return 0;
+ }
+
+ len = strlen(p_str);
+
+ while (len)
+ {
+ len--;
+
+ if ((p_str[len] >= '0') && (p_str[len] <= '9')) // Skip unsupported characters.
+ {
+ ret_val += mul * (p_str[len] - '0');
+ mul *= 10;
+ }
+ }
+
+ return ret_val;
+}
+
+/**@brief: Converts unsigned number into string.
+ *
+ * @param[in] number Input number.
+ * @param[out] p_str Pointer to the output string.
+ * @param[in] len Length of the passed output string buffer.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static uint32_t uint_to_str(uint32_t number, char * p_str, uint16_t len)
+{
+ uint32_t i = 0;
+ uint32_t temp = number;
+
+ if (len == 0)
+ {
+ return NRF_ERROR_INVALID_LENGTH;
+ }
+
+ // Check how many characters will be needed.
+ if (temp == 0)
+ {
+ i = 1;
+ }
+
+ while (temp)
+ {
+ i++;
+ temp /= 10;
+ }
+
+ // Set null character and check length.
+ if (i + 1 > len)
+ {
+ p_str[0] = '\0';
+
+ return NRF_ERROR_INVALID_LENGTH;
+ }
+
+ p_str[i] = '\0';
+
+ // Set digits.
+ while (i--)
+ {
+ p_str[i] = '0' + number % 10;
+ number /= 10;
+ }
+
+ return NRF_SUCCESS;
+}
+
+/**@brief Compare strings in a case insensitive way.
+ *
+ * @param[in] p_str1 Pointer to the first string.
+ * @param[in] p_str2 Pointer to the second String.
+ *
+ * @retval If strings are equal returns 0, otherwise number of common characters.
+ */
+static uint32_t strcmp_ci(char * p_str1, char* p_str2)
+{
+ uint32_t min_len = 0;
+ uint32_t str1_len;
+ uint32_t str2_len;
+ uint32_t i = 0;
+
+ str1_len = strlen(p_str1);
+ str2_len = strlen(p_str2);
+
+ min_len = str1_len;
+
+ if (str2_len < str1_len)
+ {
+ min_len = str2_len;
+ }
+
+ for (i = 0; i < min_len; i++)
+ {
+ char c1 = ((p_str1[i] >= 'a' && p_str1[i] <= 'z') ? p_str1[i] + 'A' - 'a' : p_str1[i]);
+ char c2 = ((p_str2[i] >= 'a' && p_str2[i] <= 'z') ? p_str2[i] + 'A' - 'a' : p_str2[i]);
+
+ if (c1 != c2)
+ {
+ return i + 1;
+ }
+ }
+
+ if (str1_len != str2_len)
+ {
+ return i + 1;
+ }
+
+ return 0;
+}
+
+/**@brief Allocates p_buffer and fills in common fields.
+ *
+ * @param[in] type First field describing packet type.
+ * @param[in] id Second field (Block ID / Error Code).
+ * @param[out] pp_buffer Sets pointer to the newly allocated buffer.
+ * @param[in] payload_len Length of payload (additional fields / data).
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static uint32_t compose_packet(uint16_t type,
+ uint16_t id,
+ iot_pbuffer_t ** pp_buffer,
+ uint32_t payload_len)
+{
+ uint32_t err_code;
+ iot_pbuffer_alloc_param_t buffer_param;
+ iot_pbuffer_t * p_buffer;
+ uint32_t byte_index;
+
+ memset(&buffer_param, 0, sizeof(iot_pbuffer_alloc_param_t));
+ buffer_param.length = TFTP_HEADER_SIZE + TFTP_BLOCK_ID_SIZE + payload_len;
+ buffer_param.type = UDP6_PACKET_TYPE;
+ buffer_param.flags = PBUFFER_FLAG_DEFAULT;
+
+ err_code = iot_pbuffer_allocate(&buffer_param, &p_buffer);
+ if (err_code != NRF_SUCCESS)
+ {
+ return err_code;
+ }
+
+ memset(p_buffer->p_payload, 0, buffer_param.length);
+ byte_index = 0;
+
+ // Insert type opcode.
+ byte_index += uint16_encode(HTONS(type), &p_buffer->p_payload[byte_index]);
+
+ if (type == TYPE_ERR)
+ {
+ // Insert err code.
+ byte_index += uint16_encode(CONVERT_TO_TFTP_ERROR(id), &p_buffer->p_payload[byte_index]);
+ }
+ else
+ {
+ // Insert block ID.
+ byte_index += uint16_encode(HTONS(id), &p_buffer->p_payload[byte_index]);
+ }
+
+ *pp_buffer = p_buffer;
+
+ return NRF_SUCCESS;
+}
+
+/**@brief Reset instance request timer.
+ *
+ * @param[in] index Index of pending instance.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static uint32_t retr_timer_reset(uint32_t index)
+{
+ uint32_t err_code;
+ iot_timer_time_in_ms_t wall_clock_value;
+
+ // Get wall clock time.
+ err_code = iot_timer_wall_clock_get(&wall_clock_value);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ m_instances[index].request_timeout = wall_clock_value + m_instances[index].connect_params.next_retr * 1000;
+ }
+
+ return err_code;
+}
+
+/**@brief Function for checking if retransmission time of TFTP instance request has been expired.
+ *
+ * @param[in] index Index of pending instance.
+ *
+ * @retval True if timer has been expired, False otherwise.
+ */
+static bool instance_timer_is_expired(uint32_t index)
+{
+ uint32_t err_code;
+ iot_timer_time_in_ms_t wall_clock_value;
+
+ // Get wall clock time.
+ err_code = iot_timer_wall_clock_get(&wall_clock_value);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ if (wall_clock_value >= m_instances[index].request_timeout)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**@brief Sets all instance values to defaults. */
+static void instance_reset(uint32_t index)
+{
+ m_instances[index].state = STATE_FREE;
+ m_instances[index].init_params.next_retr = 0;
+ m_instances[index].init_params.block_size = TFTP_DEFAULT_BLOCK_SIZE;
+ m_instances[index].connect_params.next_retr = 0;
+ m_instances[index].connect_params.block_size = TFTP_DEFAULT_BLOCK_SIZE;
+ m_instances[index].p_file = NULL;
+ m_instances[index].block_id = 0;
+ m_instances[index].p_packet = NULL;
+ m_instances[index].dst_port = TFTP_DEFAULT_PORT;
+ m_instances[index].dst_tid = TFTP_DEFAULT_PORT;
+ m_instances[index].retries = 0;
+ m_instances[index].request_timeout = 0;
+ m_instances[index].callback = NULL;
+ m_instances[index].src_tid = 0;
+ m_instances[index].p_password = NULL;
+ memset(&m_instances[index].addr, 0, sizeof(ipv6_addr_t));
+ memset(&m_instances[index].socket, 0, sizeof(udp6_socket_t));
+}
+
+/**@brief This function creates error packet for specified TFTP instance.
+ *
+ * @param[in] index Index of TFTP instance.
+ * @param[in] p_err_evt Event data structure (message and code).
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static uint32_t send_err_msg(uint32_t index, iot_tftp_evt_err_t * p_err_evt)
+{
+ iot_pbuffer_t * p_buffer;
+ uint8_t * p_resp_packet;
+ uint32_t msg_len = 0;
+ uint16_t byte_index = 0;
+ uint32_t err_code;
+
+ TFTP_TRC("Send ERROR packet.");
+
+ if (p_err_evt->p_msg != NULL)
+ {
+ msg_len = strlen(p_err_evt->p_msg) + 1;
+ }
+
+ err_code = compose_packet(TYPE_ERR, p_err_evt->code, &p_buffer, msg_len);
+ if (err_code != NRF_SUCCESS)
+ {
+ return err_code;
+ }
+
+ p_resp_packet = p_buffer->p_payload;
+ byte_index = TFTP_HEADER_SIZE + TFTP_ERR_CODE_SIZE;
+
+ if (p_err_evt->p_msg != NULL)
+ {
+ memcpy(&p_resp_packet[byte_index], p_err_evt->p_msg, msg_len);
+ byte_index += msg_len;
+ }
+
+ p_buffer->length = byte_index;
+
+ TFTP_TRC("Send packet to UDP module.");
+
+ UNUSED_VARIABLE(retr_timer_reset(index));
+
+ err_code = udp6_socket_sendto(&m_instances[index].socket,
+ &m_instances[index].addr,
+ m_instances[index].dst_tid,
+ p_buffer);
+
+ TFTP_TRC("Recv code: %08lx.", err_code);
+
+ return err_code;
+}
+
+/**@brief This function creeates error packet for TID error, not being found.
+ *
+ * @param[in] p_socket Socket from which error message is sent.
+ * @param[in] p_addr IPv6 Address to where error message is sent.
+ * @param[in] tid Erronous TID from the sender.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static uint32_t send_err_tid(const udp6_socket_t * p_socket, const ipv6_addr_t * p_addr, uint16_t tid)
+{
+ iot_pbuffer_t * p_buffer;
+ uint32_t msg_len = 0;
+ uint16_t byte_index = 0;
+ uint32_t err_code;
+
+ TFTP_TRC("Send TID ERROR packet.");
+
+ err_code = compose_packet(TYPE_ERR, ERR_INVALID_TID, &p_buffer, msg_len);
+ if (err_code != NRF_SUCCESS)
+ {
+ return err_code;
+ }
+
+ byte_index = TFTP_HEADER_SIZE + TFTP_ERR_CODE_SIZE;
+ p_buffer->length = byte_index;
+
+ TFTP_TRC("Send packet to UDP module.");
+
+ err_code = udp6_socket_sendto(p_socket, p_addr, tid, p_buffer);
+
+ TFTP_TRC("Recv code: %08lx.", err_code);
+
+ return err_code;
+}
+
+/**@brief Sends ACK or next data chunk (block) after calling user callback or when hold timer expires.
+ *
+ * @param[in] p_tftp Pointer to the TFTP instance (from user space).
+ * @param[in] p_evt Pointer to the event structure. Used for sending error messages.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static uint32_t send_response(iot_tftp_t * p_tftp)
+{
+ uint32_t index;
+ uint32_t err_code;
+
+ TFTP_TRC("Send packet.");
+
+ err_code = find_instance(p_tftp, &index);
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_ERR("Cannot find instance (send)!");
+
+ return err_code;
+ }
+
+ switch (m_instances[index].state)
+ {
+ case STATE_IDLE:
+ // Server should go to the CONNECTING state (request received).
+ TFTP_TRC("Inside IDLE state (send). ");
+ return NRF_SUCCESS;
+
+ case STATE_SENDING:
+ case STATE_RECEIVING:
+ case STATE_SEND_HOLD:
+ case STATE_RECV_HOLD:
+ case STATE_RECV_COMPLETE:
+ // Send DATA/ACK packet.
+ TFTP_TRC("Send packet to UDP module.");
+
+ UNUSED_VARIABLE(retr_timer_reset(index));
+ err_code = udp6_socket_sendto(&m_instances[index].socket,
+ &m_instances[index].addr,
+ m_instances[index].dst_tid,
+ m_instances[index].p_packet);
+ TFTP_TRC("Recv code: %08lx.", err_code);
+ return err_code;
+
+ default:
+ TFTP_ERR("Invalid state (send)!");
+ return (NRF_ERROR_INVALID_STATE | IOT_TFTP_ERR_BASE);
+ }
+}
+
+
+
+/**@brief Aborts TFTP client ongoing procedure.
+ *
+ * @param[in] index Index of TFTP instance.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+void instance_abort(uint32_t index)
+{
+ uint32_t internal_err;
+
+ switch (m_instances[index].state)
+ {
+ case STATE_SEND_HOLD:
+ case STATE_RECV_HOLD:
+ // Free pbuffer.
+ internal_err = iot_pbuffer_free(m_instances[index].p_packet, true);
+ if (internal_err != NRF_SUCCESS)
+ {
+ TFTP_ERR("Cannot free pbuffer - %p", m_instances[index].p_packet);
+ }
+
+ // Close file.
+ internal_err = iot_file_fclose(m_instances[index].p_file);
+ if (internal_err != NRF_SUCCESS)
+ {
+ TFTP_ERR("Cannot close file - %p", m_instances[index].p_file);
+ }
+
+ break;
+ case STATE_SENDING:
+ case STATE_RECEIVING:
+ // Close file.
+ internal_err = iot_file_fclose(m_instances[index].p_file);
+ if (internal_err != NRF_SUCCESS)
+ {
+ TFTP_ERR("Cannot close file - %p", m_instances[index].p_file);
+ }
+ break;
+ case STATE_CONNECTING_RRQ:
+ case STATE_CONNECTING_WRQ:
+ case STATE_IDLE:
+ case STATE_FREE:
+ default:
+ break;
+ }
+
+ TFTP_TRC("Reset instance %ld.", index);
+
+ m_instances[index].state = STATE_IDLE;
+ m_instances[index].block_id = 0;
+ m_instances[index].dst_tid = m_instances[index].dst_port;
+ m_instances[index].retries = 0;
+ m_instances[index].request_timeout = 0;
+ memcpy(&m_instances[index].connect_params,
+ &m_instances[index].init_params,
+ sizeof(iot_tftp_trans_params_t));
+}
+
+
+/**@brief Generates event for application.
+ *
+ * @param[in] index Index of TFTP instance.
+ * @param[in] evt_id Id of generated event.
+ * @param[in] p_param Pointer to event parameter union.
+ */
+static void handle_evt(uint32_t index, iot_tftp_evt_id_t evt_id, iot_tftp_evt_param_t * p_param)
+{
+ uint32_t internal_err;
+ iot_tftp_evt_t evt;
+
+ memset(&evt, 0, sizeof(evt));
+ evt.id = evt_id;
+ evt.p_file = m_instances[index].p_file;
+
+ if (p_param != NULL)
+ {
+ evt.param = * p_param;
+ }
+
+ if (evt_id == IOT_TFTP_EVT_ERROR)
+ {
+ uint32_t err_code = evt.param.err.code;
+
+ TFTP_TRC("Raise an ERROR event.");
+
+ if ((err_code & IOT_TFTP_ERR_BASE) == IOT_TFTP_ERR_BASE)
+ {
+ evt.param.err.code = CONVERT_TO_TFTP_ERROR(err_code);
+
+ internal_err = send_err_msg(index, &evt.param.err);
+ if (internal_err != NRF_SUCCESS)
+ {
+ TFTP_TRC("Cannot send error message to peer %08lx.", internal_err);
+ }
+ }
+
+ // Restore error code.
+ evt.param.err.code = err_code;
+
+ // Return to IDLE and notify.
+ instance_abort(index);
+
+ app_notify(&index, &evt);
+ }
+ else if ((evt_id == IOT_TFTP_EVT_TRANSFER_GET_COMPLETE) ||
+ (evt_id == IOT_TFTP_EVT_TRANSFER_PUT_COMPLETE))
+ {
+ TFTP_TRC("Raise TRANSFER COMPLETE event.");
+
+ if (m_instances[index].state == STATE_RECV_HOLD)
+ {
+ TFTP_TRC("Holding last ACK transfer.");
+ m_instances[index].state = STATE_RECV_COMPLETE;
+ }
+ else if (m_instances[index].state != STATE_RECV_COMPLETE)
+ {
+ // Skip into IDLE state.
+ instance_abort(index);
+
+ app_notify(&index, &evt);
+ }
+ }
+ else if (evt_id == IOT_TFTP_EVT_TRANSFER_DATA_RECEIVED)
+ {
+ app_notify(&index, &evt);
+ }
+}
+
+/**@brief Generates error event for application.
+ *
+ * @param[in] index Index of TFTP instance.
+ * @param[in] err_code Code of error event.
+ * @param[in] p_msg Character string containing error message.
+ */
+static void handle_evt_err(uint32_t index, uint32_t err_code, char * p_msg)
+{
+ iot_tftp_evt_param_t evt_param;
+
+ memset(&evt_param, 0, sizeof(evt_param));
+
+ evt_param.err.code = err_code;
+ evt_param.err.p_msg = p_msg;
+ evt_param.err.size_transfered = m_instances[index].block_id * m_instances[index].connect_params.block_size;
+
+ handle_evt(index, IOT_TFTP_EVT_ERROR, &evt_param);
+}
+
+/**@brief: Find instance number by Transmission ID (UDP source port).
+ *
+ * @param[in] port UDP port number on which new message was received.
+ * @param[out] p_index Index of found TFTP instance assigned to the passed UDP port.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static __INLINE uint32_t find_instance_by_tid(uint16_t port, uint32_t * p_index)
+{
+ uint32_t index;
+
+ for (index = 0; index < TFTP_MAX_INSTANCES; index++)
+ {
+ if (m_instances[index].src_tid == port)
+ {
+ break;
+ }
+ }
+
+ if (index == TFTP_MAX_INSTANCES)
+ {
+ return (NRF_ERROR_NOT_FOUND | IOT_TFTP_ERR_BASE);
+ }
+
+ *p_index = index;
+
+ return NRF_SUCCESS;
+}
+
+/**@brief: Negotiation procedure. This function skips through input option string and modifies instance parameters according to negotiated values.
+ *
+ * @param[in] p_iter Pointer to iterator configured to parse RQ/OACK packet.
+ * @param[out] p_instance Pointer to the instance, which parameters should be negotiated.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else TFTP_OPTION_REJECT error indicating
+ * that server/client requests unsupported option values.
+ */
+static uint32_t option_negotiate(option_iter_t * p_iter, tftp_instance_t * p_instance)
+{
+ uint32_t err_code;
+ bool op_size_set = false;
+ bool op_blksize_set = false;
+ bool op_time_set = false;
+
+ TFTP_TRC("Negotiate options:");
+
+ if (p_iter != NULL) // If NULL passed - reset option values to those defined by RFC.
+ {
+ UNUSED_VARIABLE(op_get_next(p_iter));
+
+ while (p_iter->curr.p_key != p_iter->p_end)
+ {
+ if (strcmp_ci(p_iter->curr.p_key, OPTION_TIMEOUT) == 0)
+ {
+ if (p_instance->init_params.next_retr != 0)
+ {
+ uint16_t server_time = str_to_uint(p_iter->curr.p_value);
+
+ if (server_time < p_instance->init_params.next_retr) // Take minimum.
+ {
+ p_instance->connect_params.next_retr = server_time;
+ }
+ else
+ {
+ p_instance->connect_params.next_retr = p_instance->init_params.next_retr;
+ }
+
+ op_time_set = true;
+
+ TFTP_TRC("TIMEOUT: %ld", p_instance->connect_params.next_retr);
+ }
+ }
+ else if (strcmp_ci(p_iter->curr.p_key, OPTION_SIZE) == 0)
+ {
+ if (p_instance->p_file != NULL)
+ {
+ uint32_t file_size = str_to_uint(p_iter->curr.p_value);
+
+ err_code = iot_file_fopen(p_instance->p_file, file_size);
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_TRC(" TSIZE: REJECT!");
+ return TFTP_OPTION_REJECT;
+ }
+
+ op_size_set = true;
+ TFTP_TRC(" TSIZE: %ld", file_size);
+ }
+ }
+ else if (strcmp_ci(p_iter->curr.p_key, OPTION_BLKSIZE) == 0)
+ {
+ uint16_t block_size = str_to_uint(p_iter->curr.p_value);
+ op_blksize_set = true;
+
+ if (p_instance->init_params.block_size >= block_size)
+ {
+ p_instance->connect_params.block_size = block_size;
+ }
+ else
+ {
+ TFTP_TRC("BLKSIZE: REJECT!");
+ return TFTP_OPTION_REJECT;
+ }
+
+ TFTP_TRC("BLKSIZE: %d", p_instance->connect_params.block_size);
+ }
+ else if ((strlen(p_iter->curr.p_key) > 0) && (p_iter->curr.p_value == p_iter->p_end))
+ {
+ // Password option.
+ TFTP_TRC("PASSWORD FOUND: %s", p_iter->curr.p_key);
+ p_iter->curr.p_key = p_iter->p_end;
+ }
+ else
+ {
+ TFTP_TRC("UNKNOWN OPTION");
+ }
+
+ UNUSED_VARIABLE(op_get_next(p_iter));
+ }
+ }
+
+ // Set values of not negotiated options.
+ if (!op_size_set && p_instance->p_file != NULL)
+ {
+ err_code = iot_file_fopen(p_instance->p_file, 0);// Open with default file size (not known).
+
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_TRC("TSIZE: REJECT!");
+ return TFTP_OPTION_REJECT;
+ }
+
+ TFTP_TRC("TSIZE: %ld", p_instance->p_file->file_size);
+ }
+
+ if (!op_blksize_set)
+ {
+ if ((p_instance->init_params.block_size < TFTP_DEFAULT_BLOCK_SIZE) &&
+ (p_instance->init_params.block_size != 0))
+ {
+ TFTP_TRC("BLKSIZE: REJECT!");
+ return TFTP_OPTION_REJECT;
+ }
+ else
+ {
+ p_instance->connect_params.block_size = TFTP_DEFAULT_BLOCK_SIZE;
+ }
+
+ TFTP_TRC("BLKSIZE: %d", p_instance->connect_params.block_size);
+ }
+
+ if (!op_time_set)
+ {
+ p_instance->connect_params.next_retr = p_instance->init_params.next_retr;
+
+ TFTP_TRC("TIMEOUT: %ld", p_instance->connect_params.next_retr);
+ }
+
+ return NRF_SUCCESS;
+}
+
+
+
+/**@brief This function holds ongoing transmission of TFTP.
+ *
+ * @param[in] index Index of TFTP instance.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static uint32_t transfer_hold(uint32_t index)
+{
+ if (m_instances[index].state == STATE_SENDING)
+ {
+ m_instances[index].state = STATE_SEND_HOLD;
+ }
+ else if (m_instances[index].state == STATE_RECEIVING)
+ {
+ m_instances[index].state = STATE_RECV_HOLD;
+ }
+ else if (m_instances[index].state == STATE_RECV_COMPLETE)
+ {
+ m_instances[index].state = STATE_RECV_COMPLETE;
+ }
+ else
+ {
+ TFTP_ERR("Hold called in invalid state.");
+
+ return (NRF_ERROR_INVALID_STATE | IOT_TFTP_ERR_BASE);
+ }
+
+ return NRF_SUCCESS;
+}
+
+
+/**@brief This function resumes ongoing transmission of TFTP.
+ *
+ * @param[in] index Index of TFTP instance.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static uint32_t transfer_resume(uint32_t index)
+{
+ uint32_t err_code = NRF_SUCCESS;
+
+ if ((m_instances[index].state != STATE_SEND_HOLD) &&
+ (m_instances[index].state != STATE_RECV_HOLD) &&
+ (m_instances[index].state != STATE_RECV_COMPLETE))
+ {
+ TFTP_ERR("Failed due to invalid state.");
+ TFTP_EXIT();
+ return (NRF_ERROR_INVALID_STATE | IOT_TFTP_ERR_BASE);
+ }
+
+ err_code = send_response(&index);
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_ERR("Failed to send packet after resume.");
+ }
+
+ if (m_instances[index].state == STATE_SEND_HOLD)
+ {
+ m_instances[index].state = STATE_SENDING;
+ }
+ else if (m_instances[index].state == STATE_RECV_HOLD)
+ {
+ m_instances[index].state = STATE_RECEIVING;
+ }
+ else if (m_instances[index].state == STATE_RECV_COMPLETE)
+ {
+ m_instances[index].state = STATE_RECEIVING;
+ TFTP_ERR("Complete due to STATE_RECV_COMPLETE state.");
+ handle_evt(index, IOT_TFTP_EVT_TRANSFER_GET_COMPLETE, NULL);
+ }
+
+ return err_code;
+}
+
+
+/**@brief This function creates ACK packet for specified TFTP instance.
+ *
+ * @param[in] index Index of TFTP instance.
+ * @param[in] block_id Data block ID to be acknowledged.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static uint32_t create_ack_packet(uint32_t index, uint16_t block_id)
+{
+ uint32_t err_code;
+
+ TFTP_ENTRY();
+
+ // Reuse p_packet pointer for a new (response) packet. Previous one will be automatically freed by IPv6 module.
+ err_code = compose_packet(TYPE_ACK,
+ block_id,
+ &m_instances[index].p_packet,
+ 0);
+
+ return err_code;
+}
+
+/**@brief This function creates data packet for specified TFTP instance.
+ *
+ * @param[in] index Index of TFTP instance.
+ * @param[in] block_id Data block ID to be sent.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static uint32_t create_data_packet(uint32_t index, uint16_t block_id)
+{
+ uint32_t err_code;
+ uint32_t internal_err;
+ uint32_t cursor;
+ uint32_t byte_index;
+ iot_pbuffer_t * p_buffer = NULL;
+
+ TFTP_ENTRY();
+
+ if (m_instances[index].p_file == NULL)
+ {
+ TFTP_ERR("[TFTP]: Error while sending response. Reason: Missing file to send.\r\n");
+ return NRF_ERROR_INVALID_DATA;
+ }
+
+ // If ACK block ID doesn't match last sent packet number.
+ if (m_instances[index].block_id != block_id)
+ {
+ // fseek on file to move to the right point. If fseek returns error - whole transmission will be dropped.
+ err_code = iot_file_fseek(m_instances[index].p_file,
+ (block_id) * m_instances[index].connect_params.block_size);
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_ERR("Unable to fseek on input data file!");
+ }
+ else
+ {
+ m_instances[index].block_id = block_id + 1;
+ }
+ }
+ else
+ {
+ m_instances[index].block_id = block_id + 1;
+ }
+
+ cursor = (m_instances[index].block_id - 1) * m_instances[index].connect_params.block_size;
+
+ // If previous DATA packet wasn't fully filled.
+ if (cursor > m_instances[index].p_file->file_size)
+ {
+ TFTP_TRC("Transfer complete. Don't send data.");
+ handle_evt(index, IOT_TFTP_EVT_TRANSFER_PUT_COMPLETE, NULL);
+ return NRF_SUCCESS;
+ }
+ else if (cursor + m_instances[index].connect_params.block_size >
+ m_instances[index].p_file->file_size) // If current sendto operation will send all remaining data.
+ {
+ TFTP_TRC("Send last data packet.");
+ err_code = compose_packet(TYPE_DATA,
+ m_instances[index].block_id,
+ &p_buffer,
+ m_instances[index].p_file->file_size - cursor);
+ }
+ else
+ {
+ TFTP_TRC("Send regular data packet.");
+ err_code = compose_packet(TYPE_DATA,
+ m_instances[index].block_id,
+ &p_buffer,
+ m_instances[index].connect_params.block_size);
+ }
+
+ if (err_code != NRF_SUCCESS)
+ {
+ return err_code;
+ }
+
+ TFTP_TRC("Created packet:");
+ TFTP_TRC("length: %ld", p_buffer->length);
+ TFTP_TRC(" type: %d", p_buffer->p_payload[0] * 256 + p_buffer->p_payload[1]);
+ TFTP_TRC(" ID: %d", p_buffer->p_payload[2] * 256 + p_buffer->p_payload[3]);
+
+ byte_index = TFTP_HEADER_SIZE + TFTP_BLOCK_ID_SIZE;
+
+ // Save reference to correctly filled packet buffer.
+ m_instances[index].p_packet = p_buffer;
+
+ if (p_buffer->length - TFTP_HEADER_SIZE - TFTP_BLOCK_ID_SIZE != 0)
+ {
+ TFTP_MUTEX_UNLOCK();
+
+ // Hold transfer.
+ internal_err = transfer_hold(index);
+ if (internal_err != NRF_SUCCESS)
+ {
+ TFTP_ERR("Error while holding the transfer. Reason: %08lx.", internal_err);
+ }
+
+ err_code = iot_file_fread(m_instances[index].p_file,
+ &(p_buffer->p_payload[byte_index]),
+ p_buffer->length - TFTP_HEADER_SIZE - TFTP_BLOCK_ID_SIZE);
+
+ // Unlock instance if file has not assigned callback (probably no needs more time to perform read/write).
+ if (m_instances[index].p_file->p_callback == NULL)
+ {
+ internal_err = transfer_resume(index);
+ if (internal_err != NRF_SUCCESS)
+ {
+ TFTP_ERR("Error while resuming the transfer. Reason: %08lx.", internal_err);
+ }
+ }
+ TFTP_MUTEX_LOCK();
+ }
+ else
+ {
+ // TFTP is going to send empty data packet, so file callback won't be called.
+ internal_err = send_response(&index);
+ if (internal_err != NRF_SUCCESS)
+ {
+ TFTP_ERR("Error while sending response. Reason: %08lx.", internal_err);
+ }
+ }
+
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_ERR("Failed to save received data (fread)!");
+ handle_evt_err(index, TFTP_ACCESS_DENIED, ACCESS_ERROR_MSG);
+ }
+
+ return err_code;
+}
+
+
+/**@brief Callback handler to receive data on the UDP port.
+ *
+ * @param[in] p_socket Socket identifier.
+ * @param[in] p_ip_header IPv6 header containing source and destination addresses.
+ * @param[in] p_udp_header UDP header identifying local and remote endpoints.
+ * @param[in] process_result Result of data reception, there could be possible errors like
+ * invalid checksum etc.
+ * @param[in] p_rx_packet Packet buffer containing the received data packet.
+ *
+ * @retval NRF_SUCCESS Indicates received data was handled successfully, else an an
+ * error code indicating reason for failure..
+ */
+static uint32_t client_process(const udp6_socket_t * p_socket,
+ const ipv6_header_t * p_ip_header,
+ const udp6_header_t * p_udp_header,
+ uint32_t process_result,
+ iot_pbuffer_t * p_rx_packet)
+{
+ uint32_t index;
+ iot_tftp_evt_t evt;
+ uint8_t * p_new_packet;
+ uint32_t byte_index;
+ uint32_t err_code;
+ uint32_t internal_err;
+ uint16_t packet_opcode;
+ option_iter_t oack_iter;
+ uint16_t recv_block_id;
+
+ TFTP_ENTRY();
+
+ TFTP_MUTEX_LOCK();
+
+ err_code = find_instance_by_tid(p_udp_header->destport, &index);
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_ERR("Unable to find TFTP instance associated with given UDP port.");
+
+ // Send TID error to the server.
+ err_code = send_err_tid(p_socket, &p_ip_header->srcaddr, p_udp_header->destport);
+
+ TFTP_EXIT();
+ TFTP_MUTEX_UNLOCK();
+
+ return err_code;
+ }
+
+ // Check UDP status code.
+ if (process_result != NRF_SUCCESS)
+ {
+ TFTP_ERR("UDP error!");
+
+ evt.id = IOT_TFTP_EVT_ERROR;
+ evt.param.err.code = process_result;
+ evt.param.err.p_msg = UDP_ERROR_MSG;
+
+ // Call user callback (inform about error).
+ app_notify(&index, &evt);
+
+ TFTP_EXIT();
+
+ TFTP_MUTEX_UNLOCK();
+
+ return process_result;
+ }
+
+ // Check packet length.
+ if (p_rx_packet->length < TFTP_HEADER_SIZE + TFTP_BLOCK_ID_SIZE)
+ {
+ TFTP_ERR("Invalid packet length!");
+
+ evt.id = IOT_TFTP_EVT_ERROR;
+ evt.param.err.code = TFTP_INVALID_PACKET;
+ evt.param.err.p_msg = (char *)"Invalid packet length!";
+
+ app_notify(&index, &evt);
+
+ TFTP_EXIT();
+
+ TFTP_MUTEX_UNLOCK();
+
+ return evt.param.err.code;
+ }
+
+ // Read received packet type.
+ p_new_packet = p_rx_packet->p_payload;
+ packet_opcode = uint16_decode(p_new_packet);
+ packet_opcode = NTOHS(packet_opcode);
+ byte_index = TFTP_HEADER_SIZE;
+
+ if ((m_instances[index].state == STATE_SEND_HOLD) ||
+ (m_instances[index].state == STATE_RECV_HOLD) ||
+ (m_instances[index].state == STATE_IDLE))
+ {
+ TFTP_TRC("Ignore packets in HOLD/IDLE states.");
+
+ TFTP_MUTEX_UNLOCK();
+
+ TFTP_EXIT();
+
+ return NRF_SUCCESS; // Ignore retransmission of other side.
+ }
+
+ switch (packet_opcode)
+ {
+ case TYPE_OACK:
+ if ((m_instances[index].state != STATE_CONNECTING_RRQ) &&
+ (m_instances[index].state != STATE_CONNECTING_WRQ) &&
+ (m_instances[index].state != STATE_IDLE && m_instances[index].retries > 0))
+ {
+ TFTP_ERR("Invalid TFTP instance state!");
+ break;
+ }
+
+ op_init(&oack_iter,
+ (char *)&p_new_packet[byte_index],
+ p_rx_packet->length - TFTP_HEADER_SIZE); // Options uses whole packet except opcode.
+
+ TFTP_TRC("Received OACK.");
+ err_code = option_negotiate(&oack_iter, &m_instances[index]);
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_ERR("Failed to negotiate options!");
+ handle_evt_err(index, TFTP_OPTION_REJECT, OPTION_ERROR_MESSAGE);
+ break;
+ }
+
+ // Set server transmission id.
+ m_instances[index].dst_tid = p_udp_header->srcport;
+
+ if (m_instances[index].state == STATE_CONNECTING_RRQ)
+ {
+ m_instances[index].p_packet = p_rx_packet;
+ m_instances[index].state = STATE_RECEIVING;
+
+ err_code = create_ack_packet(index, 0);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ err_code = send_response(&index);
+ }
+ }
+ else if (m_instances[index].state == STATE_CONNECTING_WRQ)
+ {
+ m_instances[index].state = STATE_SENDING;
+
+ err_code = create_data_packet(index, 0);
+ }
+ else
+ {
+ TFTP_ERR("Incorrect state to receive OACK!");
+ }
+
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_ERR("Failed to create packet!");
+ handle_evt_err(index, err_code, NULL);
+ }
+ break;
+
+ case TYPE_ACK:
+ recv_block_id = uint16_decode(&p_new_packet[byte_index]);
+ recv_block_id = NTOHS(recv_block_id);
+
+ if (m_instances[index].state == STATE_CONNECTING_WRQ)
+ {
+ TFTP_TRC("Options not supported. Received ACK.");
+ err_code = option_negotiate(NULL, &m_instances[index]);
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_ERR("Failed to negotiate default options!");
+ handle_evt_err(index, TFTP_OPTION_REJECT, OPTION_ERROR_MESSAGE);
+ break;
+ }
+
+ // Set server transmission id.
+ m_instances[index].dst_tid = p_udp_header->srcport;
+
+ // Set instance state.
+ m_instances[index].state = STATE_SENDING;
+ m_instances[index].block_id = 0;
+ }
+
+ if (m_instances[index].state == STATE_SENDING || m_instances[index].state == STATE_RECEIVING)
+ {
+ if (recv_block_id == m_instances[index].block_id)
+ {
+ m_instances[index].retries = 0;
+ }
+ TFTP_TRC("Received ACK. Send block %4d of %ld.", m_instances[index].block_id + 1,
+ CEIL_DIV(m_instances[index].p_file->file_size, m_instances[index].connect_params.block_size) +
+ ((m_instances[index].p_file->file_size % m_instances[index].connect_params.block_size == 0) ? 0 : 1));
+
+ err_code = create_data_packet(index, recv_block_id);
+ if ((err_code != (NRF_ERROR_DATA_SIZE | IOT_TFTP_ERR_BASE)) && (err_code != NRF_SUCCESS))
+ {
+ TFTP_ERR("Failed to create data packet.");
+ handle_evt_err(index, err_code, NULL);
+ }
+ }
+ else
+ {
+ TFTP_ERR("Invalid state to receive ACK packet.");
+
+ TFTP_MUTEX_UNLOCK();
+
+ TFTP_EXIT();
+
+ return NRF_SUCCESS;
+ }
+ break;
+
+ case TYPE_DATA:
+ recv_block_id = uint16_decode(&p_new_packet[byte_index]);
+ recv_block_id = NTOHS(recv_block_id);
+ byte_index += TFTP_BLOCK_ID_SIZE;
+
+ if (m_instances[index].state == STATE_CONNECTING_RRQ)
+ {
+ TFTP_TRC("Options not supported. Received DATA.");
+ err_code = option_negotiate(NULL, &m_instances[index]);
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_ERR("Failed to set default options.");
+ handle_evt_err(index, TFTP_OPTION_REJECT, OPTION_ERROR_MESSAGE);
+ break;
+ }
+
+ if (recv_block_id == 1) // Received first data block.
+ {
+ m_instances[index].block_id = 0;
+ m_instances[index].state = STATE_RECEIVING;
+ }
+
+ // Set server transmission id.
+ m_instances[index].dst_tid = p_udp_header->srcport;
+ }
+
+ if (m_instances[index].state == STATE_RECEIVING)
+ {
+ TFTP_TRC("Received DATA.");
+
+ m_instances[index].p_packet = p_rx_packet;
+
+ if (recv_block_id == m_instances[index].block_id + 1)
+ {
+ TFTP_TRC("Received next DATA (n+1).");
+
+ m_instances[index].retries = 0;
+
+ err_code = create_ack_packet(index, m_instances[index].block_id + 1);
+
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_ERR("Failed to create ACK packet!");
+ handle_evt_err(index, err_code, NULL);
+ break;
+ }
+ }
+ else
+ {
+ TFTP_TRC("Skip current DATA packet. Try to request proper block ID by sending ACK.");
+
+ err_code = create_ack_packet(index, m_instances[index].block_id);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ err_code = send_response(&index);
+ }
+ else
+ {
+ TFTP_ERR("Failed to create ACK packet.");
+ handle_evt_err(index, err_code, NULL);
+ }
+ }
+
+ // Check if payload size is smaller than defined block size.
+ if ((p_rx_packet->length - TFTP_BLOCK_ID_SIZE - TFTP_HEADER_SIZE) <
+ m_instances[index].connect_params.block_size)
+ {
+ m_instances[index].state = STATE_RECV_COMPLETE;
+ }
+ else if (err_code != NRF_SUCCESS)
+ {
+ TFTP_ERR("Failed to create ACK packet.");
+ handle_evt_err(index, err_code, NULL);
+ break;
+ }
+
+ TFTP_TRC("Send block %4d of %ld ACK.", m_instances[index].block_id,
+ m_instances[index].p_file->file_size / m_instances[index].connect_params.block_size);
+
+ if (recv_block_id == m_instances[index].block_id + 1)
+ {
+ m_instances[index].block_id = recv_block_id;
+ TFTP_MUTEX_UNLOCK();
+
+ if (p_rx_packet->length - byte_index > 0)
+ {
+ iot_tftp_evt_param_t evt_param;
+ memset(&evt_param, 0, sizeof(evt_param));
+ evt_param.data_received.p_data = &p_new_packet[byte_index];
+ evt_param.data_received.size = p_rx_packet->length - byte_index;
+
+ internal_err = transfer_hold(index);
+ if (internal_err != NRF_SUCCESS)
+ {
+ TFTP_ERR("Error while holding the transfer. Reason: %08lx.", internal_err);
+ }
+
+ if (m_instances[index].p_file != NULL)
+ {
+ err_code = iot_file_fwrite(m_instances[index].p_file,
+ evt_param.data_received.p_data,
+ evt_param.data_received.size);
+ }
+
+ handle_evt(index, IOT_TFTP_EVT_TRANSFER_DATA_RECEIVED, &evt_param);
+
+ // Unlock instance if file has not assigned callback (probably not needs more time to perform read/write).
+ if (m_instances[index].p_file == NULL || m_instances[index].p_file->p_callback == NULL)
+ {
+ internal_err = transfer_resume(index);
+ if (internal_err != NRF_SUCCESS)
+ {
+ TFTP_ERR("Error while resuming the transfer. Reason: %08lx.", internal_err);
+ }
+ }
+ }
+ else
+ {
+ if (m_instances[index].p_file != NULL)
+ {
+ err_code = iot_file_fclose(m_instances[index].p_file);
+ }
+
+ internal_err = send_response(&index);
+ if (internal_err != NRF_SUCCESS)
+ {
+ TFTP_ERR("Error while sending response. Reason: %08lx.", internal_err);
+ }
+
+ TFTP_ERR("Complete due to packet length. (%ld: %ld)", p_rx_packet->length, byte_index);
+ m_instances[index].state = STATE_RECEIVING;
+ handle_evt(index, IOT_TFTP_EVT_TRANSFER_GET_COMPLETE, NULL);
+ }
+
+ TFTP_MUTEX_LOCK();
+
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_ERR("Failed to save received data (fwrite)!");
+ handle_evt_err(index, TFTP_ACCESS_DENIED, ACCESS_ERROR_MSG);
+ break;
+ }
+ }
+ }
+ else
+ {
+ TFTP_ERR("Invalid state to receive DATA packet.");
+
+ TFTP_MUTEX_UNLOCK();
+
+ TFTP_EXIT();
+
+ return NRF_SUCCESS;
+ }
+ break;
+
+ case TYPE_ERR:
+ recv_block_id = uint16_decode(&p_new_packet[byte_index]);
+ recv_block_id = NTOHS(recv_block_id);
+ byte_index += TFTP_ERR_CODE_SIZE;
+
+ TFTP_ERR("Received error packet!");
+
+ evt.id = IOT_TFTP_EVT_ERROR;
+ evt.param.err.code = CONVERT_TO_IOT_ERROR(recv_block_id);
+
+ if (p_rx_packet->length > TFTP_HEADER_SIZE + TFTP_ERR_CODE_SIZE)
+ {
+ evt.param.err.p_msg = (char *) &p_new_packet[byte_index];
+ p_new_packet[p_rx_packet->length-1] = '\0';
+ }
+
+ app_notify(&index, &evt);
+
+ TFTP_MUTEX_UNLOCK();
+
+ TFTP_EXIT();
+
+ if (evt.param.err.code != TFTP_INVALID_TID)
+ {
+ instance_abort(index);
+ }
+
+ return evt.param.err.code;
+
+ default:
+ TFTP_ERR("Invalid TFTP packet opcode!");
+ handle_evt_err(index, TFTP_INVALID_PACKET, NULL);
+ break;
+ }
+
+ TFTP_EXIT();
+
+ return err_code;
+}
+
+/**@brief Count how many bytes are required to store options inside request packet.
+ *
+ * @param[in] index Index of TFTP instance.
+ * @param[in] type Type of TFTP request.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static uint32_t count_options_length(uint32_t index, uint32_t type)
+{
+ uint32_t op_length = 0;
+ char next_retr_str[NEXT_RETR_MAX_LENGTH];
+ char block_size_str[BLKSIZE_MAX_LENGTH];
+ char file_size_str[FILE_SIZE_MAX_LENGTH];
+
+ if ((m_instances[index].init_params.next_retr > 0) &&
+ (m_instances[index].init_params.next_retr < 256))
+ {
+ UNUSED_VARIABLE(uint_to_str(m_instances[index].init_params.next_retr, next_retr_str, NEXT_RETR_MAX_LENGTH));
+ op_length += sizeof(OPTION_TIMEOUT); // Time out option length.
+ op_length += strlen(next_retr_str) + 1; // The '\0' character ate the end of a string.
+ }
+
+ if ((m_instances[index].init_params.block_size > 0) &&
+ (m_instances[index].init_params.block_size != TFTP_DEFAULT_BLOCK_SIZE))
+ {
+ UNUSED_VARIABLE(uint_to_str(m_instances[index].init_params.block_size, block_size_str, sizeof(block_size_str)));
+ op_length += sizeof(OPTION_BLKSIZE); // Time out option length.
+ op_length += strlen(block_size_str) + 1; // The '\0' character ate the end of a string.
+ }
+
+ op_length += sizeof(OPTION_SIZE); // TFTP tsize option length.
+
+ if (type == TYPE_RRQ)
+ {
+ op_length += sizeof(OPTION_SIZE_REQUEST_VALUE); // Just send 0 to inform other side that you support this option and would like to receive file size inside OptionACK.
+ }
+
+ if (type == TYPE_WRQ)
+ {
+ UNUSED_VARIABLE(uint_to_str(m_instances[index].p_file->file_size, file_size_str, sizeof(file_size_str)));
+ op_length += strlen(file_size_str) + 1;
+ }
+
+ if (m_instances[index].p_password != NULL)
+ {
+ if (m_instances[index].p_password[0] != '\0')
+ {
+ op_length += strlen(m_instances[index].p_password) + 1; // Password is always sent as last string without option name.
+ }
+ }
+
+ return op_length;
+}
+
+/**@brief This function inserts options into request packet payload using passed option iterator.
+ *
+ * @param[in] index Index of TFTP instance.
+ * @param[in] type Type of TFTP request.
+ * @param[in] p_iter Iterator which will be used to set options.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static uint32_t insert_options(uint32_t index, uint32_t type, option_iter_t * p_iter)
+{
+ uint32_t err_code = NRF_SUCCESS;
+ char next_retr_str[NEXT_RETR_MAX_LENGTH];
+ char block_size_str[BLKSIZE_MAX_LENGTH];
+ char file_size_str[FILE_SIZE_MAX_LENGTH];
+
+ if (type == TYPE_RRQ)
+ {
+ err_code = op_set(p_iter, OPTION_SIZE, OPTION_SIZE_REQUEST_VALUE);
+ }
+
+ if (type == TYPE_WRQ)
+ {
+ UNUSED_VARIABLE(uint_to_str(m_instances[index].p_file->file_size, file_size_str, FILE_SIZE_MAX_LENGTH));
+ err_code = op_set(p_iter, OPTION_SIZE, file_size_str);
+ }
+
+ if (err_code == NRF_SUCCESS)
+ {
+ if ((m_instances[index].init_params.next_retr > 0) &&
+ (m_instances[index].init_params.next_retr < 256))
+ {
+ UNUSED_VARIABLE(uint_to_str(m_instances[index].init_params.next_retr, next_retr_str, NEXT_RETR_MAX_LENGTH));
+ err_code = op_set(p_iter, OPTION_TIMEOUT, next_retr_str);
+ if (err_code != NRF_SUCCESS)
+ {
+ return err_code;
+ }
+ }
+
+ if ((m_instances[index].init_params.block_size > 0) &&
+ (m_instances[index].init_params.block_size != TFTP_DEFAULT_BLOCK_SIZE))
+ {
+ UNUSED_VARIABLE(uint_to_str(m_instances[index].init_params.block_size, block_size_str, BLKSIZE_MAX_LENGTH));
+ err_code = op_set(p_iter, OPTION_BLKSIZE, block_size_str);
+ if (err_code != NRF_SUCCESS)
+ {
+ return err_code;
+ }
+ }
+
+ if (m_instances[index].p_password != NULL)
+ {
+ if (m_instances[index].p_password[0] != '\0')
+ {
+ err_code = op_set(p_iter, NULL, m_instances[index].p_password);
+ }
+ }
+ }
+
+ return err_code;
+}
+
+/**@Sends Read/Write TFTP Request.
+ *
+ * @param[in] type Type of request (allowed values: TYPE_RRQ and TYPE_WRQ).
+ * @param[in] p_tftp Pointer to the TFTP instance (from user space).
+ * @param[in] p_file Pointer to the file, which should be assigned to passed instance.
+ * @param[in] p_params Pointer to transmission parameters structure (retransmission time, block size).
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static uint32_t send_request(uint32_t type,
+ iot_tftp_t * p_tftp,
+ iot_file_t * p_file,
+ const char * p_path)
+{
+ uint32_t err_code;
+ iot_pbuffer_t * p_buffer;
+ iot_pbuffer_alloc_param_t buffer_param;
+ uint8_t * p_rrq_packet;
+ option_iter_t rrq_iter;
+ uint32_t index;
+ uint32_t byte_index;
+
+ err_code = find_instance(p_tftp, &index);
+ if (err_code != NRF_SUCCESS)
+ {
+ return err_code;
+ }
+
+ if (p_path == NULL || p_path[0] == '\0')
+ {
+ TFTP_ERR("[TFTP]: Invalid path passed.\r\n");
+ return (NRF_ERROR_INVALID_PARAM | IOT_TFTP_ERR_BASE);
+ }
+
+ if (p_file != NULL && p_file->p_filename[0] == '\0')
+ {
+ TFTP_ERR("Invalid file name passed!");
+ return (NRF_ERROR_INVALID_PARAM | IOT_TFTP_ERR_BASE);
+ }
+
+ if (m_instances[index].state != STATE_IDLE)
+ {
+ TFTP_ERR("Invalid instance state!");
+ return (NRF_ERROR_INVALID_STATE | IOT_TFTP_ERR_BASE);
+ }
+
+ // Assign file with TFTP instance.
+ m_instances[index].p_file = p_file;
+ m_instances[index].p_path = p_path;
+ m_instances[index].block_id = 0;
+ m_instances[index].dst_tid = m_instances[index].dst_port;
+
+ memset(&buffer_param, 0, sizeof(buffer_param));
+ buffer_param.type = UDP6_PACKET_TYPE;
+ buffer_param.flags = PBUFFER_FLAG_DEFAULT;
+
+ // Calculate size of required packet.
+ buffer_param.length = TFTP_HEADER_SIZE; // Bytes reserved for TFTP opcode value.
+ buffer_param.length += strlen(p_path) + 1; // File name with '\0' character.
+ buffer_param.length += sizeof(OPTION_MODE_OCTET); // Mode option value length.
+
+ TFTP_TRC("Estimated packet length without options: %ld.", buffer_param.length);
+
+ buffer_param.length += count_options_length(index, type); // TFTP options.
+
+ TFTP_TRC("Estimated packet length with options: %ld.", buffer_param.length);
+
+ // Allocate packet buffer.
+ err_code = iot_pbuffer_allocate(&buffer_param, &p_buffer);
+ if (err_code != NRF_SUCCESS)
+ {
+ return err_code;
+ }
+
+ memset(p_buffer->p_payload, 0, buffer_param.length);
+ byte_index = 0;
+
+ // Compose TFTP Read Request according to configuration.
+ p_rrq_packet = p_buffer->p_payload;
+ uint16_t size = uint16_encode(HTONS(type), &p_rrq_packet[byte_index]);
+ byte_index += size;
+
+ // Initialization of option iterator.
+ op_init(&rrq_iter, (char *)&p_rrq_packet[byte_index], buffer_param.length - TFTP_HEADER_SIZE); // Options uses whole packet except opcode.
+ rrq_iter.p_start[0] = '\0';
+
+ // Insert file path and mode strings.
+ err_code = op_set(&rrq_iter, NULL, p_path);
+ PBUFFER_FREE_IF_ERROR(err_code);
+
+ err_code = op_set(&rrq_iter, NULL, OPTION_MODE_OCTET);
+ PBUFFER_FREE_IF_ERROR(err_code);
+
+ // Insert TFTP options into packet.
+ err_code = insert_options(index, type, &rrq_iter);
+ PBUFFER_FREE_IF_ERROR(err_code);
+
+ // Change instance status to connecting.
+ if (type == TYPE_RRQ)
+ {
+ m_instances[index].state = STATE_CONNECTING_RRQ;
+ }
+ else
+ {
+ m_instances[index].state = STATE_CONNECTING_WRQ;
+ }
+
+ // Send read request.
+ UNUSED_VARIABLE(retr_timer_reset(index));
+ err_code = udp6_socket_sendto(&m_instances[index].socket,
+ &m_instances[index].addr,
+ m_instances[index].dst_tid,
+ p_buffer);
+
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_ERR("Unable to send request!");
+ m_instances[index].state = STATE_IDLE;
+ }
+
+ PBUFFER_FREE_IF_ERROR(err_code);
+
+ return NRF_SUCCESS;
+}
+
+/**@brief Function used in order to change initial connection parameters. */
+uint32_t iot_tftp_set_params(iot_tftp_t * p_tftp, iot_tftp_trans_params_t * p_params)
+{
+ uint32_t err_code;
+ uint32_t index;
+
+ NULL_PARAM_CHECK(p_params);
+ NULL_PARAM_CHECK(p_tftp);
+
+ TFTP_ENTRY();
+
+ TFTP_MUTEX_LOCK();
+
+ err_code = find_instance(p_tftp, &index);
+ if (err_code == NRF_SUCCESS)
+ {
+ // Modifying connection parameters could be done only when TFTP instance is disconnected.
+ // NOTE: STATE_FREE is not allowed, because there have to be a moment (e.g. after calling iot_tftp_init()) when transmission parameters were set to default values.
+ if (m_instances[index].state == STATE_IDLE)
+ {
+ // Reset connection parameters to initial values. They will be set (negotiated) after get/put call.
+ memcpy(&m_instances[index].init_params, p_params, sizeof(iot_tftp_trans_params_t));
+ }
+ else
+ {
+ err_code = (NRF_ERROR_INVALID_STATE | IOT_TFTP_ERR_BASE);
+
+ TFTP_ERR("Cannot modify connection parameters inside %d state!", m_instances[index].state);
+ }
+ }
+ else
+ {
+ TFTP_ERR("Unable to find TFTP instance!");
+ }
+
+ TFTP_MUTEX_UNLOCK();
+
+ TFTP_EXIT();
+
+ return err_code;
+}
+
+/**@brief Function for performing retransmissions of TFTP acknowledgments. */
+void iot_tftp_timeout_process(iot_timer_time_in_ms_t wall_clock_value)
+{
+ uint32_t index;
+ uint32_t err_code;
+
+ UNUSED_PARAMETER(wall_clock_value);
+
+ TFTP_MUTEX_LOCK();
+
+ for (index = 0; index < TFTP_MAX_INSTANCES; index++)
+ {
+ if ((m_instances[index].state == STATE_CONNECTING_RRQ) ||
+ (m_instances[index].state == STATE_CONNECTING_WRQ) ||
+ (m_instances[index].state == STATE_SENDING) ||
+ (m_instances[index].state == STATE_RECEIVING))
+ {
+ TFTP_ENTRY();
+ TFTP_TRC("Current timer: %ld, %ld", m_instances[index].request_timeout, wall_clock_value);
+
+ if (instance_timer_is_expired(index))
+ {
+ err_code = NRF_SUCCESS;
+
+ if (m_instances[index].retries < TFTP_MAX_RETRANSMISSION_COUNT)
+ {
+ TFTP_TRC("Query retransmission [%d] for file %s.",
+ m_instances[index].retries, m_instances[index].p_file->p_filename);
+
+ // Increase retransmission number.
+ m_instances[index].retries++;
+
+ TFTP_TRC("Compose packet for retransmission.");
+ // Send packet again.
+ if (m_instances[index].state == STATE_RECEIVING)
+ {
+ TFTP_TRC("Retransmission of ACK packet.");
+ err_code = create_ack_packet(index, m_instances[index].block_id);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ err_code = send_response(&index);
+ }
+ else
+ {
+ TFTP_ERR("Failed to create packet!");
+ handle_evt_err(index, err_code, NULL);
+ }
+ }
+ else if (m_instances[index].state == STATE_SENDING)
+ {
+ TFTP_TRC("Retransmission of DATA packet.");
+ err_code = create_data_packet(index, m_instances[index].block_id - 1);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ if (m_instances[index].p_file->p_callback == NULL)
+ {
+ err_code = send_response(&index);
+ }
+ }
+ else
+ {
+ TFTP_ERR("Failed to create packet!");
+ handle_evt_err(index, err_code, NULL);
+ }
+ }
+ else if (m_instances[index].state == STATE_CONNECTING_RRQ)
+ {
+ TFTP_TRC("OACK time out. Retransmit RRQ.");
+ m_instances[index].state = STATE_IDLE;
+ err_code = send_request(TYPE_RRQ, &index, m_instances[index].p_file, m_instances[index].p_path);
+ }
+ else if (m_instances[index].state == STATE_CONNECTING_WRQ)
+ {
+ TFTP_TRC("OACK time out. Retransmit WRQ.");
+ m_instances[index].state = STATE_IDLE;
+ err_code = send_request(TYPE_WRQ, &index, m_instances[index].p_file, NULL);
+ }
+ else
+ {
+ TFTP_TRC("In idle state.");
+ }
+ }
+ else
+ {
+ TFTP_ERR("TFTP server did not response on query for file %s.",
+ m_instances[index].p_file->p_filename);
+
+ // No response from server.
+ err_code = TFTP_REMOTE_UNREACHABLE;
+ }
+
+ if (err_code != NRF_SUCCESS)
+ {
+ // Inform application that timeout occurs.
+ TFTP_ERR("Timeout error.");
+ handle_evt_err(index, err_code, NULL);
+ }
+ }
+ TFTP_MUTEX_UNLOCK();
+
+ TFTP_EXIT();
+
+ return;
+ }
+ }
+
+ TFTP_MUTEX_UNLOCK();
+}
+
+/**@brief Initializes TFTP client. */
+uint32_t iot_tftp_init(iot_tftp_t * p_tftp, iot_tftp_init_t * p_init_params)
+{
+ uint32_t index = 0;
+ uint32_t err_code;
+
+ NULL_PARAM_CHECK(p_tftp);
+ NULL_PARAM_CHECK(p_init_params);
+ NULL_PARAM_CHECK(p_init_params->p_ipv6_addr);
+
+ TFTP_ENTRY();
+
+ TFTP_MUTEX_LOCK();
+
+ // Find first available instance.
+ err_code = find_free_instance(&index);
+ if (err_code == NRF_SUCCESS)
+ {
+ // Reset instance values.
+ instance_reset(index);
+
+ // Assign new values.
+ *p_tftp = index;
+ m_instances[index].callback = p_init_params->callback;
+ m_instances[index].src_tid = p_init_params->src_port;
+ m_instances[index].dst_port = p_init_params->dst_port;
+ m_instances[index].p_password = p_init_params->p_password;
+ m_instances[index].dst_tid = m_instances[index].dst_port;
+ memcpy(&m_instances[index].addr, p_init_params->p_ipv6_addr, sizeof(ipv6_addr_t));
+
+ // Configure socket.
+ err_code = udp6_socket_allocate(&m_instances[index].socket);
+ if (err_code == NRF_SUCCESS)
+ {
+ err_code = udp6_socket_bind(&m_instances[index].socket,
+ IPV6_ADDR_ANY,
+ p_init_params->src_port);
+ if (err_code == NRF_SUCCESS)
+ {
+ // Attach callback.
+ err_code = udp6_socket_recv(&m_instances[index].socket, client_process);
+ if (err_code == NRF_SUCCESS)
+ {
+ m_instances[index].state = STATE_IDLE;
+ }
+ }
+
+ if (err_code != NRF_SUCCESS)
+ {
+ (void)udp6_socket_free(&m_instances[index].socket);
+
+ TFTP_ERR("UDP socket configuration failure!");
+ }
+ }
+ }
+ else
+ {
+ TFTP_ERR("No more free instances left!");
+ }
+
+ TFTP_MUTEX_UNLOCK();
+
+ TFTP_EXIT();
+
+ return err_code;
+}
+
+/**@brief Resets TFTP client instance, so it is possible to make another request after error. */
+uint32_t iot_tftp_abort(iot_tftp_t * p_tftp)
+{
+ uint32_t err_code;
+ uint32_t index;
+
+ NULL_PARAM_CHECK(p_tftp);
+
+ TFTP_ENTRY();
+
+ TFTP_MUTEX_LOCK();
+
+ err_code = find_instance(p_tftp, &index);
+ if (err_code == NRF_SUCCESS)
+ {
+ instance_abort(index);
+ }
+
+ TFTP_MUTEX_UNLOCK();
+
+ TFTP_EXIT();
+
+ return err_code;
+}
+
+/**@brief Frees assigned sockets. */
+uint32_t iot_tftp_uninit(iot_tftp_t * p_tftp)
+{
+ uint32_t err_code;
+ uint32_t index;
+
+ NULL_PARAM_CHECK(p_tftp);
+
+ TFTP_ENTRY();
+
+ TFTP_MUTEX_LOCK();
+
+ err_code = find_instance(p_tftp, &index);
+ if (err_code == NRF_SUCCESS)
+ {
+ if (m_instances[index].state == STATE_SEND_HOLD ||
+ m_instances[index].state == STATE_RECV_HOLD ||
+ m_instances[index].state == STATE_SENDING ||
+ m_instances[index].state == STATE_RECEIVING)
+ {
+ // Free pbuffer.
+ err_code = iot_pbuffer_free(m_instances[index].p_packet, true);
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_ERR("Cannot free pbuffer - %p", m_instances[index].p_packet);
+ }
+
+ err_code = iot_file_fclose(m_instances[index].p_file);
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_ERR("Cannot close file - %p", m_instances[index].p_file);
+ }
+ }
+
+ if (m_instances[index].state != STATE_IDLE)
+ {
+ handle_evt_err(index, TFTP_UNDEFINED_ERROR, UNINT_ERROR_MSG);
+ }
+
+ (void)udp6_socket_free(&m_instances[index].socket);
+ m_instances[index].state = STATE_FREE;
+ }
+
+ TFTP_MUTEX_UNLOCK();
+
+ TFTP_EXIT();
+
+ return err_code;
+}
+
+/**@brief Retrieves file from remote server into p_file. */
+uint32_t iot_tftp_get(iot_tftp_t * p_tftp, iot_file_t * p_file, const char * p_path)
+{
+ uint32_t err_code;
+
+ NULL_PARAM_CHECK(p_tftp);
+ NULL_PARAM_CHECK(p_path);
+ if (p_file != NULL)
+ {
+ NULL_PARAM_CHECK(p_file->p_filename);
+ }
+
+ TFTP_ENTRY();
+
+ TFTP_MUTEX_LOCK();
+
+ err_code = send_request(TYPE_RRQ, p_tftp, p_file, p_path);
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_ERR("Error while sending read request. Reason: %08lx.", err_code);
+ }
+
+ TFTP_MUTEX_UNLOCK();
+
+ TFTP_EXIT();
+
+ return err_code;
+}
+
+/**@brief Sends local file p_file to a remote server. */
+uint32_t iot_tftp_put(iot_tftp_t * p_tftp, iot_file_t * p_file, const char * p_path)
+{
+ uint32_t err_code;
+
+ NULL_PARAM_CHECK(p_tftp);
+ NULL_PARAM_CHECK(p_file);
+ NULL_PARAM_CHECK(p_file->p_filename);
+
+ TFTP_ENTRY();
+
+ TFTP_MUTEX_LOCK();
+
+ err_code = send_request(TYPE_WRQ, p_tftp, p_file, p_path);
+ if (err_code != NRF_SUCCESS)
+ {
+ TFTP_ERR("Error while sending write request. Reason: %08lx.", err_code);
+ }
+
+ TFTP_MUTEX_UNLOCK();
+
+ TFTP_EXIT();
+
+ return err_code;
+}
+
+/**@brief Holds transmission of ACK (use in order to slow transmission). */
+uint32_t iot_tftp_hold(iot_tftp_t * p_tftp)
+{
+ uint32_t index;
+ uint32_t err_code;
+
+ NULL_PARAM_CHECK(p_tftp);
+
+ TFTP_ENTRY();
+
+ TFTP_MUTEX_LOCK();
+
+ err_code = find_instance(p_tftp, &index);
+ if (err_code == NRF_SUCCESS)
+ {
+ // Hold transfer.
+ err_code = transfer_hold(index);
+ }
+ else
+ {
+ TFTP_ERR("Hold called on unknown TFTP instance.");
+ }
+
+ TFTP_MUTEX_UNLOCK();
+
+ TFTP_EXIT();
+
+ return err_code;
+}
+
+/**@brief Resumes transmission. */
+uint32_t iot_tftp_resume(iot_tftp_t * p_tftp)
+{
+ uint32_t index;
+ uint32_t err_code;
+
+ NULL_PARAM_CHECK(p_tftp);
+
+ TFTP_ENTRY();
+
+ TFTP_MUTEX_LOCK();
+
+ err_code = find_instance(p_tftp, &index);
+ if (err_code == NRF_SUCCESS)
+ {
+ err_code = transfer_resume(index);
+ }
+ else
+ {
+ TFTP_ERR("Failed to find instance.");
+ }
+
+ TFTP_MUTEX_UNLOCK();
+
+ TFTP_EXIT();
+
+ return err_code;
+}
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/tftp/iot_tftp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/tftp/iot_tftp.h
new file mode 100644
index 0000000..0405af2
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/tftp/iot_tftp.h
@@ -0,0 +1,245 @@
+/**
+ * Copyright (c) 2015 - 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.
+ *
+ */
+/** @file iot_tftp.h
+ *
+ * @defgroup iot_tftp TFTP Application Interface for Nordic's IPv6 stack
+ * @ingroup iot_sdk_stack
+ * @{
+ * @brief Trivial File Transfer Protocol module provides implementation of TFTP Client.
+ *
+ */
+
+#ifndef IOT_TFTP_H__
+#define IOT_TFTP_H__
+
+#include "sdk_common.h"
+#include "iot_common.h"
+#include "iot_timer.h"
+#include "iot_file.h"
+#include "iot_defines.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**@brief TFTP global instance number. */
+typedef uint32_t iot_tftp_t;
+
+/**@brief TFTP module Events. */
+typedef enum
+{
+ IOT_TFTP_EVT_ERROR, /**< Event code indicating that an error occurred. */
+ IOT_TFTP_EVT_TRANSFER_DATA_RECEIVED, /**< Event code indicating that a data packet was received during read transfer. */
+ IOT_TFTP_EVT_TRANSFER_GET_COMPLETE, /**< Event code indicating that transfer read was completed. */
+ IOT_TFTP_EVT_TRANSFER_PUT_COMPLETE, /**< Event code indicating that transfer write was completed. */
+} iot_tftp_evt_id_t;
+
+/**@brief TFTP error event structure. */
+typedef struct
+{
+ uint32_t code; /**< Error code. */
+ char * p_msg; /**< Message describing the reason or NULL if no description is available. */
+ uint32_t size_transfered; /**< In case of an error, variable indicates a number of successfully read or write bytes. */
+} iot_tftp_evt_err_t;
+
+/**@brief TFTP data received event structure. */
+typedef struct
+{
+ uint8_t * p_data; /**< Pointer to received data chunk. */
+ uint16_t size; /**< Size of received data chunk. */
+} iot_tftp_evt_data_received_t;
+
+/**@brief TFTP event structure. */
+typedef union
+{
+ iot_tftp_evt_err_t err; /**< Error event structure. Used only in case of IOT_TFTP_EVT_ERROR error. */
+ iot_tftp_evt_data_received_t data_received; /**< Data received event structure. Used only in case of IOT_TFTP_EVT_TRANSFER_DATA_RECEIVED event. */
+} iot_tftp_evt_param_t;
+
+/**@brief Asynchronous event type. */
+typedef struct
+{
+ iot_tftp_evt_id_t id; /**< Event code. */
+ iot_tftp_evt_param_t param; /**< Union to structures describing event. */
+ iot_file_t * p_file; /**< File associated with TFTP transfer. */
+} iot_tftp_evt_t;
+
+/**@brief TFTP Transmission initialization structure (both GET and PUT). */
+typedef struct
+{
+ uint32_t next_retr; /**< Number of seconds between retransmissions. */
+ uint16_t block_size; /**< Maximum or negotiated size of data block. */
+} iot_tftp_trans_params_t;
+
+/**@brief User callback from TFTP module.
+ *
+ * @note TFTP module user callback will be invoked even if user asks TFTP to abort (TFTP error event).
+ *
+ * @param[in] p_tftp Pointer to the TFTP instance.
+ * @param[in] p_evt Pointer to the TFTP event structure, describing reason.
+ *
+ * @retval None.
+ */
+typedef void (*iot_tftp_callback_t)(iot_tftp_t * p_tftp, iot_tftp_evt_t * p_evt);
+
+
+/**@brief TFTP initialization structure. */
+typedef struct
+{
+ ipv6_addr_t * p_ipv6_addr; /**< IPv6 address of the server. */
+ uint16_t src_port; /**< Source port (local UDP port) from which all request and data will be sent. Should be choosen randomly. */
+ uint16_t dst_port; /**< Destination port - UDP port on which server listens for new connections. */
+ iot_tftp_callback_t callback; /**< Reference to the user callback. */
+ const char * p_password; /**< Server password for all requests. Shall be NULL if no password is required. */
+} iot_tftp_init_t;
+
+
+/**@brief Initializes TFTP client.
+ *
+ * @param[in] p_tftp Pointer to the TFTP instance. Should not be NULL.
+ * @param[in] p_init_params Initialization structure for TFTP client. Should not be NULL.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+uint32_t iot_tftp_init(iot_tftp_t * p_tftp, iot_tftp_init_t * p_init_params);
+
+
+/**@brief Function used in order to change initial connection parameters.
+ *
+ * @param[in] p_tftp Reference to the TFTP instance.
+ * @param[in] p_params Pointer to transmission parameters structure. Should not be NULL.
+ *
+ * @retval NRF_SUCCESS if parameters successfully set, else an error code indicating reason
+ * for failure.
+ */
+uint32_t iot_tftp_set_params(iot_tftp_t * p_tftp, iot_tftp_trans_params_t * p_params);
+
+
+/**@brief Retrieves file from remote server into p_file.
+ *
+ * If @p p_file is a NULL pointer, the content of received file can be retrieved by handling
+ * @ref IOT_TFTP_EVT_TRANSFER_DATA_RECEIVED event. This event is generated each time a data
+ * packet (containing a chunk of requested file) is received.
+ * IOT_TFTP_EVT_TRANSFER_GET_COMPLETE event is generated after download is complete.
+ *
+ * @note This function should not be called until previous download operation is completed.
+ *
+ * @param[in] p_tftp Pointer to the TFTP instance.
+ * @param[in] p_file Reference to the file from which data should be read.
+ * @param[in] p_path Path of the requested file on the remote server. Shall not be NULL.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+uint32_t iot_tftp_get(iot_tftp_t * p_tftp, iot_file_t * p_file, const char * p_path);
+
+
+/**@brief Sends local file p_file to a remote server.
+ *
+ * @param[in] p_tftp Pointer to the TFTP instance.
+ * @param[in] p_file Reference to the file to which data should be stored. Should not be NULL.
+ * @param[in] p_path Path of the file on the remote server. Shall not be NULL.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+uint32_t iot_tftp_put(iot_tftp_t * p_tftp, iot_file_t * p_file, const char * p_path);
+
+
+/**@brief Holds transmission of ACK (use in order to slow transmission).
+ *
+ * @param[in] p_tftp Pointer to the TFTP instance.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+uint32_t iot_tftp_hold(iot_tftp_t * p_tftp);
+
+
+/**@brief Resumes transmission.
+ *
+ * @param[in] p_tftp Pointer to the TFTP instance.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+uint32_t iot_tftp_resume(iot_tftp_t * p_tftp);
+
+
+/**@brief Resets TFTP client instance, so it is possible to make another request after error.
+ *
+ * @param[in] p_tftp Pointer to the TFTP instance.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+uint32_t iot_tftp_abort(iot_tftp_t * p_tftp);
+
+
+/**@brief Frees assigned sockets.
+ *
+ * @param[in] p_tftp Pointer to the TFTP instance.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+uint32_t iot_tftp_uninit(iot_tftp_t * p_tftp);
+
+
+/**@brief Function for performing retransmissions of TFTP acknowledgments.
+ *
+ * @note TFTP module implements the retransmission mechanism by invoking this function periodically.
+ * So that method has to be added to IoT Timer client list and has to be called with minimum of
+ * TFTP_RETRANSMISSION_INTERVAL resolution.
+ *
+ * @param[in] wall_clock_value The value of the wall clock that triggered the callback.
+ *
+ * @retval None.
+ */
+void iot_tftp_timeout_process(iot_timer_time_in_ms_t wall_clock_value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_TFTP_H__
+
+/** @} */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/udp/udp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/udp/udp.h
new file mode 100644
index 0000000..ae316b0
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/udp/udp.h
@@ -0,0 +1,98 @@
+/**
+ * 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.
+ *
+ */
+/** @cond */
+ /** @file udp.h
+ *
+ * @defgroup iot_udp UDP Module Header.
+ * @ingroup iot_sdk
+ * @{
+ * @brief User Datagram Protocol module header defining interface between
+ * UDP and other IP stack layers which are not exposed to the application.
+ */
+
+#ifndef UDP_H__
+#define UDP_H__
+
+#include "sdk_config.h"
+#include "sdk_common.h"
+#include "ipv6_api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Module initialization function called from ipv6_init(). This shall not be called by the
+ * application explicitly.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+uint32_t udp_init(void);
+
+
+/**
+ * @brief Function to feed incoming UDP packets to the module. To be called by the IPv6
+ * stack only and never by the application.
+ *
+ * @param[in] p_interface Identifies network interface on which the packet is received.
+ * @param[in] p_ip_header IP Header of the UDP packet being fed to the module.
+ * @param[in] p_packet UDP packet being notified to the module. p_packet->p_payload points the
+ * IPv6 payload and p_packet->length indicates total length of the payload.
+ *
+ * @note This routine is called by the stack with next header field value is set to UDP protocol
+ * value of 17.
+ *
+ * @retval NRF_SUCCESS on successful handling of the packet, else an error code indicating reason
+ * for failure.
+ */
+uint32_t udp_input(const iot_interface_t * p_interface,
+ const ipv6_header_t * p_ip_header,
+ iot_pbuffer_t * p_packet);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //UDP_H__
+
+/**@} */
+
+/** @endcond */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/udp/udp6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/udp/udp6.c
new file mode 100644
index 0000000..0572c4a
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/udp/udp6.c
@@ -0,0 +1,708 @@
+/**
+ * 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;
+}
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/utils/ipv6_utils.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/utils/ipv6_utils.c
new file mode 100644
index 0000000..8b70cba
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/utils/ipv6_utils.c
@@ -0,0 +1,101 @@
+/**
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include "nordic_common.h"
+#include "sdk_common.h"
+#include "sdk_config.h"
+#include "ipv6_utils.h"
+
+void ipv6_checksum_calculate(const uint8_t * p_data, uint16_t len, uint16_t * p_checksum, bool flip_flag)
+{
+ uint16_t checksum_even = (((*p_checksum) & 0xFF00) >> 8);
+ uint16_t checksum_odd = ((*p_checksum) & 0x00FF);
+
+ while (len)
+ {
+ if ( len == 1 )
+ {
+ checksum_even += (*p_data);
+ len -= 1;
+ }
+ else
+ {
+ checksum_even += *p_data++;
+ checksum_odd += *p_data++;
+ len -= 2;
+ }
+
+ if (checksum_odd & 0xFF00)
+ {
+ checksum_even += ((checksum_odd & 0xFF00) >> 8);
+ checksum_odd = (checksum_odd & 0x00FF);
+ }
+
+ if (checksum_even & 0xFF00)
+ {
+ checksum_odd += ((checksum_even & 0xFF00) >> 8);
+ checksum_even = (checksum_even & 0x00FF);
+ }
+ }
+
+ checksum_even = (checksum_even << 8) + (checksum_odd & 0xFFFF);
+
+ if (flip_flag)
+ {
+ // We use 0xFFFF instead of 0x0000 because of not operator.
+ if (checksum_even == 0xFFFF)
+ {
+ checksum_even = 0x0000;
+ }
+ }
+
+ (*p_checksum) = (uint16_t)(checksum_even);
+}
+
+void ipv6_header_init(ipv6_header_t * p_ip_header)
+{
+ p_ip_header->version_traffic_class = IPV6_DEFAULT_VER_TC;
+ p_ip_header->traffic_class_flowlabel = IPV6_DEFAULT_TC_FL;
+ p_ip_header->flowlabel = IPV6_DEFAULT_FL;
+ p_ip_header->next_header = IPV6_NEXT_HEADER_RESERVED;
+ p_ip_header->hoplimit = IPV6_DEFAULT_HOP_LIMIT;
+ p_ip_header->length = 0;
+}
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/utils/ipv6_utils.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/utils/ipv6_utils.h
new file mode 100644
index 0000000..5cbdf15
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/utils/ipv6_utils.h
@@ -0,0 +1,93 @@
+/**
+ * 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.
+ *
+ */
+/** @file ipv6_utils.h
+ *
+ * @defgroup iot_utils Common utils
+ * @ingroup iot_sdk_common
+ * @{
+ * @brief Abstracts common IOT macros needed by IOT modules.
+ *
+ * @details Common macros related to IOT are defined here for use by all IOT modules.
+ */
+
+#ifndef IPV6_UTILS_H__
+#define IPV6_UTILS_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "iot_defines.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**@brief Function for calculating checksum using IPv6 algorithm.
+ *
+ * @param[in] p_data Pointer to the data needs to be checksummed.
+ * @param[in] len Length of the data.
+ * @param[in] p_checksum Pointer to actual value of checksum.
+ * @param[out] p_checksum Value of calculated checksum.
+ *
+ * @retval None.
+ */
+void ipv6_checksum_calculate(const uint8_t * p_data,
+ uint16_t len,
+ uint16_t * p_checksum,
+ bool flip_zero);
+
+
+/**@brief Function for initializing default values of IPv6 Header.
+ *
+ * @note Function initializes Version, Traffic Class, Flow Label, Next Header, Hop Limit and
+ * Length fields.
+ *
+ * @param[in] p_ip_header Pointer to the IPv6 Header.
+ *
+ * @retval None.
+ */
+void ipv6_header_init(ipv6_header_t * p_ip_header);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //IPV6_UTILS_H__
+
+/**@} */