mbed-os/features/nanostack/sal-stack-nanostack/source/6LoWPAN/Thread/thread_router_bootstrap.c

2804 lines
113 KiB
C

/*
* 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 <string.h>
#include <ns_types.h>
#include <ns_list.h>
#include <nsdynmemLIB.h>
#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 */