/* * 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_router_bootstrap.c * \brief Add short description about this file!!! * */ #include "nsconfig.h" #include #include #include #include #include "eventOS_event.h" #include "eventOS_event_timer.h" #include "randLIB.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_bootstrap.h" #include "6LoWPAN/Thread/thread_router_bootstrap.h" #include "6LoWPAN/Thread/thread_host_bootstrap.h" #include "6LoWPAN/Thread/thread_management_internal.h" #include "6LoWPAN/Thread/thread_network_synch.h" #include "6LoWPAN/Thread/thread_discovery.h" #include "6LoWPAN/Thread/thread_joiner_application.h" #include "6LoWPAN/Thread/thread_address_registration_client.h" #include "6LoWPAN/Thread/thread_management_client.h" #include "6LoWPAN/Thread/thread_management_server.h" #include "6LoWPAN/Thread/thread_extension.h" #include "6LoWPAN/Thread/thread_leader_service.h" #include "6LoWPAN/Thread/thread_beacon.h" #include "6LoWPAN/Thread/thread_network_data_lib.h" #include "6LoWPAN/Thread/thread_lowpower_private_api.h" #include "6LoWPAN/Thread/thread_tmfcop_lib.h" #include "6LoWPAN/Thread/thread_nvm_store.h" #include "6LoWPAN/Thread/thread_neighbor_class.h" #include "thread_management_if.h" #include "Common_Protocols/ipv6.h" #include "Common_Protocols/icmpv6.h" #include "Common_Protocols/icmpv6_radv.h" #include "MLE/mle.h" #include "MLE/mle_tlv.h" #include "thread_config.h" #include "multicast_api.h" #include "Service_Libs/nd_proxy/nd_proxy.h" #include "Service_Libs/mle_service/mle_service_api.h" #include "Service_Libs/blacklist/blacklist.h" #include "DHCPv6_client/dhcpv6_client_api.h" #include "6LoWPAN/MAC/mac_helper.h" #include "mac_api.h" #include "6LoWPAN/MAC/mac_data_poll.h" #include "thread_border_router_api.h" #include "Core/include/address.h" #include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h" #ifdef HAVE_THREAD_ROUTER #define TRACE_GROUP "trbs" static uint8_t *thread_tlv_add(protocol_interface_info_entry_t *cur, uint8_t *ptr, uint8_t tlv_type, uint8_t mode); static void mle_multicast_push_to_sleep_child(protocol_interface_info_entry_t *cur, buffer_t *buf); static void thread_bootstrap_client_router_id_cb(int8_t interface_id, int8_t status, uint16_t router_rloc, const uint8_t router_mask_ptr[9]); static void thread_router_synch_receive_cb(int8_t interface_id, mle_message_t *mle_msg, mle_security_header_t *security_headers); static void thread_bootstrap_client_router_id_release_cb(int8_t interface_id, int8_t status, uint16_t router_rloc, const uint8_t router_mask_ptr[9]); static int thread_router_synch_accept_request_build(protocol_interface_info_entry_t *cur, mle_message_t *mle_msg, uint16_t shortAddress, uint8_t *challenge, uint8_t chalLen, uint8_t *tlvReq, uint8_t tlvReqLen); static int thread_router_accept_to_endevice(protocol_interface_info_entry_t *cur, mle_message_t *mle_msg, uint8_t *challenge, uint8_t chalLen); static int thread_router_accept_request_build(protocol_interface_info_entry_t *cur, mle_message_t *mle_msg, uint16_t shortAddress, uint8_t *challenge, uint8_t chalLen, uint8_t type, bool rssi_tlv, uint8_t rssi); static int thread_child_update_response(protocol_interface_info_entry_t *cur, uint8_t *dst_address, uint8_t mode, uint16_t short_address, uint32_t timeout, mle_tlv_info_t *addressRegisterTlv, mle_tlv_info_t *tlvReq, mle_tlv_info_t *challengeTlv, uint64_t active_timestamp, uint64_t pending_timestamp); static int mle_build_and_send_data_response_msg(protocol_interface_info_entry_t *cur, uint8_t *dst_address, uint8_t *data_ptr, uint16_t data_len, mle_tlv_info_t *request_tlv, uint8_t mode); static int thread_attach_parent_response_build(protocol_interface_info_entry_t *cur, uint8_t *dstAddress, uint8_t *challenge, uint16_t chalLen, uint8_t linkMargin, uint8_t scanMask, uint8_t mode); static int mle_attach_child_id_response_build(protocol_interface_info_entry_t *cur, uint8_t *dstAddress, thread_pending_child_id_req_t *child_req, struct mac_neighbor_table_entry *neigh_info); static int8_t thread_router_bootstrap_synch_request_send(protocol_interface_info_entry_t *cur); static bool thread_child_id_request(protocol_interface_info_entry_t *cur, struct mac_neighbor_table_entry *entry_temp); static bool thread_router_parent_address_check(protocol_interface_info_entry_t *cur, uint8_t *source_addr) { if (thread_info(cur)->thread_endnode_parent && addr_iid_matches_eui64(source_addr + 8, thread_info(cur)->thread_endnode_parent->mac64)) { return true; } return false; } static void thread_send_proactive_an(protocol_interface_info_entry_t *cur) { ns_list_foreach(if_address_entry_t, e, &cur->ip_addresses) { if (e->source == ADDR_SOURCE_DHCP || e->source == ADDR_SOURCE_SLAAC) { uint8_t br_ml_addr[16]; ns_list_foreach(thread_network_data_prefix_cache_entry_t, prefix, &cur->thread_info->networkDataStorage.localPrefixList) { uint8_t prefixBytesLen = prefixBits_to_bytes(prefix->servicesPrefixLen); if (memcmp(e->address, prefix->servicesPrefix, prefixBytesLen) != 0) { continue; } ns_list_foreach(thread_network_server_data_entry_t, br, &prefix->borderRouterList) { if (br->routerID == 0xfffe) { continue; } uint16_t rloc16 = mac_helper_mac16_address_get(cur); thread_addr_write_mesh_local_16(br_ml_addr, br->routerID, cur->thread_info); tr_debug("Sending proactive AN to border router: %s", trace_ipv6(br_ml_addr)); thread_management_client_proactive_an(cur->id, e->address, rloc16, cur->iid_slaac, br_ml_addr); } } } } } int thread_router_bootstrap_mle_advertise(protocol_interface_info_entry_t *cur) { /* How was this 40 calculated? Seems to be more than enough, at least. * MODE = 2+1, SRC_ADDR = 2+2, LINK = 2+1+4*neighbours, ROUTE = 2+MLE_ROUTE_MIN_OPTION_LEN+routers * Total = 10 + neighbours * 4 */ uint16_t neig_cache_size = 40 + 3; uint8_t *ptr; if (!cur) { return -1; } if (!thread_is_connected(cur)) { return -1; } if (thread_i_am_router(cur)) { neig_cache_size += thread_route_option_size(cur); neig_cache_size += thread_leader_data_tlv_size(cur); } uint16_t buf_id = mle_service_msg_allocate(cur->id, neig_cache_size, false, MLE_COMMAND_ADVERTISEMENT); if (buf_id == 0) { return -1; } uint32_t keySequence; if (thread_management_get_current_keysequence(cur->id, &keySequence) == 0) { mle_service_msg_update_security_params(buf_id, 5, 2, keySequence); } //SET Destination address mle_service_set_msg_destination_address(buf_id, ADDR_LINK_LOCAL_ALL_NODES); ptr = mle_service_get_data_pointer(buf_id); ptr = mle_general_write_source_address(ptr, cur); if (thread_i_am_router(cur)) { ptr = thread_route_option_write(cur, ptr); } else { *ptr++ = MLE_TYPE_ROUTE; *ptr++ = 0; } ptr = thread_leader_data_tlv_write(ptr, cur); if (mle_service_update_length_by_ptr(buf_id, ptr) != 0) { tr_debug("Buffer overflow at message write"); } tr_debug("Send MLE Secured Advertisement"); return mle_service_send_message(buf_id); } static void clone_multicast_to_unicast(protocol_interface_info_entry_t *cur, mac_neighbor_table_entry_t *entry, buffer_t *buf) { link_configuration_s *linkConfiguration = thread_joiner_application_get_config(cur->id); if (!linkConfiguration) { return; } buffer_t *new_buf = buffer_clone(buf); if ((new_buf->info & B_DIR_MASK) == B_DIR_UP) { buffer_data_pointer(new_buf)[IPV6_HDROFF_HOP_LIMIT] = --new_buf->options.hop_limit; } new_buf->info = (buffer_info_t)(B_DIR_DOWN | B_TO_IPV6_TXRX | B_FROM_IPV6_FWD); uint8_t next_hop[16]; memcpy(next_hop, ADDR_LINK_LOCAL_PREFIX, 8); memcpy(&next_hop[8], entry->mac64, 8); next_hop[8] ^= 2; ipv6_buffer_route_to(new_buf, next_hop, cur); protocol_push(new_buf); tr_debug("Cloned %s to %s", tr_ipv6(buf->dst_sa.address), tr_ipv6(next_hop)); } static void mle_multicast_push_to_sleep_child(protocol_interface_info_entry_t *cur, buffer_t *buf) { mac_neighbor_table_list_t *mac_table_list = &mac_neighbor_info(cur)->neighbour_list; ns_list_foreach(mac_neighbor_table_entry_t, cur_entry, mac_table_list) { if (!cur_entry->rx_on_idle) { clone_multicast_to_unicast(cur, cur_entry, buf); } } } static void thread_multicast_forward_to_child(protocol_interface_info_entry_t *cur, buffer_t *buffer) { mle_multicast_push_to_sleep_child(cur, buffer); } static void thread_registered_mcast_forward_to_child(protocol_interface_info_entry_t *cur, buffer_t *buffer) { thread_registered_mcast_addr_t *addr = thread_registered_mcast_addr_entry_find(cur, buffer->dst_sa.address); if (addr) { tr_debug("Forwarding to registered multicast address: %s", trace_ipv6(addr->address)); ns_list_foreach(thread_mcast_child_t, child, &addr->children) { mac_neighbor_table_entry_t *entry = mac_neighbor_table_address_discover(mac_neighbor_info(cur), child->mac64, ADDR_802_15_4_LONG); if (entry && !entry->rx_on_idle) { clone_multicast_to_unicast(cur, entry, buffer); } } } } void thread_router_bootstrap_multicast_forwarder_enable(protocol_interface_info_entry_t *cur, buffer_t *buf) { //tr_debug("Considering forward %s (%s)", tr_ipv6(buf->dst_sa.address), (buf->info & B_DIR_MASK) == B_DIR_UP ? "up" : "down"); if (addr_ipv6_multicast_scope(buf->dst_sa.address) <= IPV6_SCOPE_INTERFACE_LOCAL) { return; } // Safe guard: do NOT forward received link-local // multicast packets to our sleepy children if ((buf->info & B_DIR_MASK) == B_DIR_UP && addr_ipv6_multicast_scope(buf->dst_sa.address) <= IPV6_SCOPE_LINK_LOCAL) { tr_debug("Received link-local multicast; return..."); return; } uint8_t addr[16]; thread_bootstrap_all_nodes_address_generate(addr, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, IPV6_SCOPE_LINK_LOCAL); if (addr_ipv6_equal(buf->dst_sa.address, addr)) { thread_multicast_forward_to_child(cur, buf); return; } thread_bootstrap_all_nodes_address_generate(addr, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, IPV6_SCOPE_REALM_LOCAL); if (addr_ipv6_equal(buf->dst_sa.address, addr)) { thread_multicast_forward_to_child(cur, buf); return; } thread_registered_mcast_forward_to_child(cur, buf); } static void thread_router_synch_receive_cb(int8_t interface_id, mle_message_t *mle_msg, mle_security_header_t *security_headers) { thread_leader_data_t leaderData; uint8_t linkMarginfronNeigh; uint16_t version, shortAddress, address16; uint32_t llFrameCounter; mle_tlv_info_t routing; mac_neighbor_table_entry_t *entry_temp; tr_debug("Thread MLE message router sync"); //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; } if (!thread_leader_data_parse(mle_msg->data_ptr, mle_msg->data_length, &leaderData)) { return; } //Validate ready TLV for router Synch if ((!mle_tlv_read_16_bit_tlv(MLE_TYPE_VERSION, mle_msg->data_ptr, mle_msg->data_length, &version)) || (!mle_tlv_read_16_bit_tlv(MLE_TYPE_ADDRESS16, mle_msg->data_ptr, mle_msg->data_length, &address16)) || (!mle_tlv_read_32_bit_tlv(MLE_TYPE_LL_FRAME_COUNTER, mle_msg->data_ptr, mle_msg->data_length, &llFrameCounter)) || (!mle_tlv_read_tlv(MLE_TYPE_ROUTE, mle_msg->data_ptr, mle_msg->data_length, &routing)) || (!mle_tlv_read_16_bit_tlv(MLE_TYPE_SRC_ADDRESS, mle_msg->data_ptr, mle_msg->data_length, &shortAddress))) { return ; } uint8_t linkMargin = thread_compute_link_margin(mle_msg->dbm); switch (mle_msg->message_type) { case MLE_COMMAND_ACCEPT: { tr_info("Accept (ROUTER handler)"); uint32_t mleFrameCounter; bool new_neigbour; uint16_t messageId = mle_tlv_validate_response(mle_msg->data_ptr, mle_msg->data_length); if (messageId == 0) { tr_debug("No matching challenge"); return; } /*Link accept command has an optional MLE Frame counter TLV, if this is not present use link layer frame counter TLV * as MLE frame counter TLV*/ if (!mle_tlv_read_32_bit_tlv(MLE_TYPE_MLE_FRAME_COUNTER, mle_msg->data_ptr, mle_msg->data_length, &mleFrameCounter)) { mleFrameCounter = llFrameCounter; } if (!thread_is_router_addr(address16)) { return; } //Allocate neighbor entry entry_temp = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur), mle_msg->packet_src_address, true, &new_neigbour); if (!entry_temp) { return; } if (security_headers->KeyIdMode == MAC_KEY_ID_MODE_SRC4_IDX) { thread_management_key_synch_req(cur->id, common_read_32_bit(security_headers->Keysource)); thread_key_guard_timer_reset(cur); } else { tr_error("Key ID Mode 2 not used; dropped."); return; } thread_neighbor_class_update_link(&cur->thread_info->neighbor_class, entry_temp->index, linkMargin, new_neigbour); thread_neighbor_last_communication_time_update(&cur->thread_info->neighbor_class, entry_temp->index); //Free Response mle_service_msg_free(messageId); entry_temp->mac16 = shortAddress; //when allocating neighbour entry, use MLE Frame counter if present to validate further advertisements from the neighbour mle_service_frame_counter_entry_add(cur->id, entry_temp->index, mleFrameCounter); uint32_t timeout_tlv; mle_tlv_info_t mle_tlv_info; if (mle_tlv_option_discover(mle_msg->data_ptr, mle_msg->data_length, MLE_TYPE_TIMEOUT, &mle_tlv_info) > 0) { timeout_tlv = common_read_32_bit(mle_tlv_info.dataPtr); } else { if (new_neigbour) { timeout_tlv = THREAD_DEFAULT_LINK_LIFETIME; } else { timeout_tlv = entry_temp->link_lifetime; } } mac_neighbor_table_neighbor_refresh(mac_neighbor_info(cur), entry_temp, timeout_tlv); if (thread_is_router_addr(shortAddress)) { entry_temp->connected_device = 1; } thread_neighbor_class_request_full_data_setup_set(&cur->thread_info->neighbor_class, entry_temp->index, true); if (mle_tlv_read_8_bit_tlv(MLE_TYPE_RSSI, mle_msg->data_ptr, mle_msg->data_length, &linkMarginfronNeigh)) { thread_routing_update_link_margin(cur, entry_temp->mac16, linkMargin, linkMarginfronNeigh); } 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, security_headers->KeyIndex, new_neigbour); //Copy Leader Data *cur->thread_info->thread_leader_data = leaderData; thread_bootstrap_update_ml16_address(cur, address16); thread_info(cur)->routerShortAddress = address16; thread_info(cur)->thread_attached_state = THREAD_STATE_CONNECTED_ROUTER; if (cur->thread_info->thread_leader_data->leaderRouterId == thread_router_id_from_addr(address16)) { if (thread_leader_service_thread_partitition_restart(interface_id, &routing)) { return; } thread_network_data_request_send(cur, mle_msg->packet_src_address, false); // force leader to learn active/pending sets from data response cur->thread_info->leader_synced = true; // Prevent the Leader to learn network data from data response cur->thread_info->networkDataRequested = false; tr_info("Router synch OK as Leader"); } else { // Decrement data version and request network data to be updated cur->thread_info->thread_leader_data->dataVersion--; cur->thread_info->thread_leader_data->stableDataVersion--; thread_network_data_request_send(cur, mle_msg->packet_src_address, true); // remove any existing rloc mapping in nvm thread_nvm_store_mleid_rloc_map_remove(); tr_info("Router synch OK as Router"); } thread_router_bootstrap_route_tlv_push(cur, routing.dataPtr, routing.tlvLen, linkMargin, entry_temp); thread_bootstrap_attached_ready(cur); } break; default: break; } } static int thread_router_synch_accept_request_build(protocol_interface_info_entry_t *cur, mle_message_t *mle_msg, uint16_t shortAddress, uint8_t *challenge, uint8_t chalLen, uint8_t *tlvReq, uint8_t tlvReqLen) { uint16_t length = 127; uint8_t i, tlvType; mle_message_timeout_params_t timeout; struct link_configuration *linkConfiguration; linkConfiguration = thread_joiner_application_get_config(cur->id); if (!linkConfiguration) { return -1; } tr_debug("MLE Router Link Synh response"); for (i = 0; i < tlvReqLen; i++) { tlvType = tlvReq[i]; if (tlvType == MLE_TYPE_NETWORK_DATA) { length += 2 + thread_network_data_tlv_size(cur, true); break; } } uint16_t bufId = mle_service_msg_allocate(cur->id, length, false, MLE_COMMAND_ACCEPT); if (bufId == 0) { return -1; } uint8_t *ptr = mle_service_get_data_pointer(bufId); ptr = thread_leader_data_tlv_write(ptr, cur); ptr = mle_tlv_write_version(ptr, cur->thread_info->version); 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, challenge, chalLen); ptr = mle_general_write_source_address(ptr, cur); for (i = 0; i < tlvReqLen; i++) { tlvType = tlvReq[i]; if (tlvType == MLE_TYPE_NETWORK_DATA) { ptr = thread_network_data_tlv_write(cur, ptr, true); } else if (tlvType == MLE_TYPE_ROUTE) { ptr = thread_route_option_write(cur, ptr); } else if (tlvType == MLE_TYPE_ADDRESS16) { ptr = mle_tlv_write_short_address(ptr, shortAddress); } else if (tlvType == MLE_TYPE_RSSI) { } } timeout.retrans_max = 0; timeout.timeout_init = 0; timeout.timeout_max = 0; timeout.delay = MLE_STANDARD_RESPONSE_DELAY; if (mle_service_update_length_by_ptr(bufId, ptr) != 0) { tr_debug("Buffer overflow at message write"); } //SET Destination address mle_service_set_msg_destination_address(bufId, mle_msg->packet_src_address); mle_service_set_msg_timeout_parameters(bufId, &timeout); uint32_t keySequence; thread_management_get_current_keysequence(cur->id, &keySequence); mle_service_msg_update_security_params(bufId, 5, 2, keySequence); mle_service_send_message(bufId); return 0; } static int thread_router_accept_to_endevice(protocol_interface_info_entry_t *cur, mle_message_t *mle_msg, uint8_t *challenge, uint8_t chalLen) { mle_message_timeout_params_t timeout; uint16_t bufId = mle_service_msg_allocate(cur->id, 64, false, MLE_COMMAND_ACCEPT); if (bufId == 0) { return -1; } if (addr_is_ipv6_multicast(mle_msg->packet_src_address)) { timeout.delay = MLE_STANDARD_RESPONSE_DELAY; } else { timeout.delay = MLE_NO_DELAY; } tr_debug("MLE Router Link Request response to REED or ED"); uint8_t *ptr = mle_service_get_data_pointer(bufId); timeout.retrans_max = 0; timeout.timeout_init = 0; timeout.timeout_max = 0; ptr = mle_tlv_write_version(ptr, cur->thread_info->version); 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, challenge, chalLen); ptr = thread_leader_data_tlv_write(ptr, cur); ptr = mle_general_write_source_address(ptr, cur); if (mle_service_update_length_by_ptr(bufId, ptr) != 0) { tr_debug("Buffer overflow at message write"); } //SET Destination address mle_service_set_msg_destination_address(bufId, mle_msg->packet_src_address); mle_service_set_msg_timeout_parameters(bufId, &timeout); uint32_t keySequence; thread_management_get_current_keysequence(cur->id, &keySequence); mle_service_msg_update_security_params(bufId, 5, 2, keySequence); mle_service_send_message(bufId); return 0; } static int thread_router_accept_request_build(protocol_interface_info_entry_t *cur, mle_message_t *mle_msg, uint16_t shortAddress, uint8_t *challenge, uint8_t chalLen, uint8_t type, bool rssi_tlv, uint8_t rssi) { mle_message_timeout_params_t timeout; uint16_t bufId; (void)shortAddress; uint32_t keySequence; if (type == MLE_COMMAND_ACCEPT) { timeout.retrans_max = 0; timeout.timeout_init = 0; timeout.timeout_max = 0; bufId = mle_service_msg_allocate(cur->id, 128, false, type); } else { timeout.retrans_max = 1; timeout.timeout_init = 2; timeout.timeout_max = 3; bufId = mle_service_msg_allocate(cur->id, 128, true, type); } if (bufId == 0) { return -1; } if (addr_is_ipv6_multicast(mle_msg->packet_dst_address)) { timeout.delay = MLE_HALF_SECOND_MAX_DELAY; } else { timeout.delay = MLE_NO_DELAY; } tr_debug("MLE Router Link Request 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 = thread_leader_data_tlv_write(ptr, cur); if (rssi_tlv) { ptr = mle_tlv_write_version(ptr, cur->thread_info->version); ptr = mle_tlv_rssi_tlv(ptr, rssi); if (type != MLE_COMMAND_ACCEPT) { uint8_t req_tlv = MLE_TYPE_RSSI; ptr = mle_tlv_req_tlv(ptr, &req_tlv, 1); } } if (challenge && chalLen) { 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, challenge, chalLen); } ptr = mle_general_write_source_address(ptr, cur); if (mle_service_update_length_by_ptr(bufId, ptr) != 0) { tr_debug("Buffer overflow at message write"); } mle_service_set_msg_destination_address(bufId, mle_msg->packet_src_address); mle_service_set_msg_timeout_parameters(bufId, &timeout); mle_service_send_message(bufId); return 0; } static uint16_t thread_tlv_len(protocol_interface_info_entry_t *cur, uint8_t *tlv_ptr, uint16_t tlv_len, uint8_t mode) { // Calculates the extra length for bigger TLVs it is assumed that smaller ones fit in extra reserved space uint16_t len = 0; for (uint16_t i = 0; i < tlv_len; i++) { if (*tlv_ptr == MLE_TYPE_ROUTE) { len += thread_route_option_size(cur); } else if (*tlv_ptr == MLE_TYPE_NETWORK_DATA) { len += 2 + thread_network_data_tlv_size(cur, mode & MLE_THREAD_REQ_FULL_DATA_SET); } else if (*tlv_ptr == MLE_TYPE_ADDRESS16) { //Short Address len += 4; } tlv_ptr++; } return len; } static uint8_t *thread_tlv_add(protocol_interface_info_entry_t *cur, uint8_t *ptr, uint8_t tlv_type, uint8_t mode) { struct link_configuration *linkConfiguration; linkConfiguration = thread_joiner_application_get_config(cur->id); if (!linkConfiguration) { return ptr; } switch (tlv_type) { case MLE_TYPE_SRC_ADDRESS: /* Add a MLE Source Address TLV */ ptr = mle_general_write_source_address(ptr, cur); /* TODO cur normally first in param list */ break; case MLE_TYPE_MODE: { /* Add a MLE Mode TLV */ *ptr++ = MLE_TYPE_MODE; *ptr++ = 1; *ptr++ = thread_mode_get_by_interface_ptr(cur); } break; case MLE_TYPE_TIMEOUT: /* Add a MLE Timeout TLV */ ptr = mle_tlv_write_timeout(ptr, cur->thread_info->host_link_timeout); break; case MLE_TYPE_LL_FRAME_COUNTER: /* Add a MLE Timeout TLV */ ptr = mle_general_write_link_layer_framecounter(ptr, cur); break; case MLE_TYPE_NWK_PARAM: /* Add a MLE Network Parameter TLV */ /* TODO */ break; case MLE_TYPE_MLE_FRAME_COUNTER: /* Add a MLE MLE Frame Counter TLV */ /* TODO */ /* Not ostensibly used in Thread */ break; case MLE_TYPE_ROUTE: /* Add a MLE Router Table TLV */ ptr = thread_route_option_write(cur, ptr); break; case MLE_TYPE_ADDRESS16: /* Add a MLE Short Address TLV */ /* TODO */ break; case MLE_TYPE_LEADER_DATA: ptr = thread_leader_data_tlv_write(ptr, cur); break; case MLE_TYPE_NETWORK_DATA: ptr = thread_network_data_tlv_write(cur, ptr, mode & MLE_THREAD_REQ_FULL_DATA_SET); break; case MLE_TYPE_SCAN_MASK: break; case MLE_TYPE_CONNECTIVITY: break; case MLE_TYPE_RSSI: break; default: /* No other TLV type can be requested */ /* LQI possibly - but not for thread */ break; } return ptr; } static int thread_child_update_response(protocol_interface_info_entry_t *cur, uint8_t *dst_address, uint8_t mode, uint16_t short_address, uint32_t timeout, mle_tlv_info_t *addressRegisterTlv, mle_tlv_info_t *tlvReq, mle_tlv_info_t *challengeTlv, uint64_t active_timestamp, uint64_t pending_timestamp) { uint16_t i; uint16_t len = 64; uint32_t keySequence; bool add_active_configuration = false; bool add_pending_configuration = false; uint64_t own_pending_timestamp = 0; uint8_t *ptr; if (!thread_info(cur)) { return -1; } link_configuration_s *link_configuration; link_configuration = thread_joiner_application_get_config(cur->id); if (!link_configuration) { return -1; } len += 10; // active timestamp tlv size len += thread_pending_timestamp_tlv_size(cur); if (!active_timestamp || active_timestamp != link_configuration->timestamp) { len += thread_active_operational_dataset_size(cur); add_active_configuration = true; } own_pending_timestamp = thread_joiner_application_pending_config_timestamp_get(cur->id); // if pending config is not in sync from requested device if (!pending_timestamp || (own_pending_timestamp && own_pending_timestamp != pending_timestamp)) { len += thread_pending_operational_dataset_size(cur); add_pending_configuration = true; } if (tlvReq && tlvReq->tlvLen) { mle_tlv_ignore(tlvReq->dataPtr, tlvReq->tlvLen, MLE_TYPE_LL_FRAME_COUNTER); len += thread_tlv_len(cur, tlvReq->dataPtr, tlvReq->tlvLen, mode); } if (addressRegisterTlv && addressRegisterTlv->tlvLen) { len += addressRegisterTlv->tlvLen; } uint16_t bufId = mle_service_msg_allocate(cur->id, len, false, MLE_COMMAND_CHILD_UPDATE_RESPONSE); if (bufId == 0) { return -1; } tr_debug("MLE Child synch response"); 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); ptr = mle_tlv_write_mode(ptr, mode); //Write Mode Allways if (addressRegisterTlv && addressRegisterTlv->tlvLen) { *ptr++ = MLE_TYPE_ADDRESS_REGISTRATION; *ptr++ = addressRegisterTlv->tlvLen; memcpy(ptr, addressRegisterTlv->dataPtr, addressRegisterTlv->tlvLen); ptr += addressRegisterTlv->tlvLen; } //Set SRC ptr = mle_general_write_source_address(ptr, cur); //SET leader data ptr = thread_leader_data_tlv_write(ptr, cur); if (timeout) { ptr = mle_tlv_write_timeout(ptr, timeout); } if (challengeTlv && challengeTlv->tlvLen) { ptr = mle_tlv_write_response(ptr, challengeTlv->dataPtr, challengeTlv->tlvLen); 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)); } if (tlvReq && tlvReq->tlvLen) { /* Go through the requested TLV mask */ for (i = 0; i < tlvReq->tlvLen; i++) { ptr = thread_tlv_add(cur, ptr, tlvReq->dataPtr[i], mode); } if (mle_tlv_requested(tlvReq->dataPtr, tlvReq->tlvLen, MLE_TYPE_ADDRESS16)) { ptr = mle_tlv_write_short_address(ptr, short_address); } if (mle_tlv_requested(tlvReq->dataPtr, tlvReq->tlvLen, MLE_TYPE_NETWORK_DATA)) { ptr = thread_active_timestamp_write(cur, ptr); ptr = thread_pending_timestamp_write(cur, ptr); if (add_active_configuration) { ptr = thread_active_operational_dataset_write(cur, ptr); } if (add_pending_configuration) { ptr = thread_pending_operational_dataset_write(cur, ptr); } } } 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; } static int mle_build_and_send_data_response_msg(protocol_interface_info_entry_t *cur, uint8_t *dst_address, uint8_t *data_ptr, uint16_t data_len, mle_tlv_info_t *request_tlv, uint8_t mode) { uint16_t length = 64 + 20 + 4; // source address 4 bytes uint8_t *ptr; uint_fast16_t i; uint64_t active_timestamp = 0; uint64_t pending_timestamp = 0; bool add_active_configuration = false; bool add_pending_configuration = false; bool active_timestamp_present_in_request = false; bool pending_timestamp_present_in_request = false; uint64_t own_pending_timestamp = 0; link_configuration_s *link_configuration; link_configuration = thread_joiner_application_get_config(cur->id); if (!link_configuration) { return 0; } active_timestamp_present_in_request = mle_tlv_read_64_bit_tlv(MLE_TYPE_ACTIVE_TIMESTAMP, data_ptr, data_len, &active_timestamp); pending_timestamp_present_in_request = mle_tlv_read_64_bit_tlv(MLE_TYPE_PENDING_TIMESTAMP, data_ptr, data_len, &pending_timestamp); length += thread_pending_timestamp_tlv_size(cur); if (!active_timestamp_present_in_request || active_timestamp != link_configuration->timestamp) { length += thread_active_operational_dataset_size(cur); add_active_configuration = true; } own_pending_timestamp = thread_joiner_application_pending_config_timestamp_get(cur->id); // if pending config is not in sync from requested device if (!pending_timestamp_present_in_request || (own_pending_timestamp && own_pending_timestamp != pending_timestamp)) { length += thread_pending_operational_dataset_size(cur); add_pending_configuration = true; } // Leader data is added by default mle_tlv_ignore(request_tlv->dataPtr, request_tlv->tlvLen, MLE_TYPE_LEADER_DATA); length += thread_tlv_len(cur, request_tlv->dataPtr, request_tlv->tlvLen, mode); //link metrics info length += thread_lowpower_link_metrics_length(cur, dst_address); uint16_t bufId = mle_service_msg_allocate(cur->id, length, false, MLE_COMMAND_DATA_RESPONSE); if (bufId == 0) { return -1; } tr_debug("Send MLE data response, %s mode=%x", trace_ipv6(dst_address), mode); ptr = mle_service_get_data_pointer(bufId); ptr = thread_leader_data_tlv_write(ptr, cur); /* Go through the requested TLV mask */ for (i = 0; i < request_tlv->tlvLen; i++) { ptr = thread_tlv_add(cur, ptr, request_tlv->dataPtr[i], mode); } ptr = thread_active_timestamp_write(cur, ptr); ptr = thread_pending_timestamp_write(cur, ptr); ptr = mle_general_write_source_address(ptr, cur); if (add_active_configuration) { ptr = thread_active_operational_dataset_write(cur, ptr); } if (add_pending_configuration) { ptr = thread_pending_operational_dataset_write(cur, ptr); } ptr = thread_lowpower_link_metrics_write(cur, dst_address, ptr); 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); //Set Security uint32_t keySequence; thread_management_get_current_keysequence(cur->id, &keySequence); mle_service_msg_update_security_params(bufId, 5, 2, keySequence); mle_service_send_message(bufId); return 0; } //this responds with zeros currently, needs to be updated after results are computed static int thread_attach_parent_response_build(protocol_interface_info_entry_t *cur, uint8_t *dstAddress, uint8_t *challenge, uint16_t chalLen, uint8_t linkMargin, uint8_t scanMask, uint8_t mode) { /** * This Message need to add Next Items: * - MLE_TYPE_SRC_ADDRESS * - MLE_TYPE_LEADER_DATA * - MLE_TYPE_LL_FRAME_COUNTER * - MLE_TYPE_MLE FRAME_COUNTER * - MLE_TYPE_CHALLENGE * - MLE_TYPE_RESPONSE * - MLE_TYPE_CONNECTIVITY * - MLE_TYPE_RSSI * - MLE_TYPE_VERSION */ mle_message_timeout_params_t timeout; uint16_t bufId = mle_service_msg_allocate(cur->id, 128, true, MLE_COMMAND_PARENT_RESPONSE); if (bufId == 0) { return -1; } tr_debug("Build MLE Parent response"); uint32_t keySequence; uint8_t *ptr = mle_service_get_data_pointer(bufId); //Set Security thread_management_get_current_keysequence(cur->id, &keySequence); mle_service_msg_update_security_params(bufId, 5, 2, keySequence); //SET Leader Data ptr = thread_leader_data_tlv_write(ptr, cur); // SET Connectivity TLV ptr = thread_connectivity_tlv_write(ptr, cur, mode); //SET LL Frame Counter 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)); //SET Source Address ptr = mle_general_write_source_address(ptr, cur); //SET Response if (challenge != NULL) { ptr = mle_tlv_write_response(ptr, challenge, chalLen); } //RSSI ptr = mle_tlv_rssi_tlv(ptr, linkMargin); //Set Version ptr = mle_tlv_write_version(ptr, cur->thread_info->version); if (mle_service_update_length_by_ptr(bufId, ptr) != 0) { tr_debug("Buffer overflow at message write"); } timeout.retrans_max = 1; timeout.timeout_init = 6; timeout.timeout_max = 6; switch (scanMask & 0xc0) { case 0x80: timeout.delay = MLE_HALF_SECOND_MAX_DELAY; break; default: timeout.delay = MLE_STANDARD_RESPONSE_DELAY; break; } //SET Destination address mle_service_set_msg_destination_address(bufId, dstAddress); mle_service_set_msg_timeout_parameters(bufId, &timeout); mle_service_send_message(bufId); return 0; } int thread_router_bootstrap_reset_child_info(protocol_interface_info_entry_t *cur, struct mac_neighbor_table_entry *child) { /* Cleanup occurs for /any/ link we lose to something that looks like a child address, * not just links that are /now/ our children. * Due to REED/partition transitions the address may not look like a current child address; * we could be holding a child entry for future repromotion to router with same ID. */ if (thread_is_router_addr(child->mac16) || child->mac16 >= 0xfffe) { return -1; } tr_debug("Child free %x", child->mac16); thread_dynamic_storage_child_info_clear(cur->id, child); /* As we are losing a link to a child address, we can assume that if we have an IP neighbour cache * mapping to that address, it is no longer valid. We must have been their parent, and they must be * finding a new parent, and hence a new 16-bit address. (Losing a link to a router address would not * invalidate our IP->16-bit mapping.) */ protocol_6lowpan_release_short_link_address_from_neighcache(cur, child->mac16); // If Child's RLOC16 appears in the Network Data send the RLOC16 to the Leader if (thread_network_data_services_registered(&cur->thread_info->networkDataStorage, child->mac16)) { tr_debug("Remove references to Child's RLOC16 from the Network Data"); thread_management_client_network_data_unregister(cur->id, child->mac16); } // Clear all (sleepy) child registrations to multicast groups thread_child_mcast_entries_remove(cur, child->mac64); return 0; } void thread_router_bootstrap_child_information_clear(protocol_interface_info_entry_t *cur) { /* make sure that the child info (from previous partition if any) is cleared if no router address is got from leader */ if (!cur->thread_info) { return; } // Remove mle neighbour entries for children in previous partition mac_neighbor_table_list_t *mac_table_list = &mac_neighbor_info(cur)->neighbour_list; ns_list_foreach_safe(mac_neighbor_table_entry_t, table_entry, mac_table_list) { if (table_entry->mac16 < 0xfffe && !thread_is_router_addr(table_entry->mac16)) { mac_neighbor_table_neighbor_remove(mac_neighbor_info(cur), table_entry); } } } static void thread_router_bootstrap_invalid_child_information_clear(protocol_interface_info_entry_t *cur, uint16_t router_rloc) { tr_debug("Thread Short address changed old: %x new: %x", cur->thread_info->routerShortAddress, router_rloc); mac_neighbor_table_list_t *mac_table_list = &mac_neighbor_info(cur)->neighbour_list; // scrub neighbours with child addresses that are not ours ns_list_foreach_safe(mac_neighbor_table_entry_t, table_entry, mac_table_list) { if (table_entry->mac16 < 0xfffe && !thread_is_router_addr(table_entry->mac16) && thread_router_addr_from_addr(table_entry->mac16) != router_rloc) { mac_neighbor_table_neighbor_remove(mac_neighbor_info(cur), table_entry); } } } static void thread_bootstrap_client_router_id_cb(int8_t interface_id, int8_t status, uint16_t router_rloc, const uint8_t router_mask_ptr[9]) { uint8_t ml16[16]; protocol_interface_info_entry_t *cur; uint8_t parent_router_id = FAST_ROUTE_NO_ROUTE; cur = protocol_stack_interface_info_get_by_id(interface_id); tr_debug("RouterID CB"); if (!cur) { return; } if (!cur->thread_info->routerIdRequested) { return; } cur->thread_info->routerIdRequested = false; if (cur->thread_info->thread_device_mode != THREAD_DEVICE_MODE_ROUTER || cur->thread_info->leader_private_data) { // Test api can cause these states or some out of sync operation. tr_warn("Router address for non-REED"); return; } if (status < 0) { thread_bootstrap_router_id_get_fail(cur); return; } uint8_t routeId = *router_mask_ptr++; if (cur->thread_info->thread_endnode_parent) { parent_router_id = cur->thread_info->thread_endnode_parent->router_id; } thread_router_bootstrap_invalid_child_information_clear(cur, router_rloc); // Release network data from old address cur->thread_info->localServerDataBase.release_old_address = true; //ADD New ML16 // This should be used thread_bootstrap_update_ml16_address(cur, router_rloc); thread_clean_old_16_bit_address_based_addresses(cur); mac_helper_mac16_address_set(cur, router_rloc); cur->thread_info->routerShortAddress = router_rloc; memcpy(ml16, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, 8); memcpy(&ml16[8], ADDR_SHORT_ADR_SUFFIC, 6); common_write_16_bit(router_rloc, &ml16[14]); cur->global_address_available = true; //Register UL16 if_address_entry_t *def_address = addr_add(cur, ml16, 64, ADDR_SOURCE_UNKNOWN, 0xffffffff, 0xffffffff, true); if (!def_address) { return; } // /* XXX Is short_src_adr ever reset? Is it undefined if info not in msg? */ tr_debug("Route seq %d Router mask: %s", routeId, trace_array(router_mask_ptr, 8)); cur->thread_info->routing.router_id_sequence_valid = false; if (parent_router_id != FAST_ROUTE_NO_ROUTE) { thread_routing_force_next_hop(cur, routeId, router_mask_ptr, parent_router_id); } else { tr_warn("No parent information available, no initial route set."); thread_routing_update_id_set(cur, routeId, router_mask_ptr); } thread_bootstrap_attached_active_router(cur); } void thread_router_bootstrap_router_id_request(protocol_interface_info_entry_t *cur, uint8_t status) { int router_id_req_status; tr_debug("Router ID Request"); if (cur->thread_info->routerIdRequested) { tr_warn("Router ID already requested"); return; } router_id_req_status = thread_management_client_router_id_get(cur->id, cur->mac, cur->thread_info->routerShortAddress, thread_bootstrap_client_router_id_cb, status); tr_debug("RouterIDReq COAP ID=%d", router_id_req_status); if (router_id_req_status > 0) { cur->thread_info->routerIdRequested = true; } } static int mle_attach_child_id_response_build(protocol_interface_info_entry_t *cur, uint8_t *dstAddress, thread_pending_child_id_req_t *child_req, struct mac_neighbor_table_entry *neigh_info) { uint16_t len = 12 + 4; //Leader data and short address uint8_t *ptr; bool fullList = false; uint64_t pending_timestamp; struct link_configuration *linkConfiguration; linkConfiguration = thread_joiner_application_get_config(cur->id); if (!linkConfiguration) { return -1; } if (thread_neighbor_class_request_full_data_setup(&cur->thread_info->neighbor_class, neigh_info->index)) { fullList = true; } if (child_req->shortAddressReq) { //Short Address len += 4; } if (child_req->networkDataReq) { len += 2 + thread_network_data_tlv_size(cur, fullList); } if (child_req->routeReq) { len += thread_route_option_size(cur); } if (child_req->request_active_config || child_req->active_timestamp != linkConfiguration->timestamp) { len += thread_active_operational_dataset_size(cur); } //always put active timestamp len += 10; len += thread_pending_timestamp_tlv_size(cur); pending_timestamp = thread_joiner_application_pending_config_timestamp_get(cur->id); if (pending_timestamp && pending_timestamp != child_req->pending_timestamp) { len += thread_pending_operational_dataset_size(cur); } mle_message_timeout_params_t timeout; uint16_t bufId = mle_service_msg_allocate(cur->id, len, false, MLE_COMMAND_CHILD_ID_RESPONSE); if (bufId == 0) { return -1; } uint32_t keySequence; thread_management_get_current_keysequence(cur->id, &keySequence); mle_service_msg_update_security_params(bufId, 5, 2, keySequence); tr_debug("Send MLE Child ID response"); ptr = mle_service_get_data_pointer(bufId); ptr = thread_leader_data_tlv_write(ptr, cur); ptr = mle_general_write_source_address(ptr, cur); if (child_req->shortAddressReq) { ptr = mle_tlv_write_short_address(ptr, neigh_info->mac16); } if (child_req->routeReq) { ptr = thread_route_option_write(cur, ptr); } if (child_req->networkDataReq) { ptr = thread_network_data_tlv_write(cur, ptr, fullList); } if (child_req->active_timestamp != linkConfiguration->timestamp) { ptr = thread_active_operational_dataset_write(cur, ptr); } //always write active timestamp ptr = thread_active_timestamp_write(cur, ptr); if (pending_timestamp > 0 && pending_timestamp != child_req->pending_timestamp) { ptr = thread_pending_operational_dataset_write(cur, ptr); } ptr = thread_pending_timestamp_write(cur, ptr); if (mle_service_update_length_by_ptr(bufId, ptr) != 0) { tr_debug("Buffer overflow at message write"); } timeout.retrans_max = 0; timeout.timeout_init = 0; timeout.timeout_max = 0; timeout.delay = MLE_NO_DELAY; mle_service_set_msg_destination_address(bufId, dstAddress); mle_service_set_msg_timeout_parameters(bufId, &timeout); mle_service_send_message(bufId); return 0; } int thread_router_bootstrap_child_max_timeout_get(protocol_interface_info_entry_t *cur, uint32_t *max_child_timeout) { uint16_t router_address = thread_info(cur)->routerShortAddress; uint32_t max_timeout = 0; if (router_address >= 0xfffe) { router_address = mac_helper_mac16_address_get(cur); } if (router_address & THREAD_CHILD_MASK) { return -1; //I am child } if (router_address >= 0xfffe) { return -1; } mac_neighbor_table_list_t *mac_table_list = &mac_neighbor_info(cur)->neighbour_list; ns_list_foreach(mac_neighbor_table_entry_t, cur_entry, mac_table_list) { if (thread_router_addr_from_addr(cur_entry->mac16) == router_address && !cur_entry->ffd_device && cur_entry->lifetime > max_timeout) { max_timeout = cur_entry->lifetime; } } *max_child_timeout = max_timeout; return 0; } uint16_t thread_router_bootstrap_child_count_get(protocol_interface_info_entry_t *cur) { uint16_t child_count = 0; uint16_t router_address = thread_info(cur)->routerShortAddress; if (router_address >= 0xfffe) { router_address = mac_helper_mac16_address_get(cur); } if (router_address & THREAD_CHILD_MASK) { return 0; //I am child } if (router_address >= 0xfffe) { return 0; } mac_neighbor_table_list_t *mac_table_list = &mac_neighbor_info(cur)->neighbour_list; ns_list_foreach(mac_neighbor_table_entry_t, cur_entry, mac_table_list) { if (thread_router_addr_from_addr(cur_entry->mac16) == router_address) { child_count++; } } return child_count; } static uint16_t thread_router_bootstrap_child_address_generate(protocol_interface_info_entry_t *cur) { mac_neighbor_table_list_t *mac_table_list = &mac_neighbor_info(cur)->neighbour_list; if (thread_router_bootstrap_child_count_get(cur) >= cur->thread_info->maxChildCount) { tr_info("Maximum count %d reached", cur->thread_info->maxChildCount); return 0xfffe; } bool address_allocated = false; for (uint16_t i = 0; i < cur->thread_info->maxChildCount; i++) { address_allocated = false; cur->thread_info->lastAllocatedChildAddress = (cur->thread_info->lastAllocatedChildAddress % THREAD_MAX_CHILD_ID_COUNT) + 1; ns_list_foreach(mac_neighbor_table_entry_t, cur_entry, mac_table_list) { if ((cur_entry->mac16 & THREAD_CHILD_MASK) == cur->thread_info->lastAllocatedChildAddress) { address_allocated = true; break; } } if (!address_allocated) { break; } } if (address_allocated) { // all possible addresses already allocated return 0xfffe; } return ((mac_helper_mac16_address_get(cur) & THREAD_ROUTER_MASK) | cur->thread_info->lastAllocatedChildAddress); } static bool thread_child_id_request(protocol_interface_info_entry_t *cur, struct mac_neighbor_table_entry *entry_temp) { //Remove All Short address links from router if (entry_temp->mac16 < 0xfffe) { protocol_6lowpan_release_short_link_address_from_neighcache(cur, entry_temp->mac16); } //allocate child address if current is router, 0xffff or not our child if (!thread_addr_is_child(mac_helper_mac16_address_get(cur), entry_temp->mac16)) { entry_temp->mac16 = thread_router_bootstrap_child_address_generate(cur); } if (entry_temp->mac16 >= 0xfffe) { return false; } // Store this child info to the NVM thread_dynamic_storage_child_info_store(cur, entry_temp); //} return true; } void thread_router_bootstrap_child_id_handler(protocol_interface_info_entry_t *cur) { thread_pending_child_id_req_t *req; bool new_neigbour = false; if (cur->thread_info->thread_attached_state == THREAD_STATE_CONNECTED) { if (!cur->thread_info->routerIdRequested) { tr_info("Upgrade REED to Router"); thread_router_bootstrap_router_id_request(cur, THREAD_COAP_STATUS_TLV_HAVE_CHILD_ID_REQUEST); } return; } req = thread_child_id_request_entry_get_from_the_list(cur); if (!req) { tr_debug("Child Id Req pending list empty"); return; } if (cur->thread_info->thread_attached_state != THREAD_STATE_CONNECTED_ROUTER) { // We are not router can not process goto free_request; } uint8_t ll64[16]; //set LL64 destination address memcpy(ll64, ADDR_LINK_LOCAL_PREFIX, 8); memcpy(&ll64[8], req->euid64, 8); ll64[8] ^= 2; //Allocate entry mac_neighbor_table_entry_t *entry_temp = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur), ll64, true, &new_neigbour); if (!entry_temp) { //Send link reject thread_link_reject_send(cur, ll64); goto free_request; } thread_neighbor_class_update_link(&cur->thread_info->neighbor_class, entry_temp->index, req->linkMargin, new_neigbour); thread_neighbor_last_communication_time_update(&cur->thread_info->neighbor_class, entry_temp->index); // If mac frame couter is less than previous we need to leave the old one //Update or set neigh info mac_neighbor_table_neighbor_refresh(mac_neighbor_info(cur), entry_temp, req->timeout); mle_mode_parse_to_mac_entry(entry_temp, req->mode); thread_neighbor_class_mode_parse_to_entry(&cur->thread_info->neighbor_class, entry_temp->index, req->mode); entry_temp->connected_device = 1; mle_service_frame_counter_entry_add(cur->id, entry_temp->index, req->mleFrameCounter); if (req->shortAddressReq) { if (!thread_child_id_request(cur, entry_temp)) { mac_neighbor_table_neighbor_remove(mac_neighbor_info(cur), entry_temp); thread_link_reject_send(cur, ll64); goto free_request; } } if (new_neigbour) { mlme_device_descriptor_t device_desc; mac_helper_device_description_write(cur, &device_desc, entry_temp->mac64, entry_temp->mac16, req->frameCounter, false); mac_helper_devicetable_set(&device_desc, cur, entry_temp->index, req->keyId, new_neigbour); } else { // in get response handler this will update the short address from MLE table mlme_get_t get_req; get_req.attr = macDeviceTable; get_req.attr_index = entry_temp->index; cur->mac_api->mlme_req(cur->mac_api, MLME_GET, &get_req); } //Register MLEID if RRFD device if (!entry_temp->ffd_device) { uint8_t tempIPv6Address[16]; memcpy(tempIPv6Address, thread_info(cur)->threadPrivatePrefixInfo.ulaPrefix, 8); memcpy(&tempIPv6Address[8], req->eiid, 8); thread_neighbor_class_add_mleid(&cur->thread_info->neighbor_class, entry_temp->index, req->eiid); tr_debug("Register %s", trace_ipv6(tempIPv6Address)); //Register GP --> 16 thread_nd_address_registration(cur, tempIPv6Address, entry_temp->mac16, cur->mac_parameters->pan_id, entry_temp->mac64, NULL); } mle_attach_child_id_response_build(cur, ll64, req, entry_temp); free_request: ns_dyn_mem_free(req); thread_bootstrap_child_id_request(cur); } static void thread_address_registration_tlv_parse(uint8_t *ptr, uint16_t data_length, protocol_interface_info_entry_t *cur, uint16_t mac16, uint8_t *mac64) { lowpan_context_t *ctx; uint8_t tempIPv6Address[16]; uint8_t ctxId; bool new_neighbour_created; thread_child_mcast_entries_remove(cur, mac64); while (data_length) { //Read ctxId = *ptr++; if (ctxId & 0x80) { ctxId &= 0x0f; ctx = lowpan_contex_get_by_id(&cur->lowpan_contexts, ctxId); if (ctx) { memcpy(tempIPv6Address, ctx->prefix, 8); memcpy(&tempIPv6Address[8], ptr, 8); tr_debug("Register %s", trace_ipv6(tempIPv6Address)); //Register GP --> 16 int retVal = thread_nd_address_registration(cur, tempIPv6Address, mac16, cur->mac_parameters->pan_id, mac64, &new_neighbour_created); thread_extension_address_registration(cur, tempIPv6Address, mac64, new_neighbour_created, retVal == -2); } else { tr_debug("No Context %u", ctxId); } ptr += 8; data_length -= 9; } else { tr_debug("Register %s", trace_ipv6(ptr)); if (addr_is_ipv6_multicast(ptr)) { // Register multicast address (link-local & higher) if (addr_ipv6_multicast_scope(ptr) >= IPV6_SCOPE_LINK_LOCAL) { addr_add_group(cur, ptr); if (thread_child_mcast_entry_get(cur, ptr, mac64)) { tr_debug("Added sleepy multicast registration entry."); } } } else { //Register GP --> 16 int retVal = thread_nd_address_registration(cur, ptr, mac16, cur->mac_parameters->pan_id, mac64, &new_neighbour_created); thread_extension_address_registration(cur, ptr, mac64, new_neighbour_created, retVal == -2); } ptr += 16; data_length -= 17; } } } void thread_router_bootstrap_mle_receive_cb(int8_t interface_id, mle_message_t *mle_msg, mle_security_header_t *security_headers) { (void) interface_id; thread_leader_data_t leaderData; mac_neighbor_table_entry_t *entry_temp; bool rssiTLV; bool leaderDataReceived; protocol_interface_info_entry_t *cur = mle_msg->interface_ptr; //TLV REQUSTED if (mle_tlv_type_requested(MLE_TYPE_RSSI, mle_msg->data_ptr, mle_msg->data_length)) { rssiTLV = true; } else { rssiTLV = false; } if (thread_leader_data_parse(mle_msg->data_ptr, mle_msg->data_length, &leaderData)) { leaderDataReceived = true; } else { leaderDataReceived = false; } uint8_t linkMargin = thread_compute_link_margin(mle_msg->dbm); tr_debug("Thread MLE REED Handler"); switch (mle_msg->message_type) { case MLE_COMMAND_PARENT_REQUEST: { uint8_t scanMask, mode; uint16_t version; mle_tlv_info_t challengeTlv; tr_info("Recv MLE Parent Request"); if (cur->nwk_bootstrap_state != ER_BOOTSRAP_DONE && cur->nwk_bootstrap_state != ER_MLE_ATTACH_READY) { // If own attach is ongoing, do not process parent request return; } if (security_headers->KeyIdMode != MAC_KEY_ID_MODE_SRC4_IDX) { tr_debug("Wrong key mode %u ", security_headers->KeyIdMode); return; } if (!thread_router_bootstrap_routing_allowed(cur)) { tr_debug("R bit is off in security policy; drop packet"); return; } if (thread_am_reed(cur)) { // If we are in REED mode and receive PARENT_REQ from our parent, don't send response. if (thread_router_parent_address_check(cur, mle_msg->packet_src_address)) { tr_debug("Drop PARENT_REQ from own parent"); return; } } // parent request received entry_temp = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur), mle_msg->packet_src_address, false, NULL); if (entry_temp) { //Set MAC modes mle_mode_parse_to_mac_entry(entry_temp, (MLE_FFD_DEV | MLE_RX_ON_IDLE)); thread_neighbor_class_mode_parse_to_entry(&cur->thread_info->neighbor_class, entry_temp->index, MLE_THREAD_REQ_FULL_DATA_SET); thread_neighbor_class_update_link(&cur->thread_info->neighbor_class, entry_temp->index, linkMargin, false); thread_neighbor_last_communication_time_update(&cur->thread_info->neighbor_class, entry_temp->index); } if (!entry_temp) { if (!mle_tlv_read_8_bit_tlv(MLE_TYPE_MODE, mle_msg->data_ptr, mle_msg->data_length, &mode)) { tr_debug("NO Mode"); return; } // New child trying to attach check if we have room if (mle_service_interface_tx_queue_size(cur->id) > THREAD_MAX_PARALLEL_MLE_PARENT_REQUEST) { tr_info("Drop MLE message: too many simultaneous MLE messages"); return; } if (mle_class_free_entry_count_get(cur) < 1) { tr_info("Drop MLE message: no space left in the MLE table"); return; } if ((mode & MLE_DEV_MASK) == MLE_RFD_DEV && mle_class_rfd_entry_count_get(cur) >= THREAD_MAX_MTD_CHILDREN) { tr_info("Drop MLE message: maximum MTD child count reached."); return; } if (!(mode & MLE_RX_ON_IDLE) && mle_class_sleepy_entry_count_get(cur) >= THREAD_MAX_SED_CHILDREN) { tr_info("Drop MLE message: maximum SED child count reached."); return; } if (cur->thread_info->thread_attached_state == THREAD_STATE_CONNECTED_ROUTER && thread_router_bootstrap_child_count_get(cur) >= cur->thread_info->maxChildCount) { tr_info("Drop MLE message: maximum Child count reached (%d)", cur->thread_info->maxChildCount); return; } } if (thread_route_ready_to_leader(cur) != 0) { tr_debug("Leader Path infinite"); return; } /** * MLE attached First packet Mandatory TLV:s are validated at this function * - MLE_TYPE_SCAN_MASK * - MLE_TYPE_CHALLENGE * - MLE_TYPE_VERSION */ //Validate Mask if (!mle_tlv_read_8_bit_tlv(MLE_TYPE_SCAN_MASK, mle_msg->data_ptr, mle_msg->data_length, &scanMask)) { tr_debug("NO Scan mask"); return; } if (!mle_tlv_read_8_bit_tlv(MLE_TYPE_MODE, mle_msg->data_ptr, mle_msg->data_length, &mode)) { tr_debug("NO Mode"); return; } if (!thread_scan_mask_validation(cur, scanMask)) { tr_debug("Not my type"); return; } if (cur->thread_info->thread_attached_state == THREAD_STATE_CONNECTED && 31 < thread_routing_count_active_routers(&cur->thread_info->routing)) { tr_info("No possibility to upgrade to router"); return; } if (!mle_tlv_read_16_bit_tlv(MLE_TYPE_VERSION, mle_msg->data_ptr, mle_msg->data_length, &version)) { tr_debug("NO version"); return; } //Read Challenge for Build Response if (!mle_tlv_read_tlv(MLE_TYPE_CHALLENGE, mle_msg->data_ptr, mle_msg->data_length, &challengeTlv)) { tr_debug("No challenge"); return; } if (thread_attach_parent_response_build(cur, mle_msg->packet_src_address, challengeTlv.dataPtr, challengeTlv.tlvLen, linkMargin, scanMask, mode) == 0) { tr_debug("Send MLE Parent response"); } break; } case MLE_COMMAND_CHILD_ID_REQUEST: tr_info("Recv MLE Child ID Request from %s", trace_ipv6(mle_msg->packet_src_address)); if (cur->nwk_bootstrap_state != ER_BOOTSRAP_DONE && cur->nwk_bootstrap_state != ER_MLE_ATTACH_READY) { // If own attach is ongoing, do not process child ID req return; } if (!thread_router_bootstrap_routing_allowed(cur)) { tr_debug("R bit is off in security policy; drop packet"); return; } // If we are in REED mode and receive child ID request from our parent, call connection error. if (thread_am_reed(cur)) { if (thread_router_parent_address_check(cur, mle_msg->packet_src_address)) { tr_debug("Child ID req from own parent -> connection error"); entry_temp = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur), mle_msg->packet_src_address, false, NULL); if (entry_temp) { mac_neighbor_table_neighbor_remove(mac_neighbor_info(cur), entry_temp); } thread_bootstrap_connection_error(cur->id, CON_PARENT_CONNECT_DOWN, NULL); return; } } if (security_headers->KeyIdMode != MAC_KEY_ID_MODE_SRC4_IDX) { tr_debug("Wrong key mode %u ", security_headers->KeyIdMode); return; } else { thread_pending_child_id_req_t *id_req; uint8_t tlvType; mle_tlv_info_t tlvRequest, addressRegisteredTlv; uint8_t *t_ptr; uint16_t messageId = mle_tlv_validate_response(mle_msg->data_ptr, mle_msg->data_length); if (messageId == 0) { tr_debug("No matching challenge"); return; } /* Perform key synchronization */ thread_management_key_synch_req(cur->id, common_read_32_bit(security_headers->Keysource)); //Free Response mle_service_msg_free(messageId); mle_msg->packet_src_address[8] ^= 2; id_req = thread_child_id_request_entry_get(cur, (mle_msg->packet_src_address + 8)); mle_msg->packet_src_address[8] ^= 2; if (!id_req) { tr_debug("No room for child id req"); return; } /** * MLE attached Tree packet Mandatory TLV:s are validated at this function * - MLE_TYPE_TIMEOUT * - MLE_TYPE_MODE * - MLE_TYPE_LL_FRAME_COUNTER * - MLE_TYPE_TLV_REQUEST * - MLE_TYPE_VERSION * Optional: * - MLE_TYPE_ADDRESS_REGISTRATION * - MLE_TYPE_MLE_FRAME_COUNTER */ if ((!mle_tlv_read_32_bit_tlv(MLE_TYPE_TIMEOUT, mle_msg->data_ptr, mle_msg->data_length, &id_req->timeout)) || (!mle_tlv_read_8_bit_tlv(MLE_TYPE_MODE, mle_msg->data_ptr, mle_msg->data_length, &id_req->mode)) || (!mle_tlv_read_16_bit_tlv(MLE_TYPE_VERSION, mle_msg->data_ptr, mle_msg->data_length, &id_req->version)) || (!mle_tlv_read_32_bit_tlv(MLE_TYPE_LL_FRAME_COUNTER, mle_msg->data_ptr, mle_msg->data_length, &id_req->frameCounter)) || (!mle_tlv_read_tlv(MLE_TYPE_TLV_REQUEST, mle_msg->data_ptr, mle_msg->data_length, &tlvRequest))) { thread_child_id_request_entry_remove(cur, id_req); tr_debug("Illegal child id req"); 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, &id_req->mleFrameCounter)) { id_req->mleFrameCounter = id_req->frameCounter; } t_ptr = tlvRequest.dataPtr; for (uint8_t i = 0; i < tlvRequest.tlvLen; i++) { tlvType = *t_ptr++; if (tlvType == MLE_TYPE_ADDRESS16) { id_req->shortAddressReq = true; } else if (tlvType == MLE_TYPE_NETWORK_DATA) { id_req->networkDataReq = true; } else if (tlvType == MLE_TYPE_ROUTE) { id_req->routeReq = true; } } if (mle_tlv_read_tlv(MLE_TYPE_ADDRESS_REGISTRATION, mle_msg->data_ptr, mle_msg->data_length, &addressRegisteredTlv)) { uint8_t context; t_ptr = addressRegisteredTlv.dataPtr; context = *t_ptr++; if (context & 0x80) { context &= 0x0f; if (addressRegisteredTlv.tlvLen == 9 && (context == 0)) { memcpy(id_req->eiid, t_ptr, 8); } } else { t_ptr += 8; memcpy(id_req->eiid, t_ptr, 8); } } if (!(id_req->mode & MLE_FFD_DEV) && addressRegisteredTlv.tlvLen == 0) { tr_debug("No address registration TLV in MTD child id request"); thread_child_id_request_entry_remove(cur, id_req); return; } id_req->keyId = security_headers->KeyIndex; id_req->keySeq = common_read_32_bit(security_headers->Keysource); id_req->linkMargin = linkMargin; id_req->active_timestamp = 0; id_req->request_active_config = false; if (!mle_tlv_read_64_bit_tlv(MLE_TYPE_ACTIVE_TIMESTAMP, mle_msg->data_ptr, mle_msg->data_length, &id_req->active_timestamp)) { id_req->request_active_config = true; } if (!mle_tlv_read_64_bit_tlv(MLE_TYPE_PENDING_TIMESTAMP, mle_msg->data_ptr, mle_msg->data_length, &id_req->pending_timestamp)) { id_req->pending_timestamp = 0; } //Push Event to queue if (thread_bootstrap_child_id_request(cur) != 0) { thread_child_id_request_entry_remove(cur, id_req); } break; } case MLE_COMMAND_REQUEST: { uint16_t shortAddress, version; mle_tlv_info_t addressRegisteredTlv, requestTlv, challengeTlv; uint8_t response_type = MLE_COMMAND_ACCEPT_AND_REQUEST; //Router attachment tr_info("Recv Router Link request"); if (!thread_attach_active_router(cur)) { return; //Do respond until we are reed } if (!mle_tlv_read_tlv(MLE_TYPE_CHALLENGE, mle_msg->data_ptr, mle_msg->data_length, &challengeTlv)) { 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_tlv(MLE_TYPE_TLV_REQUEST, mle_msg->data_ptr, mle_msg->data_length, &requestTlv)) { /** * This cuold cause problem if any dummy coder add this to message * Validate is it REED or End device message link synch Request * - MLE_TYPE_SRC_ADDRESS * - MLE_TYPE_VERSION * - MLE_TYPE_CHALLENGE */ if (mle_tlv_read_16_bit_tlv(MLE_TYPE_SRC_ADDRESS, mle_msg->data_ptr, mle_msg->data_length, &shortAddress)) { //Correct TLV's lets response if (!thread_is_router_addr(shortAddress)) { if (leaderDataReceived && thread_partition_match(cur, &leaderData)) { //REED or end device send response thread_router_accept_to_endevice(cur, mle_msg, challengeTlv.dataPtr, challengeTlv.tlvLen); } else { tr_debug("Drop Link Request from REED/ED in a different partition."); // Do nothing... ignore. } } } return; } entry_temp = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur), mle_msg->packet_src_address, false, NULL); if (!mle_tlv_read_16_bit_tlv(MLE_TYPE_SRC_ADDRESS, mle_msg->data_ptr, mle_msg->data_length, &shortAddress)) { /** * MLE Router Synch mandatory options are * - MLE_TYPE_VERSION * - MLE_TYPE_CHALLENGE * - MLE_TYPE_TLV_REQUEST * */ if (entry_temp && entry_temp->connected_device) { mac_neighbor_table_neighbor_refresh(mac_neighbor_info(cur), entry_temp, entry_temp->link_lifetime); thread_neighbor_class_update_link(&cur->thread_info->neighbor_class, entry_temp->index, linkMargin, false); thread_neighbor_last_communication_time_update(&cur->thread_info->neighbor_class, entry_temp->index); thread_router_synch_accept_request_build(cur, mle_msg, entry_temp->mac16, challengeTlv.dataPtr, challengeTlv.tlvLen, requestTlv.dataPtr, requestTlv.tlvLen); } } bool update_mac_mib = false; /** * MLE attached First packet Mandatory TLV:s are validated at this function * - MLE_TYPE_SRC_ADDRESS * - MLE_TYPE_CHALLENGE * - MLE_TYPE_LEADER_DATA * - MLE_TYPE_VERSION * - MLE_TYPE_TLV_REQUEST */ //Read Leader Data and verify connectivity if (!leaderDataReceived) { return; } //validate partition id if (!thread_partition_match(cur, &leaderData)) { tr_debug("Drop link request from wrong partition"); return; } if (entry_temp) { /*remove from child list when becoming router*/ // Was this previously our child? If yes, update. if ((entry_temp->mac16 & THREAD_CHILD_MASK) && thread_router_addr_from_addr(entry_temp->mac16) == cur->thread_info->routerShortAddress) { thread_dynamic_storage_child_info_clear(cur->id, entry_temp); protocol_6lowpan_release_short_link_address_from_neighcache(cur, entry_temp->mac16); } update_mac_mib = true; entry_temp->mac16 = shortAddress; // short address refreshed if (entry_temp->connected_device) { if (mle_tlv_read_tlv(MLE_TYPE_ADDRESS_REGISTRATION, mle_msg->data_ptr, mle_msg->data_length, &addressRegisteredTlv)) { if (!entry_temp->ffd_device) { thread_address_registration_tlv_parse(addressRegisteredTlv.dataPtr, addressRegisteredTlv.tlvLen, cur, entry_temp->mac16, entry_temp->mac64); } } mac_neighbor_table_neighbor_refresh(mac_neighbor_info(cur), entry_temp, entry_temp->link_lifetime); thread_neighbor_class_update_link(&cur->thread_info->neighbor_class, entry_temp->index, linkMargin, false); thread_neighbor_last_communication_time_update(&cur->thread_info->neighbor_class, entry_temp->index); if (!mle_neigh_entry_frame_counter_update(entry_temp, mle_msg->data_ptr, mle_msg->data_length, cur, security_headers->KeyIndex) && update_mac_mib) { //GET mlme_get_t get_req; get_req.attr = macDeviceTable; get_req.attr_index = entry_temp->index; cur->mac_api->mlme_req(cur->mac_api, MLME_GET, &get_req); } response_type = MLE_COMMAND_ACCEPT; } response_type = MLE_COMMAND_ACCEPT; } bool accept_link = false; if (entry_temp) { // We know already so we can accept accept_link = true; } else if (addr_is_ipv6_multicast(mle_msg->packet_dst_address)) { if (thread_bootstrap_link_create_check(cur, shortAddress) && thread_bootstrap_link_create_allowed(cur, shortAddress, mle_msg->packet_src_address)) { // Multicast and we can respond to message accept_link = true; } } else if (thread_bootstrap_link_create_check(cur, shortAddress)) { // Unicast request and we should make link accept_link = true; } if (accept_link) { if (!thread_is_router_addr(shortAddress)) { response_type = MLE_COMMAND_ACCEPT; } thread_router_accept_request_build(cur, mle_msg, shortAddress, challengeTlv.dataPtr, challengeTlv.tlvLen, response_type, rssiTLV, linkMargin); } break; } case MLE_COMMAND_ACCEPT_AND_REQUEST: { uint8_t linkMarginfronNeigh; uint16_t version, shortAddress; uint32_t llFrameCounter; mle_tlv_info_t requestTlv, challengeTlv; bool createNew, new_entry; tr_info("Recv Router Accept & Request"); uint16_t messageId = mle_tlv_validate_response(mle_msg->data_ptr, mle_msg->data_length); if (messageId == 0) { tr_debug("No matching challenge"); return; } if (!addr_is_ipv6_multicast(mle_service_get_msg_destination_address_pointer(messageId))) { //Free Response only if it is unicast mle_service_msg_free(messageId); } if ((!mle_tlv_read_tlv(MLE_TYPE_CHALLENGE, mle_msg->data_ptr, mle_msg->data_length, &challengeTlv)) || (!mle_tlv_read_16_bit_tlv(MLE_TYPE_VERSION, mle_msg->data_ptr, mle_msg->data_length, &version)) || (!mle_tlv_read_tlv(MLE_TYPE_TLV_REQUEST, mle_msg->data_ptr, mle_msg->data_length, &requestTlv)) || (!mle_tlv_read_8_bit_tlv(MLE_TYPE_RSSI, mle_msg->data_ptr, mle_msg->data_length, &linkMarginfronNeigh)) || (!mle_tlv_read_32_bit_tlv(MLE_TYPE_LL_FRAME_COUNTER, mle_msg->data_ptr, mle_msg->data_length, &llFrameCounter)) || (!mle_tlv_read_16_bit_tlv(MLE_TYPE_SRC_ADDRESS, mle_msg->data_ptr, mle_msg->data_length, &shortAddress))) { thread_link_reject_send(cur, mle_msg->packet_src_address); return; } /* Call to determine whether or not we should create a new link */ createNew = thread_bootstrap_link_create_check(cur, shortAddress); //Send Response entry_temp = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur), mle_msg->packet_src_address, createNew, &new_entry); if (entry_temp) { thread_neighbor_class_update_link(&cur->thread_info->neighbor_class, entry_temp->index, linkMargin, new_entry); thread_neighbor_last_communication_time_update(&cur->thread_info->neighbor_class, entry_temp->index); if (security_headers->KeyIdMode == MAC_KEY_ID_MODE_SRC4_IDX) { thread_management_key_synch_req(cur->id, common_read_32_bit(security_headers->Keysource)); } entry_temp->mac16 = shortAddress; 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, security_headers->KeyIndex, new_entry); uint32_t link_lifetime; if (new_entry) { link_lifetime = THREAD_DEFAULT_LINK_LIFETIME; } else { link_lifetime = entry_temp->link_lifetime; } mac_neighbor_table_neighbor_refresh(mac_neighbor_info(cur), entry_temp, link_lifetime); if (thread_is_router_addr(shortAddress)) { entry_temp->connected_device = 1; } thread_neighbor_class_request_full_data_setup_set(&cur->thread_info->neighbor_class, entry_temp->index, true); thread_routing_update_link_margin(cur, entry_temp->mac16, linkMargin, linkMarginfronNeigh); //Read Source address and Challenge mac_data_poll_protocol_poll_mode_decrement(cur); thread_router_accept_request_build(cur, mle_msg, entry_temp->mac16, challengeTlv.dataPtr, challengeTlv.tlvLen, MLE_COMMAND_ACCEPT, rssiTLV, linkMargin); blacklist_update(mle_msg->packet_src_address, true); } else { thread_link_reject_send(cur, mle_msg->packet_src_address); } break; } case MLE_COMMAND_CHILD_UPDATE_REQUEST: tr_info("Recv Router Child Update request"); { uint8_t mode; uint8_t status; uint32_t timeout = 0; uint64_t active_timestamp = 0; uint64_t pending_timestamp = 0; mle_tlv_info_t addressRegisterTlv = {0}; mle_tlv_info_t challengeTlv = {0}; mle_tlv_info_t tlv_req = {0}; entry_temp = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur), mle_msg->packet_src_address, false, NULL); if (mle_tlv_read_8_bit_tlv(MLE_TYPE_STATUS, mle_msg->data_ptr, mle_msg->data_length, &status)) { if (1 == status && thread_check_is_this_my_parent(cur, entry_temp)) { tr_debug("Parent has removed REED"); mac_neighbor_table_neighbor_remove(mac_neighbor_info(cur), entry_temp); thread_bootstrap_connection_error(cur->id, CON_PARENT_CONNECT_DOWN, NULL); } return; } mle_tlv_read_tlv(MLE_TYPE_CHALLENGE, mle_msg->data_ptr, mle_msg->data_length, &challengeTlv); if (!entry_temp) { tr_debug("Not Neighbor anymore"); thread_host_bootstrap_child_update_negative_response(cur, mle_msg->packet_src_address, &challengeTlv); return; } if (!mle_tlv_read_8_bit_tlv(MLE_TYPE_MODE, mle_msg->data_ptr, mle_msg->data_length, &mode)) { tr_debug("No Mode"); return; } //Keep alive updated thread_neighbor_class_update_link(&cur->thread_info->neighbor_class, entry_temp->index, linkMargin, false); thread_neighbor_last_communication_time_update(&cur->thread_info->neighbor_class, entry_temp->index); mle_mode_parse_to_mac_entry(entry_temp, mode); thread_neighbor_class_mode_parse_to_entry(&cur->thread_info->neighbor_class, entry_temp->index, mode); addressRegisterTlv.tlvType = MLE_TYPE_UNASSIGNED; mle_tlv_read_tlv(MLE_TYPE_ADDRESS_REGISTRATION, mle_msg->data_ptr, mle_msg->data_length, &addressRegisterTlv); mle_tlv_read_64_bit_tlv(MLE_TYPE_ACTIVE_TIMESTAMP, mle_msg->data_ptr, mle_msg->data_length, &active_timestamp); mle_tlv_read_64_bit_tlv(MLE_TYPE_PENDING_TIMESTAMP, mle_msg->data_ptr, mle_msg->data_length, &pending_timestamp); mle_tlv_read_tlv(MLE_TYPE_TLV_REQUEST, mle_msg->data_ptr, mle_msg->data_length, &tlv_req); if (addressRegisterTlv.tlvType == MLE_TYPE_ADDRESS_REGISTRATION && !entry_temp->ffd_device) { tr_debug("Register child address"); thread_address_registration_tlv_parse(addressRegisterTlv.dataPtr, addressRegisterTlv.tlvLen, cur, entry_temp->mac16, entry_temp->mac64); } if (mle_tlv_read_32_bit_tlv(MLE_TYPE_TIMEOUT, mle_msg->data_ptr, mle_msg->data_length, &timeout)) { tr_debug("Setting child timeout, value=%"PRIu32, timeout); mac_neighbor_table_neighbor_refresh(mac_neighbor_info(cur), entry_temp, timeout); } else { mac_neighbor_table_neighbor_refresh(mac_neighbor_info(cur), entry_temp, entry_temp->link_lifetime); } if (!leaderDataReceived) { tr_debug("Child synch req"); } tr_debug("Keep-Alive -->Respond Child"); //Response thread_child_update_response(cur, mle_msg->packet_src_address, mode, entry_temp->mac16, timeout, &addressRegisterTlv, &tlv_req, &challengeTlv, active_timestamp, pending_timestamp); } break; case MLE_COMMAND_UPDATE: tr_info("Recv Router Update"); break; case MLE_COMMAND_UPDATE_REQUEST: tr_info("Recv Router update Request"); break; case MLE_COMMAND_DATA_REQUEST: { mle_tlv_info_t requestTlv; tr_info("Recv Router Data Request"); entry_temp = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur), mle_msg->packet_src_address, false, NULL); if (!entry_temp || !mle_tlv_read_tlv(MLE_TYPE_TLV_REQUEST, mle_msg->data_ptr, mle_msg->data_length, &requestTlv)) { return; } uint8_t mode = mle_mode_write_from_mac_entry(entry_temp); /* check if thread neighbor class is not initialized */ if ((thread_neighbor_entry_linkmargin_get(&cur->thread_info->neighbor_class, entry_temp->index) == 0) && (thread_neighbor_last_communication_time_get(&cur->thread_info->neighbor_class, entry_temp->index) == 0)) { /* * Thread neighbor class is not yet initialized and we receive data_request from such child. * Always send full network data in this case */ mode |= MLE_THREAD_REQ_FULL_DATA_SET | MLE_THREAD_SECURED_DATA_REQUEST; } else { mode |= thread_neighbor_class_mode_write_from_entry(&cur->thread_info->neighbor_class, entry_temp->index); } thread_neighbor_class_update_link(&cur->thread_info->neighbor_class, entry_temp->index, linkMargin, false); thread_neighbor_last_communication_time_update(&cur->thread_info->neighbor_class, entry_temp->index); mle_build_and_send_data_response_msg(cur, mle_msg->packet_src_address, mle_msg->data_ptr, mle_msg->data_length, &requestTlv, mode); } break; default: tr_warn("Recv Router UNKNOWN message %d", mle_msg->message_type); break; } } static int8_t thread_router_bootstrap_synch_request_send(protocol_interface_info_entry_t *cur) { uint8_t req_tlv[3]; mle_message_timeout_params_t timeout; uint32_t keySequence; uint16_t buf_id = mle_service_msg_allocate(cur->id, 32, true, MLE_COMMAND_REQUEST); if (buf_id == 0) { return -1; } thread_management_get_current_keysequence(cur->id, &keySequence); mle_service_msg_update_security_params(buf_id, 5, 2, keySequence); mle_service_set_msg_destination_address(buf_id, ADDR_LINK_LOCAL_ALL_ROUTERS); req_tlv[0] = MLE_TYPE_ADDRESS16; req_tlv[1] = MLE_TYPE_ROUTE; uint8_t *ptr = mle_service_get_data_pointer(buf_id); ptr = mle_tlv_write_version(ptr, cur->thread_info->version); ptr = mle_tlv_req_tlv(ptr, req_tlv, 2); if (mle_service_update_length_by_ptr(buf_id, ptr) != 0) { tr_debug("Buffer overflow at message write"); } // timeout set to two seconds, no retries timeout.retrans_max = 1; timeout.timeout_init = 2; 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; } static int8_t thread_router_synch_new_router(protocol_interface_info_entry_t *cur, const uint8_t *destAddress) { mle_message_timeout_params_t timeout; uint32_t keySequence; if (thread_info(cur)) { if (!thread_info(cur)->thread_leader_data) { return -1; } } uint16_t bufId = mle_service_msg_allocate(cur->id, 64, true, MLE_COMMAND_REQUEST); if (bufId == 0) { return -1; } uint8_t req_tlv = MLE_TYPE_RSSI; thread_management_get_current_keysequence(cur->id, &keySequence); mle_service_msg_update_security_params(bufId, 5, 2, keySequence); mle_service_set_msg_destination_address(bufId, destAddress); uint8_t *ptr = mle_service_get_data_pointer(bufId); ptr = mle_tlv_write_version(ptr, cur->thread_info->version); ptr = thread_leader_data_tlv_write(ptr, cur); ptr = mle_general_write_source_address(ptr, cur); ptr = mle_tlv_req_tlv(ptr, &req_tlv, 1); if (!addr_is_ipv6_multicast(destAddress)) { timeout.retrans_max = 2; timeout.timeout_init = 2; timeout.timeout_max = 3; timeout.delay = MLE_STANDARD_RESPONSE_DELAY; } else { // Multicasts need transaction timer timeout.retrans_max = 1; timeout.timeout_init = 2; timeout.timeout_max = 3; timeout.delay = 0; } mle_service_set_packet_callback(bufId, thread_link_request_timeout); mle_service_set_msg_timeout_parameters(bufId, &timeout); if (mle_service_update_length_by_ptr(bufId, ptr) != 0) { tr_debug("Buffer overflow at message write"); } mle_service_send_message(bufId); return 0; } int thread_router_bootstrap_link_synch_start(protocol_interface_info_entry_t *cur) { //Send Link Request for if (thread_router_bootstrap_synch_request_send(cur) == 0) { //SET Router synch receiver handler mle_service_interface_receiver_handler_update(cur->id, thread_router_synch_receive_cb); cur->nwk_bootstrap_state = ER_MLE_SYNCH; return 0; } return -1; } bool thread_router_bootstrap_router_downgrade(protocol_interface_info_entry_t *cur) { uint8_t activeRouterCount; uint8_t goodLinks; if (cur->thread_info->thread_attached_state != THREAD_STATE_CONNECTED_ROUTER) { return false; } // if we are commissioner border router we have registered our address and cant upgrade or downgrade if (cur->thread_info->registered_commissioner.commissioner_valid && addr_get_entry(cur, cur->thread_info->registered_commissioner.border_router_address)) { return false; } // spec: Not be the Leader if (cur->thread_info->leader_private_data) { if (!thread_router_bootstrap_routing_allowed(cur)) { tr_debug("Cannot be leader anymore"); // Settings have changed, cannot be leader anymore thread_bootstrap_reset_restart(cur->id); } // Leader can not downgrade return false; } if (!thread_router_bootstrap_routing_allowed(cur)) { return true; } //spec: If the number of active Routers on the network exceeds ROUTER_DOWNGRADE_THRESHOLD activeRouterCount = thread_routing_count_active_routers(&cur->thread_info->routing); if (activeRouterCount <= cur->thread_info->routerSelectParameters.routerDowngradeThresHold) { return false; } uint_fast8_t asGood; goodLinks = thread_routing_count_neighbours_for_downgrade(&cur->thread_info->routing, &asGood); //spec: Have at least MIN_DOWNGRADE_NEIGHBORS neighbors in set M. if (goodLinks < MIN_DOWNGRADE_NEIGHBORS) { return false; } //spec: Have at least one neighbor that has as good or better quality links to all Routers in M. if (asGood == 0) { return false; } //spec: Not be the sole Border Router for a particular Provisioning Domain if (!ns_list_is_empty(&cur->thread_info->localServerDataBase.prefix_list)) { return false; } uint16_t child_count = thread_router_bootstrap_child_count_get(cur); //Cant downgrade if child count is larger than 3 times exceed routers if (child_count > (3 * (activeRouterCount - cur->thread_info->routerSelectParameters.routerDowngradeThresHold))) { return false; } tr_info("Reed downgrade:ChildCount %u Active Routers %u good links %u downgrade threshold %u", child_count, activeRouterCount, goodLinks, cur->thread_info->routerSelectParameters.routerDowngradeThresHold); return true; } bool thread_router_bootstrap_reed_upgrade(protocol_interface_info_entry_t *cur) { uint8_t activeRouterCount; if (!thread_router_bootstrap_routing_allowed(cur)) { return false; } if (thread_am_router(cur)) { return false; } // if we are commissioner border router we have registered our address and cant upgrade or downgrade if (cur->thread_info->registered_commissioner.commissioner_valid && addr_get_entry(cur, cur->thread_info->registered_commissioner.border_router_address)) { return false; } if (!cur->thread_info->routing.router_id_sequence_valid) { // In case routing information is not up-to-date yet... return false; } activeRouterCount = thread_routing_count_active_routers(&cur->thread_info->routing); if (activeRouterCount >= cur->thread_info->routerSelectParameters.routerUpgradeThresHold) { return false; } tr_info("Reed Upgrade:Active Routers %u upgrade threshold %u", activeRouterCount, cur->thread_info->routerSelectParameters.routerUpgradeThresHold); return true; } void thread_router_bootstrap_child_id_reject(protocol_interface_info_entry_t *cur) { thread_pending_child_id_req_t *req; req = thread_child_id_request_entry_get_from_the_list(cur); while (req) { tr_debug("Remove entry from list"); //Remove entry from list mac_neighbor_table_entry_t *neighbor = mac_neighbor_table_address_discover(mac_neighbor_info(cur), req->euid64, ADDR_802_15_4_LONG); if (neighbor) { mac_neighbor_table_neighbor_remove(mac_neighbor_info(cur), neighbor); } ns_dyn_mem_free(req); req = thread_child_id_request_entry_get_from_the_list(cur); } } void thread_router_bootstrap_active_router_attach(protocol_interface_info_entry_t *cur) { uint8_t *parent_mac_addr = NULL; arm_nwk_6lowpan_thread_test_print_routing_database(cur->id); uint16_t address16 = mac_helper_mac16_address_get(cur); cur->thread_info->thread_attached_state = THREAD_STATE_CONNECTED_ROUTER; if (cur->thread_info->thread_leader_data->leaderRouterId == address16 && !cur->thread_info->leader_private_data) { // Error we are set up as leader but no private data tr_error("Leader setup error"); } cur->lowpan_info |= INTERFACE_NWK_ROUTER_DEVICE; thread_routing_activate(&cur->thread_info->routing); thread_router_synch_new_router(cur, ADDR_LINK_LOCAL_ALL_ROUTERS); thread_bootstrap_ready(cur); thread_bootstrap_network_prefixes_process(cur); thread_nd_service_activate(cur->id); thread_router_bootstrap_mle_advertise(cur); if (cur->thread_info->thread_endnode_parent) { parent_mac_addr = cur->thread_info->thread_endnode_parent->mac64; } thread_nvm_store_link_info_write(parent_mac_addr, mac_helper_mac16_address_get(cur)); } static int thread_validate_own_routeid_from_new_mask(const uint8_t *master_router_id_mask, uint8_t router_id) { int ret_val = -1; if (bit_test(master_router_id_mask, router_id)) { ret_val = 0; } return ret_val; } int thread_router_bootstrap_route_tlv_push(protocol_interface_info_entry_t *cur, uint8_t *route_tlv, uint8_t route_len, uint8_t linkMargin, struct mac_neighbor_table_entry *entry) { (void) route_len; uint8_t route_id_seq; const uint8_t *router_id_mask, *route_data; route_id_seq = *route_tlv++; router_id_mask = route_tlv; route_tlv += 8; route_data = route_tlv; uint16_t mac16 = mac_helper_mac16_address_get(cur); if (!thread_is_router_addr(entry->mac16)) { // Received route tlv from non router ignore tr_info("drop route Processing from end device %x", entry->mac16); return 0; } if (thread_i_am_router(cur)) { thread_routing_info_t *routing = &cur->thread_info->routing; if (routing->router_id_sequence_valid && common_serial_number_greater_8(route_id_seq, routing->router_id_sequence)) { thread_routing_leader_connection_validate(cur->thread_info, routing->networkFragmentationTimer); routing->networkFragmentationTimer = 0; if (thread_validate_own_routeid_from_new_mask(router_id_mask, thread_router_id_from_addr(mac16)) != 0) { tr_debug("RouterID not valid any More"); thread_bootstrap_connection_error(cur->id, CON_ERROR_NETWORK_KICK, NULL); return 0; } } } else if (!thread_info(cur)->thread_endnode_parent || thread_info(cur)->thread_endnode_parent->shortAddress != entry->mac16) { return 0; } /* XXX Is short_src_adr ever reset? Is it undefined if info not in msg? */ /* Don't add routing link if MLE link is NOT bi-directional (i.e. we can only hear) */ if (entry->connected_device) { thread_routing_add_link(cur, entry->mac16, linkMargin, route_id_seq, router_id_mask, route_data, false); } return 0; } static void thread_bootstrap_client_router_id_release_cb(int8_t interface_id, int8_t status, uint16_t router_rloc, const uint8_t router_mask_ptr[9]) { protocol_interface_info_entry_t *cur; (void) status; (void)router_mask_ptr; (void)router_rloc; cur = protocol_stack_interface_info_get_by_id(interface_id); tr_debug("RouterID ReleaseCB"); if (!cur) { return; } if (!cur->thread_info->releaseRouterId) { return; } cur->thread_info->releaseRouterId = false; thread_bootstrap_router_id_release_ready(cur); return; } static uint32_t thread_reed_timeout_calculate(thread_router_select_t *routerSelect) { return randLIB_get_random_in_range(routerSelect->reedAdvertisementInterval, (routerSelect->reedAdvertisementInterval) + (routerSelect->reedAdvertisementJitterInterval)); } static int thread_reed_advertise(protocol_interface_info_entry_t *cur) { uint32_t keySequence; struct link_configuration *linkConfiguration; linkConfiguration = thread_joiner_application_get_config(cur->id); if (!linkConfiguration) { return -1; } if (!thread_info(cur)) { return -1; } // FED not allowed to send advertisements if (thread_info(cur)->thread_device_mode == THREAD_DEVICE_MODE_FULL_END_DEVICE) { return -1; } uint16_t bufId = mle_service_msg_allocate(cur->id, 16, false, MLE_COMMAND_ADVERTISEMENT); if (bufId == 0) { return -1; } tr_debug("MLE REED ADVERTISEMENT STARTED"); 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); /*Add Leader Data & Source Address TLVs */ ptr = thread_leader_data_tlv_write(ptr, cur); ptr = mle_general_write_source_address(ptr, cur); if (mle_service_update_length_by_ptr(bufId, ptr) != 0) { tr_debug("Buffer overflow at message write"); } mle_service_set_msg_destination_address(bufId, ADDR_LINK_LOCAL_ALL_NODES); mle_service_send_message(bufId); return 0; } static void thread_reed_advertisements_cb(void *arg) { if (!arg) { return; } protocol_interface_info_entry_t *cur = arg; cur->thread_info->routerSelectParameters.reedAdvertisementTimeout = NULL; if (cur->nwk_bootstrap_state != ER_BOOTSRAP_DONE && cur->nwk_bootstrap_state != ER_MLE_ATTACH_READY) { /* Own attach is ongoing, try to send advertisement after few seconds */ cur->thread_info->routerSelectParameters.reedAdvertisementTimeout = eventOS_timeout_ms(thread_reed_advertisements_cb, 2 * 1000, cur); return; } if (cur->thread_info->thread_attached_state == THREAD_STATE_CONNECTED && cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_ROUTER) { thread_reed_advertise(cur); thread_router_bootstrap_child_information_clear(cur); cur->thread_info->routerSelectParameters.reedAdvertisementTimeout = eventOS_timeout_ms(thread_reed_advertisements_cb, thread_reed_timeout_calculate(&cur->thread_info->routerSelectParameters) * 1000, cur); } } void thread_router_bootstrap_reed_advertisements_start(protocol_interface_info_entry_t *cur) { uint32_t timeout = THREAD_REED_ADVERTISEMENT_DELAY; if (!cur) { return; } eventOS_timeout_cancel(cur->thread_info->routerSelectParameters.reedAdvertisementTimeout); cur->thread_info->routerSelectParameters.reedAdvertisementTimeout = NULL; cur->thread_info->reedJitterTimer = thread_router_bootstrap_random_upgrade_jitter(); if (!thread_is_connected(cur)) { return; } if (cur->thread_info->releaseRouterId || thread_router_bootstrap_child_count_get(cur) == 0) { // If we dont have any children or are are downgrading send REED advertisement immediately timeout = 1; } cur->thread_info->routerSelectParameters.reedAdvertisementTimeout = eventOS_timeout_ms(thread_reed_advertisements_cb, timeout, cur); } void thread_router_bootstrap_reed_merge_advertisement(protocol_interface_info_entry_t *cur) { if (cur->thread_info->reedMergeAdvTimer > 1) { return; } thread_reed_advertise(cur); // 120s second timer reinitialised before next merge advertisement cur->thread_info->reedMergeAdvTimer = THREAD_REED_MERGE_ADVERTISEMENT_INTERVAL; } void thread_router_bootstrap_router_id_release(protocol_interface_info_entry_t *cur) { tr_debug("Router ID Release"); if (thread_management_client_router_id_release(cur->id, cur->mac, cur->thread_info->routerShortAddress, thread_bootstrap_client_router_id_release_cb) == 0) { // Release message sent succesfully cur->thread_info->routerShortAddress = 0xfffe; cur->thread_info->releaseRouterId = true; } } uint32_t thread_router_bootstrap_random_upgrade_jitter() { if (thread_router_selection_jitter <= 2) { return 1; } return randLIB_get_random_in_range(2, thread_router_selection_jitter); } void thread_router_bootstrap_timer(protocol_interface_info_entry_t *cur, uint32_t ticks) { thread_info_t *thread_info = cur->thread_info; if (thread_am_host(cur)) { return; } //Do we have a childs and we are REED state if (cur->thread_info->thread_attached_state == THREAD_STATE_CONNECTED && thread_router_bootstrap_child_count_get(cur)) { tr_debug("Trig quick router ID request"); thread_bootstrap_attched_upgrade_reed(cur); return; } if (thread_info->reedJitterTimer > ticks) { // Reed status is checked every random jitter values thread_info->reedJitterTimer -= ticks; } else { thread_info->reedJitterTimer = thread_router_bootstrap_random_upgrade_jitter(); if (thread_router_bootstrap_reed_upgrade(cur)) { //Check UpGrade possibility tr_debug("REED Upgrade to router"); thread_bootstrap_attched_upgrade_reed(cur); } if (thread_router_bootstrap_router_downgrade(cur)) { tr_debug("Router downgrade to REED"); thread_bootstrap_attached_downgrade_router(cur); } } if (cur->thread_info->reedMergeAdvTimer > ticks) { cur->thread_info->reedMergeAdvTimer -= ticks; } else { cur->thread_info->reedMergeAdvTimer = 0; } if (!thread_info->leader_private_data && thread_info->thread_attached_state == THREAD_STATE_CONNECTED_ROUTER) { // Non leader router checks if (thread_info->routing.activated) { if (thread_info->routing.networkFragmentationTimer++ >= thread_info->routing.networkIdTimeout) { tr_debug("Leader Connection Lost"); thread_bootstrap_connection_error(cur->id, CON_ERROR_NETWORK_REATTACH, NULL); } } else { tr_debug("Routing Disabled?"); } } if (thread_info->proactive_an_timer) { if (thread_info->proactive_an_timer > ticks) { thread_info->proactive_an_timer -= ticks; } else { thread_send_proactive_an(cur); thread_info->proactive_an_timer = 0; } } thread_leader_service_timer(cur, ticks); return; } void thread_router_bootstrap_advertiment_analyze(protocol_interface_info_entry_t *cur, uint8_t *src_address, struct mac_neighbor_table_entry *entry_temp, uint16_t shortAddress) { if (entry_temp) { if (thread_is_router_addr(shortAddress)) { entry_temp->link_lifetime = THREAD_DEFAULT_LINK_LIFETIME; entry_temp->link_lifetime++; } if (thread_is_router_addr(shortAddress)) { //Update MAC Security PIB table by get & set Operation mlme_get_t get_req; get_req.attr = macDeviceTable; get_req.attr_index = entry_temp->index; cur->mac_api->mlme_req(cur->mac_api, MLME_GET, &get_req); entry_temp->lifetime = entry_temp->link_lifetime; } } else { // if (!thread_attach_active_router(cur)) { // We are not router return; } if (!thread_is_router_addr(shortAddress)) { // Reed advertisement return; } if (thread_bootstrap_link_create_check(cur, shortAddress) && thread_bootstrap_link_create_allowed(cur, shortAddress, src_address)) { //Challenge new router neighbor tr_debug("Synch new neighbor"); thread_router_synch_new_router(cur, src_address); } } } static void thread_router_bootstrap_dhcp_server_any_cast_address_update(protocol_interface_info_entry_t *cur, uint16_t anycastAddress) { uint8_t ipv6_address[16]; thread_addr_write_mesh_local_16(ipv6_address, anycastAddress, cur->thread_info); tr_debug("generate DHCP anycast address %s", trace_ipv6(ipv6_address)); addr_add(cur, ipv6_address, 124, ADDR_SOURCE_THREAD_ALOC, 0xffffffff, 0, true); } static void thread_bootstrap_dhcp_anycast_address_generate(protocol_interface_info_entry_t *cur) { uint8_t ipv6_address[16]; thread_addr_write_mesh_local_16(ipv6_address, 0xfc00, cur->thread_info); addr_delete_matching(cur, ipv6_address, 124, ADDR_SOURCE_THREAD_ALOC); ns_list_foreach(thread_network_data_prefix_cache_entry_t, curPrefix, &cur->thread_info->networkDataStorage.localPrefixList) { // Go through all prefixes ns_list_foreach(thread_network_server_data_entry_t, curBorderRouter, &curPrefix->borderRouterList) { if (curBorderRouter->P_dhcp && curBorderRouter->routerID == cur->mac_parameters->mac_short_address) { // We host this dhcp service we must add anycast address ns_list_foreach(thread_network_data_context_entry_t, curRoute, &curPrefix->contextList) { // Search what is the context id for this prefix uint16_t anycastAddress = 0xfc00; anycastAddress |= curRoute->cid; thread_router_bootstrap_dhcp_server_any_cast_address_update(cur, anycastAddress); } } } } } static void thread_bootstrap_service_anycast_address_generate(protocol_interface_info_entry_t *cur) { uint8_t ipv6_address[16]; // Delete old address thread_addr_write_mesh_local_16(ipv6_address, 0xfc10, cur->thread_info); addr_delete_matching(cur, ipv6_address, 124, ADDR_SOURCE_THREAD_ALOC); ns_list_foreach(thread_network_data_service_cache_entry_t, curService, &cur->thread_info->networkDataStorage.service_list) { // Go through all prefixes ns_list_foreach(thread_network_data_service_server_entry_t, curServiceServer, &curService->server_list) { if (curServiceServer->router_id == cur->mac_parameters->mac_short_address) { // We host this service we must add anycast address // Search what is the context id for this prefix thread_addr_write_mesh_local_16(ipv6_address, 0xfc10 | curService->S_id, cur->thread_info); tr_debug("generate Service anycast address %s", trace_ipv6(ipv6_address)); addr_add(cur, ipv6_address, 124, ADDR_SOURCE_THREAD_ALOC/*?*/, 0xffffffff, 0, true); } } } } static void thread_router_bootstrap_commissioner_aloc_generate(protocol_interface_info_entry_t *cur) { uint8_t commissioner_anycast[16]; // Delete old address thread_addr_write_mesh_local_16(commissioner_anycast, 0xfc30 + (cur->thread_info->registered_commissioner.session_id % 8), cur->thread_info); addr_delete_matching(cur, commissioner_anycast, 125, ADDR_SOURCE_THREAD_ALOC); if (!cur->thread_info->registered_commissioner.commissioner_valid) { // No commissioner available return; } if (!addr_get_entry(cur, cur->thread_info->registered_commissioner.border_router_address)) { // Not our address return; } // Add commissioning border router address tr_debug("generate commissioner anycast address %s", trace_ipv6(commissioner_anycast)); addr_add(cur, commissioner_anycast, 64, ADDR_SOURCE_THREAD_ALOC, 0xffffffff, 0, true); } void thread_router_bootstrap_anycast_address_register(protocol_interface_info_entry_t *cur) { uint8_t leader_anycast_address[16]; tr_debug("Register anycast address:"); // Generate leader ALOC address thread_addr_write_mesh_local_16(leader_anycast_address, 0xfc00, cur->thread_info); if (cur->thread_info->leader_private_data) { tr_debug("Register leader anycast address: %s", trace_ipv6(leader_anycast_address)); addr_add(cur, leader_anycast_address, 128, ADDR_SOURCE_UNKNOWN, 0xffffffff, 0, true); } else { // Delete Leader ALOC if ml prefix was changed address Source is the defining rule addr_delete_matching(cur, leader_anycast_address, 128, ADDR_SOURCE_UNKNOWN); } thread_bootstrap_dhcp_anycast_address_generate(cur); thread_bootstrap_service_anycast_address_generate(cur); thread_router_bootstrap_commissioner_aloc_generate(cur); thread_extension_aloc_generate(cur); } static int thread_router_bootstrap_network_data_propagation(protocol_interface_info_entry_t *cur, uint8_t *childUnicastAddress, bool fullList) { uint8_t *payload_ptr; uint16_t payload_size = 16 + 4 + 20 + 4; //Version 4 bytes and Source address 4 bytes struct link_configuration *linkConfiguration; linkConfiguration = thread_joiner_application_get_config(cur->id); if (!linkConfiguration) { return -1; } if (!thread_info(cur)) { return -1; } payload_size += 2 + thread_network_data_tlv_size(cur, fullList); payload_size += thread_pending_timestamp_tlv_size(cur); mle_message_timeout_params_t timeout; uint16_t buf_id = mle_service_msg_allocate(cur->id, payload_size, false, MLE_COMMAND_DATA_RESPONSE); if (buf_id == 0) { return -1; } tr_debug("Send MLE data response network data changed"); payload_ptr = mle_service_get_data_pointer(buf_id); uint8_t *address_ptr = mle_service_get_msg_destination_address_pointer(buf_id); /* Send to ULA16 of DATA_REQUEST originator. */ /* Current_gp_prefix should have ULA prefix in */ if (childUnicastAddress) { memcpy(address_ptr, childUnicastAddress, 16); } else { memcpy(address_ptr, ADDR_LINK_LOCAL_ALL_NODES, 16); } /*Add Leader Data & Network Data */ payload_ptr = thread_leader_data_tlv_write(payload_ptr, cur); payload_ptr = thread_network_data_tlv_write(cur, payload_ptr, fullList); payload_ptr = thread_active_timestamp_write(cur, payload_ptr); payload_ptr = thread_pending_timestamp_write(cur, payload_ptr); payload_ptr = mle_general_write_source_address(payload_ptr, cur); if (mle_service_update_length_by_ptr(buf_id, payload_ptr) != 0) { tr_debug("Buffer overflow at message write"); } timeout.retrans_max = 0; timeout.timeout_init = 0; timeout.timeout_max = 0; timeout.delay = MLE_HALF_SECOND_MAX_DELAY; mle_service_set_msg_timeout_parameters(buf_id, &timeout); //Set Security uint32_t keySequence; thread_management_get_current_keysequence(cur->id, &keySequence); mle_service_msg_update_security_params(buf_id, 5, 2, keySequence); mle_service_send_message(buf_id); return 0; } static void thread_router_bootstrap_network_data_push_to_sleep_child(protocol_interface_info_entry_t *cur, bool stableDataUpdate) { uint8_t childLinkLocalAddress[16]; mac_neighbor_table_list_t *mac_table_list = &mac_neighbor_info(cur)->neighbour_list; memcpy(childLinkLocalAddress, ADDR_LINK_LOCAL_PREFIX, 8); ns_list_foreach(mac_neighbor_table_entry_t, cur_entry, mac_table_list) { if (!cur_entry->rx_on_idle) { memcpy(&childLinkLocalAddress[8], cur_entry->mac64, 8); childLinkLocalAddress[8] ^= 2; if (thread_neighbor_class_request_full_data_setup(&cur->thread_info->neighbor_class, cur_entry->index)) { thread_router_bootstrap_network_data_propagation(cur, childLinkLocalAddress, true); } else { if (stableDataUpdate) { thread_router_bootstrap_network_data_propagation(cur, childLinkLocalAddress, false); } } } } } void thread_router_bootstrap_network_data_distribute(protocol_interface_info_entry_t *cur) { if (!thread_i_am_router(cur)) { return; } tr_debug("Propagate New Network Data"); thread_router_bootstrap_network_data_propagation(cur, NULL, true); thread_router_bootstrap_network_data_push_to_sleep_child(cur, cur->thread_info->networkDataStorage.stableUpdatePushed); } bool thread_router_bootstrap_routing_allowed(struct protocol_interface_info_entry *cur) { link_configuration_s *link_conf_ptr = thread_management_configuration_get(cur->id); if (!link_conf_ptr) { return true; } return !(!thread_extension_version_check(cur->thread_info->version) && !(link_conf_ptr->securityPolicy & SECURITY_POLICY_ALL_ROUTERS_JOIN_ALLOWED)); } void thread_router_bootstrap_address_change_notify_send(protocol_interface_info_entry_t *cur) { thread_info(cur)->proactive_an_timer = THREAD_PROACTIVE_AN_SEND_DELAY; } void thread_router_bootstrap_delay_reed_jitter(int8_t interface_id, uint16_t delay) { protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return; } if (cur->thread_info->thread_device_mode != THREAD_DEVICE_MODE_ROUTER) { return; } // delay reed jitter timer to allow for settings changes to distribute thread_info(cur)->reedJitterTimer += delay; return; } #endif /* HAVE_THREAD_ROUTER */