diff options
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack')
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(¤t_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__ + +/**@} */ |