/* * Copyright (c) 2014-2018, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * 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 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 the copyright holder nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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 "nsconfig.h" #include #include #include #include #include "randLIB.h" #include "ns_trace.h" #include "common_functions.h" #include "NWK_INTERFACE/Include/protocol.h" #include "NWK_INTERFACE/Include/protocol_abstract.h" #include "6LoWPAN/Thread/thread_config.h" #include "6LoWPAN/Thread/thread_common.h" #include "6LoWPAN/Thread/thread_routing.h" #include "6LoWPAN/Thread/thread_joiner_application.h" #include "6LoWPAN/Thread/thread_leader_service.h" #include "6LoWPAN/Thread/thread_management_internal.h" #include "6LoWPAN/Thread/thread_bootstrap.h" #include "6LoWPAN/Thread/thread_router_bootstrap.h" #include "6LoWPAN/Thread/thread_discovery.h" #include "6LoWPAN/Thread/thread_nvm_store.h" #include "6LoWPAN/Thread/thread_extension_bootstrap.h" #include "6LoWPAN/Thread/thread_neighbor_class.h" #include "MLE/mle.h" #include "thread_meshcop_lib.h" #include "thread_diagcop_lib.h" #include "coap_service_api.h" #include "Service_Libs/mle_service/mle_service_api.h" #include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h" #include "6LoWPAN/MAC/mac_helper.h" #define TRACE_GROUP "tapi" int_fast8_t arm_nwk_6lowpan_thread_test_add_neighbour( int8_t interface_id, uint16_t neighbour_short_addr, uint_fast8_t link_margin_db, uint8_t id_sequence, const uint8_t *id_mask, const uint8_t *route_data) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { tr_warn("Invalid interface id"); return -1; } return thread_routing_add_link(cur, neighbour_short_addr, link_margin_db, id_sequence, id_mask, route_data, true); #else (void)interface_id; (void)neighbour_short_addr; (void)link_margin_db; (void)id_sequence; (void)id_mask; (void)route_data; return -1; #endif } int_fast8_t arm_nwk_6lowpan_thread_test_remove_neighbour( int8_t interface_id, uint16_t neighbour_short_addr) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { tr_warn("Invalid interface id"); return -1; } return thread_routing_remove_link(cur, neighbour_short_addr); #else (void)interface_id; (void)neighbour_short_addr; return -1; #endif } void arm_nwk_6lowpan_thread_test_print_routing_database(int8_t interface_id) { #if defined(FEA_TRACE_SUPPORT) && defined(HAVE_THREAD) const protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { tr_warn("Invalid interface id"); return; } const thread_info_t *thread = cur->thread_info; if (!thread) { tr_warn("Thread not active"); return; } //tr_debug("Thread routing database"); if (thread_i_am_router(cur)) { tr_info("I am Thread Router (ID: %d)", thread_router_id_from_addr(mac_helper_mac16_address_get(cur))); } else { tr_info("I am not a Thread Router"); } //tr_debug("Router ID Set + Current Route:"); if (thread_i_am_router(cur)) { for (int r = 0; r < N_THREAD_ROUTERS; r++) { if (thread->routing.fast_route_table[r] != FAST_ROUTE_INVALID_ID) { if (thread->routing.fast_route_table[r] == FAST_ROUTE_NO_ROUTE) { tr_debug("Current Route - fast no route: %d -> ?", r); } else { tr_debug("Current Route - fast route: %d -> %d", r, thread->routing.fast_route_table[r]); } } } } ns_list_foreach(const thread_router_link_t, n, &thread->routing.link_set) { tr_debug("Link Set - Id:%d Margin:%d.%x InQ:%d OutQ:%d As good:%d Age:%ds", n->router_id, n->link_margin >> THREAD_LINK_MARGIN_SCALING, n->link_margin & ((1 << THREAD_LINK_MARGIN_SCALING) - 1), n->incoming_quality, n->outgoing_quality, n->as_good, n->age / 10); } if (thread_i_am_router(cur)) { ns_list_foreach(const thread_route_t, route, &thread->routing.route_set) { tr_debug("Route Set - Dest:%d Next hop:%d Cost:%d", route->destination, route->next_hop, route->route_cost); } } #else (void)interface_id; #endif } int8_t thread_routing_set_network_id_timeout(int8_t interface_id, uint16_t network_id_timeout) { #ifdef HAVE_THREAD const protocol_interface_info_entry_t *cur; if (network_id_timeout < 5) { tr_warn("Too small timeout value, min: 5"); return -3; } cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { tr_warn("Invalid interface id"); return -1; } if (!cur->thread_info) { tr_warn("Thread not active"); return -2; } cur->thread_info->routing.networkIdTimeout = network_id_timeout; return 0; #else (void)interface_id; (void)network_id_timeout; return -1; #endif } int8_t thread_routing_get_network_id_timeout(int8_t interface_id, uint16_t *network_id_timeout) { #ifdef HAVE_THREAD const protocol_interface_info_entry_t *cur; if (!network_id_timeout) { tr_warn("Invalid input ptr"); return -3; } cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { tr_warn("Invalid interface id"); return -1; } if (!cur->thread_info) { tr_warn("Thread not active"); return -2; } *network_id_timeout = cur->thread_info->routing.networkIdTimeout; return 0; #else (void)interface_id; (void)network_id_timeout; return -1; #endif } int thread_test_set_context_id_reuse_timeout( int8_t interface_id, uint32_t timeout) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; if (timeout < 60 || timeout > THREAD_CONTEXT_ID_REUSE_TIMEOUT) { return -1; } cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { tr_warn("Invalid interface id"); return -1; } if (!cur->thread_info) { tr_warn("Not Thread specific interface"); return -1; } cur->thread_info->networkDataStorage.contex_id_reuse_timeout = timeout; return 0; #else (void)interface_id; (void)timeout; return -1; #endif } int thread_test_remove_router_by_id(int8_t interface_id, uint8_t routerId) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return -1; } if (!cur->thread_info) { return -1; } if (!cur->thread_info->leader_private_data) { return -1; } if (thread_attach_ready(cur) != 0) { return -1; } if (routerId >= 64) { return -1; } if (!thread_leader_service_route_mask_bit_clear(cur->thread_info->leader_private_data, routerId)) { return -1; } tr_debug("Release Routerid %x", routerId); thread_leader_service_update_id_set(cur); return 0; #else (void)interface_id; (void)routerId; return -1; #endif } int thread_test_router_downgrade(int8_t interface_id) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return -1; } if (!cur->thread_info) { return -1; } if (thread_attach_ready(cur) != 0) { return -1; } tr_debug("Trigger REED router downgrade process"); thread_bootstrap_attached_downgrade_router(cur); return 0; #else (void)interface_id; return -1; #endif } int thread_test_print_network_data(int8_t interface_id) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return -1; } if (!cur->thread_info) { return -1; } if (thread_attach_ready(cur) != 0) { return -1; } thread_nd_network_data_print(&cur->thread_info->networkDataStorage, mac_helper_mac16_address_get(cur)); return 0; #else (void)interface_id; return -1; #endif } int8_t thread_reed_set_advertisement_interval(int8_t interface_id, uint16_t advertisement_interval, uint16_t jitter_interval) { #ifdef HAVE_THREAD const protocol_interface_info_entry_t *cur; if (!advertisement_interval) { tr_warn("Invalid input ptr"); return -3; } cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { tr_warn("Invalid interface id"); return -1; } if (!cur->thread_info) { tr_warn("Thread not active"); return -2; } if (!jitter_interval) { jitter_interval = 0; } cur->thread_info->routerSelectParameters.reedAdvertisementInterval = (uint16_t) advertisement_interval; cur->thread_info->routerSelectParameters.reedAdvertisementJitterInterval = (uint16_t) jitter_interval; tr_debug("new advertisement interval set to %d seconds and jitter %d seconds", (uint16_t) advertisement_interval, (uint16_t) jitter_interval); return 0; #else (void)interface_id; (void)advertisement_interval; (void)jitter_interval; return -1; #endif } int thread_test_key_sequence_counter_update(int8_t interface_id, uint32_t thrKeySequenceCounter) { #ifdef HAVE_THREAD int ret_val = -1; protocol_interface_info_entry_t *cur; link_configuration_s *linkConfiguration; linkConfiguration = thread_joiner_application_get_config(interface_id); if (!linkConfiguration) { return -1; } cur = protocol_stack_interface_info_get_by_id(interface_id); if (cur && cur->thread_info) { /* Not sure if this check is needed - not sure exactly what the flag means! */ if (cur->lowpan_info & INTERFACE_NWK_ACTIVE) { if (cur->configure_flags & INTERFACE_BOOTSTRAP_DEFINED) { if (thrKeySequenceCounter > linkConfiguration->key_sequence) { ret_val = thread_management_key_sets_calc(cur, linkConfiguration, thrKeySequenceCounter); } } } } return ret_val; #else (void)interface_id; (void)thrKeySequenceCounter; return -1; #endif } int thread_test_stack_cache_reset(int8_t interface_id) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { tr_warn("Invalid interface id"); return -1; } // Reset link information (void) thread_nvm_store_link_info_clear(); return 0; #else (void)interface_id; return -1; #endif } int thread_test_key_rotation_update(int8_t interface_id, uint32_t thrKeyRotation) { #ifdef HAVE_THREAD int ret_val = -1; protocol_interface_info_entry_t *cur; link_configuration_s *linkConfiguration; linkConfiguration = thread_joiner_application_get_config(interface_id); if (!linkConfiguration) { return -1; } cur = protocol_stack_interface_info_get_by_id(interface_id); if (cur && cur->thread_info) { /* Not sure if this check is needed - not sure exactly what the flag means! */ if (cur->lowpan_info & INTERFACE_NWK_ACTIVE) { if (cur->configure_flags & INTERFACE_BOOTSTRAP_DEFINED) { linkConfiguration->key_rotation = thrKeyRotation; thread_key_guard_timer_calculate(cur, linkConfiguration, false); ret_val = 0; } } } return ret_val; #else (void)interface_id; (void)thrKeyRotation; return -1; #endif } int thread_test_router_select_threshold_values_set( int8_t interface_id, uint8_t upgradeThreshold, uint8_t downgradeThreshold) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; if (downgradeThreshold == 0) { tr_warn("Invalid Threshold value"); return -1; } if (upgradeThreshold >= downgradeThreshold) { tr_warn("Invalid Threshold value %u >= %u", upgradeThreshold, downgradeThreshold); return -1; } cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { tr_warn("Invalid interface id"); return -1; } if (!cur->thread_info) { tr_warn("Not Thread specific interface"); return -1; } cur->thread_info->routerSelectParameters.routerUpgradeThresHold = upgradeThreshold; cur->thread_info->routerSelectParameters.routerDowngradeThresHold = downgradeThreshold; return 0; #else (void)interface_id; (void)upgradeThreshold; (void)downgradeThreshold; return -1; #endif } int thread_test_max_accepted_router_id_limit_set( int8_t interface_id, uint8_t maxRouterLimit) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; if ((maxRouterLimit < 1) || (maxRouterLimit > 32)) { tr_error("Accept values are between 1-32 for max Router Id assign limit"); return -1; } cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { tr_warn("Invalid interface id"); return -1; } if (!cur->thread_info) { tr_warn("Not Thread specific interface"); return -1; } cur->thread_info->testMaxActiveRouterIdLimit = maxRouterLimit; tr_debug("New max Router Id assign limit is %u", maxRouterLimit); return 0; #else (void)interface_id; (void)maxRouterLimit; return -1; #endif } /** * Set Thread Security Master Key and Key Index * * \param interface_id Network Interface * \param enableSecurity Boolean for enable security or disable * \param threadMasterKey Thread Master Key material which will be used for generating new key * \param threadMasterKeyIndex Thread key material key index which will be increment periodically * \param keyRollPeriod Define Key index & key update process * * return 0, ADD OK * return <0 Add Not OK */ int thread_test_security_material_set(int8_t interface_id, bool enableSecurity, uint8_t *thrMasterKey, uint32_t thrKeySequenceCounter, uint32_t thrKeyRotation) { #ifdef HAVE_THREAD int ret_val = -1; uint8_t key_material[32]; uint8_t key_index; protocol_interface_info_entry_t *cur; link_configuration_s *linkConfiguration; linkConfiguration = thread_joiner_application_get_config(interface_id); if (!linkConfiguration) { return -1; } cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return -1; } if ((cur->lowpan_info & INTERFACE_NWK_ACTIVE) == 0) { return -4; } if (cur->thread_info) { if (cur->configure_flags & INTERFACE_BOOTSTRAP_DEFINED) { mac_helper_link_frame_counter_set(cur->id, 0); if (enableSecurity) { if (thrMasterKey) { ret_val = 0; linkConfiguration->key_rotation = thrKeyRotation; linkConfiguration->key_sequence = thrKeySequenceCounter; cur->if_lowpan_security_params->nwk_security_mode = NET_SEC_MODE_PSK_LINK_SECURITY; cur->mac_parameters->mac_configured_sec_level = 5; cur->thread_info->masterSecretMaterial.historyKeyValid = false; cur->thread_info->masterSecretMaterial.valid_Info = true; memcpy(linkConfiguration->master_key, thrMasterKey, 16); cur->thread_info->masterSecretMaterial.keyRotation = thrKeyRotation; //Define KEY's thread_key_get(thrMasterKey, key_material, thrKeySequenceCounter); key_index = THREAD_KEY_INDEX(thrKeySequenceCounter); //Set Keys mac_helper_security_default_key_set(cur, &key_material[16], key_index, MAC_KEY_ID_MODE_IDX); mle_service_security_set_security_key(cur->id, key_material, key_index, true); //Gen also Next Key thread_security_next_key_generate(cur, linkConfiguration->master_key, thrKeySequenceCounter); thread_key_guard_timer_calculate(cur, linkConfiguration, false); } } else { ret_val = 0; cur->if_lowpan_security_params->nwk_security_mode = NET_SEC_MODE_NO_LINK_SECURITY; cur->mac_parameters->mac_configured_sec_level = 0; cur->thread_info->masterSecretMaterial.valid_Info = false; } if (ret_val == 0) { cur->lowpan_info &= ~INTERFACE_NWK_BOOTSRAP_PANA_AUTHENTICATION; cur->configure_flags |= INTERFACE_SECURITY_DEFINED; } } } return ret_val; #else (void)interface_id; (void)enableSecurity; (void)thrMasterKey; (void)thrKeySequenceCounter; (void)thrKeyRotation; return -1; #endif } int thread_test_version_set(int8_t interface_id, uint8_t version) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; thread_version = version; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { /*We already stored the new Thread version above, so even if cur is NULL the version is updated.*/ return 0; } cur->thread_info->version = version; return 0; #else (void)interface_id; return -1; #endif } int thread_test_router_selection_jitter_set(int8_t interface_id, uint32_t jitter) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; if (jitter < 1) { return -1; } thread_router_selection_jitter = jitter; cur = protocol_stack_interface_info_get_by_id(interface_id); if (cur && cur->thread_info && cur->thread_info->reedJitterTimer > jitter) { cur->thread_info->reedJitterTimer = thread_router_bootstrap_random_upgrade_jitter(); } return 0; #else (void)interface_id; (void)jitter; return -1; #endif } int thread_test_min_delay_timer_set(int8_t interface_id, uint32_t delay_timer_value) { #ifdef HAVE_THREAD (void)interface_id; if (delay_timer_value < 1) { return -1; } thread_delay_timer_default = delay_timer_value; return 0; #else (void)interface_id; (void)delay_timer_value; return -1; #endif } int thread_test_increment_key_sequence_counter(int8_t interface_id) { #ifdef HAVE_THREAD return thread_management_increment_key_sequence_counter(interface_id); #else (void)interface_id; return -1; #endif } #ifdef HAVE_THREAD static int thread_test_panid_conflict_cb(int8_t service_id, uint8_t source_address[16], uint16_t source_port, sn_coap_hdr_s *request_ptr) { (void)source_address; (void)source_port; uint16_t panid; uint8_t *ptr; uint16_t len MAYBE_UNUSED; tr_debug("Thread test panid conflict request"); if (2 <= thread_meshcop_tlv_data_get_uint16(request_ptr->payload_ptr, request_ptr->payload_len, MESHCOP_TLV_PANID, &panid)) { tr_info("PANID TLV %02x", panid); } if ((len = thread_meshcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, MESHCOP_TLV_CHANNEL_MASK, &ptr)) > 0) { tr_info("Channel mask TLV %s", trace_array(ptr, len)); } coap_service_response_send(service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, COAP_MSG_CODE_RESPONSE_CHANGED, COAP_CT_OCTET_STREAM, NULL, 0); return 0; } #endif #ifdef HAVE_THREAD static int thread_test_energy_scan_result_cb(int8_t service_id, uint8_t source_address[16], uint16_t source_port, sn_coap_hdr_s *request_ptr) { (void)source_address; (void)source_port; uint8_t *ptr; uint16_t len MAYBE_UNUSED; tr_debug("Thread test energy scan result"); if ((len = thread_meshcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, MESHCOP_TLV_CHANNEL_MASK, &ptr)) > 0) { tr_info("Channel mask TLV %s", trace_array(ptr, len)); } if ((len = thread_meshcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, MESHCOP_TLV_ENERGY_LIST, &ptr)) > 0) { tr_info("Energy list TLV %s", trace_array(ptr, len)); } coap_service_response_send(service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, COAP_MSG_CODE_RESPONSE_CHANGED, COAP_CT_OCTET_STREAM, NULL, 0); return 0; } #ifdef HAVE_THREAD static uint8_t coap_service = 0; static response_cb *generic_response_cb_ptr = NULL; static coap_response_cb *coap_response_cb_ptr = NULL; static int thread_test_response_cb(int8_t service_id, uint8_t source_address[static 16], uint16_t source_port, sn_coap_hdr_s *response_ptr) { (void)source_address; (void)source_port; tr_debug("Thread test response received"); if (!response_ptr) { return -1; } /* Call callback function when receiving response, but do not call when receiving message code "empty" (Just ACK - not piggybacked response) */ if (generic_response_cb_ptr && (response_ptr->msg_code != COAP_MSG_CODE_EMPTY)) { generic_response_cb_ptr(service_id, response_ptr->payload_ptr, response_ptr->payload_len); } if (response_ptr->uri_path_ptr && (!memcmp(response_ptr->uri_path_ptr, THREAD_URI_DIAGNOSTIC_ANSWER, strlen(THREAD_URI_DIAGNOSTIC_ANSWER)))) { coap_service_response_send(service_id, COAP_REQUEST_OPTIONS_NONE, response_ptr, COAP_MSG_CODE_RESPONSE_CHANGED, COAP_CT_OCTET_STREAM, NULL, 0); } return 0; } static int thread_coap_response_cb(int8_t service_id, uint8_t source_address[static 16], uint16_t source_port, sn_coap_hdr_s *response_ptr) { #ifndef HAVE_DEBUG (void) source_address; (void) source_port; #endif tr_debug("Thread coap response received %s :%d", trace_ipv6(source_address), source_port); if (!response_ptr) { if (coap_response_cb_ptr) { coap_response_cb_ptr(service_id, 0xff, 0xff, NULL, 0); } return 0; } /* Call callback function when receiving response, but do not call when receiving message code "empty" (Just ACK - not piggybacked response) */ if (coap_response_cb_ptr && (response_ptr->msg_code != COAP_MSG_CODE_EMPTY)) { coap_response_cb_ptr(service_id, (uint8_t)(response_ptr->msg_code), (uint8_t)(response_ptr->msg_type), response_ptr->payload_ptr, response_ptr->payload_len); } return 0; } static int thread_coap_multicast_response_cb(int8_t service_id, uint8_t source_address[static 16], uint16_t source_port, sn_coap_hdr_s *response_ptr) { #ifndef HAVE_DEBUG (void) source_address; (void) source_port; #endif tr_debug("Thread coap multicast response received %s :%d", trace_ipv6(source_address), source_port); /* Call callback function when receiving response, but do not call when receiving message code "empty" (Just ACK - not piggybacked response) */ if (coap_response_cb_ptr) { coap_response_cb_ptr(service_id, (uint8_t)(response_ptr->msg_code), (uint8_t)(response_ptr->msg_type), response_ptr->payload_ptr, response_ptr->payload_len); } coap_service_response_send(service_id, COAP_REQUEST_OPTIONS_NONE, response_ptr, COAP_MSG_CODE_RESPONSE_CHANGED, COAP_CT_OCTET_STREAM, NULL, 0); return 0; } #endif static int thread_test_service_init(int8_t interface_id) { if (coap_service == 0) { coap_service = coap_service_initialize(interface_id, THREAD_MANAGEMENT_PORT, COAP_SERVICE_OPTIONS_NONE, NULL, NULL); coap_service_register_uri(coap_service, THREAD_URI_PANID_CONFLICT, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_test_panid_conflict_cb); coap_service_register_uri(coap_service, THREAD_URI_ED_REPORT, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_test_energy_scan_result_cb); coap_service_register_uri(coap_service, THREAD_URI_DIAGNOSTIC_ANSWER, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_coap_multicast_response_cb); } return 0; } static uint8_t *channel_mask_write(uint8_t *ptr, uint8_t channel_page, uint8_t *mask_ptr) { uint8_t mask_len = 0; uint8_t mask_tlv[6];// if (channel_page == 0) { mask_len = 6; mask_tlv[0] = channel_page; mask_tlv[1] = 4; memcpy(&mask_tlv[2], mask_ptr, 4); } ptr = thread_meshcop_tlv_data_write(ptr, MESHCOP_TLV_CHANNEL_MASK, mask_len, mask_tlv); return ptr; } #endif int thread_test_panid_query_send(int8_t interface_id, uint8_t *address_ptr, uint16_t session_id, uint16_t panid, uint8_t channel_page, uint8_t *mask_ptr) { #ifdef HAVE_THREAD sn_coap_msg_type_e msg_type; uint8_t payload[16];// 2+6 + 2+2 + 2+2 uint8_t *ptr; thread_test_service_init(interface_id); if (address_ptr[0] == 0xff) { msg_type = COAP_MSG_TYPE_NON_CONFIRMABLE; } else { msg_type = COAP_MSG_TYPE_CONFIRMABLE; } ptr = payload; ptr = channel_mask_write(ptr, channel_page, mask_ptr); ptr = thread_meshcop_tlv_data_write_uint16(ptr, MESHCOP_TLV_PANID, panid); ptr = thread_meshcop_tlv_data_write_uint16(ptr, MESHCOP_TLV_COMMISSIONER_SESSION_ID, session_id); coap_service_request_send(coap_service, COAP_REQUEST_OPTIONS_NONE, address_ptr, THREAD_MANAGEMENT_PORT, msg_type, COAP_MSG_CODE_REQUEST_POST, THREAD_URI_PANID_QUERY, COAP_CT_OCTET_STREAM, payload, ptr - payload, thread_test_response_cb); return 0; #else (void)interface_id; (void)address_ptr; (void)session_id; (void)panid; (void)channel_page; (void)mask_ptr; return -1; #endif } int thread_test_energy_scan_send(int8_t interface_id, uint8_t *address_ptr, uint16_t session_id, uint8_t channel_page, uint8_t *mask_ptr, uint16_t period, uint8_t count, uint16_t duration) { #ifdef HAVE_THREAD sn_coap_msg_type_e msg_type; uint8_t payload[2 + 2 + 2 + 1 + 2 + 1 + 2 + 6 + 2 + 2]; uint8_t *ptr; thread_test_service_init(interface_id); if (address_ptr[0] == 0xff) { msg_type = COAP_MSG_TYPE_NON_CONFIRMABLE; } else { msg_type = COAP_MSG_TYPE_CONFIRMABLE; } ptr = payload; ptr = thread_meshcop_tlv_data_write_uint16(ptr, MESHCOP_TLV_PERIOD, period); ptr = thread_meshcop_tlv_data_write_uint8(ptr, MESHCOP_TLV_COUNT, count); ptr = thread_meshcop_tlv_data_write_uint16(ptr, MESHCOP_TLV_DURATION, duration); ptr = channel_mask_write(ptr, channel_page, mask_ptr); ptr = thread_meshcop_tlv_data_write_uint16(ptr, MESHCOP_TLV_COMMISSIONER_SESSION_ID, session_id); coap_service_request_send(coap_service, COAP_REQUEST_OPTIONS_NONE, address_ptr, THREAD_MANAGEMENT_PORT, msg_type, COAP_MSG_CODE_REQUEST_POST, THREAD_URI_ED_SCAN, COAP_CT_OCTET_STREAM, payload, ptr - payload, thread_test_response_cb); return 0; #else (void)interface_id; (void)address_ptr; (void)session_id; (void)channel_page; (void)mask_ptr; (void)period; (void)count; (void)duration; return -1; #endif } int thread_test_diagnostic_command_send(int8_t interface_id, uint8_t *address_ptr, const char *uri_ptr, uint8_t request_length, uint8_t *request_ptr, response_cb *resp_cb) { #ifdef HAVE_THREAD uint8_t payload[17 + 2] = {0}; uint8_t *ptr = NULL; generic_response_cb_ptr = resp_cb; sn_coap_msg_type_e msg_type; sn_coap_msg_code_e msg_code = COAP_MSG_CODE_REQUEST_GET; thread_test_service_init(interface_id); if (request_length > 17 || !address_ptr) { return -1; } if (address_ptr[0] == 0xff) { msg_type = COAP_MSG_TYPE_NON_CONFIRMABLE; } else { msg_type = COAP_MSG_TYPE_CONFIRMABLE; } if (!strcmp(uri_ptr, "d/dr")) { msg_code = COAP_MSG_CODE_REQUEST_POST; } ptr = payload; if (request_length > 0 && request_ptr) { ptr = thread_diagcop_tlv_data_write(ptr, DIAGCOP_TLV_TYPE_LIST, request_length, request_ptr); } coap_service_request_send(coap_service, COAP_REQUEST_OPTIONS_NONE, address_ptr, THREAD_MANAGEMENT_PORT, msg_type, msg_code, uri_ptr, COAP_CT_OCTET_STREAM, payload, ptr - payload, thread_test_response_cb); return 0; #else (void)interface_id; (void)address_ptr; (void)uri_ptr; (void)request_length; (void)request_ptr; (void)resp_cb; return -1; #endif } int thread_test_coap_request_send(int8_t interface_id, uint8_t *address_ptr, uint16_t port, uint8_t msg_type, uint8_t msg_code, uint16_t content_format, const char *uri_ptr, uint8_t *request_ptr, uint8_t request_length, coap_response_cb *resp_cb) { #ifdef HAVE_THREAD coap_response_cb_ptr = resp_cb; thread_test_service_init(interface_id); coap_service_request_send(coap_service, COAP_REQUEST_OPTIONS_NONE, address_ptr, port, (sn_coap_msg_type_e)msg_type, (sn_coap_msg_code_e)msg_code, uri_ptr, (sn_coap_content_format_e) content_format, request_ptr, request_length, thread_coap_response_cb); return 0; #else (void)interface_id; (void)address_ptr; (void)port; (void)msg_type; (void)msg_code; (void)content_format; (void)uri_ptr; (void)request_length; (void)request_ptr; (void)resp_cb; return -1; #endif } int thread_test_announce_ntf_send(int8_t interface_id, uint8_t *address_ptr, uint32_t channel, uint16_t panid, uint64_t timestamp) { #ifdef HAVE_THREAD (void)address_ptr; protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur || !cur->thread_info) { return -1; } return thread_bootstrap_announce_send(cur, (uint8_t)(channel >> 16), (uint16_t) channel, panid, timestamp, (uint16_t) channel); #else (void)interface_id; (void)address_ptr; (void)channel; (void)panid; (void)timestamp; return -1; #endif } int thread_test_announce_begin_send(int8_t interface_id, uint8_t *address_ptr, uint16_t session_id, uint8_t channel_page, uint8_t *mask_ptr, uint16_t period, uint8_t count) { #ifdef HAVE_THREAD uint8_t payload[ 2 + 2 + 2 + 2 + 2 + 1 + 2 + 6]; uint8_t *ptr; sn_coap_msg_type_e msg_type; thread_test_service_init(interface_id); if (address_ptr[0] == 0xff) { msg_type = COAP_MSG_TYPE_NON_CONFIRMABLE; } else { msg_type = COAP_MSG_TYPE_CONFIRMABLE; } ptr = payload; ptr = thread_meshcop_tlv_data_write_uint16(ptr, MESHCOP_TLV_COMMISSIONER_SESSION_ID, session_id); ptr = thread_meshcop_tlv_data_write_uint16(ptr, MESHCOP_TLV_PERIOD, period); ptr = thread_meshcop_tlv_data_write_uint8(ptr, MESHCOP_TLV_COUNT, count); ptr = channel_mask_write(ptr, channel_page, mask_ptr); coap_service_request_send(coap_service, COAP_REQUEST_OPTIONS_NONE, address_ptr, THREAD_MANAGEMENT_PORT, msg_type, COAP_MSG_CODE_REQUEST_POST, THREAD_URI_MANAGEMENT_ANNOUNCE_BEGIN, COAP_CT_OCTET_STREAM, payload, ptr - payload, thread_test_response_cb); return 0; #else (void)interface_id; (void)address_ptr; (void)session_id; (void)channel_page; (void)mask_ptr; (void)period; (void)count; return -1; #endif } int thread_test_partition_info_get(int8_t interface_id, uint32_t *partition_id, uint8_t *weighting, uint8_t *data_version, uint8_t *stable_data_version, uint8_t *leader_id) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur || !cur->thread_info || !cur->thread_info->thread_leader_data) { return -1; } if (thread_attach_ready(cur) != 0) { return -1; } if (partition_id) { *partition_id = cur->thread_info->thread_leader_data->partitionId; } if (weighting) { *weighting = cur->thread_info->thread_leader_data->weighting; } if (data_version) { *data_version = cur->thread_info->thread_leader_data->dataVersion; } if (stable_data_version) { *stable_data_version = cur->thread_info->thread_leader_data->stableDataVersion; } if (leader_id) { *leader_id = cur->thread_info->thread_leader_data->leaderRouterId; } return 0; #else (void)interface_id; (void)partition_id; (void)weighting; (void)data_version; (void)stable_data_version; (void)leader_id; return -1; #endif } int thread_test_partition_info_set(int8_t interface_id, uint32_t partition_id) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur || !cur->thread_info) { return -1; } cur->thread_info->testRandomPartitionId = partition_id; return 0; #else (void)interface_id; (void)partition_id; return -1; #endif } int8_t thread_test_thread_information_get(int8_t interface_id, uint16_t *short_addr, uint8_t *router_count, bool *network_stable) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur || !cur->thread_info) { return -1; } if (short_addr) { *short_addr = mac_helper_mac16_address_get(cur); } if (router_count) { *router_count = thread_routing_count_active_routers(&cur->thread_info->routing); } if (network_stable) { if (cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_SLEEPY_END_DEVICE || cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_END_DEVICE || cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_FULL_END_DEVICE) { *network_stable = true; } else { *network_stable = !(thread_router_bootstrap_reed_upgrade(cur) || thread_router_bootstrap_router_downgrade(cur)); } } return 0; #else (void)interface_id; (void)short_addr; (void)router_count; (void)network_stable; return -1; #endif } int8_t thread_test_child_count_get(int8_t interface_id) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur || !cur->thread_info || !(cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_ROUTER)) { return -1; } return thread_router_bootstrap_child_count_get(cur); #else (void)interface_id; return -1; #endif } int8_t thread_test_child_info_get(int8_t interface_id, uint8_t index, uint16_t *short_addr, bool *sleepy, uint8_t *mac64, uint8_t *margin) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; uint8_t n = 0; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur || !cur->mac_parameters || !cur->thread_info || cur->thread_info->thread_device_mode != THREAD_DEVICE_MODE_ROUTER) { return -1; } uint16_t mac16 = mac_helper_mac16_address_get(cur); mac_neighbor_table_list_t *mac_table_list = &mac_neighbor_info(cur)->neighbour_list; if (!mac_table_list) { return -1; } ns_list_foreach(mac_neighbor_table_entry_t, cur_entry, mac_table_list) { if (thread_router_addr_from_addr(cur_entry->mac16) == thread_router_addr_from_addr(mac16)) { if (n == index) { *short_addr = cur_entry->mac16; memcpy(mac64, cur_entry->mac64, 8); *sleepy = cur_entry->rx_on_idle != true; *margin = thread_neighbor_entry_linkmargin_get(&cur->thread_info->neighbor_class, cur_entry->index); return 0; } n++; } } return -1; #else (void)interface_id; (void)index; (void)short_addr; (void)sleepy; (void)mac64; (void)margin; return -1; #endif } int8_t thread_test_neighbour_info_get(int8_t interface_id, uint8_t index, uint16_t *short_addr, uint8_t *mac64, uint8_t *margin) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; uint8_t n = 0; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur || !cur->thread_info) { return -1; } uint16_t mac16 = mac_helper_mac16_address_get(cur); mac_neighbor_table_list_t *mac_table_list = &mac_neighbor_info(cur)->neighbour_list; if (!mac_table_list) { return -1; } ns_list_foreach(mac_neighbor_table_entry_t, cur_entry, mac_table_list) { if (thread_router_addr_from_addr(cur_entry->mac16) != thread_router_addr_from_addr(mac16)) { if (n == index) { *short_addr = cur_entry->mac16; memcpy(mac64, cur_entry->mac64, 8); *margin = thread_neighbor_entry_linkmargin_get(&cur->thread_info->neighbor_class, cur_entry->index); return 0; } n++; } } return -1; #else (void)interface_id; (void)index; (void)short_addr; (void)mac64; (void)margin; return -1; #endif } int8_t thread_test_initial_slaac_iid_set(int8_t interface_id, uint8_t *iid) { #ifdef HAVE_THREAD (void)interface_id;// I somehow feel this might be needed later return addr_opaque_initial_iid_set(iid); #else (void)interface_id; (void)iid; return -1; #endif } int8_t thread_test_router_id_request_send(int8_t interface_id, uint8_t status) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur || !cur->thread_info) { return -1; } if (thread_attach_ready(cur) != 0) { return -1; } tr_debug("Trigger REED router upgrade process with status %d", status); thread_router_bootstrap_router_id_request(cur, status); return 0; #else (void)interface_id; (void)status; return -1; #endif } int8_t thread_test_router_address_set(int8_t interface_id, uint16_t router_address) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur || !cur->thread_info) { return -1; } if ((router_address & THREAD_ROUTER_MASK) == THREAD_ROUTER_MASK) { tr_warn("address requested is reserved for anycast locator"); return -2; } if (cur->thread_info->routerShortAddress != 0xfffe) { tr_debug("router short address already present"); return -3; } cur->thread_info->routerShortAddress = router_address; return 0; #else (void)interface_id; (void)router_address; return -1; #endif } int8_t thread_test_joiner_router_joiner_port_set(uint16_t port) { #ifdef HAVE_THREAD /* If parameter is '0', then use default port */ if (0 == port) { port = THREAD_DEFAULT_JOINER_PORT; } thread_joiner_port = port; return 0; #else (void)port; return -1; #endif } int thread_test_mle_message_send(int8_t interface_id, uint8_t *dst_address, uint8_t msg_id, bool write_src_addr, bool write_leader_data, bool write_network_data, bool write_timestamp, bool write_operational_set, bool write_challenge, uint8_t *msg_ptr, uint8_t msg_len) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; uint16_t len = 32; uint32_t keySequence; uint8_t *ptr; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur || !cur->thread_info) { return -1; } len += msg_len; if (write_operational_set) { len += thread_active_operational_dataset_size(cur); len += thread_pending_operational_dataset_size(cur); } if (write_network_data) { len += thread_network_data_tlv_size(cur, true); } uint16_t bufId = mle_service_msg_allocate(cur->id, len, write_challenge, msg_id); if (bufId == 0) { return -1; } tr_debug("thread test send MLE message"); thread_management_get_current_keysequence(cur->id, &keySequence); mle_service_msg_update_security_params(bufId, 5, 2, keySequence); ptr = mle_service_get_data_pointer(bufId); //Set SRC if (write_src_addr) { ptr = mle_general_write_source_address(ptr, cur); } //SET leader data if (write_leader_data) { ptr = thread_leader_data_tlv_write(ptr, cur); } if (write_timestamp) { ptr = thread_active_timestamp_write(cur, ptr); ptr = thread_pending_operational_dataset_write(cur, ptr); } if (write_operational_set) { ptr = thread_active_operational_dataset_write(cur, ptr); ptr = thread_pending_operational_dataset_write(cur, ptr); } if (write_network_data) { ptr = thread_network_data_tlv_write(cur, ptr, true); } // Add any user decided TLVs if (msg_ptr && msg_len) { memcpy(ptr, msg_ptr, msg_len); ptr += msg_len; } if (mle_service_update_length_by_ptr(bufId, ptr) != 0) { tr_debug("Buffer overflow at message write"); } mle_service_set_msg_destination_address(bufId, dst_address); mle_service_send_message(bufId); return 0; #else (void)interface_id; (void)dst_address; (void)msg_id; (void)write_src_addr; (void)write_leader_data; (void)write_operational_set; (void)write_challenge; (void)msg_ptr; (void)msg_len; return -1; #endif } int thread_test_extension_name_set(int8_t interface_id, char extension_name[16]) { (void) interface_id; (void) extension_name; #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur || !cur->thread_info) { return -1; } return thread_extension_bootstrap_thread_name_set(cur, extension_name); #else return -1; #endif }