/* * Copyright (c) 2015-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. */ /* * \file thread_host_bootstrap.c * \brief Add short description about this file!!! * */ #include "nsconfig.h" #ifdef HAVE_THREAD #include #include #include #include "eventOS_event.h" #include "eventOS_event_timer.h" #include "shalib.h" #include "common_functions.h" #include "NWK_INTERFACE/Include/protocol.h" #include "net_thread_test.h" #include "ns_trace.h" #include "6LoWPAN/Bootstraps/protocol_6lowpan.h" #include "6LoWPAN/Thread/thread_common.h" #include "6LoWPAN/Thread/thread_routing.h" #include "6LoWPAN/Thread/thread_nd.h" #include "6LoWPAN/Thread/thread_network_synch.h" #include "6LoWPAN/Thread/thread_bootstrap.h" #include "6LoWPAN/Thread/thread_host_bootstrap.h" #include "6LoWPAN/Thread/thread_router_bootstrap.h" #include "6LoWPAN/Thread/thread_leader_service.h" #include "6LoWPAN/Thread/thread_management_internal.h" #include "6LoWPAN/Thread/thread_network_synch.h" #include "6LoWPAN/Thread/thread_lowpower_private_api.h" #include "6LoWPAN/Thread/thread_extension.h" #include "6LoWPAN/Thread/thread_joiner_application.h" #include "6LoWPAN/Thread/thread_management_internal.h" #include "6LoWPAN/Thread/thread_management_client.h" #include "6LoWPAN/Thread/thread_network_data_lib.h" #include "6LoWPAN/Thread/thread_tmfcop_lib.h" #include "6LoWPAN/Thread/thread_neighbor_class.h" #include "thread_management_if.h" #include "Common_Protocols/ipv6.h" #include "MPL/mpl.h" #include "MLE/mle_tlv.h" #include "thread_config.h" #include "Service_Libs/mle_service/mle_service_api.h" #include "Service_Libs/blacklist/blacklist.h" #include "6LoWPAN/MAC/mac_helper.h" #include "6LoWPAN/MAC/mac_data_poll.h" #include "Core/include/address.h" #include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h" #define TRACE_GROUP "tebs" static bool thread_child_update_timeout_cb(int8_t interface_id, uint16_t msgId, bool usedAllRerties); static bool thread_parent_discover_timeout_cb(int8_t interface_id, uint16_t msgId, bool usedAllRetries); static bool thread_child_id_req_timeout(int8_t interface_id, uint16_t msgId, bool usedAllRetries); static void thread_child_synch_receive_cb(int8_t interface_id, mle_message_t *mle_msg, mle_security_header_t *security_headers); static void thread_mle_child_request_receive_cb(int8_t interface_id, mle_message_t *mle_msg, mle_security_header_t *security_headers); static int thread_parent_request_build(protocol_interface_info_entry_t *cur); static int thread_attach_child_id_request_build(protocol_interface_info_entry_t *cur); static int thread_end_device_synch_response_validate(protocol_interface_info_entry_t *cur, uint8_t *ptr, uint16_t data_length, uint8_t linkMargin, uint8_t *src_address, mle_security_header_t *securityHeader); static int8_t thread_end_device_synch_start(protocol_interface_info_entry_t *cur); void thread_child_set_default_route(protocol_interface_info_entry_t *cur) { thread_parent_info_t *parent = cur->thread_info->thread_endnode_parent; if (!parent) { tr_debug("child default route set fail"); return; } cur->lowpan_info &= ~INTERFACE_NWK_ROUTER_DEVICE; uint8_t ula[16]; // SET Default route to ::/0 memcpy(ula, ADDR_LINK_LOCAL_PREFIX, 8); memcpy(&ula[8], parent->mac64, 8); ula[8] ^= 2; if (ipv6_route_add(NULL, 0, cur->id, ula, ROUTE_THREAD, 0xffffffff, 0) == NULL) { tr_error("fail to add default route"); } } static void thread_network_data_clean(protocol_interface_info_entry_t *cur) { tr_debug("Clean network data"); thread_network_data_router_id_mark_delete(&cur->thread_info->networkDataStorage, 0xffff, false); thread_network_data_router_id_free(&cur->thread_info->networkDataStorage, false, cur); } static void thread_merge_prepare(protocol_interface_info_entry_t *cur) { thread_partition_data_purge(cur); thread_clean_old_16_bit_address_based_addresses(cur); mpl_clear_realm_scope_seeds(cur); ipv6_route_table_remove_info(cur->id, ROUTE_THREAD_PROXIED_HOST, NULL); ipv6_route_table_remove_info(cur->id, ROUTE_THREAD_PROXIED_DUA_HOST, NULL); thread_network_data_clean(cur); cur->nwk_mode = ARM_NWK_GP_IP_MODE; } //This function is for Thread Parent scan callback static bool thread_parent_discover_timeout_cb(int8_t interface_id, uint16_t msgId, bool usedAllRetries) { protocol_interface_info_entry_t *cur; bool new_entry_created; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return false; } cur->nwk_nd_re_scan_count++; tr_debug("Parse parent scan result"); if (cur->thread_info->thread_attach_scanned_parent) { uint8_t ll64[16]; thread_scanned_parent_t *parent = cur->thread_info->thread_attach_scanned_parent; link_configuration_s *linkConfiguration; mac_neighbor_table_entry_t *entry_temp; linkConfiguration = thread_joiner_application_get_config(interface_id); if (!linkConfiguration) { return false; } /* SED requires that scanned parent is added to MLE table */ memcpy(ll64, ADDR_LINK_LOCAL_PREFIX, 8); memcpy(&ll64[8], parent->mac64, 8); ll64[8] ^= 2; entry_temp = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur), ll64, true, &new_entry_created); if (entry_temp == NULL) { return false; } thread_neighbor_class_update_link(&cur->thread_info->neighbor_class, entry_temp->index, parent->linkMarginToParent, new_entry_created); thread_neighbor_last_communication_time_update(&cur->thread_info->neighbor_class, entry_temp->index); entry_temp->mac16 = parent->shortAddress; entry_temp->link_role = PRIORITY_PARENT_NEIGHBOUR; mle_service_frame_counter_entry_add(interface_id, entry_temp->index, parent->mleFrameCounter); thread_management_key_sets_calc(cur, linkConfiguration, cur->thread_info->thread_attach_scanned_parent->keySequence); thread_key_guard_timer_calculate(cur, linkConfiguration, true); mlme_device_descriptor_t device_desc; mac_helper_device_description_write(cur, &device_desc, entry_temp->mac64, entry_temp->mac16, parent->linLayerFrameCounter, false); mac_helper_devicetable_set(&device_desc, cur, entry_temp->index, mac_helper_default_key_index_get(cur), new_entry_created); mac_neighbor_table_neighbor_refresh(mac_neighbor_info(cur), entry_temp, THREAD_DEFAULT_LINK_LIFETIME); if (cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_SLEEPY_END_DEVICE) { nwk_thread_host_control(cur, NET_HOST_FAST_POLL_MODE, 50); } tr_debug("Parent found, send child ID req"); thread_attach_child_id_request_build(cur); return false; } else { uint8_t *dataPtr; uint16_t dataLength; if (usedAllRetries) { if (thread_is_connected(cur)) { // Did not find any better partition to join. Be happy. cur->nwk_bootstrap_state = ER_MLE_ATTACH_READY; return true; } // Trig new state which call this thread_bootstrap_connection_error(cur->id, CON_ERROR_NETWORK_ATTACH_FAIL, NULL); return false; } //GET Data pointer dataPtr = mle_service_get_payload_start_point(msgId); dataLength = mle_service_get_payload_length(msgId); if (dataPtr) { mle_tlv_info_t tlv_info; //Scan MLE_TYPE_SCAN_MASK if (mle_tlv_option_discover(dataPtr, dataLength, MLE_TYPE_SCAN_MASK, &tlv_info)) { dataPtr = tlv_info.dataPtr; *dataPtr |= 0x40; //ADD REED Bit } } } return true; } static int thread_parent_request_build(protocol_interface_info_entry_t *cur) { uint8_t *ptr; uint8_t mode; mle_message_timeout_params_t timeout; uint32_t keySequence; uint8_t scanMask = 0x80; mle_service_interface_tx_queue_clean(cur->id); uint16_t buf_id = mle_service_msg_allocate(cur->id, 32, true, MLE_COMMAND_PARENT_REQUEST); if (buf_id == 0) { return -1; } timeout.retrans_max = THREAD_PARENT_REQUEST_MAX_RETRY_CNT; timeout.timeout_init = THREAD_PARENT_REQ_SCANMASK_R_TIMEOUT; timeout.timeout_max = THREAD_PARENT_REQ_SCANMASK_RE_TIMEOUT; timeout.delay = MLE_STANDARD_RESPONSE_DELAY; if (cur->thread_info->thread_attached_state == THREAD_STATE_REATTACH || cur->thread_info->thread_attached_state == THREAD_STATE_REATTACH_RETRY || cur->thread_info->thread_attached_state == THREAD_STATE_CONNECTED || cur->thread_info->thread_attached_state == THREAD_STATE_CONNECTED_ROUTER) { // When doing re-attach End devices are immediately accepted as parents scanMask |= 0x40; timeout.timeout_init = THREAD_PARENT_REQ_SCANMASK_RE_TIMEOUT; } thread_management_get_current_keysequence(cur->id, &keySequence); mle_service_msg_update_security_params(buf_id, 5, 2, keySequence); //SET Multicast to all Router ff02::2 mle_service_set_msg_destination_address(buf_id, ADDR_LINK_LOCAL_ALL_ROUTERS); tr_debug("Thread parent request"); ptr = mle_service_get_data_pointer(buf_id); ptr = mle_tlv_write_scan_mask(ptr, scanMask); ptr = mle_tlv_write_version(ptr, cur->thread_info->version); mode = thread_mode_get_by_interface_ptr(cur); /* If we are a SLEEPY host, we do NOT set RX_ON_IDLE bit in parent requests */ /* NOTE: the RX_ON_IDLE is temporarily set on the interface during bootstrap */ if (cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_SLEEPY_END_DEVICE) { mode &= ~MLE_RX_ON_IDLE; } ptr = mle_tlv_write_mode(ptr, mode); if (mle_service_update_length_by_ptr(buf_id, ptr) != 0) { tr_debug("Buffer overflow at message write"); } cur->nwk_nd_re_scan_count = 1; mle_service_set_packet_callback(buf_id, thread_parent_discover_timeout_cb); if (cur->thread_info->thread_attached_state == THREAD_STATE_NETWORK_DISCOVER) { mle_service_interface_receiver_handler_update(cur->id, thread_mle_parent_discover_receive_cb); } else { mle_service_interface_receiver_handler_update(cur->id, thread_general_mle_receive_cb); } mle_service_set_msg_timeout_parameters_fast(buf_id, &timeout); mle_service_send_message(buf_id); return 0; } void thread_network_attach_start(protocol_interface_info_entry_t *cur) { if (thread_parent_request_build(cur) == 0) { tr_debug("MLE Parent request"); cur->nwk_bootstrap_state = ER_MLE_SCAN; cur->bootsrap_state_machine_cnt = 0; /* advance trickle timer by 6 (in 100ms ticks) seconds if needed */ thread_routing_trickle_advance(&cur->thread_info->routing, 6 * 10); } else { cur->bootsrap_state_machine_cnt = 5; } } static int thread_end_device_synch_response_validate(protocol_interface_info_entry_t *cur, uint8_t *ptr, uint16_t data_length, uint8_t linkMargin, uint8_t *src_address, mle_security_header_t *securityHeader) { uint8_t shortAddress[2]; uint8_t status, mode; uint16_t srcAddress; uint16_t address16; uint32_t llFrameCounter; thread_leader_data_t leaderData; mle_tlv_info_t addressRegisteredTlv; mac_neighbor_table_entry_t *entry_temp; bool new_entry_created; tr_debug("Validate Link Synch Response"); //Check First Status if (mle_tlv_read_8_bit_tlv(MLE_TYPE_STATUS, ptr, data_length, &status)) { tr_debug("Synch status response %x", status); //Clean synch state and start return -1; } // Validate response // MLE_TYPE_MODE // Address // MLE_TYPE_SRC_ADDRESS // MLE_TYPE_LEADER_DATA // MLE_TYPE_ADDRESS_REGISTRATION if (!mle_tlv_read_8_bit_tlv(MLE_TYPE_MODE, ptr, data_length, &mode) || !mle_tlv_read_16_bit_tlv(MLE_TYPE_SRC_ADDRESS, ptr, data_length, &srcAddress) || !mle_tlv_read_16_bit_tlv(MLE_TYPE_ADDRESS16, ptr, data_length, &address16) || !thread_leader_data_parse(ptr, data_length, &leaderData) || !mle_tlv_read_32_bit_tlv(MLE_TYPE_LL_FRAME_COUNTER, ptr, data_length, &llFrameCounter)) { tr_debug("missing TLV's"); return -1; } if (!(mode & THREAD_DEVICE_FED)) { // check for presence of Address registration TLV for MTDs if (!mle_tlv_read_tlv(MLE_TYPE_ADDRESS_REGISTRATION, ptr, data_length, &addressRegisteredTlv) || (addressRegisteredTlv.tlvLen == 0)) { tr_debug("MTD missed address registration TLV - reattach"); return -1; } } // check if the source address is a router address if (!thread_is_router_addr(srcAddress)) { return -1; } // check if the address16 is a valid child address if (!thread_addr_is_child(srcAddress, address16)) { return -1; } if (securityHeader->KeyIdMode == MAC_KEY_ID_MODE_SRC4_IDX) { thread_management_key_synch_req(cur->id, common_read_32_bit(securityHeader->Keysource)); // if learning key sequence from link sync actual guard timer value is not known thread_key_guard_timer_reset(cur); } else { tr_debug("Key ID Mode 2 not used; dropped."); return -3; } //Update parent link information entry_temp = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur), src_address, true, &new_entry_created); if (!entry_temp) { tr_debug("Neighbor allocate fail"); return -2; } thread_neighbor_class_update_link(&cur->thread_info->neighbor_class, entry_temp->index, linkMargin, new_entry_created); thread_neighbor_last_communication_time_update(&cur->thread_info->neighbor_class, entry_temp->index); /* */ entry_temp->mac16 = srcAddress; entry_temp->connected_device = 1; entry_temp->link_role = PRIORITY_PARENT_NEIGHBOUR; // Make this our parent common_write_16_bit(entry_temp->mac16, shortAddress); mac_helper_coordinator_address_set(cur, ADDR_802_15_4_SHORT, shortAddress); mac_neighbor_table_neighbor_refresh(mac_neighbor_info(cur), entry_temp, thread_info(cur)->host_link_timeout); mlme_device_descriptor_t device_desc; mac_helper_device_description_write(cur, &device_desc, entry_temp->mac64, entry_temp->mac16, llFrameCounter, false); mac_helper_devicetable_set(&device_desc, cur, entry_temp->index, securityHeader->KeyIndex, new_entry_created); thread_info(cur)->thread_attached_state = THREAD_STATE_CONNECTED; thread_bootstrap_update_ml16_address(cur, address16); //SET Parent Info and free scan response info thread_info(cur)->thread_endnode_parent->shortAddress = srcAddress; thread_info(cur)->thread_endnode_parent->router_id = srcAddress >> 10; memcpy(thread_info(cur)->thread_endnode_parent->mac64, entry_temp->mac64, 8); mle_tlv_info_t routing; if (mle_tlv_read_tlv(MLE_TYPE_ROUTE, ptr, data_length, &routing)) { thread_router_bootstrap_route_tlv_push(cur, routing.dataPtr, routing.tlvLen, linkMargin, entry_temp); } //Copy Leader Data *thread_info(cur)->thread_leader_data = leaderData; /*save the data version to one less than what was received so that any inconsistencies in network data is * fixed by a data request to parent */ thread_info(cur)->thread_leader_data->stableDataVersion = leaderData.stableDataVersion - 1; thread_info(cur)->thread_leader_data->dataVersion = leaderData.dataVersion - 1; return 0; } static void thread_child_synch_receive_cb(int8_t interface_id, mle_message_t *mle_msg, mle_security_header_t *security_headers) { (void) interface_id; tr_debug("Thread MLE message child_synch handler"); //State machine What packet shuold accept in this case protocol_interface_info_entry_t *cur = mle_msg->interface_ptr; /* Check that message is from link-local scope */ if (!addr_is_ipv6_link_local(mle_msg->packet_src_address)) { return; } uint16_t messageId; uint8_t linkMargin = thread_compute_link_margin(mle_msg->dbm); //State machine What packet shuold accept in this case switch (mle_msg->message_type) { case MLE_COMMAND_CHILD_UPDATE_RESPONSE: messageId = mle_tlv_validate_response(mle_msg->data_ptr, mle_msg->data_length); if (messageId == 0) { tr_debug("No matching challenge"); return; } if (thread_end_device_synch_response_validate(cur, mle_msg->data_ptr, mle_msg->data_length, linkMargin, mle_msg->packet_src_address, security_headers) != 0) { tr_warn("End device synch failed"); mle_service_msg_free(messageId); thread_bootsrap_device_synch_fail(cur); return; } mle_tlv_info_t networkDataTlv; thread_leader_data_t leaderData; tr_debug("End device synch Possible"); cur->thread_info->thread_attached_state = THREAD_STATE_CONNECTED; // read network data, and leader data check. Send data request sent if pending set is not in sync if (mle_tlv_read_tlv(MLE_TYPE_NETWORK_DATA, mle_msg->data_ptr, mle_msg->data_length, &networkDataTlv) && thread_leader_data_parse(mle_msg->data_ptr, mle_msg->data_length, &leaderData) && thread_joiner_application_pending_delay_timer_in_sync(cur->id)) { thread_bootstrap_network_data_save(cur, &leaderData, networkDataTlv.dataPtr, networkDataTlv.tlvLen); } else { thread_bootstrap_parent_network_data_request(cur, true); } thread_bootstrap_attached_ready(cur); //SET For sleepy state and mode update if (cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_SLEEPY_END_DEVICE) { nwk_thread_host_control(cur, NET_HOST_FAST_POLL_MODE, 1); //TRIG Child Update state thread_bootstrap_child_update_trig(cur); } mle_service_msg_free(messageId); break; } } static bool thread_host_prefer_parent_response(protocol_interface_info_entry_t *cur, thread_scanned_parent_t *scanned_parent, uint16_t version, thread_connectivity_t *connectivity) { (void) connectivity; (void) cur; if (!thread_extension_version_check(thread_info(cur)->version)) { return false; } if (version < scanned_parent->version) { tr_debug("prefer existing parent response from %"PRIX16, scanned_parent->shortAddress); return true; } return false; } /* MLE callback for parent response messages. * * The Parent Response contains these TLVs: * - Source Address TLV * - Leader Data TLV * - Link-layer Frame Counter TLV * - MLE Frame Counter TLV (optional) * - Response TLV * - Challenge TLV * - Link Margin TLV * - Connectivity TLV * - Version TLV */ void thread_mle_parent_discover_receive_cb(int8_t interface_id, mle_message_t *mle_msg, mle_security_header_t *security_headers) { (void) interface_id; protocol_interface_info_entry_t *cur = mle_msg->interface_ptr; /* Check that message is from link-local scope */ if (!addr_is_ipv6_link_local(mle_msg->packet_src_address)) { return; } tr_debug("MLE Parent response handler"); //State machine What packet should accept in this case switch (mle_msg->message_type) { case MLE_COMMAND_PARENT_RESPONSE: { thread_scanned_parent_t *scan_result = NULL; uint16_t messageId; uint16_t srcAddress, version; uint32_t llFrameCounter; uint32_t mleFrameCounter; uint8_t rssiValue; mle_tlv_info_t challengeTlv; uint8_t currentMargin, newMargin; thread_link_quality_e newLqi; thread_leader_data_t leaderData; thread_connectivity_t connectivityTlv; bool accept_response; tr_info("Parent Response"); if (security_headers->KeyIdMode != MAC_KEY_ID_MODE_SRC4_IDX) { tr_debug("Wrong key mode %u ", security_headers->KeyIdMode); return; } messageId = mle_tlv_validate_response(mle_msg->data_ptr, mle_msg->data_length); if (messageId == 0) { tr_debug("No matching challenge"); return; } //Read Leader Data and verify connectivity if (!thread_leader_data_parse(mle_msg->data_ptr, mle_msg->data_length, &leaderData)) { return; } if (!mle_tlv_read_8_bit_tlv(MLE_TYPE_RSSI, mle_msg->data_ptr, mle_msg->data_length, &rssiValue)) { return; } if (!thread_connectivity_tlv_parse(mle_msg->data_ptr, mle_msg->data_length, &connectivityTlv)) { return; } if (!mle_tlv_read_16_bit_tlv(MLE_TYPE_SRC_ADDRESS, mle_msg->data_ptr, mle_msg->data_length, &srcAddress)) { return; } if (!mle_tlv_read_16_bit_tlv(MLE_TYPE_VERSION, mle_msg->data_ptr, mle_msg->data_length, &version)) { return; } if (!mle_tlv_read_32_bit_tlv(MLE_TYPE_LL_FRAME_COUNTER, mle_msg->data_ptr, mle_msg->data_length, &llFrameCounter)) { return; } if (!mle_tlv_read_tlv(MLE_TYPE_CHALLENGE, mle_msg->data_ptr, mle_msg->data_length, &challengeTlv)) { return; } //If MLE MLE_TYPE_MLE_FRAME_COUNTER TLV is present then use it for validating further messages else use link layer frame counter if (!mle_tlv_read_32_bit_tlv(MLE_TYPE_MLE_FRAME_COUNTER, mle_msg->data_ptr, mle_msg->data_length, &mleFrameCounter)) { mleFrameCounter = llFrameCounter; } /** * At THREAD_STATE_REATTACH state only accept same Partition ID and Higher ID than current */ if (thread_info(cur)->thread_attached_state == THREAD_STATE_REATTACH || thread_info(cur)->thread_attached_state == THREAD_STATE_REATTACH_RETRY) { tr_debug("Reattach"); if (thread_info(cur)->thread_leader_data) { if (!thread_partition_match(cur, &leaderData)) { //accept only same ID at reattach phase return; } //Compare ID - when downgrading, accept all if (!thread_info(cur)->releaseRouterId) { if (!common_serial_number_greater_8(connectivityTlv.idSequence, thread_info(cur)->routing.router_id_sequence)) { tr_debug("Drop old partition by old ID"); return; } else { tr_debug("Accept old partition by new ID"); } } } } else if (thread_info(cur)->thread_attached_state == THREAD_STATE_ATTACH_ANY || thread_info(cur)->thread_attached_state == THREAD_STATE_CONNECTED || thread_info(cur)->thread_attached_state == THREAD_STATE_CONNECTED_ROUTER) { if (thread_info(cur)->thread_leader_data) { if (thread_partition_match(cur, &leaderData)) { //accept only different ID at anyattach phase tr_debug("Drop old partition"); return; } } else { //TODO Fix this tr_error("No leader Data allocated for AnyAttach,Why?"); } } if (blacklist_reject(mle_msg->packet_src_address)) { tr_debug("Drop Parent Response because blacklisted"); return; } /* Calculate this parent link margin */ newMargin = thread_calculate_link_margin(mle_msg->dbm, rssiValue); newLqi = thread_link_margin_to_quality(newMargin << THREAD_LINK_MARGIN_SCALING); tr_debug("Parent scan count %d addr:%04x Margin:%d lqi:%d", cur->nwk_nd_re_scan_count, srcAddress, newMargin, newLqi); if (newLqi == QUALITY_BAD) { tr_debug("Drop Bad Link"); accept_response = false; } else { if (cur->nwk_nd_re_scan_count <= 1) { if (newLqi == QUALITY_20dB) { accept_response = true; } else { tr_debug("Drop first time less than QUALITY_20dB"); accept_response = false; } } else { accept_response = true; } } if (thread_extension_enabled(cur) && thread_info(cur)->thread_device_mode == THREAD_DEVICE_MODE_ROUTER && leaderData.weighting < thread_info(cur)->partition_weighting) { // Only applies to extensions and only routers that can form new partitions can ignore lower weight tr_debug("Drop parent due weighting %d<%d", leaderData.weighting, thread_info(cur)->partition_weighting); return; } if (accept_response) { if (thread_info(cur)->thread_attach_scanned_parent == NULL) { thread_info(cur)->thread_attach_scanned_parent = ns_dyn_mem_temporary_alloc(sizeof(thread_scanned_parent_t)); if (!thread_info(cur)->thread_attach_scanned_parent) { return; } thread_info(cur)->thread_attach_scanned_parent->child_id_request_id = 0; scan_result = thread_info(cur)->thread_attach_scanned_parent; tr_debug("Partition %"PRIu32, leaderData.partitionId); } else { uint32_t currentPartitionId = thread_info(cur)->thread_attach_scanned_parent->leader_data.partitionId; uint8_t currentWeighting = thread_info(cur)->thread_attach_scanned_parent->leader_data.weighting; tr_debug("Current partition %"PRIu32" old:%"PRIu32" weighting %"PRIu8" old:%"PRIu8, currentPartitionId, leaderData.partitionId, currentWeighting, leaderData.weighting); if ((leaderData.partitionId != currentPartitionId) || (leaderData.weighting != currentWeighting)) { int retVal = thread_bootstrap_partition_process(cur, connectivityTlv.activeRouters, &leaderData, NULL); if (retVal > 0) { // New partition is Higher scan_result = thread_info(cur)->thread_attach_scanned_parent; } } else if (leaderData.partitionId == currentPartitionId) { thread_link_quality_e currentLqi; //Calculate Current summed LQI scan_result = thread_info(cur)->thread_attach_scanned_parent; currentMargin = thread_parent_margin_calc(scan_result->linkMarginFromParent, scan_result->linkMarginToParent); currentLqi = thread_link_margin_to_quality(currentMargin << THREAD_LINK_MARGIN_SCALING); tr_debug("Compare LQI from margins:"); tr_debug("New Margin %d vs Cur %d", newMargin, currentMargin); tr_debug("New Lqi %d vs Cur %d", newLqi, currentLqi); if (newLqi > currentLqi) { /*Override old parent data*/ tr_debug("Better Parent Lqi"); } else { if (newLqi == currentLqi) { //Compare if REED if (thread_is_router_addr(scan_result->shortAddress) && !thread_is_router_addr(srcAddress)) { scan_result = NULL; tr_debug("Dropped Response from REED over router"); } else if (connectivityTlv.parentPriority > scan_result->parentPriority) { tr_debug("Better parent priority %d>%d", connectivityTlv.parentPriority, scan_result->parentPriority); } else if (connectivityTlv.parentPriority < scan_result->parentPriority) { tr_debug("Dropped Response - lower parent priority %d<%d", connectivityTlv.parentPriority, scan_result->parentPriority); scan_result = NULL; } else { tr_debug("Similar LQI check connectivity old: %d,%d,%d new:%d,%d,%d", scan_result->linkQuality3, scan_result->linkQuality2, scan_result->linkQuality1, connectivityTlv.linkQuality3, connectivityTlv.linkQuality2, connectivityTlv.linkQuality1); if (scan_result->linkQuality3 > connectivityTlv.linkQuality3) { scan_result = NULL; } if (scan_result && scan_result->linkQuality3 == connectivityTlv.linkQuality3 && scan_result->linkQuality2 > connectivityTlv.linkQuality2) { scan_result = NULL; } if (scan_result && scan_result->linkQuality3 == connectivityTlv.linkQuality3 && scan_result->linkQuality2 == connectivityTlv.linkQuality2 && scan_result->linkQuality1 > connectivityTlv.linkQuality1) { scan_result = NULL; } if (!scan_result) { tr_debug("Dropped Connectivity is not as good"); } } } else { scan_result = NULL; } } } if (scan_result && thread_host_prefer_parent_response(cur, scan_result, version, &connectivityTlv)) { scan_result = NULL; } } } if (scan_result) { // save the result thread_leader_data_t *leader; scan_result->linkMarginToParent = thread_compute_link_margin(mle_msg->dbm); scan_result->linkMarginFromParent = rssiValue; scan_result->linLayerFrameCounter = llFrameCounter; scan_result->mleFrameCounter = mleFrameCounter; scan_result->shortAddress = srcAddress; scan_result->version = version; memcpy(scan_result->challengeData, challengeTlv.dataPtr, challengeTlv.tlvLen); scan_result->chal_len = challengeTlv.tlvLen; //Save MAC address from LL64 memcpy(scan_result->mac64, (mle_msg->packet_src_address + 8), 8); scan_result->mac64[0] ^= 2; leader = &scan_result->leader_data; //Copy Leader Data *leader = leaderData; scan_result->routeCostToLeader = connectivityTlv.leaderCost; scan_result->linkQuality3 = connectivityTlv.linkQuality3; scan_result->linkQuality2 = connectivityTlv.linkQuality2; scan_result->linkQuality1 = connectivityTlv.linkQuality1; scan_result->parentPriority = connectivityTlv.parentPriority; scan_result->activeRouters = connectivityTlv.activeRouters; scan_result->security_key_index = security_headers->KeyIndex; thread_routing_update_link_margin(cur, scan_result->shortAddress, scan_result->linkMarginToParent, scan_result->linkMarginFromParent); scan_result->keySequence = common_read_32_bit(security_headers->Keysource); tr_debug("Current %"PRIu32" RX %"PRIu32" Cnt%"PRIu32, scan_result->leader_data.partitionId, leaderData.partitionId, scan_result->linLayerFrameCounter); } break; } default: break; } } /* Callback for child ID request. * * Child ID Response contains these TLVs: * - Source Address TLV * - Leader Data TLV * - Address16 TLV * - Network Data TLV (optional) * - Route64 TLV (optional) * - Address Registration TLV (optional) * - Active Operational Dataset TLV (optional) * - Pending Operational Dataset TLV (optional) */ static void thread_mle_child_request_receive_cb(int8_t interface_id, mle_message_t *mle_msg, mle_security_header_t *security_headers) { thread_leader_data_t leaderData; mac_neighbor_table_entry_t *entry_temp; protocol_interface_info_entry_t *cur = mle_msg->interface_ptr; link_configuration_s *link_configuration; link_configuration = thread_joiner_application_get_config(cur->id); if (!link_configuration) { return; } /* Check that message is from link-local scope */ if (!addr_is_ipv6_link_local(mle_msg->packet_src_address)) { return; } tr_debug("Thread MLE Child ID response handler"); switch (mle_msg->message_type) { case MLE_COMMAND_CHILD_ID_RESPONSE: { uint8_t src_mac64[8]; uint8_t shortAddress[2]; uint16_t childId; mle_tlv_info_t routeTlv, addressRegisteredTlv, networkDataTlv; mle_tlv_info_t ConfigurationTlv; uint64_t pending_timestamp = 0; uint64_t active_timestamp; thread_scanned_parent_t *scan_result = thread_info(cur)->thread_attach_scanned_parent; bool new_entry_created; tr_info("Recv Child ID Response"); // Validate that response is coming from the scanned parent candidate memcpy(src_mac64, (mle_msg->packet_src_address + 8), 8); src_mac64[0] ^= 2; if (memcmp(src_mac64, scan_result->mac64, 8) != 0) { tr_debug("Drop Child ID response from previous request"); return; } // Clear old data if (cur->thread_info->releaseRouterId) { thread_bootstrap_clear_neighbor_entries(cur); } cur->thread_info->localServerDataBase.release_old_address = true; thread_neighbor_list_clean(cur); thread_leader_service_stop(interface_id); thread_leader_service_leader_data_free(cur->thread_info); thread_merge_prepare(cur); // Create entry for new parent entry_temp = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur), mle_msg->packet_src_address, true, &new_entry_created); if (entry_temp == NULL) { // todo: what to do here? return; } thread_neighbor_class_update_link(&cur->thread_info->neighbor_class, entry_temp->index, thread_compute_link_margin(mle_msg->dbm), new_entry_created); thread_neighbor_last_communication_time_update(&cur->thread_info->neighbor_class, entry_temp->index); //Parse mandatory TLV's if (!thread_leader_data_parse(mle_msg->data_ptr, mle_msg->data_length, &leaderData)) { return; } if (!mle_tlv_read_16_bit_tlv(MLE_TYPE_SRC_ADDRESS, mle_msg->data_ptr, mle_msg->data_length, &entry_temp->mac16)) { return; } if (!mle_tlv_read_16_bit_tlv(MLE_TYPE_ADDRESS16, mle_msg->data_ptr, mle_msg->data_length, &childId)) { return; } //Read Optional TLV's mle_tlv_read_tlv(MLE_TYPE_ADDRESS_REGISTRATION, mle_msg->data_ptr, mle_msg->data_length, &addressRegisteredTlv); mle_tlv_read_tlv(MLE_TYPE_NETWORK_DATA, mle_msg->data_ptr, mle_msg->data_length, &networkDataTlv); mle_tlv_read_tlv(MLE_TYPE_ROUTE, mle_msg->data_ptr, mle_msg->data_length, &routeTlv); // update operational datasets mle_tlv_read_tlv(MLE_TYPE_OPERATIONAL_DATASET, mle_msg->data_ptr, mle_msg->data_length, &ConfigurationTlv); if (mle_tlv_read_64_bit_tlv(MLE_TYPE_ACTIVE_TIMESTAMP, mle_msg->data_ptr, mle_msg->data_length, &active_timestamp)) { thread_active_operational_dataset_process(cur, ConfigurationTlv.dataPtr, ConfigurationTlv.tlvLen, active_timestamp); } // TODO check if result is true then need to update all configurations mle_tlv_read_tlv(MLE_TYPE_PENDING_OPERATIONAL_DATASET, mle_msg->data_ptr, mle_msg->data_length, &ConfigurationTlv); if (mle_tlv_read_64_bit_tlv(MLE_TYPE_PENDING_TIMESTAMP, mle_msg->data_ptr, mle_msg->data_length, &pending_timestamp)) { thread_pending_operational_dataset_process(cur, pending_timestamp, ConfigurationTlv.dataPtr, ConfigurationTlv.tlvLen); } else if (pending_timestamp < thread_joiner_application_pending_config_timestamp_get(cur->id)) { // parent did not have timestamp but we haver tr_info("save pending set for leader"); thread_joiner_application_next_pending_config_save(cur->id); thread_joiner_application_pending_config_delete(cur->id); } *thread_info(cur)->thread_leader_data = leaderData; thread_parent_info_t *parent = thread_parent_data_allocate(cur->thread_info); if (!parent) { tr_debug("Parent allocate fail."); return; } common_write_16_bit(entry_temp->mac16, shortAddress); //Update possible reed address by real router address scan_result->shortAddress = entry_temp->mac16; entry_temp->connected_device = 1; entry_temp->link_role = PRIORITY_PARENT_NEIGHBOUR; mac_helper_coordinator_address_set(cur, ADDR_802_15_4_SHORT, shortAddress); mac_neighbor_table_neighbor_refresh(mac_neighbor_info(cur), entry_temp, thread_info(cur)->host_link_timeout); if (scan_result->security_key_index != security_headers->KeyIndex) { // KeyIndex has been changed between parent_response and child_id_response, reset link layer frame counter scan_result->linLayerFrameCounter = 0; scan_result->security_key_index = security_headers->KeyIndex; } mlme_device_descriptor_t device_desc; mac_helper_device_description_write(cur, &device_desc, entry_temp->mac64, entry_temp->mac16, scan_result->linLayerFrameCounter, false); mac_helper_devicetable_set(&device_desc, cur, entry_temp->index, security_headers->KeyIndex, new_entry_created); thread_info(cur)->thread_attached_state = THREAD_STATE_CONNECTED; thread_bootstrap_update_ml16_address(cur, childId); if (!thread_is_router_addr(thread_info(cur)->routerShortAddress)) { thread_info(cur)->routerShortAddress = 0xfffe; } mle_service_msg_free(scan_result->child_id_request_id); scan_result->child_id_request_id = 0; //SET Parent Info and free scan response info parent->pathCostToLeader = thread_sum_rx_path_cost_and_link_cost(scan_result->linkMarginToParent, scan_result->linkMarginFromParent, scan_result->routeCostToLeader); parent->shortAddress = scan_result->shortAddress; parent->router_id = (scan_result->shortAddress >> 10); memcpy(parent->mac64, scan_result->mac64, 8); //Check Network Data TLV if (networkDataTlv.tlvType == MLE_TYPE_NETWORK_DATA) { thread_bootstrap_network_data_save(cur, &leaderData, networkDataTlv.dataPtr, networkDataTlv.tlvLen); } else { cur->thread_info->thread_leader_data->dataVersion--; cur->thread_info->thread_leader_data->stableDataVersion--; } blacklist_update(mle_msg->packet_src_address, true); // if (routeTlv.tlvType == MLE_TYPE_ROUTE && routeTlv.tlvLen) { thread_router_bootstrap_route_tlv_push(cur, routeTlv.dataPtr, routeTlv.tlvLen, thread_compute_link_margin(mle_msg->dbm), entry_temp); } thread_bootstrap_attached_ready(cur); break; } default: tr_debug("Skip msg type %d", mle_msg->message_type); break; } } static int8_t thread_end_device_synch_start(protocol_interface_info_entry_t *cur) { mle_message_timeout_params_t timeout; uint32_t keySequence; uint16_t buf_id = mle_service_msg_allocate(cur->id, 150 + 3 + 6 + 10, true, MLE_COMMAND_CHILD_UPDATE_REQUEST); if (buf_id == 0) { return -1; } uint8_t tlv_req[3]; thread_management_get_current_keysequence(cur->id, &keySequence); mle_service_msg_update_security_params(buf_id, 5, 2, keySequence); uint8_t *address_ptr = mle_service_get_msg_destination_address_pointer(buf_id); //Set Parent Address memcpy(address_ptr, ADDR_LINK_LOCAL_PREFIX, 8); address_ptr += 8; memcpy(address_ptr, cur->thread_info->thread_endnode_parent->mac64, 8); *address_ptr ^= 2; uint8_t *ptr = mle_service_get_data_pointer(buf_id); //Add mode ptr = mle_tlv_write_mode(ptr, thread_mode_get_by_interface_ptr(cur)); ptr = thread_address_registration_tlv_write(ptr, cur); tlv_req[0] = MLE_TYPE_ADDRESS16; tlv_req[1] = MLE_TYPE_NETWORK_DATA; if (cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_ROUTER) { // REEDs request Route64 as well tlv_req[2] = MLE_TYPE_ROUTE; ptr = mle_tlv_req_tlv(ptr, tlv_req, 3); } else { ptr = mle_tlv_req_tlv(ptr, tlv_req, 2); } if (mle_service_update_length_by_ptr(buf_id, ptr) != 0) { tr_debug("Buffer overflow at message write"); } tr_debug("Send MLE Child Update Request (Child synchronisation)"); timeout.retrans_max = THREAD_REQUEST_MAX_RETRY_CNT; timeout.timeout_init = 1; timeout.timeout_max = 3; timeout.delay = MLE_NO_DELAY; mle_service_set_packet_callback(buf_id, thread_device_synch_timeout); mle_service_set_msg_timeout_parameters(buf_id, &timeout); mle_service_send_message(buf_id); return 0; } void thread_endevice_synch_start(protocol_interface_info_entry_t *cur) { if (cur->thread_info->thread_endnode_parent) { bool new_entry_created; // Add the parent to the MLE neighbor table mac_neighbor_table_entry_t *mac_entry = mac_neighbor_entry_get_by_mac64(mac_neighbor_info(cur), cur->thread_info->thread_endnode_parent->mac64, true, &new_entry_created); if (mac_entry) { //Add link margin 64 thread_neighbor_class_update_link(&cur->thread_info->neighbor_class, mac_entry->index, 64, new_entry_created); thread_neighbor_last_communication_time_update(&cur->thread_info->neighbor_class, mac_entry->index); mac_entry->mac16 = cur->thread_info->thread_endnode_parent->shortAddress; mac_entry->connected_device = 1; // In case we don't get response to sync; use temporary timeout here, // Child ID Response handler will set correct value later mac_neighbor_table_neighbor_refresh(mac_neighbor_info(cur), mac_entry, mac_entry->link_lifetime); // Add the parent to the MAC table (for e.g. secured/fragmented Child Update Response) mlme_device_descriptor_t device_desc; mac_helper_device_description_write(cur, &device_desc, mac_entry->mac64, mac_entry->mac16, 0, false); mac_helper_devicetable_set(&device_desc, cur, mac_entry->index, cur->mac_parameters->mac_default_key_index, new_entry_created); } } //Send Child Synch if (thread_end_device_synch_start(cur) == 0) { //SET Child synch receiver handler mle_service_interface_receiver_handler_update(cur->id, thread_child_synch_receive_cb); cur->nwk_bootstrap_state = ER_MLE_SYNCH; } } static bool thread_child_id_req_timeout(int8_t interface_id, uint16_t msgId, bool usedAllRetries) { mac_neighbor_table_entry_t *entry_temp; thread_scanned_parent_t *scanned_parent; protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id); (void)msgId; if (!cur) { return false; } // Not last retry - keep trying if (!usedAllRetries) { return true; } //Do not release message inside this call MLE service will do that. cur->thread_info->thread_attach_scanned_parent->child_id_request_id = 0; uint8_t *addr = mle_service_get_msg_destination_address_pointer(msgId); tr_debug("Child ID Request timed out: %s", trace_ipv6(addr)); blacklist_update(addr, false); scanned_parent = cur->thread_info->thread_attach_scanned_parent; /* Used all retries - if we are on any attach state or */ /* if we don't have route to leader, start any-attach */ if (thread_info(cur)->thread_attached_state == THREAD_STATE_NETWORK_DISCOVER || -1 == thread_route_ready_to_leader(cur) || !scanned_parent) { tr_debug("start any-attach"); thread_bootstrap_connection_error(interface_id, CON_ERROR_NETWORK_ATTACH_FAIL, NULL); goto exit; } /* Go back to old partition */ tr_debug("Back to old partition"); /* If scanned parent is from other partition, delete from MLE table */ if ((scanned_parent->leader_data.partitionId != thread_info(cur)->thread_leader_data->partitionId) || (scanned_parent->leader_data.weighting != thread_info(cur)->thread_leader_data->weighting)) { entry_temp = mac_neighbor_table_address_discover(mac_neighbor_info(cur), scanned_parent->mac64, ADDR_802_15_4_LONG); if (entry_temp) { bool my_parent = thread_check_is_this_my_parent(cur, entry_temp); mac_neighbor_table_neighbor_remove(mac_neighbor_info(cur), entry_temp); if (my_parent) { tr_debug("No parent resp - any-attach"); thread_bootstrap_connection_error(interface_id, CON_ERROR_NETWORK_ATTACH_FAIL, NULL); goto exit; } } } if (thread_info(cur)->thread_endnode_parent) { mac_helper_coordinator_address_set(cur, ADDR_802_15_4_LONG, thread_info(cur)->thread_endnode_parent->mac64); } if (cur->thread_info->routerShortAddress == 0xfffe || !thread_is_router_addr(cur->thread_info->routerShortAddress)) { thread_info(cur)->thread_attached_state = THREAD_STATE_CONNECTED; } else { thread_info(cur)->thread_attached_state = THREAD_STATE_CONNECTED_ROUTER; } cur->thread_info->localServerDataBase.release_old_address = false; cur->thread_info->releaseRouterId = false; cur->thread_info->networkDataRequested = false; cur->nwk_bootstrap_state = ER_BOOTSRAP_DONE; exit: mle_service_interface_receiver_handler_update(cur->id, thread_general_mle_receive_cb); ns_dyn_mem_free(cur->thread_info->thread_attach_scanned_parent); cur->thread_info->thread_attach_scanned_parent = NULL; return false; } static int thread_attach_child_id_request_build(protocol_interface_info_entry_t *cur) { uint8_t *ptr, *address_ptr; uint8_t mode; mle_message_timeout_params_t timeout; uint32_t keySequence; uint16_t buf_id = mle_service_msg_allocate(cur->id, 128, false, MLE_COMMAND_CHILD_ID_REQUEST); if (buf_id == 0) { return -1; } thread_scanned_parent_t *scan_parent = thread_info(cur)->thread_attach_scanned_parent; //Set ll64 address_ptr = mle_service_get_msg_destination_address_pointer(buf_id); memcpy(address_ptr, ADDR_LINK_LOCAL_PREFIX, 8); memcpy(address_ptr + 8, scan_parent->mac64, 8); address_ptr[8] ^= 2; thread_management_get_current_keysequence(cur->id, &keySequence); mle_service_msg_update_security_params(buf_id, 5, 2, keySequence); uint8_t request_tlv_list[3]; uint8_t macShort[2]; uint8_t reqTlvCnt; mode = thread_mode_get_by_interface_ptr(cur); common_write_16_bit(scan_parent->shortAddress, macShort); //SET Coordinator Address mac_helper_coordinator_address_set(cur, ADDR_802_15_4_LONG, scan_parent->mac64); ptr = mle_service_get_data_pointer(buf_id); /* Allocate a new challenge */ ptr = mle_tlv_write_mode(ptr, thread_mode_get_by_interface_ptr(cur)); ptr = mle_general_write_link_layer_framecounter(ptr, cur); //SET MLE Frame Counter ptr = mle_tlv_write_framecounter(ptr, mle_service_security_get_frame_counter(cur->id)); ptr = mle_tlv_write_response(ptr, scan_parent->challengeData, scan_parent->chal_len); //Add ML-EID if ((mode & MLE_FFD_DEV) == 0) { ptr = thread_ml_address_tlv_write(ptr, cur); } reqTlvCnt = 2; request_tlv_list[0] = MLE_TYPE_NETWORK_DATA; request_tlv_list[1] = MLE_TYPE_ADDRESS16; if (thread_info(cur)->thread_device_mode == THREAD_DEVICE_MODE_ROUTER) { request_tlv_list[2] = MLE_TYPE_ROUTE; reqTlvCnt = 3; } ptr = mle_tlv_req_tlv(ptr, request_tlv_list, reqTlvCnt); ptr = mle_tlv_write_timeout(ptr, cur->thread_info->host_link_timeout); //Set Version ptr = mle_tlv_write_version(ptr, cur->thread_info->version); //add only active timestamp to the child id request ptr = thread_active_timestamp_write(cur, ptr); if (mle_service_update_length_by_ptr(buf_id, ptr) != 0) { tr_debug("Buffer overflow at message write"); } timeout.retrans_max = THREAD_CHILD_ID_REQUEST_MAX_RETRY_CNT; timeout.timeout_init = 1; timeout.timeout_max = 3; timeout.delay = MLE_NO_DELAY; mle_service_set_packet_callback(buf_id, thread_child_id_req_timeout); mle_service_set_msg_timeout_parameters(buf_id, &timeout); mle_service_send_message(buf_id); //Update MLE handler state mle_service_interface_receiver_handler_update(cur->id, thread_mle_child_request_receive_cb); cur->nwk_bootstrap_state = ER_CHILD_ID_REQ; scan_parent->child_id_request_id = buf_id; return 0; } static bool thread_child_update_timeout_cb(int8_t interface_id, uint16_t msgId, bool usedAllRerties) { protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur || !cur->thread_info->thread_endnode_parent) { return false; } if (msgId != cur->thread_info->childUpdateReqMsgId) { //Wrong message id return false; } if (usedAllRerties) { tr_debug("Child Update timed out"); cur->thread_info->thread_endnode_parent->childUpdatePending = false; cur->thread_info->thread_endnode_parent->childUpdateProcessActive = false; mac_data_poll_protocol_poll_mode_decrement(cur); thread_bootstrap_reset_restart(cur->id); tr_debug("Restart attachment"); return false; } if (cur->thread_info->thread_endnode_parent->childUpdateProcessActive) { // we have not received response so re-send return true; } return false; } bool thread_host_bootstrap_child_update(protocol_interface_info_entry_t *cur, const uint8_t *mac64) { mle_message_timeout_params_t timeout; uint8_t mode; uint32_t keySequence; if (!cur->thread_info->thread_endnode_parent) { tr_debug("Not end device parent info for NUD"); return false; } if (cur->thread_info->thread_endnode_parent->childUpdateProcessActive) { //Set Pending if earlier process is already started cur->thread_info->thread_endnode_parent->childUpdatePending = true; return false; } //Trig event cur->thread_info->thread_endnode_parent->childUpdatePending = false; cur->thread_info->thread_endnode_parent->childUpdateProcessActive = true; tr_debug("Child Update Request"); mode = thread_mode_get_by_interface_ptr(cur); //Build packet uint16_t bufId = mle_service_msg_allocate(cur->id, 150 + 3 + 6 + 10, false, MLE_COMMAND_CHILD_UPDATE_REQUEST); if (bufId == 0) { return false; } thread_management_get_current_keysequence(cur->id, &keySequence); mle_service_msg_update_security_params(bufId, 5, 2, keySequence); uint8_t *address_ptr = mle_service_get_msg_destination_address_pointer(bufId); memcpy(address_ptr, ADDR_LINK_LOCAL_PREFIX, 8); memcpy(address_ptr + 8, mac64, 8); address_ptr[8] ^= 2; uint8_t *ptr = mle_service_get_data_pointer(bufId); ptr = mle_tlv_write_mode(ptr, mode); ptr = mle_general_write_source_address(ptr, cur); ptr = mle_tlv_write_timeout(ptr, cur->thread_info->host_link_timeout); ptr = thread_leader_data_tlv_write(ptr, cur); //Set Addresss TLV if ((mode & MLE_FFD_DEV) == 0) { ptr = thread_address_registration_tlv_write(ptr, cur); } if (mle_service_update_length_by_ptr(bufId, ptr) != 0) { tr_debug("Buffer overflow at message write"); } timeout.retrans_max = 3; timeout.timeout_init = 1; timeout.timeout_max = 4; timeout.delay = MLE_NO_DELAY; net_host_mode_t macHostMode; if (mac_data_poll_host_mode_get(cur, &macHostMode) == 0 && macHostMode == NET_HOST_RX_ON_IDLE) { thread_end_device_mode_set(cur, false); } mac_data_poll_init_protocol_poll(cur); cur->thread_info->childUpdateReqMsgId = bufId; mle_service_set_packet_callback(bufId, thread_child_update_timeout_cb); mle_service_set_msg_timeout_parameters(bufId, &timeout); mle_service_send_message(bufId); return true; } int thread_host_bootstrap_child_update_negative_response(protocol_interface_info_entry_t *cur, uint8_t *dstAddress, mle_tlv_info_t *challenge) { uint32_t keySequence; if (!challenge) { return -2; } uint16_t bufId = mle_service_msg_allocate(cur->id, 16 + challenge->tlvLen, false, MLE_COMMAND_CHILD_UPDATE_RESPONSE); if (bufId == 0) { return -1; } tr_debug("MLE Child negative Response"); thread_management_get_current_keysequence(cur->id, &keySequence); mle_service_msg_update_security_params(bufId, 5, 2, keySequence); uint8_t *ptr = mle_service_get_data_pointer(bufId); *ptr++ = MLE_TYPE_STATUS; *ptr++ = 1; *ptr++ = MLE_STATUS_ERROR; if (challenge->tlvLen) { //Add response ptr = mle_tlv_write_response(ptr, challenge->dataPtr, challenge->tlvLen); } if (mle_service_update_length_by_ptr(bufId, ptr) != 0) { tr_debug("Buffer overflow at message write"); } mle_service_set_msg_destination_address(bufId, dstAddress); mle_service_send_message(bufId); return 0; } #endif