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

3013 lines
119 KiB
C
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/*
* Copyright (c) 2014-2018, Arm Limited and affiliates.
* SPDX-License-Identifier: BSD-3-Clause
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* \file thread_bootstrap.c
* \brief Add short description about this file!!!
*
*/
#include "nsconfig.h"
#ifdef HAVE_THREAD
#include <string.h>
#include <ns_types.h>
#include <nsdynmemLIB.h>
#include "eventOS_event.h"
#include "eventOS_event_timer.h"
#include "randLIB.h"
#include "ns_sha256.h"
#include "common_functions.h"
#include "NWK_INTERFACE/Include/protocol.h"
#include "net_thread_test.h"
#include "ipv6_stack/protocol_ipv6.h"
#include "libDHCPv6/libDHCPv6.h"
#include "libDHCPv6/libDHCPv6_server.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_host_bootstrap.h"
#include "6LoWPAN/Thread/thread_discovery.h"
#include "6LoWPAN/Thread/thread_leader_service.h"
#include "6LoWPAN/Thread/thread_router_bootstrap.h"
#include "6LoWPAN/Thread/thread_management_internal.h"
#include "6LoWPAN/Thread/thread_management_server.h"
#include "6LoWPAN/Thread/thread_neighbor_class.h"
#include "6LoWPAN/Thread/thread_network_data_lib.h"
#include "6LoWPAN/Thread/thread_network_synch.h"
#include "6LoWPAN/Thread/thread_joiner_application.h"
#include "6LoWPAN/Thread/thread_extension.h"
#include "6LoWPAN/Thread/thread_extension_bbr.h"
#include "6LoWPAN/Thread/thread_management_client.h"
#include "6LoWPAN/Thread/thread_address_registration_client.h"
#include "6LoWPAN/Thread/thread_joiner_application.h"
#include "6LoWPAN/Thread/thread_bbr_api_internal.h"
#include "6LoWPAN/Thread/thread_border_router_api_internal.h"
#include "6LoWPAN/Thread/thread_beacon.h"
#include "6LoWPAN/Thread/thread_nvm_store.h"
#include "6LoWPAN/Thread/thread_extension_bootstrap.h"
#include "6LoWPAN/MAC/mac_helper.h"
#include "6LoWPAN/Thread/thread_mle_message_handler.h"
#include "mac_api.h"
#include "RPL/rpl_control.h" // insanity - bootstraps shouldn't be doing each others' clean-up
#include "thread_management_if.h"
#include "thread_border_router_api.h"
#include "thread_tmfcop_lib.h"
#include "Common_Protocols/ipv6.h"
#include "Common_Protocols/icmpv6.h"
#include "Common_Protocols/icmpv6_radv.h"
#include "MPL/mpl.h"
#include "MLE/mle.h"
#include "MLE/mle_tlv.h"
#include "DHCPv6_client/dhcpv6_client_api.h"
#include "thread_config.h"
#include "thread_meshcop_lib.h"
#include "multicast_api.h"
#include "mlme.h"
#include "Service_Libs/etx/etx.h"
#include "Service_Libs/nd_proxy/nd_proxy.h"
#include "Service_Libs/blacklist/blacklist.h"
#include "Service_Libs/mle_service/mle_service_api.h"
#include "6LoWPAN/MAC/mac_data_poll.h"
#include "6LoWPAN/lowpan_adaptation_interface.h"
#include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h"
#include "platform/topo_trace.h"
#define TRACE_GROUP "thbs"
//#define EXTRA_DEBUG_INFO
#ifdef EXTRA_DEBUG_INFO
#define tr_debug_extra(...) tr_debug(__VA_ARGS__)
#else
#define tr_debug_extra(...)
#endif
void thread_bootstrap_attached_finish(protocol_interface_info_entry_t *cur);
static void thread_bootstrap_orphan_scan_start(struct protocol_interface_info_entry *cur_interface);
static int thread_configuration_security_activate(protocol_interface_info_entry_t *cur, link_configuration_s *linkConfiguration);
static void thread_interface_bootsrap_mode_init(protocol_interface_info_entry_t *cur);
static void thread_bootstrap_generate_leader_and_link(protocol_interface_info_entry_t *cur);
static int thread_bootstrap_attach_start(int8_t interface_id, thread_bootsrap_state_type_e state);
static void thread_bootsrap_network_discovery_failure(int8_t interface_id);
static void thread_neighbor_remove(mac_neighbor_table_entry_t *entry_ptr, void *user_data);
static void thread_bootsrap_network_join_start(struct protocol_interface_info_entry *cur_interface, discovery_response_list_t *nwk_info);
static void thread_neighbor_remove(mac_neighbor_table_entry_t *entry_ptr, void *user_data)
{
protocol_interface_info_entry_t *cur = user_data;
lowpan_adaptation_remove_free_indirect_table(cur, entry_ptr);
thread_reset_neighbour_info(cur, entry_ptr);
//Removes ETX neighbor
etx_neighbor_remove(cur->id, entry_ptr->index);
//Remove MLE frame counter info
mle_service_frame_counter_entry_delete(cur->id, entry_ptr->index);
}
static bool thread_neighbor_entry_nud_notify(mac_neighbor_table_entry_t *entry_ptr, void *user_data)
{
protocol_interface_info_entry_t *cur_interface = user_data;
if (thread_am_router(cur_interface)) {
return false; //Never do Keep alive with any one
}
if (entry_ptr->link_role != PRIORITY_PARENT_NEIGHBOUR) {
return false; //Do not never challenge than priority parent
}
if (cur_interface->lowpan_info & INTERFACE_NWK_CONF_MAC_RX_OFF_IDLE) {
return false; //Sleepy end device should not never challenge
}
if (entry_ptr->lifetime > MLE_TABLE_CHALLENGE_TIMER) {
return false;
}
return thread_host_bootstrap_child_update(cur_interface, entry_ptr->mac64);
}
int8_t thread_mle_class_init(int8_t interface_id)
{
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
if (!cur) {
return -1;
}
mac_description_storage_size_t buffer;
//Read MAC device table sizes
if (cur->mac_api->mac_storage_sizes_get(cur->mac_api, &buffer) != 0) {
return -1;
}
if (buffer.key_description_table_size < 4) {
return -1;
}
thread_neighbor_class_delete(&cur->thread_info->neighbor_class);
if (!thread_neighbor_class_create(&cur->thread_info->neighbor_class, buffer.device_decription_table_size - 1)) {
return -1;
}
if (!mac_neighbor_info(cur)) {
mac_neighbor_info(cur) = mac_neighbor_table_create(buffer.device_decription_table_size - 1, thread_neighbor_remove
, thread_neighbor_entry_nud_notify, cur);
if (!mac_neighbor_info(cur)) {
return -1;
}
}
if (mle_service_frame_counter_table_allocate(interface_id, buffer.device_decription_table_size - 1)) {
return -1;
}
if (!etx_storage_list_allocate(cur->id, buffer.device_decription_table_size - 1)) {
return -1;
}
lowpan_adaptation_interface_etx_update_enable(cur->id);
//Defined well know neighbour for discovery
return 0;
}
uint8_t thread_mode_get_by_interface_ptr(protocol_interface_info_entry_t *cur)
{
uint8_t mle_mode = 0;
if (!thread_info(cur)) {
return 0;
}
if (cur->mac_parameters->RxOnWhenIdle) {
mle_mode |= MLE_RX_ON_IDLE;
}
if (thread_info(cur)->requestFullNetworkData) {
mle_mode |= (MLE_THREAD_REQ_FULL_DATA_SET);
}
if (thread_joiner_application_provisioning_get(cur->id) == PROVISIONING_STATUS_NOT_DONE) {
// if provisioning is not done Sleepy devices need to temporarily request full network data
// To receive commissioner information
mle_mode |= (MLE_THREAD_REQ_FULL_DATA_SET);
}
/* We always send secured data requests */
mle_mode |= MLE_THREAD_SECURED_DATA_REQUEST;
switch (thread_info(cur)->thread_device_mode) {
case THREAD_DEVICE_MODE_ROUTER:
case THREAD_DEVICE_MODE_FULL_END_DEVICE:
mle_mode |= MLE_FFD_DEV;
break;
default:
break;
}
return mle_mode;
}
/**
* Return lower (worse) of the two margins.
*/
uint8_t thread_parent_margin_calc(uint8_t marginFromParent, uint8_t marginToParent)
{
if (marginFromParent > marginToParent) {
return marginToParent;
} else {
return marginFromParent;
}
}
uint8_t thread_compute_link_margin(int8_t rssi)
{
if (rssi < -94) {
return 0;
}
return (rssi + 94);
}
uint8_t thread_calculate_link_margin(int8_t dbm, uint8_t compLinkMarginFromParent)
{
uint8_t compLinkMarginToParent;
uint8_t newLqi;
compLinkMarginToParent = thread_compute_link_margin(dbm);
//Calculate New Combined LQI
newLqi = thread_parent_margin_calc(compLinkMarginFromParent, compLinkMarginToParent);
return newLqi;
}
bool thread_check_is_this_my_parent(protocol_interface_info_entry_t *cur, mac_neighbor_table_entry_t *entry_temp)
{
if (entry_temp && thread_info(cur)->thread_endnode_parent) {
if (memcmp(entry_temp->mac64, thread_info(cur)->thread_endnode_parent->mac64, 8) == 0) {
return true;
}
}
return false;
}
bool thread_bootstrap_request_network_data(protocol_interface_info_entry_t *cur, thread_leader_data_t *leaderData, uint16_t short_address)
{
bool requestNetworkdata = false;
thread_leader_data_t *leaderInfo = thread_info(cur)->thread_leader_data;
if (thread_info(cur)->thread_endnode_parent->shortAddress != short_address) {
return false;
}
if (!thread_partition_match(cur, leaderData)) {
tr_debug("Learn new Network Data");
requestNetworkdata = true;
thread_partition_info_update(cur, leaderData);
} else if (common_serial_number_greater_8(leaderData->dataVersion, leaderInfo->dataVersion)) {
requestNetworkdata = true;
} else if (common_serial_number_greater_8(leaderData->stableDataVersion, leaderInfo->stableDataVersion)) {
requestNetworkdata = true;
}
if (requestNetworkdata) {
thread_bootstrap_parent_network_data_request(cur, true);
}
return true;
}
static int thread_router_check_previous_partition_info(protocol_interface_info_entry_t *cur, thread_leader_data_t *leaderData, mle_tlv_info_t *routeTlv)
{
if (!routeTlv || !routeTlv->dataPtr || !routeTlv->tlvLen || !leaderData) {
//check for parameters
return -1;
}
if ((leaderData->partitionId == cur->thread_info->previous_partition_info.partitionId) &&
(leaderData->weighting == cur->thread_info->previous_partition_info.weighting) &&
(routeTlv->dataPtr[0] == cur->thread_info->previous_partition_info.idSequence)) {
//drop the advertisement from previuos partition
return 1;
} else {
//do not drop the advertisement
return 0;
}
}
int thread_bootstrap_partition_process(protocol_interface_info_entry_t *cur, uint8_t heard_partition_routers, thread_leader_data_t *heard_partition_leader_data, mle_tlv_info_t *routeTlv)
{
uint8_t active_routers = 0;
thread_leader_data_t *current_leader_data = NULL;
/* if there scanned parent, then the comparison is between parent responses so retrieve the previously scanned parent info
* else comparison is between existing leader data and heard advertisement leader data so retrieve existing leader data
*/
if (thread_info(cur)->thread_attach_scanned_parent) {
current_leader_data = &thread_info(cur)->thread_attach_scanned_parent->leader_data;
active_routers = thread_info(cur)->thread_attach_scanned_parent->activeRouters;
} else {
current_leader_data = thread_info(cur)->thread_leader_data;
active_routers = thread_routing_count_active_routers(&thread_info(cur)->routing);
}
if (!current_leader_data) {
tr_warn("There is no leader data present");
return -2;
}
if (1 == thread_router_check_previous_partition_info(cur, heard_partition_leader_data, routeTlv)) {
tr_debug("Dropping advertisement from old partition without sequence number increase");
return -2;
}
/*Rule 0: If we are going to form Higher partition than heard we dont try to attach to lower ones
*/
if (thread_extension_enabled(cur) &&
thread_info(cur)->thread_device_mode == THREAD_DEVICE_MODE_ROUTER) {
if (heard_partition_leader_data->weighting < thread_info(cur)->partition_weighting) {
tr_debug("Heard a lower weight partition");
return -2;
}
if (heard_partition_leader_data->weighting > thread_info(cur)->partition_weighting) {
return 2;
}
}
if ((heard_partition_routers == 0 && active_routers == 1) && thread_am_router(cur)) {
//heard a REED and I am a lonely router in a singleton partition, so merge
tr_debug("Heard a REED and I am a singleton - merge");
return 2;
}
//Rule 1: A non-singleton Thread Network Partition always has higher priority than a singleton Thread Network Partition
if (heard_partition_routers > 1 && active_routers == 1) {
tr_debug("Heard a nonsingleton and i am a singleton");
return 2;
}
if (active_routers > 1 && heard_partition_routers == 1) {
return -2;
}
/*Rule 2: When comparing two singleton or two non-singleton Thread Network Partitions,
the one with the higher 8-bit weight value has higher priority. */
if (heard_partition_leader_data->weighting > current_leader_data->weighting) {
tr_debug("Heard a greater weighting");
return 2;
}
if (heard_partition_leader_data->weighting < current_leader_data->weighting) {
return -2;
}
/*Rule 3: When comparing two singleton or two non-singleton Thread Network Partitions that have the same 8-bit weight value,
* the one with the higher Partition ID, considered as unsigned 32-bit numbers, has higher priority.
*/
if (heard_partition_leader_data->partitionId > current_leader_data->partitionId) {
tr_debug("Heard a greater partition id");
return 2;
}
if (heard_partition_leader_data->partitionId < current_leader_data->partitionId) {
return -2;
}
return -2;
}
int thread_leader_data_validation(protocol_interface_info_entry_t *cur, thread_leader_data_t *leaderData, mle_tlv_info_t *routeTlv)
{
if (!thread_info(cur)->thread_leader_data) {
return -1;
}
if (!thread_partition_match(cur, leaderData)) {
uint8_t routers_in_route_tlv = thread_get_router_count_from_route_tlv(routeTlv);
//partition checks
return thread_bootstrap_partition_process(cur, routers_in_route_tlv, leaderData, routeTlv);
}
//Should check is there new version numbers
if (common_serial_number_greater_8(leaderData->dataVersion, thread_info(cur)->thread_leader_data->dataVersion) ||
common_serial_number_greater_8(leaderData->stableDataVersion, thread_info(cur)->thread_leader_data->stableDataVersion)) {
// Version number increased by some-one else -> there is leader in the network
if (thread_info(cur)->leader_private_data) {
tr_error("Another leader detected -> bootstrap");
thread_bootstrap_reset_restart(cur->id);
return -1;
}
tr_debug("NEW Network Data available");
return 1;
}
return 0;
}
void thread_bootstrap_all_nodes_address_generate(uint8_t multicast_address[16], uint8_t prefix[8], uint8_t scope)
{
memset(multicast_address, 0, 16);
multicast_address[0] = 0xff;
multicast_address[1] = 0x30 | scope; //Thread specification says p and t bits are 1
multicast_address[2] = 0x00; //Reserved
multicast_address[3] = 0x40; //Prefix length 64 bits
memcpy(&multicast_address[4], prefix, 8);
multicast_address[15] = 1;
}
void thread_bootstrap_all_nodes_multicast_register(protocol_interface_info_entry_t *cur)
{
uint8_t multicast_address[16];
switch (cur->thread_info->thread_device_mode) {
#ifdef HAVE_THREAD_ROUTER
case THREAD_DEVICE_MODE_ROUTER:
cur->if_special_multicast_forwarding = thread_router_bootstrap_multicast_forwarder_enable;
break;
#endif
default:
cur->if_special_multicast_forwarding = NULL;
break;
}
// Register to link local all thread nodes multicast
thread_bootstrap_all_nodes_address_generate(multicast_address, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, IPV6_SCOPE_LINK_LOCAL);
tr_debug("Register multicast address: %s", trace_ipv6(multicast_address));
addr_add_group(cur, multicast_address);
// Register to mesh local all thread nodes multicast
thread_bootstrap_all_nodes_address_generate(multicast_address, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, IPV6_SCOPE_REALM_LOCAL);
tr_debug("Register multicast address: %s", trace_ipv6(multicast_address));
addr_add_group(cur, multicast_address);
}
void thread_bootstrap_all_nodes_multicast_unregister(protocol_interface_info_entry_t *cur)
{
uint8_t multicast_address[16];
if (!cur->thread_info->threadPrivatePrefixInfo.ulaValid) {
//Prefix not valid do not delete
return;
}
// Unregister to link local all thread nodes multicast
thread_bootstrap_all_nodes_address_generate(multicast_address, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, 2);
tr_debug("Free multicast address: %s", trace_ipv6(multicast_address));
//multicast_free_address(multicast_address);
addr_remove_group(cur, multicast_address);
// Unregister to mesh local all thread nodes multicast
thread_bootstrap_all_nodes_address_generate(multicast_address, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, 3);
tr_debug("Free multicast address: %s", trace_ipv6(multicast_address));
//multicast_free_address(multicast_address);
addr_remove_group(cur, multicast_address);
}
void thread_end_device_mode_set(protocol_interface_info_entry_t *cur, bool sleepy)
{
if (sleepy) {
cur->lowpan_info |= INTERFACE_NWK_CONF_MAC_RX_OFF_IDLE;
mac_helper_pib_boolean_set(cur, macRxOnWhenIdle, false);
} else {
cur->lowpan_info &= ~INTERFACE_NWK_CONF_MAC_RX_OFF_IDLE;
mac_helper_pib_boolean_set(cur, macRxOnWhenIdle, true);
}
}
int8_t nwk_thread_host_control(protocol_interface_info_entry_t *cur, net_host_mode_t mode, uint32_t delay)
{
int8_t ret_val = 0;
//Check IF Bootsrap Ready and type is Host
if (cur == NULL || cur->rfd_poll_info == NULL) {
return -1;
}
nwk_rfd_poll_setups_s *rf_ptr = cur->rfd_poll_info;
switch (mode) {
case NET_HOST_FAST_POLL_MODE:
//fast mode
//Check Host current sleep state
if (rf_ptr->host_mode == NET_HOST_RX_ON_IDLE) {
tr_debug("Enable Fast poll mode. Init Poll timer and period");
thread_end_device_mode_set(cur, true);
mac_poll_timer_trig(delay, cur);
rf_ptr->nwk_app_poll_time = 300;
rf_ptr->host_mode = NET_HOST_FAST_POLL_MODE;
}
break;
case NET_HOST_RX_ON_IDLE:
// Non-sleep mode
thread_end_device_mode_set(cur, false);
rf_ptr->host_mode = NET_HOST_RX_ON_IDLE;
break;
default:
ret_val = -1;
break;
}
return ret_val;
}
void thread_set_link_local_address(protocol_interface_info_entry_t *cur)
{
ns_list_foreach_safe(if_address_entry_t, addr, &cur->ip_addresses) {
if (memcmp(addr->address, ADDR_LINK_LOCAL_PREFIX, 8) == 0) {
tr_debug("deleting address %s", trace_ipv6(addr->address));
ns_list_remove(&cur->ip_addresses, addr);
ns_dyn_mem_free(addr);
}
}
/* Fix EUId64 also to start use 0*/
memcpy(cur->iid_eui64, cur->mac, 8);
cur->iid_eui64[0] ^= 2;
addr_interface_set_ll64(cur, NULL);
}
static int thread_configuration_security_activate(protocol_interface_info_entry_t *cur, link_configuration_s *linkConfiguration)
{
tr_debug("MAC SET Security Mode");
if (!(cur->lowpan_info & INTERFACE_NWK_ACTIVE) || !(cur->configure_flags & INTERFACE_BOOTSTRAP_DEFINED)) {
return -1;
}
mac_helper_security_key_clean(cur);
mac_helper_default_security_level_set(cur, 5);
mac_helper_default_security_key_id_mode_set(cur, MAC_KEY_ID_MODE_IDX);
cur->if_lowpan_security_params->nwk_security_mode = NET_SEC_MODE_PSK_LINK_SECURITY;
cur->mac_parameters->mac_configured_sec_level = 5;
cur->thread_info->masterSecretMaterial.historyKeyValid = false;
cur->thread_info->masterSecretMaterial.valid_Info = true;
// Update the guard timer value
thread_key_guard_timer_calculate(cur, linkConfiguration, true);
//Define KEY's
thread_security_prev_key_generate(cur, linkConfiguration->master_key, linkConfiguration->key_sequence);
thread_security_key_generate(cur, linkConfiguration->master_key, linkConfiguration->key_sequence);
thread_security_next_key_generate(cur, linkConfiguration->master_key, linkConfiguration->key_sequence);
return 0;
}
void thread_bootstrap_mac_activate(protocol_interface_info_entry_t *cur, uint16_t channel, uint16_t panid, bool coordinator)
{
mlme_start_t start_req;
memset(&start_req, 0, sizeof(mlme_start_t));
cur->mac_parameters->pan_id = panid;
cur->mac_parameters->mac_channel = channel;
start_req.PANId = panid;
start_req.LogicalChannel = channel;
start_req.BeaconOrder = 0x0f;
start_req.SuperframeOrder = 0x0f;
start_req.PANCoordinator = coordinator;
thread_discovery_responser_enable(cur->id, coordinator);
if (cur->mac_api) {
cur->mac_api->mlme_req(cur->mac_api, MLME_START, (void *)&start_req);
}
}
int thread_configuration_mac_activate(protocol_interface_info_entry_t *cur, uint16_t channel, uint16_t panid, uint8_t *extended_random_mac)
{
ipv6_neighbour_cache_flush(&cur->ipv6_neighbour_cache);
mac_helper_mac64_set(cur, extended_random_mac);
thread_set_link_local_address(cur);
//SET Thread default here
mac_helper_mac_mlme_max_retry_set(cur->id, THREAD_MAX_FRAME_RETRIES);
bool coordinator = (cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_ROUTER);
thread_bootstrap_mac_activate(cur, channel, panid, coordinator);
mac_data_poll_init(cur);
return 0;
}
int thread_configuration_6lowpan_activate(protocol_interface_info_entry_t *cur)
{
tr_debug("6lowpan configure");
cur->lowpan_info &= ~INTERFACE_NWK_BOOTSRAP_PANA_AUTHENTICATION;
cur->configure_flags |= INTERFACE_SECURITY_DEFINED;
return 0;
}
static void thread_bootstrap_ml_address_update(protocol_interface_info_entry_t *cur, const link_configuration_s *conf)
{
tr_debug("Updating ML addresses and prefix information.");
uint8_t address[16];
if (cur->thread_info->threadPrivatePrefixInfo.ulaValid &&
memcmp(cur->thread_info->threadPrivatePrefixInfo.ulaPrefix,
conf->mesh_local_ula_prefix, 8) != 0) {
// Current prefix is valid and old vs. new different: update old addresses
// Update the addresses in the neighbor cache (replace the old ULA prefix part)
ns_list_foreach(ipv6_neighbour_t, entry, &cur->ipv6_neighbour_cache.list) {
tr_debug("Neighbor cache address: %s", trace_ipv6(entry->ip_address));
if (bitsequal(entry->ip_address, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, 64)) {
memcpy(entry->ip_address, conf->mesh_local_ula_prefix, 8);
tr_debug("Updated to %s.", trace_ipv6(entry->ip_address));
}
}
// Delete the ML64 address
thread_delete_ml64_address(cur);
// Free previously registered multicast addresses
thread_bootstrap_all_nodes_multicast_unregister(cur);
// In-place replace all other mesh local addresses...
ns_list_foreach_safe(if_address_entry_t, e, &cur->ip_addresses) {
tr_debug("IP address: %s", trace_ipv6(e->address));
if (bitsequal(e->address, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, 64)) {
memcpy(address, conf->mesh_local_ula_prefix, 8);
memcpy(address + 8, e->address + 8, 8);
tr_debug("Updated to: %s", trace_ipv6(address));
addr_add(cur, address, e->prefix_len, e->source, e->valid_lifetime, e->preferred_lifetime, true);
addr_delete_entry(cur, e);
}
}
}
// Set new ML-EID and ULA prefix
uint8_t *ml_eid = thread_joiner_application_ml_eid_get(cur->id);
memcpy(cur->iid_slaac, ml_eid, 8);
arm_thread_private_ula_prefix_set(cur, conf->mesh_local_ula_prefix);
// Generate new ML64 address
thread_generate_ml64_address(cur);
// Register multicast addresses
thread_bootstrap_all_nodes_multicast_register(cur);
// Register leader anycast address (if we are the leader)
thread_router_bootstrap_anycast_address_register(cur);
thread_bootstrap_routing_activate(cur);
}
int thread_configuration_thread_activate(protocol_interface_info_entry_t *cur, link_configuration_s *linkConfiguration)
{
tr_debug("thread configure");
// Update existing mesh-local addresses and the ML prefix
thread_bootstrap_ml_address_update(cur, linkConfiguration);
//Define Default Contexts
lowpan_context_update(&cur->lowpan_contexts, LOWPAN_CONTEXT_C, 0xFFFF, linkConfiguration->mesh_local_ula_prefix, 64, true);
thread_extension_bbr_route_update(cur);
blacklist_clear();
blacklist_params_set(
THREAD_BLACKLIST_ENTRY_LIFETIME,
THREAD_BLACKLIST_TIMER_MAX_TIMEOUT,
THREAD_BLACKLIST_TIMER_TIMEOUT,
THREAD_BLACKLIST_ENTRY_MAX_NBR,
THREAD_BLACKLIST_PURGE_NBR,
THREAD_BLACKLIST_PURGE_TIMER_TIMEOUT);
return 0;
}
int thread_configuration_mle_activate(protocol_interface_info_entry_t *cur)
{
tr_debug("thread MLE configure");
mle_service_interface_receiver_handler_update(cur->id, thread_general_mle_receive_cb);
return 0;
}
int thread_configuration_mle_disable(protocol_interface_info_entry_t *cur)
{
tr_debug("thread MLE disable");
mle_service_interface_receiver_handler_update(cur->id, NULL);
return 0;
}
static int thread_mle_service_register(protocol_interface_info_entry_t *cur, uint8_t *mac64)
{
if (mle_service_interface_register(cur->id, cur, thread_mle_parent_discover_receive_cb, mac64, 8) != 0) {
tr_error("Mle Service init Fail");
return -1;
}
mle_service_set_frame_counter_check(true);
mle_service_set_fragmented_msg_ll_security(true);
return 0;
}
int thread_link_configuration_activate(protocol_interface_info_entry_t *cur, link_configuration_s *linkConfiguration)
{
//Generate Beacon Payload from active configuration
if (thread_beacon_create_payload(cur) != 0) {
return -1;
}
if (thread_configuration_mac_activate(cur, linkConfiguration->rfChannel, linkConfiguration->panId, thread_joiner_application_random_mac_get(cur->id))) {
return -1;
}
thread_configuration_thread_activate(cur, linkConfiguration);
thread_configuration_security_activate(cur, linkConfiguration);
thread_configuration_6lowpan_activate(cur);
return 0;
}
int thread_bootstrap_announce_send(protocol_interface_info_entry_t *cur, uint8_t channel_page, uint16_t channel, uint16_t panid, uint64_t timestamp, uint16_t selected_channel)
{
uint8_t *ptr;
uint8_t channel_tlv[3];
mle_message_timeout_params_t timeout;
uint32_t keySequence;
uint16_t buf_id = mle_service_msg_allocate(cur->id, 128, false, MLE_COMMAND_DATASET_ANNOUNCE);
if (buf_id == 0) {
return -1;
}
mle_service_set_msg_destination_address(buf_id, ADDR_LINK_LOCAL_ALL_NODES);
thread_management_get_current_keysequence(cur->id, &keySequence);
mle_service_msg_update_security_params(buf_id, 5, 2, keySequence);
mle_service_set_msg_link_layer_security_mode(buf_id, true);
ptr = mle_service_get_data_pointer(buf_id);
ptr = thread_meshcop_tlv_data_write_uint64(ptr, MLE_TYPE_ACTIVE_TIMESTAMP, timestamp);
ptr = thread_meshcop_tlv_data_write_uint16(ptr, MLE_TYPE_PANID, panid);
channel_tlv[0] = channel_page;
common_write_16_bit(channel, &channel_tlv[1]);
ptr = thread_meshcop_tlv_data_write(ptr, MLE_TYPE_CHANNEL, 3, channel_tlv);
if (mle_service_update_length_by_ptr(buf_id, ptr) != 0) {
tr_debug("Buffer overflow at message write");
}
//SET packet channel
mle_service_set_msg_rf_channel(buf_id, selected_channel);
timeout.retrans_max = 0;
timeout.timeout_init = 0;
timeout.timeout_max = 0;
timeout.delay = MLE_NO_DELAY;
mle_service_set_msg_timeout_parameters(buf_id, &timeout);
mle_service_set_msg_panid(buf_id, 0xffff);
mle_service_send_message(buf_id);
return 0;
}
static void thread_announce_ntf_cb(void *arg)
{
if (!arg) {
return;
}
protocol_interface_info_entry_t *cur = arg;
cur->thread_info->announcement_info->timer = NULL;
thread_bootsrap_event_trig(THREAD_ANNOUNCE_ACTIVE, cur->bootStrapId, ARM_LIB_HIGH_PRIORITY_EVENT);
}
static void thread_announce_success_cb(void *arg)
{
// We come here when we have succesfully attached to announced channel and then we announce this back
protocol_interface_info_entry_t *cur = arg;
if (!cur || !cur->thread_info->announcement_info) {
return;
}
cur->thread_info->announcement_info->timer = NULL;
cur->thread_info->announcement_info->announce_success = false;
thread_bootstrap_announcement_start(cur, cur->thread_info->announcement_info->channel_page, cur->thread_info->announcement_info->channel, 3, cur->thread_info->announcement_info->period);
}
void thread_bootstrap_announcement_start(protocol_interface_info_entry_t *cur, uint8_t channel_page, uint16_t channel, uint8_t count, uint16_t period)
{
if (!cur->thread_info->announcement_info) {
cur->thread_info->announcement_info = ns_dyn_mem_alloc(sizeof(thread_announcement_t));
}
if (!cur->thread_info->announcement_info) {
return;
}
tr_info("Start announcement ch: %d", channel);
cur->thread_info->announcement_info->channel = channel;
cur->thread_info->announcement_info->period = period;
cur->thread_info->announcement_info->channel_page = channel_page;
cur->thread_info->announcement_info->count = count;
cur->thread_info->announcement_info->timer = NULL;
cur->thread_info->announcement_info->announce_success = false;
cur->thread_info->announcement_info->timestamp = 0;
thread_bootsrap_event_trig(THREAD_ANNOUNCE_ACTIVE, cur->bootStrapId, ARM_LIB_HIGH_PRIORITY_EVENT);
}
void thread_bootstrap_temporary_attach(protocol_interface_info_entry_t *cur, uint8_t channel_page, uint16_t channel, uint16_t panid, uint64_t timestamp)
{
link_configuration_s *linkConfiguration = thread_joiner_application_get_config(cur->id);
if (!linkConfiguration) {
tr_error("No link configuration!");
return;
}
tr_debug("Attach to new channel %u", channel);
/*
* Save the old info and make a timer to announce it to old channels once if attachment is succesfull
* When we receive attach fail
*/
if (!cur->thread_info->announcement_info) {
cur->thread_info->announcement_info = ns_dyn_mem_alloc(sizeof(thread_announcement_t));
}
if (!cur->thread_info->announcement_info) {
return;
}
cur->thread_info->announcement_info->channel = linkConfiguration->rfChannel;
cur->thread_info->announcement_info->channel_page = linkConfiguration->channel_page;
cur->thread_info->announcement_info->panid = linkConfiguration->panId;
cur->thread_info->announcement_info->count = 1;
cur->thread_info->announcement_info->period = 1000;
cur->thread_info->announcement_info->timestamp = timestamp;
cur->thread_info->announcement_info->timer = eventOS_timeout_ms(thread_announce_success_cb, 20000, cur);
// TODO check timer value
cur->thread_info->announcement_info->announce_success = true;
linkConfiguration->channel_page = channel_page;
linkConfiguration->rfChannel = channel;
linkConfiguration->panId = panid;
thread_joiner_application_link_configuration_store(cur->id, linkConfiguration);
thread_bootstrap_reset_restart(cur->id);
}
static const trickle_params_t thread_mpl_data_trickle_params = {
.Imin = 1, /* 50ms */
.Imax = 2, /* 100ms */
.k = 0,
.TimerExpirations = 2 /* MPL core knows to suppress to 0 for non-routers */
};
static const trickle_params_t thread_mpl_control_trickle_params = {
.Imin = 11,
.Imax = 5 * 60 * 20,
.k = 0,
.TimerExpirations = 0
};
void thread_interface_init(protocol_interface_info_entry_t *cur)
{
thread_discovery_reset(cur->id);
thread_routing_set_mesh_callbacks(cur);
dhcp_client_init(cur->id);
thread_management_client_init(cur->id);
thread_address_registration_init();
cur->mpl_seed_id_mode = MULTICAST_MPL_SEED_ID_MAC_SHORT;
cur->mpl_seed_set_entry_lifetime = 90;
cur->mpl_proactive_forwarding = true;
cur->mpl_control_trickle_params = thread_mpl_control_trickle_params;
cur->mpl_data_trickle_params = thread_mpl_data_trickle_params;
cur->mpl_seed = true;
cur->mpl_treat_realm_domains_as_one = true;
cur->if_ns_transmit = thread_nd_ns_transmit;
cur->if_special_forwarding = thread_nd_special_forwarding;
cur->if_snoop = thread_nd_snoop;
cur->if_icmp_handler = thread_nd_icmp_handler;
cur->ipv6_neighbour_cache.send_nud_probes = false;
cur->ipv6_neighbour_cache.recv_addr_reg = true;
cur->send_mld = false;
cur->ip_multicast_as_mac_unicast_to_parent = true;
if (!cur->thread_info->routerShortAddress) {
cur->thread_info->routerShortAddress = 0xfffe;
}
if (cur->thread_info->thread_attach_scanned_parent) {
mle_service_msg_free(cur->thread_info->thread_attach_scanned_parent->child_id_request_id);
ns_dyn_mem_free(cur->thread_info->thread_attach_scanned_parent);
cur->thread_info->thread_attach_scanned_parent = NULL;
}
//Disable Always RPL
rpl_control_remove_domain_from_interface(cur);
mpl_domain_create(cur, ADDR_ALL_MPL_FORWARDERS, NULL, MULTICAST_MPL_SEED_ID_DEFAULT, -1, 0, NULL, NULL);
addr_add_group(cur, ADDR_REALM_LOCAL_ALL_NODES);
cur->nwk_nd_re_scan_count = 5;
}
static void thread_interface_bootsrap_mode_init(protocol_interface_info_entry_t *cur)
{
thread_routing_reset(&cur->thread_info->routing);
mac_helper_mac16_address_set(cur, 0xffff);
if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_ROUTER) {
tr_debug("Set ASPIRING Router Mode");
cur->thread_info->thread_device_mode = THREAD_DEVICE_MODE_ROUTER;
cur->lowpan_info &= ~INTERFACE_NWK_ROUTER_DEVICE;
} else if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_HOST &&
cur->thread_info->end_device_link_synch) {
tr_debug("Set FED Mode");
cur->thread_info->thread_device_mode = THREAD_DEVICE_MODE_FULL_END_DEVICE;
} else if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_SLEEPY_HOST) {
tr_debug("Set ASPIRING Sleepy Host Mode");
cur->thread_info->thread_device_mode = THREAD_DEVICE_MODE_SLEEPY_END_DEVICE;
//SET Sleepy Host To RX on Idle mode for bootsrap
nwk_thread_host_control(cur, NET_HOST_RX_ON_IDLE, 0);
cur->thread_info->childUpdateReqTimer = 0.8 * cur->thread_info->host_link_timeout;
} else {
tr_debug("Set End node Mode");
cur->thread_info->thread_device_mode = THREAD_DEVICE_MODE_END_DEVICE;
}
cur->thread_info->thread_attached_state = THREAD_STATE_NETWORK_DISCOVER;
}
int8_t thread_bootsrap_event_trig(thread_bootsrap_event_type_e event_type, int8_t Id, arm_library_event_priority_e priority)
{
arm_event_s event = {
.receiver = Id,
.sender = 0,
.event_type = event_type,
.priority = priority,
};
return eventOS_event_send(&event);
}
void thread_bootstrap_reset_restart(int8_t interface)
{
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface);
if (!cur) {
return;
}
if (cur->nwk_bootstrap_state == ER_MLE_SYNCH) {
thread_network_synch_data_free(cur->id);
}
thread_nd_service_disable(interface);
thread_routing_deactivate(&cur->thread_info->routing);
//TODO: clear CoAP resending queue
thread_bootsrap_event_trig(THREAD_BOOTSTRAP_RESET, cur->bootStrapId, ARM_LIB_HIGH_PRIORITY_EVENT);
}
void thread_tasklet(arm_event_s *event)
{
protocol_interface_info_entry_t *cur = 0;
thread_bootsrap_event_type_e event_type;
event_type = (thread_bootsrap_event_type_e)event->event_type;
cur = protocol_stack_interface_info_get_by_bootstrap_id(event->receiver);
if (!cur) {
tr_debug("Thread task unknown");
return;
}
switch (event_type) {
case THREAD_INIT_EVENT:
tr_debug_extra("Thread SM THREAD_INIT_EVENT");
tr_debug("Thread task Init");
break;
case THREAD_BOOTSTRAP_RESET:
//Reset Current Thread state
tr_debug_extra("Thread SM THREAD_BOOTSTRAP_RESET");
if (thread_bootstrap_reset(cur) == 0) {
tr_debug("Thread Attached");
} else {
tr_debug("Stop Bootsrap and send event");
}
break;
case THREAD_ATTACH_READY:
tr_debug_extra("Thread SM THREAD_ATTACH_READY");
thread_bootstrap_attached_finish(cur);
break;
case THREAD_ATTACH_UPGRADE_REED:
tr_debug_extra("Thread SM THREAD_ATTACH_UPGRADE_REED");
if (thread_router_bootstrap_child_count_get(cur) > 0) {
thread_router_bootstrap_router_id_request(cur, THREAD_COAP_STATUS_TLV_PARENT_PARTITION_CHANGE);
} else {
thread_router_bootstrap_router_id_request(cur, THREAD_COAP_STATUS_TLV_TOO_FEW_ROUTERS);
}
break;
case THREAD_ATTACH_DOWNGRADE_ROUTER:
tr_debug_extra("Thread SM THREAD_ATTACH_DOWNGRADE_ROUTER");
thread_bootstrap_attach_start(cur->id, THREAD_REATTACH_REED);
break;
case THREAD_ATTACH_ACTIVE_ROUTER:
tr_debug_extra("Thread SM THREAD_ATTACH_ACTIVE_ROUTER");
thread_bootstrap_all_nodes_multicast_register(cur);
thread_router_bootstrap_anycast_address_register(cur);
thread_router_bootstrap_active_router_attach(cur);
thread_bootstrap_child_id_request(cur);
if (thread_nd_own_service_list_data_size(&cur->thread_info->localServerDataBase)) {
// publish our services to allow leader to remove old ones
thread_border_router_publish(cur->id);
}
thread_router_bootstrap_address_change_notify_send(cur);
// Validate network data after a short period
thread_border_router_resubmit_timer_set(cur->id, 5);
break;
case THREAD_ATTACH_ROUTER_ID_GET_FAIL:
tr_debug_extra("Thread SM THREAD_ATTACH_ROUTER_ID_GET_FAIL");
tr_debug("Thread Router Id request Fail");
cur->nwk_bootstrap_state = ER_BOOTSRAP_DONE;
cur->thread_info->thread_attached_state = THREAD_STATE_CONNECTED;
thread_router_bootstrap_child_information_clear(cur);
thread_router_bootstrap_reed_advertisements_start(cur);
thread_router_bootstrap_child_id_reject(cur);
break;
case THREAD_ATTACH_ROUTER_ID_RELEASED:
tr_debug_extra("Thread SM THREAD_ATTACH_ROUTER_ID_RELEASED");
if (thread_nd_own_service_list_data_size(&cur->thread_info->localServerDataBase)) {
// publish our services to allow leader to remove old ones
thread_border_router_publish(cur->id);
}
break;
case THREAD_CHILD_ID_REQUEST:
tr_debug_extra("Thread SM THREAD_CHILD_ID_REQUEST");
thread_router_bootstrap_child_id_handler(cur);
break;
case THREAD_CHILD_UPDATE:
tr_debug_extra("Thread SM THREAD_CHILD_UPDATE");
thread_host_bootstrap_child_update(cur, cur->thread_info->thread_endnode_parent->mac64);
break;
case THREAD_ANNOUNCE_ACTIVE: {
tr_debug_extra("Thread SM THREAD_ANNOUNCE_ACTIVE");
if (cur->thread_info->announcement_info->count > 0) {
cur->thread_info->announcement_info->count--;
link_configuration_s *linkConfiguration = thread_joiner_application_get_config(cur->id);
if (!linkConfiguration) {
tr_error("No link configuration!");
break;
}
// New timeout needed
cur->thread_info->announcement_info->timer = eventOS_timeout_ms(thread_announce_ntf_cb, cur->thread_info->announcement_info->period, cur);
// Send announce_ntf
thread_bootstrap_announce_send(cur, linkConfiguration->channel_page, linkConfiguration->rfChannel, linkConfiguration->panId, linkConfiguration->timestamp, cur->thread_info->announcement_info->channel);
} else {
// Last call, delete announcement info
ns_dyn_mem_free(cur->thread_info->announcement_info);
cur->thread_info->announcement_info = NULL;
}
break;
}
default:
break;
}
}
int thread_bootstrap_tasklet_init(protocol_interface_info_entry_t *cur)
{
if (cur->bootStrapId < 0) {
cur->bootStrapId = eventOS_event_handler_create(&thread_tasklet, THREAD_INIT_EVENT);
tr_debug("Allocate Thread Tasklet");
}
if (cur->bootStrapId >= 0) {
return 0;
}
return -1;
}
int thread_proxy_validate(int8_t interface_id, uint8_t *addrerss)
{
ipv6_route_t *route;
route = ipv6_route_choose_next_hop(addrerss, interface_id, NULL);
if (!route) {
return -1;
}
if (route->prefix_len < 128 || !route->on_link) {
return -1;
}
return 0;
}
void thread_bootstrap_ready(protocol_interface_info_entry_t *cur)
{
cur->nwk_bootstrap_state = ER_BOOTSRAP_DONE;
cur->lowpan_info |= INTERFACE_NWK_BOOTSRAP_ACTIVE;
if (cur->thread_info->thread_attached_state == THREAD_STATE_CONNECTED_ROUTER) {
cur->ip_multicast_as_mac_unicast_to_parent = false;
cur->ip_forwarding = true;
} else {
cur->ip_multicast_as_mac_unicast_to_parent = true;
cur->ip_forwarding = false;
if (cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_ROUTER) {
/* REED should be able to IP forward */
cur->ip_forwarding = true;
} else {
cur->ip_forwarding = false;
}
}
tr_info("Set forwarding: ip=%d, multicast=%d", cur->ip_forwarding, thread_i_am_router(cur));
if (cur->ip_forwarding) {
addr_add_router_groups(cur);
}
// Bizarrely, Thread needs Realm-All-Routers active in FEDs for address resolution
if (cur->ip_forwarding || cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_FULL_END_DEVICE) {
addr_add_group(cur, ADDR_REALM_LOCAL_ALL_ROUTERS);
}
if (cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_ROUTER) {
if (cur->thread_info->thread_attached_state == THREAD_STATE_CONNECTED) {
thread_router_bootstrap_reed_advertisements_start(cur);
}
thread_bootstrap_mac_activate(cur, cur->mac_parameters->mac_channel, cur->mac_parameters->pan_id, true);
if (nd_proxy_downstream_interface_register(cur->id, thread_proxy_validate, thread_bbr_proxy_state_update) != 0) {
tr_debug("mesh proxy register fail");
}
}
if (cur->thread_info->leader_private_data) {
// Generate network data from network data structures
thread_leader_service_generate_network_data(cur);
}
if (thread_addresses_needs_to_be_registered(cur)) {
thread_info(cur)->childUpdateReqTimer = 1;
}
cur->bootsrap_state_machine_cnt = 0;
mac_data_poll_protocol_poll_mode_decrement(cur);
}
void thread_neighbor_list_clean(struct protocol_interface_info_entry *cur)
{
mac_neighbor_table_list_t *mac_table_list = &mac_neighbor_info(cur)->neighbour_list;
ns_list_foreach_safe(mac_neighbor_table_entry_t, cur_entry, mac_table_list) {
if (!thread_addr_is_equal_or_child(cur->thread_info->routerShortAddress, cur_entry->mac16)) {
tr_debug("Free ID %x", cur_entry->mac16);
mac_neighbor_table_neighbor_remove(mac_neighbor_info(cur), cur_entry);
}
}
}
void thread_reed_fed_neighbour_links_clean(struct protocol_interface_info_entry *cur)
{
mac_neighbor_table_list_t *mac_table_list = &mac_neighbor_info(cur)->neighbour_list;
if (thread_i_am_router(cur)) {
return;
}
if (thread_info(cur)->thread_device_mode == THREAD_DEVICE_MODE_END_DEVICE ||
thread_info(cur)->thread_device_mode == THREAD_DEVICE_MODE_SLEEPY_END_DEVICE) {
return;
}
if (!thread_info(cur)->thread_endnode_parent) {
return;
}
ns_list_foreach_safe(mac_neighbor_table_entry_t, cur_entry, mac_table_list) {
// do not remove parent entry
if (memcmp(cur_entry->mac64, thread_info(cur)->thread_endnode_parent->mac64, 8) != 0) {
tr_debug("Free short addr: %x", cur_entry->mac16);
mac_neighbor_table_neighbor_remove(mac_neighbor_info(cur), cur_entry);
}
}
}
void thread_clean_old_16_bit_address_based_addresses(protocol_interface_info_entry_t *cur)
{
uint8_t static_address[16];
//Delete old ULA16
memcpy(static_address, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, 8);
memcpy(&static_address[8], ADDR_SHORT_ADR_SUFFIC, 6);
common_write_16_bit(mac_helper_mac16_address_get(cur), &static_address[14]);
addr_delete(cur, static_address);
mac_helper_mac16_address_set(cur, 0xffff);
}
static int thread_bootstrap_attach_start(int8_t interface_id, thread_bootsrap_state_type_e state)
{
protocol_interface_info_entry_t *cur;
cur = protocol_stack_interface_info_get_by_id(interface_id);
if (!cur || !cur->thread_info) {
return -1;
}
//Trigger the bootstrap
nwk_thread_host_control(cur, NET_HOST_RX_ON_IDLE, 0);
switch (state) {
case THREAD_NORMAL_ATTACH:
cur->thread_info->thread_attached_state = THREAD_STATE_NETWORK_DISCOVER;
mac_helper_default_security_level_set(cur, cur->mac_parameters->mac_configured_sec_level);
mac_helper_default_security_key_id_mode_set(cur, MAC_KEY_ID_MODE_IDX);
break;
case THREAD_REATTACH:
tr_debug("Thread ReAttach");
//save the current partition id and sequence number before trying reattach
cur->thread_info->previous_partition_info.partitionId = cur->thread_info->thread_leader_data->partitionId;
cur->thread_info->previous_partition_info.weighting = cur->thread_info->thread_leader_data->weighting;
cur->thread_info->previous_partition_info.idSequence = cur->thread_info->routing.router_id_sequence;
cur->thread_info->routerShortAddress = mac_helper_mac16_address_get(cur);
if (cur->thread_info->thread_attached_state != THREAD_STATE_REATTACH_RETRY) {
cur->thread_info->thread_attached_state = THREAD_STATE_REATTACH;
}
break;
case THREAD_PARTITION_MERGE:
cur->thread_info->routerShortAddress = mac_helper_mac16_address_get(cur);
break;
case THREAD_ANY_ATTACH:
cur->thread_info->routerShortAddress = mac_helper_mac16_address_get(cur);
cur->thread_info->thread_attached_state = THREAD_STATE_ATTACH_ANY;
break;
case THREAD_REATTACH_REED:
cur->thread_info->releaseRouterId = true;
cur->thread_info->routerShortAddress = mac_helper_mac16_address_get(cur);
if (cur->thread_info->thread_attached_state != THREAD_STATE_REATTACH_RETRY) {
cur->thread_info->thread_attached_state = THREAD_STATE_REATTACH;
}
break;
}
// Set RX on idle
thread_end_device_mode_set(cur, false);
cur->nwk_nd_re_scan_count = 0;
cur->nwk_bootstrap_state = ER_SCAN;
cur->bootsrap_state_machine_cnt = randLIB_get_random_in_range(1, 10);
return 0;
}
static void thread_bootsrap_network_discovery_failure(int8_t interface_id)
{
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
if (!cur || !cur->thread_info) {
return;
}
//TODO we should send 3 in burst of 0.1 - 0.6 seconds and then do the exponential backup of 5s -- 80s
uint32_t backof_delay = cur->nwk_nd_re_scan_count * 2;
if (backof_delay > 600) {
backof_delay = 600; //TODO test this and check guess this is 100ms ticks
}
tr_debug("Continue network scan");
cur->nwk_bootstrap_state = ER_ACTIVE_SCAN;
cur->bootsrap_state_machine_cnt = backof_delay + randLIB_get_random_in_range(1, 6);
}
static void thread_bootstrap_generate_leader_and_link(protocol_interface_info_entry_t *cur)
{
if (cur->thread_info->thread_attached_state == THREAD_STATE_REATTACH) {
tr_debug("ReAttach Fail - retry");
thread_bootstrap_attach_start(cur->id, THREAD_REATTACH);
cur->thread_info->thread_attached_state = THREAD_STATE_REATTACH_RETRY;
} else if (cur->thread_info->thread_attached_state == THREAD_STATE_REATTACH_RETRY) {
tr_warn("ReAttach Fail");
thread_bootstrap_attach_start(cur->id, THREAD_ANY_ATTACH);
} else {
if (cur->thread_info->thread_attached_state == THREAD_STATE_NETWORK_DISCOVER) {
bootsrap_next_state_kick(ER_BOOTSTRAP_LEADER_UP, cur);
} else {
bootsrap_next_state_kick(ER_BOOTSTRAP_NEW_FRAGMENT_START, cur);
}
}
}
static int8_t thread_bootstrap_attempt_attach_with_pending_set(protocol_interface_info_entry_t *cur)
{
tr_debug("Attempting to attach with pending set");
uint32_t pending_delay_timer = thread_joiner_application_pending_config_timeout_get(cur->id);
if (pending_delay_timer > 0 && thread_joiner_application_pending_delay_timer_in_sync(cur->id)) {
tr_debug("We have a pending delay timer running");
//we already have a pending set that can be activated so return
return -1;
}
if (!thread_joiner_application_pending_config_exists(cur->id)) {
tr_debug("no pending configuration found after reset");
return -1;
}
if (thread_joiner_application_old_config_exists(cur->id)) {
//there is an existing old configuration so attempt to attach with it and set the pending timer to expire
thread_joiner_application_old_config_activate(cur->id);
thread_joiner_application_old_config_delete(cur->id);
thread_joiner_application_pending_config_enable(cur->id, 20000);
} else {
thread_joiner_pending_config_activate(cur->id);
}
return 0;
}
static void thread_bootstrap_orphan_scan_ready_cb(struct protocol_interface_info_entry *cur_interface, announce_discovery_response_t *discover_response)
{
if (!discover_response) {
thread_bootstrap_orphan_scan_start(cur_interface);
return;
}
link_configuration_s *link_configuration = thread_joiner_application_get_config(cur_interface->id);
if (!link_configuration) {
tr_debug("no link configuration found after reset");
return;
}
tr_debug("New configuration received channel %u, %x", discover_response->channel, discover_response->pan_id);
link_configuration->panId = discover_response->pan_id;
link_configuration->rfChannel = discover_response->channel;
link_configuration->channel_page = 0;
cur_interface->nwk_bootstrap_state = ER_ACTIVE_SCAN;
cur_interface->bootsrap_state_machine_cnt = 1;
ns_dyn_mem_free(discover_response);
}
static void thread_bootstrap_orphan_scan_start(struct protocol_interface_info_entry *cur)
{
thread_announce_discover_reques_t scan_req;
//default channel mask for channels 11 to 26
scan_req.channel_mask = 0x001fffe0;
#ifdef THREAD_THCI_SUPPORT
//use active operational dataset's channel mask if available
link_configuration_s *link_configuration = thread_joiner_application_get_config(cur->id);
if (!link_configuration) {
return;
}
scan_req.channel_mask = common_read_32_bit(link_configuration->channel_mask);
//the current channel is added to the mask.
scan_req.channel_mask |= (0x80000000 >> link_configuration->rfChannel);
#endif
scan_req.pan_id = cur->mac_parameters->pan_id;
scan_req.active_timestamp = 0;
scan_req.active_timestamp |= MESHCOP_TLV_ACTIVE_TIME_STAMP_U_BIT;
if (thread_discovery_announce_network_scan(cur->id, &scan_req, thread_bootstrap_orphan_scan_ready_cb) != 0) {
tr_debug("announce discover start fail");
thread_bootstrap_attach_start(cur->id, THREAD_NORMAL_ATTACH);
} else {
tr_debug("Orphan Host start announce scan");
}
return;
}
void thread_bootstrap_connection_error(int8_t interface_id, nwk_connect_error_types errorType, uint8_t *LinkId)
{
(void)LinkId;
protocol_interface_info_entry_t *cur;
switch (errorType) {
case CON_ERROR_POLL:
case CON_PARENT_CONNECT_DOWN:
thread_bootstrap_reset_restart(interface_id);
break;
case CON_ERROR_LINK_TX_FAIL:
//thread_mle_challenge_trig(interface_id,LinkId);
break;
case CON_ERROR_NETWORK_ATTACH_FAIL:
cur = protocol_stack_interface_info_get_by_id(interface_id);
if (!cur || !cur->thread_info) {
break;
}
if (thread_bootstrap_attempt_attach_with_pending_set(cur) == 0) {
thread_bootstrap_attach_start(interface_id, THREAD_NORMAL_ATTACH);
break;
}
if (cur->thread_info->announcement_info && cur->thread_info->announcement_info->timer &&
cur->thread_info->announcement_info->announce_success) {
// Attachment to announce failed we return to previous channel
link_configuration_s *linkConfiguration = thread_joiner_application_get_config(cur->id);
if (linkConfiguration) {
linkConfiguration->rfChannel = cur->thread_info->announcement_info->channel;
linkConfiguration->channel_page = cur->thread_info->announcement_info->channel_page;
linkConfiguration->panId = cur->thread_info->announcement_info->panid;
thread_joiner_application_link_configuration_store(cur->id, linkConfiguration);
} else {
tr_error("No link configuration!");
}
//set announce success flag to false because attach to new channel failed
cur->thread_info->announcement_info->announce_success = false;
thread_bootstrap_attach_start(interface_id, THREAD_NORMAL_ATTACH);
break;
}
if (cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_ROUTER) {
if (!thread_router_bootstrap_routing_allowed(cur)) {
thread_discovery_responser_enable(cur->id, false);
thread_bootstrap_orphan_scan_start(cur);
} else {
thread_bootstrap_generate_leader_and_link(cur);
}
} else {
thread_bootstrap_orphan_scan_start(cur);
}
break;
case CON_ERROR_NO_THREAD_NETWORK_AVAILABLE:
thread_bootsrap_network_discovery_failure(interface_id);
break;
case CON_ERROR_PARTITION_MERGE:
thread_bootstrap_attach_start(interface_id, THREAD_PARTITION_MERGE);
break;
case CON_ERROR_NETWORK_REATTACH:
thread_bootstrap_attach_start(interface_id, THREAD_REATTACH);
break;
case CON_ERROR_NEIGHBOR_UNREACHABLE:
break;
case CON_ERROR_NETWORK_KICK:
thread_bootstrap_reset_restart(interface_id);
break;
}
}
void thread_interface_up(protocol_interface_info_entry_t *cur)
{
thread_interface_init(cur);
thread_interface_bootsrap_mode_init(cur);
cur->nwk_nd_re_scan_count = 0;
}
int thread_bootstrap_reset(protocol_interface_info_entry_t *cur)
{
if (!cur || !cur->thread_info || (cur->lowpan_info & INTERFACE_NWK_ACTIVE) == 0) {
return -1;
}
if (cur->thread_info->thread_endnode_parent) {
ns_dyn_mem_free(cur->thread_info->thread_endnode_parent);
cur->thread_info->thread_endnode_parent = NULL;
}
neighbor_cache_flush(&cur->neigh_cache);
thread_bootstrap_stop(cur);
mac_neighbor_table_neighbor_list_clean(mac_neighbor_info(cur));
cur->bootsrap_state_machine_cnt = 0;
mac_helper_free_scan_confirm(&cur->mac_parameters->nwk_scan_params);
//tr_debug( "--> idle");
cur->lowpan_info &= ~INTERFACE_NWK_ROUTER_DEVICE;
rpl_control_remove_domain_from_interface(cur);
protocol_core_interface_info_reset(cur);
mac_data_poll_disable(cur);
//Prepare start Again
cur->lowpan_info |= INTERFACE_NWK_BOOTSRAP_ACTIVE; //Set Active Bootsrap
cur->lowpan_info &= ~INTERFACE_NWK_BOOTSRAP_ADDRESS_REGISTER_READY; //Clear Bind
protocol_6lowpan_interface_common_init(cur);
addr_interface_set_ll64(cur, NULL);
thread_interface_up(cur);
cur->nwk_mode = ARM_NWK_GP_IP_MODE;
cur->nwk_bootstrap_state = ER_ACTIVE_SCAN;
cur->nwk_nd_re_scan_count = 0;
if (cur->thread_info->thread_attached_state != THREAD_STATE_REATTACH_RETRY) {
cur->thread_info->thread_attached_state = THREAD_STATE_NETWORK_DISCOVER;
}
cur->ipv6_neighbour_cache.send_nud_probes = false; //Disable NUD probing
cur->ip_multicast_as_mac_unicast_to_parent = true;
//Define Default Contexts
if (cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_SLEEPY_END_DEVICE) {
nwk_thread_host_control(cur, NET_HOST_RX_ON_IDLE, 0);
}
thread_anycast_address_policy_update(cur->thread_info, true);
thread_bootstrap_state_machine(cur);
return 0;
}
void thread_generate_ml64_address(protocol_interface_info_entry_t *cur)
{
if_address_entry_t *def_address = NULL;
uint8_t ula[16];
memcpy(ula, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, 8);
//GENERATE ML-EID64
memcpy(&ula[8], cur->iid_slaac, 8);
def_address = addr_add(cur, ula, 64, ADDR_SOURCE_UNKNOWN, 0xffffffff, 0xffffffff, true);
if (def_address) {
tr_debug("Generated UL64: %s", trace_ipv6(ula));
}
}
void thread_delete_ml64_address(protocol_interface_info_entry_t *cur)
{
uint8_t ula[16];
memcpy(ula, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, 8);
memcpy(&ula[8], cur->iid_slaac, 8);
addr_delete(cur, ula);
}
void thread_generate_ml16_address(protocol_interface_info_entry_t *cur)
{
if_address_entry_t *def_address = NULL;
uint8_t ula[16];
uint16_t euid16 = mac_helper_mac16_address_get(cur);
if (euid16 == 0xffff) { // Safe guard
tr_debug("Do NOT generate ML16, mac16=%u", euid16);
return;
}
//GENERATE ML-16
memcpy(ula, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, 8);
memcpy(&ula[8], ADDR_SHORT_ADR_SUFFIC, 6);
common_write_16_bit(euid16, &ula[14]);
def_address = addr_add(cur, ula, 64, ADDR_SOURCE_UNKNOWN, 0xffffffff, 0xffffffff, true);
if (def_address) {
tr_debug("Generated ML16: %s", trace_ipv6(ula));
cur->global_address_available = true;
}
}
void thread_delete_ml16_addresses(protocol_interface_info_entry_t *cur)
{
uint8_t ula[16] = {0};
memcpy(ula, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, 8);
memcpy(&ula[8], ADDR_SHORT_ADR_SUFFIC, 6);
addr_delete_matching(cur, ula, 112, ADDR_SOURCE_UNKNOWN);
}
void thread_bootstrap_update_ml16_address(protocol_interface_info_entry_t *cur, uint16_t mac16)
{
thread_delete_ml16_addresses(cur);
mac_helper_mac16_address_set(cur, mac16);
thread_generate_ml16_address(cur);
thread_router_bootstrap_anycast_address_register(cur);
}
static void thread_meshlocal_route_set(protocol_interface_info_entry_t *cur)
{
cur->lowpan_info |= INTERFACE_NWK_ROUTER_DEVICE;
if (ipv6_route_add(cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, 64, cur->id, NULL, ROUTE_THREAD, 0xffffffff, 0) == NULL) {
tr_error("fail to add route");
}
}
void thread_bootstrap_attached_ready(protocol_interface_info_entry_t *cur)
{
thread_bootsrap_event_trig(THREAD_ATTACH_READY, cur->bootStrapId, ARM_LIB_HIGH_PRIORITY_EVENT);
}
void thread_bootstrap_attached_downgrade_router(protocol_interface_info_entry_t *cur)
{
thread_bootsrap_event_trig(THREAD_ATTACH_DOWNGRADE_ROUTER, cur->bootStrapId, ARM_LIB_HIGH_PRIORITY_EVENT);
}
void thread_bootstrap_attched_upgrade_reed(protocol_interface_info_entry_t *cur)
{
thread_bootsrap_event_trig(THREAD_ATTACH_UPGRADE_REED, cur->bootStrapId, ARM_LIB_HIGH_PRIORITY_EVENT);
}
void thread_bootstrap_attached_active_router(protocol_interface_info_entry_t *cur)
{
thread_bootsrap_event_trig(THREAD_ATTACH_ACTIVE_ROUTER, cur->bootStrapId, ARM_LIB_HIGH_PRIORITY_EVENT);
}
void thread_bootstrap_router_id_release_ready(protocol_interface_info_entry_t *cur)
{
thread_bootsrap_event_trig(THREAD_ATTACH_ROUTER_ID_RELEASED, cur->bootStrapId, ARM_LIB_HIGH_PRIORITY_EVENT);
}
void thread_bootstrap_router_id_get_fail(protocol_interface_info_entry_t *cur)
{
thread_bootsrap_event_trig(THREAD_ATTACH_ROUTER_ID_GET_FAIL, cur->bootStrapId, ARM_LIB_HIGH_PRIORITY_EVENT);
}
int8_t thread_bootstrap_child_id_request(protocol_interface_info_entry_t *cur)
{
return thread_bootsrap_event_trig(THREAD_CHILD_ID_REQUEST, cur->bootStrapId, ARM_LIB_HIGH_PRIORITY_EVENT);
}
void thread_bootstrap_routing_activate(protocol_interface_info_entry_t *cur)
{
if (cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_FULL_END_DEVICE ||
cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_ROUTER) {
thread_meshlocal_route_set(cur);
// FEDs and routers (REEDs) perform their own address resolution
thread_nd_service_activate(cur->id);
} else {
thread_nd_client_service_activate(cur->id);
thread_child_set_default_route(cur);
}
}
void thread_bootstrap_attached_finish(protocol_interface_info_entry_t *cur)
{
cur->nwk_bootstrap_state = ER_MLE_ATTACH_READY;
cur->lowpan_info |= INTERFACE_NWK_BOOTSRAP_ADDRESS_REGISTER_READY;
cur->lowpan_info &= ~INTERFACE_NWK_ROUTER_DEVICE;
cur->bootsrap_state_machine_cnt = 10;
cur->thread_info->routerIdRequested = false;
cur->thread_info->networkDataRequested = false;
clear_power_state(ICMP_ACTIVE);
//Free scanned Result
if (cur->thread_info->thread_attach_scanned_parent) {
mle_service_msg_free(cur->thread_info->thread_attach_scanned_parent->child_id_request_id);
ns_dyn_mem_free(cur->thread_info->thread_attach_scanned_parent);
cur->thread_info->thread_attach_scanned_parent = NULL;
}
// Make local/nwk data check after 5s
thread_border_router_resubmit_timer_set(cur->id, 5);
//Generate UL16
thread_generate_ml16_address(cur);
//GENERATE ML-EID64
thread_generate_ml64_address(cur);
thread_bootstrap_routing_activate(cur);
thread_bootstrap_network_data_update(cur);
// After successful attach if there is announcement info present, send announcement back to previous channel
if (cur->thread_info->announcement_info && cur->thread_info->announcement_info->announce_success == false) {
cur->thread_info->announcement_info->timer = eventOS_timeout_ms(thread_announce_success_cb, 20000, cur);
}
thread_configuration_mle_activate(cur);
if (cur->thread_info->releaseRouterId) {
thread_router_bootstrap_router_id_release(cur);
}
uint8_t *parent_mac_addr = NULL;
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));
thread_bootstrap_ready(cur);
if (thread_is_router_addr(mac_helper_mac16_address_get(cur))) {
// Attached as router Trigger routter attach
tr_info("Attaching directly to router");
thread_bootstrap_attached_active_router(cur);
}
}
//This function is for Thread Parent scan callback
bool thread_network_data_timeout(int8_t interface_id, uint16_t msgId, bool usedAllRetries)
{
protocol_interface_info_entry_t *cur;
(void)msgId;
cur = protocol_stack_interface_info_get_by_id(interface_id);
if (!cur || !cur->thread_info) {
return false;
}
/* If network data is not received, send again */
if (thread_info(cur)->networkDataRequested && !usedAllRetries) {
return true;
}
if (cur->thread_info->leader_synced) {
if (usedAllRetries) {
// could not learn network data from neighbour, everyone must reregister
cur->thread_info->leader_synced = false;
thread_leader_service_network_data_changed(cur, true, true);
return false;
} else {
tr_debug("retrying as leader data not yet synced");
return true;
}
}
// if REED fails to get updated network data, it reattaches
if (thread_info(cur)->networkDataRequested && !thread_attach_active_router(cur) && usedAllRetries) {
thread_bootstrap_reset_restart(interface_id);
}
thread_info(cur)->networkDataRequested = false;
mac_data_poll_protocol_poll_mode_decrement(cur);
return false;
}
bool thread_tlv_request(int8_t interface_id, uint8_t *address, bool delayed_message, uint8_t *req_tlv, uint8_t req_len)
{
mle_message_timeout_params_t timeout;
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
uint16_t buf_id;
buf_id = mle_service_msg_allocate(interface_id, 32 + 20 + thread_leader_data_tlv_size(cur), false, MLE_COMMAND_DATA_REQUEST);
if (!cur || buf_id == 0) {
return false;
}
uint8_t *ptr = mle_service_get_data_pointer(buf_id);
ptr = mle_tlv_req_tlv(ptr, req_tlv, req_len);
//SET Leader Data
ptr = thread_leader_data_tlv_write(ptr, cur);
ptr = thread_active_timestamp_write(cur, ptr);
ptr = thread_pending_timestamp_write(cur, ptr);
if (mle_service_update_length_by_ptr(buf_id, ptr) != 0) {
tr_debug("Buffer overflow at message write");
}
timeout.retrans_max = THREAD_REQUEST_MAX_RETRY_CNT;
timeout.timeout_init = 1;
timeout.timeout_max = 3;
if (delayed_message) {
timeout.delay = MLE_STANDARD_RESPONSE_DELAY;
} else {
timeout.delay = MLE_NO_DELAY;
}
mac_data_poll_init_protocol_poll(cur);
mle_service_set_packet_callback(buf_id, thread_network_data_timeout);
mle_service_set_msg_destination_address(buf_id, address);
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 true;
}
void thread_bootstrap_parent_network_data_request(protocol_interface_info_entry_t *cur, bool delay_request)
{
uint8_t dst_address[16];
memcpy(dst_address, ADDR_LINK_LOCAL_PREFIX, 8);
memcpy(&dst_address[8], thread_info(cur)->thread_endnode_parent->mac64, 8);
dst_address[8] ^= 2;
thread_network_data_request_send(cur, dst_address, delay_request);
}
void thread_parent_scan(protocol_interface_info_entry_t *cur)
{
cur->thread_info->thread_attached_state = THREAD_STATE_NETWORK_DISCOVER;
cur->nwk_nd_re_scan_count = 0;
cur->nwk_bootstrap_state = ER_SCAN;
cur->bootsrap_state_machine_cnt = randLIB_get_random_in_range(5, 15);
nwk_thread_host_control(cur, NET_HOST_RX_ON_IDLE, 0);
}
void thread_bootstrap_joiner_application_commission_done_cb(int8_t interface_id)
{
protocol_interface_info_entry_t *interface;
interface = protocol_stack_interface_info_get_by_id(interface_id);
if (!interface) {
return;
}
tr_debug("Commission Ready trig bootsrap to starting from init again");
interface->nwk_bootstrap_state = ER_ACTIVE_SCAN;
interface->bootsrap_state_machine_cnt = randLIB_get_random_in_range(1, 4);
}
static int compare_steering_and_joiner_bloom(uint8_t *steering_bloom, uint8_t *joiner_bloom, uint8_t steering_tlv_length)
{
//make sure s bit is not checked
int loop_iterator;
tr_debug("joiner bloom : %s", trace_array(joiner_bloom, steering_tlv_length));
tr_debug("steering bloom : %s", trace_array(steering_bloom, steering_tlv_length));
for (loop_iterator = 0; loop_iterator < steering_tlv_length; loop_iterator++) {
if ((joiner_bloom[loop_iterator] != (joiner_bloom[loop_iterator] & steering_bloom[loop_iterator]))) {
thci_trace("joinerDiscoveryFailedFiltered");
return 0;
}
}
return 1;
}
static bool thread_route_possible_add(thread_attach_device_mode_e threadMode)
{
bool addRoute;
if (threadMode == THREAD_DEVICE_MODE_ROUTER ||
threadMode == THREAD_DEVICE_MODE_FULL_END_DEVICE) {
addRoute = true;
} else {
addRoute = false;
}
return addRoute;
}
static void thread_dhcp_client_gua_error_cb(int8_t interface, uint8_t dhcp_addr[static 16], uint8_t prefix[static 16], bool register_status)
{
if (register_status) {
tr_debug("Get Address %s From %s", trace_ipv6(prefix), trace_ipv6(dhcp_addr));
} else {
tr_warn("Address Get fail: %s from: %s", trace_ipv6(prefix), trace_ipv6(dhcp_addr));
if (prefix && dhcp_addr) {
tr_debug("Delete Current Server data");
dhcp_client_global_address_delete(interface, dhcp_addr, prefix);
}
}
}
bool thread_dhcpv6_address_entry_available(uint8_t *prefixPtr, if_address_list_t *list)
{
bool addressReady = false;
ns_list_foreach(if_address_entry_t, entry, list) {
if (memcmp(entry->address, prefixPtr, 8) == 0) {
addressReady = true;
break;
}
}
return addressReady;
}
static int thread_bloom_and_compare(uint8_t *steering_data_ptr, uint8_t steering_data_length, uint8_t *eui64, uint8_t eui64_length)
{
if (steering_data_length == 0 || steering_data_length > 16) {
return 0;
}
uint8_t joiner_bloom_calculated[16] = {0};
uint8_t mac_extended_address[8];
ns_sha256_nbits(eui64, eui64_length, mac_extended_address, 64);
mac_extended_address[0] |= 2; //local administered bit is set
thread_beacon_calculate_bloom_filter(joiner_bloom_calculated, steering_data_length, mac_extended_address, 8);
return compare_steering_and_joiner_bloom(steering_data_ptr, joiner_bloom_calculated, steering_data_length);
}
static void thread_network_select_by_steering_data(device_configuration_s *device_configuration_ptr, thread_nwk_discovery_response_list_t *discover_response)
{
ns_list_foreach_safe(discovery_response_list_t, cur_class, discover_response) {
if (!thread_bloom_and_compare(cur_class->steering_data, cur_class->steering_data_valid, device_configuration_ptr->eui64, 8)) {
ns_list_remove(discover_response, cur_class);
ns_dyn_mem_free(cur_class);
}
}
}
static void thread_network_select(struct protocol_interface_info_entry *interface_ptr, device_configuration_s *device_configuration_ptr, thread_nwk_discovery_response_list_t *discover_response)
{
(void) interface_ptr;
discovery_response_list_t *discovered_network_ptr = thread_extension_bootstrap_network_select(interface_ptr, discover_response);
/* If network found */
if (discovered_network_ptr) {
/* Clear other networks from list */
ns_list_foreach_safe(discovery_response_list_t, cur_class, discover_response) {
if (cur_class != discovered_network_ptr) {
ns_list_remove(discover_response, cur_class);
ns_dyn_mem_free(cur_class);
}
}
} else {
thread_network_select_by_steering_data(device_configuration_ptr, discover_response);
}
}
void thread_bootsrap_discovery_ready_cb(struct protocol_interface_info_entry *cur_interface, thread_nwk_discovery_response_list_t *discover_response)
{
device_configuration_s *device_configuration_ptr = thread_joiner_application_get_device_config(cur_interface->id);
if (!device_configuration_ptr) {
tr_error("Mac scan confirm:Unknow Interface");
thci_trace("joinerDiscoveryFailedNoBeacon");
return;
}
thread_network_select(cur_interface, device_configuration_ptr, discover_response);
if (!ns_list_count(discover_response)) {
tr_debug("NO network available for scan");
thci_trace("joinerDiscoveryFailedNoBeacon");
goto exit_failure;
}
cur_interface->nwk_bootstrap_state = ER_ACTIVE_SCAN;
cur_interface->bootsrap_state_machine_cnt = 1;
return;
exit_failure:
thread_bootstrap_connection_error(cur_interface->id, CON_ERROR_NO_THREAD_NETWORK_AVAILABLE, NULL);
}
static void thread_bootstrap_create_unsecure_link_to_parent(protocol_interface_info_entry_t *interface, discovery_response_list_t *nwk_info)
{
mlme_start_t start_req;
memset(&start_req, 0, sizeof(mlme_start_t));
mac_helper_coordinator_address_set(interface, ADDR_802_15_4_LONG, nwk_info->extented_mac);
interface->mac_parameters->pan_id = nwk_info->pan_id;
interface->mac_parameters->mac_channel = nwk_info->channel;
start_req.PANId = nwk_info->pan_id;
start_req.LogicalChannel = nwk_info->channel;
start_req.ChannelPage = 0;
start_req.BeaconOrder = 0x0f;
start_req.SuperframeOrder = 0x0f;
//SET Beacon Payload
mac_helper_beacon_payload_reallocate(interface, 0);
interface->mac_api->mlme_req(interface->mac_api, MLME_START, (void *)&start_req);
mac_data_poll_init(interface);
mac_helper_mac16_address_set(interface, 0xffff);
tr_debug("Mac Ready");
}
void thread_discover_native_commissioner_response(protocol_interface_info_entry_t *interface, thread_nwk_discovery_response_list_t *nwk_info)
{
thread_commissioning_link_configuration_s *config_ptr;
int n = 0;
if (ns_list_count(nwk_info) == 0) {
tr_debug("Thread discover:No nwk");
goto exit_failure;
}
// create list of available networks for native commissioner interface
config_ptr = ns_dyn_mem_alloc(sizeof(thread_commissioning_link_configuration_s) * ns_list_count(nwk_info));
if (!config_ptr) {
tr_debug("Mac scan confirm:out of resources");
goto exit_failure;
}
ns_list_foreach(discovery_response_list_t, cur_class, nwk_info) {
config_ptr[n].panId = cur_class->pan_id;
config_ptr[n].Protocol_id = THREAD_PROTOCOL_ID;
config_ptr[n].version = THREAD_PROTOCOL_VERSION;
config_ptr[n].rfChannel = cur_class->channel;
memcpy(config_ptr[n].name, cur_class->network_name, 16);
memcpy(config_ptr[n].extented_pan_id, cur_class->extented_pan_id, 8);
memcpy(config_ptr[n].destination_address, ADDR_LINK_LOCAL_PREFIX, 8);
memcpy(&config_ptr[n].destination_address[8], cur_class->extented_mac, 8);
config_ptr[n].destination_address[8] ^= 2;
config_ptr[n].destination_port = cur_class->commissioner_port;
n++;
}
if (interface->thread_info->native_commissioner_cb) {
interface->thread_info->native_commissioner_cb(interface->id, n, config_ptr);
}
ns_dyn_mem_free(config_ptr);
if (!interface->thread_info->native_commissioner_link) {
tr_debug("Mac scan confirm:continue scanning");
goto exit_failure;
}
//Free if not matching network found
ns_list_foreach_safe(discovery_response_list_t, cur_class, nwk_info) {
if (cur_class->version != THREAD_PROTOCOL_VERSION ||
cur_class->pan_id != interface->thread_info->native_commissioner_link->panId ||
memcmp(interface->thread_info->native_commissioner_link->name, cur_class->network_name, 16) != 0 ||
memcmp(interface->thread_info->native_commissioner_link->extented_pan_id, cur_class->extented_pan_id, 8) != 0) {
ns_list_remove(nwk_info, cur_class);
ns_dyn_mem_free(cur_class);
}
}
if (ns_list_is_empty(nwk_info)) {
tr_debug("Mac scan confirm:no networks available");
goto exit_failure;
}
//select best parent link
discovery_response_list_t *best = NULL;
ns_list_foreach_safe(discovery_response_list_t, cur_class, nwk_info) {
ns_list_remove(nwk_info, cur_class);
if (!best || (best && best->dbm < cur_class->dbm)) {
if (best) {
ns_dyn_mem_free(cur_class);
}
best = cur_class;
} else {
ns_dyn_mem_free(cur_class);
}
}
// best is always not null, because the not empty check above
/* coverity[FORWARD_NULL] */
interface->thread_info->native_commissioner_port = best->commissioner_port;
thread_bootstrap_create_unsecure_link_to_parent(interface, best);
ns_dyn_mem_free(best);
/**
* we should stop scanning and saying that the interface is up
*/
tr_debug("Native commissioning interface ready");
interface->thread_info->thread_attached_state = THREAD_STATE_CONNECTED;
interface->nwk_bootstrap_state = ER_MLE_ATTACH_READY;
interface->lowpan_info |= INTERFACE_NWK_BOOTSRAP_ADDRESS_REGISTER_READY;
interface->lowpan_info &= ~INTERFACE_NWK_ROUTER_DEVICE;
interface->thread_info->routerIdRequested = false;
interface->thread_info->networkDataRequested = false;
interface->bootsrap_state_machine_cnt = 10;
clear_power_state(ICMP_ACTIVE);
thread_bootstrap_ready(interface);
return;
exit_failure:
thread_bootstrap_connection_error(interface->id, CON_ERROR_NO_THREAD_NETWORK_AVAILABLE, NULL);
}
static void thread_bootsrap_network_join_start(struct protocol_interface_info_entry *cur_interface, discovery_response_list_t *nwk_info)
{
device_configuration_s *device_configuration_ptr = thread_joiner_application_get_device_config(cur_interface->id);
uint8_t parent_ll_addr[16];
memcpy(parent_ll_addr, ADDR_LINK_LOCAL_PREFIX, 8);
memcpy(parent_ll_addr + 8, nwk_info->extented_mac, 8);
parent_ll_addr[8] ^= 2;
if (!device_configuration_ptr || blacklist_reject(parent_ll_addr)) {
ns_dyn_mem_free(nwk_info);
thci_trace("joinerDiscoveryFailedNoBeacon");
thread_bootstrap_connection_error(cur_interface->id, CON_ERROR_NO_THREAD_NETWORK_AVAILABLE, NULL);
return;
}
uint8_t private_mac[8];
ns_sha256_nbits(device_configuration_ptr->eui64, 8, private_mac, 64);
private_mac[0] |= 2; //local administered bit is set
tr_debug("joiner mac id : %s", trace_array(private_mac, 8));
mac_helper_mac64_set(cur_interface, private_mac);
thread_set_link_local_address(cur_interface); // only to generate IID
thread_bootstrap_create_unsecure_link_to_parent(cur_interface, nwk_info);
thci_trace("joinerDiscoverySuccess");
uint8_t parentLLAddress[16];
protocol_6lowpan_interface_get_link_local_cordinator_address(cur_interface, parentLLAddress);
tr_debug("Start commission with %s", trace_ipv6(parentLLAddress));
cur_interface->bootsrap_state_machine_cnt = 0;
if (0 > thread_extension_bootstrap_commission_start(cur_interface, parentLLAddress, nwk_info, thread_bootstrap_joiner_application_commission_done_cb)) {
thread_joiner_application_pskd_commission_start(cur_interface->id, parentLLAddress, nwk_info->joiner_port, nwk_info->pan_id, nwk_info->extented_pan_id, nwk_info->channel, thread_bootstrap_joiner_application_commission_done_cb);
}
ns_dyn_mem_free(nwk_info);
}
void thread_bootsrap_device_synch_fail(protocol_interface_info_entry_t *cur)
{
tr_debug("Link Synch Fail -->Parent Scan Start");
thread_bootstrap_clear_neighbor_entries(cur);
thread_network_synch_data_free(cur->id);
thread_delete_ml64_address(cur);
thread_parent_scan(cur);
}
bool thread_device_synch_timeout(int8_t interface_id, uint16_t msgId, bool usedAllRetries)
{
protocol_interface_info_entry_t *interface = protocol_stack_interface_info_get_by_id(interface_id);
(void)msgId;
if (!interface) {
return false;
}
if (usedAllRetries) {
thread_bootsrap_device_synch_fail(interface);
return false;
}
return true;
}
bool thread_link_request_timeout(int8_t interface_id, uint16_t msgId, bool usedAllRetries)
{
protocol_interface_info_entry_t *interface = protocol_stack_interface_info_get_by_id(interface_id);
if (!interface) {
return false;
}
if (usedAllRetries) {
uint8_t *addr = mle_service_get_msg_destination_address_pointer(msgId);
tr_debug("Link Request timed out, address: %s", trace_ipv6(addr));
blacklist_update(addr, false);
return false;
}
return true;
}
static bool thread_bootstrap_sync_after_reset_start(protocol_interface_info_entry_t *cur)
{
uint16_t my_short_address;
uint8_t parent_mac64[8];
// link sync is allowed only once in bootstrap start and we might get here in other cases also
if (!cur->thread_info->link_sync_allowed) {
return false;
}
cur->thread_info->link_sync_allowed = false;
int link_info_err = thread_nvm_store_link_info_get(parent_mac64, &my_short_address);
if (link_info_err != THREAD_NVM_FILE_SUCCESS) {
tr_warning("thread_nvm_store_link_info_get returned %d", link_info_err);
return false;
}
link_info_err = thread_nvm_store_link_info_clear();
if (link_info_err != THREAD_NVM_FILE_SUCCESS) {
tr_warning("thread_nvm_store_link_info_clear returned %d", link_info_err);
}
if (thread_is_router_addr(my_short_address)) {
thread_info(cur)->routerShortAddress = my_short_address;
thread_dynamic_storage_build_mle_table(cur->id);
thread_router_bootstrap_link_synch_start(cur);
return true;
}
if (!thread_parent_data_allocate(cur->thread_info)) {
tr_info("parent alloc failed");
return false;
}
cur->thread_info->thread_endnode_parent->shortAddress = 0xfffe;
memcpy(cur->thread_info->thread_endnode_parent->mac64, parent_mac64, 8);
thread_endevice_synch_start(cur);
return true;
}
void thread_bootstrap_start_network_discovery(protocol_interface_info_entry_t *cur)
{
thread_discover_reques_t scan_request;
thread_discovery_ready_cb *discover_ready;
cur->nwk_nd_re_scan_count++;
cur->mac_parameters->nwk_scan_params.stack_chan_list = cur->mac_parameters->mac_channel_list;
if (cur->thread_info->native_commissioner_cb) {
tr_debug("native commissioner network scan start");
discover_ready = thread_discover_native_commissioner_response;
scan_request.joiner_flag = false;
scan_request.native_commisioner = true;
} else {
discovery_response_list_t *thread_network = thread_discovery_network_description_get(cur->id);
if (thread_network) {
thread_bootsrap_network_join_start(cur, thread_network);
return;
}
tr_debug("scan networks for joining application");
thci_trace("joinerDiscoveryStarted");
blacklist_params_set(
THREAD_COMM_BLACKLIST_ENTRY_LIFETIME,
THREAD_COMM_BLACKLIST_TIMER_MAX_TIMEOUT,
THREAD_COMM_BLACKLIST_TIMER_TIMEOUT,
THREAD_BLACKLIST_ENTRY_MAX_NBR,
THREAD_BLACKLIST_PURGE_NBR,
THREAD_BLACKLIST_PURGE_TIMER_TIMEOUT);
scan_request.native_commisioner = false;
scan_request.joiner_flag = true;
discover_ready = thread_bootsrap_discovery_ready_cb;
}
scan_request.channel_mask = cur->mac_parameters->nwk_scan_params.stack_chan_list.channel_mask[0];
scan_request.filter_tlv_data = NULL;
scan_request.filter_tlv_length = 0;
if (thread_discovery_network_scan(cur, &scan_request, discover_ready) != 0) {
tr_error("Discovery scan start fail");
}
}
void thread_bootstrap_state_machine(protocol_interface_info_entry_t *cur)
{
link_configuration_s *linkConfiguration;
switch (cur->nwk_bootstrap_state) {
case ER_ACTIVE_SCAN:
tr_debug("Thread SM:Active Scan");
thread_joiner_application_nvm_link_configuration_load(cur->id);
linkConfiguration = thread_joiner_application_get_config(cur->id);
if (!linkConfiguration) {
thread_bootstrap_start_network_discovery(cur);
return;
}
//SET Link by Static configuration
tr_info("thread network attach start");
if (thread_mle_service_register(cur, thread_joiner_application_random_mac_get(cur->id)) != 0 ||
thread_link_configuration_activate(cur, linkConfiguration) != 0) {
tr_error("Network Bootsrap Start Fail");
bootsrap_next_state_kick(ER_BOOTSTRAP_SCAN_FAIL, cur);
return;
}
if (thread_bootstrap_sync_after_reset_start(cur)) {
// Link syncronisation started
tr_debug("link synchronisation start");
return;
}
tr_debug("Parent Scan Start");
thread_parent_scan(cur);
break;
case ER_SCAN:
tr_debug("Thread SM:Start ER Scan");
cur->nwk_nd_re_scan_count = 0;
thread_network_attach_start(cur);
break;
case ER_MLE_ATTACH_READY:
case ER_MLE_SYNCH:
case ER_MLE_SCAN:
case ER_CHILD_ID_REQ:
tr_debug("Thread SM:Attach Ready");
break;
case ER_BOOTSRAP_DONE:
tr_debug("Thread SM:Bootstrap Done");
cur->nwk_nd_re_scan_count = 0;
break;
case ER_BOOTSTRAP_SCAN_FAIL:
tr_debug("Thread SM:Scan Failed");
nwk_bootsrap_state_update(ARM_NWK_NWK_SCAN_FAIL, cur);
break;
case ER_BOOTSTRAP_LEADER_UP:
tr_debug("Thread SM:Leader Start");
thread_leader_service_thread_partitition_generate(cur->id, false);
break;
case ER_BOOTSTRAP_NEW_FRAGMENT_START:
tr_debug("Thread SM:Create New fragment");
thread_leader_service_thread_partitition_generate(cur->id, true);
break;
default:
tr_warn("Thread SM:Invalid state");
}
}
void thread_bootstrap_stop(protocol_interface_info_entry_t *cur)
{
thread_address_registration_deinit();
thread_anycast_address_policy_update(cur->thread_info, false);
ipv6_route_table_remove_info(cur->id, ROUTE_THREAD, NULL);
ipv6_route_table_remove_info(cur->id, ROUTE_THREAD_BORDER_ROUTER, NULL);
ipv6_route_table_remove_info(cur->id, ROUTE_THREAD_PROXIED_HOST, NULL);
ipv6_route_table_remove_info(cur->id, ROUTE_THREAD_PROXIED_DUA_HOST, NULL);
thread_leader_service_leader_data_free(cur->thread_info);
thread_bootstrap_all_nodes_multicast_unregister(cur);
thread_data_base_init(cur->thread_info, cur->id);
dhcp_client_delete(cur->id);
thread_nd_service_delete(cur->id);
thread_child_id_request_entry_clean(cur);
thread_registered_mcast_addr_entry_clean(cur);
cur->mesh_callbacks = NULL;
}
void thread_bootstrap_child_update_trig(protocol_interface_info_entry_t *cur)
{
if (cur->thread_info->thread_attached_state == THREAD_STATE_CONNECTED && cur->thread_info->thread_endnode_parent) {
thread_bootsrap_event_trig(THREAD_CHILD_UPDATE, cur->bootStrapId, ARM_LIB_HIGH_PRIORITY_EVENT);
}
}
static void thread_border_router_locator_copy(protocol_interface_info_entry_t *cur, thread_commissioner_t *registered_commissioner, uint8_t *data)
{
memcpy(registered_commissioner->border_router_address, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, 8);
memcpy(&registered_commissioner->border_router_address[8], ADDR_SHORT_ADR_SUFFIC, 6);
memcpy(&registered_commissioner->border_router_address[14], data, 2);
}
/* \return -1 Failed
* 0 OK, data not changed
* 1 OK, data changed */
static int thread_commission_data_tlv_parse(protocol_interface_info_entry_t *cur, uint8_t type, uint8_t length, uint8_t *data)
{
thread_commissioner_t *registered_commissioner = &cur->thread_info->registered_commissioner;
switch (type) {
// This is set by leader
case THREAD_TLV_COMMISSIONER_SESSION_ID:
if (length != 2) {
return -1;
}
if (registered_commissioner->session_id != common_read_16_bit(data)) {
registered_commissioner->session_id = common_read_16_bit(data);
return 1;
}
break;
case THREAD_TLV_STEERING_DATA:
if (length > 16) {
return -1;
}
if (registered_commissioner->steering_data_len != length || memcmp(registered_commissioner->steering_data, data, length)) {
memcpy(registered_commissioner->steering_data, data, length);
registered_commissioner->steering_data_len = length;
return 1;
}
break;
case THREAD_TLV_BORDER_ROUTER_LOCATOR:
if (length != 2) {
return -1;
}
registered_commissioner->commissioner_valid = true;
if (!registered_commissioner->commissioner_valid ||
memcmp(&registered_commissioner->border_router_address[14], data, 2) ||
memcmp(registered_commissioner->border_router_address, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, 8)) {
thread_border_router_locator_copy(cur, registered_commissioner, data);
return 1;
}
break;
default:
break;
}
return 0;
}
static int thread_nd_prefix_context_allocate(protocol_interface_info_entry_t *cur, thread_prefix_tlv_t *prefixTlv, bool stableData)
{
thread_network_local_data_context_entry_t context;
uint8_t cid;
//check possible context id or current allocated
cid = thread_nd_context_id_allocate(&cur->thread_info->networkDataStorage, &cur->thread_info->localServerDataBase, prefixTlv->Prefix, prefixTlv->PrefixLen);
if (cid == 16) {
return -1;
}
context.cid = cid;
context.compression = true;
context.stableData = stableData;
context.contextPrefixLength = prefixTlv->PrefixLen;
if (lowpan_context_update(&cur->lowpan_contexts, cid, 0xffff, prefixTlv->Prefix, prefixTlv->PrefixLen, stableData) != 0) {
return -1;
}
if (thread_nd_local_list_add_contexts(&cur->thread_info->networkDataStorage, prefixTlv, &context) != 0) {
//Free context if allocation fail
lowpan_context_update(&cur->lowpan_contexts, cid, 0, prefixTlv->Prefix, prefixTlv->PrefixLen, stableData);
return -1;
}
return 0;
}
int thread_bootstrap_network_data_process(protocol_interface_info_entry_t *cur, uint8_t *network_data_ptr, uint16_t network_data_length)
{
int ret_val = 0;
uint8_t addr[16];
uint8_t *dptr;
uint16_t length, data_length, flags;
uint8_t prefix_bytes_len, subLength, preference;
uint8_t type;
bool stableData;
bool update_data = false;
thread_prefix_tlv_t prefixTlv;
thread_network_local_data_context_entry_t context;
thread_border_router_tlv_entry_t genericService;
thread_network_data_cache_entry_t *networkDataStorage = &cur->thread_info->networkDataStorage;
link_configuration_s *linkConfiguration = thread_joiner_application_get_config(cur->id);
if (!linkConfiguration) {
return -1;
}
data_length = network_data_length;
dptr = network_data_ptr;
tr_debug("Start Parsing TLV one by One");
while (data_length) {
tr_debug("Main TLV: %s", trace_array(dptr, data_length));
prefixTlv.Prefix = NULL;
prefixTlv.PrefixLen = 0;
type = *dptr++;
if (type & THREAD_NWK_STABLE_DATA) {
stableData = true;
} else {
stableData = false;
}
type &= THREAD_NWK_DATA_TYPE_MASK;
length = *dptr++;
if ((length + 2) > data_length) {
return -1;
}
//SET Pointer & Length ready for next check
data_length -= (length + 2);
if (!stableData && !cur->thread_info->requestFullNetworkData) {
//Skip unstable data when it not requsted
dptr += length;
} else if (type == THREAD_NWK_DATA_TYPE_PREFIX) {
prefixTlv.domainId = *dptr++;
prefixTlv.PrefixLen = *dptr++;
//Decrement length by prefix length
length -= 2;
prefixTlv.Prefix = dptr;
prefix_bytes_len = prefixBits_to_bytes(prefixTlv.PrefixLen);
if (prefix_bytes_len > length) {
return -1;
}
length -= prefix_bytes_len;
dptr += prefix_bytes_len;
tr_debug("Prefix: %s", trace_ipv6_prefix(prefixTlv.Prefix, prefixTlv.PrefixLen));
if (thread_network_data_sub_tlv_malformed_check(dptr, length) == 0) {
while (length > 2) {
type = *dptr++;
subLength = *dptr++;
length -= 2;
if (type & THREAD_NWK_STABLE_DATA) {
stableData = true;
} else {
stableData = false;
}
type &= THREAD_NWK_DATA_TYPE_MASK;
tr_debug("SubType: %02x, %s", type, trace_array(dptr, subLength));
if (subLength <= length) {
length -= subLength;
if (type == THREAD_NWK_DATA_TYPE_6LOWPAN_ID) {
while (subLength) {
if (*dptr & THREAD_NWK_CONTEXT_COMPRESS_ENABLED) {
context.compression = true;
} else {
context.compression = false;
}
context.cid = (*dptr++ & 0x0f);
context.contextPrefixLength = *dptr++;
context.stableData = stableData;
subLength -= 2;
if (thread_nd_verify_contex_id_is_free(networkDataStorage, prefixTlv.Prefix, &context) == 0) {
thread_nd_local_list_add_contexts(networkDataStorage, &prefixTlv, &context);
} else {
tr_debug("CID reserved already");
}
}
} else {
while (subLength) {
genericService.routerID = common_read_16_bit(dptr);
dptr += 2;
if (type == THREAD_NWK_DATA_TYPE_BORDER_ROUTER) {
flags = common_read_16_bit(dptr);
dptr += 2;
subLength -= THREAD_BORDER_ROUTER_TLV_LENGTH;
genericService.Prf = ((flags >> THREAD_PRF_BIT_MOVE) & 3);
} else {
/* HAS_ROUTE */
preference = *dptr++;
subLength -= THREAD_HAS_ROUTE_TLV_LENGTH;
genericService.Prf = ((preference >> THREAD_HAS_ROUTE_PRF_BIT_MOVE) & 3);
}
genericService.stableData = stableData;
if (type == THREAD_NWK_DATA_TYPE_BORDER_ROUTER) {
genericService.P_configure = ((flags >> THREAD_P_CONFIGURE_BIT_MOVE) & 1);
genericService.P_default_route = ((flags >> THREAD_P_DEF_ROUTE_BIT_MOVE) & 1);
genericService.P_dhcp = ((flags >> THREAD_P_DHCP_BIT_MOVE) & 1);
genericService.P_preferred = ((flags >> THREAD_P_PREFERRED_BIT_MOVE) & 1);
genericService.P_slaac = ((flags >> THREAD_P_SLAAC_BIT_MOVE) & 1);
genericService.P_on_mesh = ((flags >> THREAD_P_ON_MESH_BIT_MOVE) & 1);
genericService.P_nd_dns = ((flags >> THREAD_P_ND_DNS_BIT_MOVE) & 1);
genericService.P_res1 = ((flags >> THREAD_P_ND_RES_BIT_MOVE) & 1);
if (thread_nd_local_list_add_on_mesh_prefix(networkDataStorage, &prefixTlv, &genericService) == 0) {
if (networkDataStorage->stableUpdatePushed || networkDataStorage->temporaryUpdatePushed) {
if (!genericService.P_slaac) {
//Delete Address by this Entry
tr_debug("Delete SLAAC address because not valid.");
addr_delete_matching(cur, prefixTlv.Prefix, 64, ADDR_SOURCE_SLAAC);
} else {
ns_list_foreach_safe(if_address_entry_t, e, &cur->ip_addresses) {
if (e->source == ADDR_SOURCE_SLAAC &&
bitsequal(e->address, prefixTlv.Prefix, 64)) {
if (genericService.P_preferred) {
tr_debug("SLAAC address set as preferred.");
} else {
tr_debug("SLAAC address set as NOT preferred.");
}
addr_set_preferred_lifetime(cur, e, genericService.P_preferred ? 0xfffffffff : 0);
}
}
}
if (!genericService.P_dhcp) {
//Delete DHCPv6 client
memcpy(addr, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, 8);
memcpy(&addr[8], ADDR_SHORT_ADR_SUFFIC, 6);
common_write_16_bit(genericService.routerID, &addr[14]);
tr_debug("Delete DHCPv6 given address");
dhcp_client_global_address_delete(cur->id, addr, prefixTlv.Prefix);
}
}
//Allocate Context
if (cur->thread_info->leader_private_data) {
if (thread_nd_prefix_context_allocate(cur, &prefixTlv, stableData) != 0) {
//Mark Delete for this setup if context allocate fail
thread_nd_local_list_del_on_mesh_server(networkDataStorage, &prefixTlv, &genericService);
}
}
}
} else if (type == THREAD_NWK_DATA_TYPE_ROUTE) {
if (genericService.routerID < 0xfffe) {
thread_nd_local_list_add_route(networkDataStorage, &prefixTlv, &genericService);
tr_debug("added route");
}
}
}
}
} else {
tr_debug("SERVER Parse fail");
break;
}
}
} else {
tr_debug("Malform Prefix sub TLV");
dptr += length;
}
} else if (type == THREAD_NWK_DATA_TYPE_COMMISSION_DATA && stableData) {
if (thread_network_data_sub_tlv_malformed_check(dptr, length) == 0) {
tr_debug("Stable Commisssion TLV: %i", length);
// Stable commissioning data has the Timestamps and is not processed here
} else {
tr_debug("Malformed stable Commisssion TLV: %i", length);
}
dptr += length;
} else if (type == THREAD_NWK_DATA_TYPE_COMMISSION_DATA && !stableData) {
if (thread_network_data_sub_tlv_malformed_check(dptr, length) == 0) {
int data_changed;
tr_debug("Unstable Commisssion TLV: %i", length);
// in thread 1.1 unstable has only commissioner information
cur->thread_info->registered_commissioner.commissioner_valid = false;
while (length > 2) {
type = *dptr++;
subLength = *dptr++;
length -= 2;
tr_debug("SubType: %02x, %s", type, trace_array(dptr, subLength));
if (subLength <= length) {
length -= subLength;
data_changed = thread_commission_data_tlv_parse(cur, type, subLength, dptr);
if (data_changed < 0) {
tr_debug("Fail");
} else {
if (data_changed == 1) {
update_data = true;
tr_debug("Changed");
} else {
tr_debug("OK");
}
}
dptr += subLength;
} else {
tr_debug("SERVER Parse fail");
break;
}
}
} else {
tr_debug("Malformed unstable Commisssion TLV: %i", length);
dptr += length;
}
} else if (type == THREAD_NWK_DATA_TYPE_SERVICE_DATA) {
thread_network_data_service_entry_t service_entry;
service_entry.T = (*dptr) >> 7;
service_entry.S_id = (*dptr++) & 0x0f;
service_entry.S_stable = stableData;
if (!service_entry.T) {
service_entry.S_enterprise_number = common_read_32_bit(dptr);
dptr += 4;
length -= 4;
} else {
service_entry.S_enterprise_number = THREAD_ENTERPRISE_NUMBER;
}
service_entry.S_service_data_length = *dptr++;
service_entry.S_service_data = dptr;
dptr += service_entry.S_service_data_length;
tr_debug("Service data: %s, enterprise number: %"PRIu32, trace_array(service_entry.S_service_data,
service_entry.S_service_data_length), service_entry.S_enterprise_number);
length -= 2 + service_entry.S_service_data_length;
while (length > 2) {
type = *dptr++;
subLength = *dptr++;
length -= 2;
if (subLength <= length) {
tr_debug("SubType: %02x, length: %d, data: %s", type, length, trace_array(dptr, subLength));
length -= subLength;
thread_network_data_service_server_entry_t server = {0};
if (type & THREAD_NWK_STABLE_DATA) {
server.stable = true;
}
server.router_id = common_read_16_bit(dptr);
subLength -= 2;
dptr += 2;
server.server_data = dptr;
server.server_data_length = subLength;
tr_debug("Router ID: %04x, Server data: %s", server.router_id, trace_array(server.server_data, server.server_data_length));
thread_nd_local_list_add_service(networkDataStorage, &service_entry, &server);
dptr += subLength;
} else {
tr_debug("Server sub-TLV parse fail!");
return -1;
}
}
} else {
return -1;
}
}
if (update_data) {
ret_val = 1;
}
return ret_val;
}
int thread_bootstrap_network_data_activate(protocol_interface_info_entry_t *cur)
{
//Mark old data to be be freed
thread_network_data_router_id_mark_delete(&cur->thread_info->networkDataStorage, 0xffff, false);
int retVal = thread_bootstrap_network_data_process(cur, cur->thread_info->networkDataStorage.network_data, cur->thread_info->networkDataStorage.network_data_len);
if (retVal < 0) {
tr_warn("network data update failed: %s", trace_array(cur->thread_info->networkDataStorage.network_data, cur->thread_info->networkDataStorage.network_data_len));
return retVal;
}
// delete marked data
if (thread_network_data_router_id_free(&cur->thread_info->networkDataStorage, false, cur)) {
thread_bootstrap_child_update_trig(cur);
}
// Learn the routes and dhcp addresses from prefixes
thread_bootstrap_network_prefixes_process(cur);
//Add new anycast addressess learned from network data.
thread_router_bootstrap_anycast_address_register(cur);
// Update joiner router status
thread_management_server_joiner_router_init(cur->id);
thread_extension_service_init(cur);
// Update border router relay
thread_bbr_commissioner_proxy_service_update(cur->id);
// update beacons
thread_beacon_create_payload(cur);
// Indicate network data change to other modules
thread_extension_network_data_process(cur);
thread_border_router_network_data_update_notify(cur);
thread_bbr_network_data_update_notify(cur);
thread_maintenance_timer_set(cur, THREAD_MAINTENANCE_TIMER_INTERVAL);
return 0;
}
int thread_bootstrap_network_data_save(protocol_interface_info_entry_t *cur, thread_leader_data_t *leader_data, uint8_t *network_data_ptr, uint16_t network_data_len)
{
if (!cur || !cur->thread_info || !leader_data || network_data_len > THREAD_MAX_NETWORK_DATA_SIZE) {
tr_warn("Network data saving failed");
return -1;
}
if (thread_attach_ready(cur) != 0) {
return -2;
}
if (thread_network_data_malformed_check(network_data_ptr, network_data_len) != 0) {
tr_warn("Malformed nw data: %s", trace_array(network_data_ptr, network_data_len));
return -3;
}
// Do not process the network data until we are synchronized
tr_debug("learn new network data");
if (thread_info(cur)->thread_leader_data->stableDataVersion != leader_data->stableDataVersion) {
thread_info(cur)->thread_leader_data->stableDataVersion = leader_data->stableDataVersion;
cur->thread_info->networkDataStorage.stableUpdatePushed = true;
}
if (thread_info(cur)->thread_leader_data->dataVersion != leader_data->dataVersion) {
thread_info(cur)->thread_leader_data->dataVersion = leader_data->dataVersion;
cur->thread_info->networkDataStorage.temporaryUpdatePushed = true;
}
if ((network_data_len != cur->thread_info->networkDataStorage.network_data_len ||
memcmp(cur->thread_info->networkDataStorage.network_data, network_data_ptr, network_data_len) != 0)) {
// Network data was changed so at least it will be unstable change
cur->thread_info->networkDataStorage.temporaryUpdatePushed = true;
tr_debug("Network data changed; size %d stable:%d, unstable:%d", network_data_len, cur->thread_info->networkDataStorage.stableUpdatePushed, cur->thread_info->networkDataStorage.temporaryUpdatePushed);
memcpy(cur->thread_info->networkDataStorage.network_data, network_data_ptr, network_data_len);
cur->thread_info->networkDataStorage.network_data_len = network_data_len;
}
return 0;
}
void thread_bootstrap_network_prefixes_process(protocol_interface_info_entry_t *cur)
{
// Route prefix is variable-length, so need to zero pad for ip6tos
uint8_t addr[16];
bool validToLearnRoutes, validToLearOnMeshRoute;
thread_network_server_data_entry_t *weHostService = NULL;
uint16_t routerId;
tr_debug("Network Data process:");
routerId = cur->mac_parameters->mac_short_address;
thread_network_data_cache_entry_t *networkData;
networkData = &cur->thread_info->networkDataStorage;
validToLearnRoutes = thread_route_possible_add(cur->thread_info->thread_device_mode);
validToLearOnMeshRoute = thread_on_mesh_route_possible_add(cur->thread_info->thread_device_mode);
ipv6_route_table_remove_info(cur->id, ROUTE_THREAD_BORDER_ROUTER, NULL);
ns_list_foreach(thread_network_data_prefix_cache_entry_t, curPrefix, &networkData->localPrefixList) {
weHostService = thread_nd_hosted_by_this_routerid(routerId, &curPrefix->routeList);
tr_debug("Local ServicePrefix: %s", trace_ipv6_prefix(curPrefix->servicesPrefix, curPrefix->servicesPrefixLen));
if (!weHostService && validToLearnRoutes) {
ns_list_foreach(thread_network_server_data_entry_t, curRouteItem, &curPrefix->routeList) {
thread_addr_write_mesh_local_16(addr, curRouteItem->routerID, cur->thread_info);
tr_debug("Add new route via: %s", trace_ipv6(addr));
ipv6_route_add(curPrefix->servicesPrefix, curPrefix->servicesPrefixLen, cur->id, addr, ROUTE_THREAD_BORDER_ROUTER, 0xffffffff, curRouteItem->Prf);
}
}
weHostService = thread_nd_hosted_by_this_routerid(routerId, &curPrefix->borderRouterList);
if (weHostService) {
tr_debug("I'm Hosting BR");
}
ns_list_foreach(thread_network_server_data_entry_t, curBorderRouter, &curPrefix->borderRouterList) {
//Set Default route ::/0
if (curBorderRouter->P_default_route) {
if (!(weHostService && weHostService->P_default_route) && validToLearnRoutes) {
thread_addr_write_mesh_local_16(addr, curBorderRouter->routerID, cur->thread_info);
tr_debug("Add default route via: %s", trace_ipv6(addr));
ipv6_route_add(NULL, 0, cur->id, addr, ROUTE_THREAD_BORDER_ROUTER, 0xffffffff, curBorderRouter->Prf);
}
}
ipv6_stack_route_advert_update(curPrefix->servicesPrefix, curPrefix->servicesPrefixLen, RA_PRF_LOW);
if (thread_nd_on_mesh_address_valid(curBorderRouter)) {
if (validToLearOnMeshRoute) {
if (curBorderRouter->P_dhcp && weHostService && nd_proxy_enabled_for_upstream(cur->id) && nd_proxy_upstream_route_onlink(cur->id, curPrefix->servicesPrefix)) {
// don't add
tr_debug("Suppressing onlink %s for proxy", trace_ipv6_prefix(curPrefix->servicesPrefix, curPrefix->servicesPrefixLen));
} else if (curBorderRouter->P_res1) {
ipv6_route_add(curPrefix->servicesPrefix, curPrefix->servicesPrefixLen, cur->id, NULL, ROUTE_THREAD_PROXIED_DUA_HOST, 0xffffffff, 0);
} else {
//add
tr_debug("Adding onlink %s", trace_ipv6_prefix(curPrefix->servicesPrefix, curPrefix->servicesPrefixLen));
ipv6_route_add(curPrefix->servicesPrefix, curPrefix->servicesPrefixLen, cur->id, NULL, ROUTE_THREAD_BORDER_ROUTER, 0xffffffff, curBorderRouter->Prf);
}
}
}
if (curBorderRouter->P_dhcp) {
/* All end device types perform BR RLOC16 -> ALOC16
replacement if stable network data was requested. */
if ((cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_HOST ||
cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_SLEEPY_HOST) &&
cur->thread_info->requestFullNetworkData == false) {
ns_list_foreach(thread_network_data_context_entry_t, curRoute, &curPrefix->contextList) {
curBorderRouter->routerID = 0xfc00;
curBorderRouter->routerID |= curRoute->cid;
tr_debug("Replaced router ID with ALOC16: %04x", curBorderRouter->routerID);
}
}
if (!thread_dhcpv6_address_entry_available(curPrefix->servicesPrefix, &cur->ip_addresses)) {
thread_addr_write_mesh_local_16(addr, curBorderRouter->routerID, cur->thread_info);
/* Do not allow multiple DHCP solicits from one prefix => delete previous */
dhcp_client_global_address_delete(cur->id, NULL, curPrefix->servicesPrefix);
if (dhcp_client_get_global_address(cur->id, addr, curPrefix->servicesPrefix, cur->mac, thread_dhcp_client_gua_error_cb) == 0) {
tr_debug("GP Address Requested");
}
}
} else {
/* All end device types perform RLOC16 -> 0xfffe
replacement if stable network data was requested. */
if ((cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_HOST ||
cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_SLEEPY_HOST) &&
cur->thread_info->requestFullNetworkData == false) {
curBorderRouter->routerID = 0xfffe;
tr_debug("Invalidated router ID: %04x", curBorderRouter->routerID);
}
}
if (curBorderRouter->P_preferred) {
if (!thread_dhcpv6_address_entry_available(curPrefix->servicesPrefix, &cur->ip_addresses)) {
icmpv6_slaac_address_add(cur, curPrefix->servicesPrefix, curPrefix->servicesPrefixLen, 0xffffffff, 0xffffffff, true, SLAAC_IID_DEFAULT);
}
}
// generate address based on res1 bit
if (curBorderRouter->P_res1) {
thread_extension_dua_address_generate(cur, curPrefix->servicesPrefix, 64);
}
} // for each borderRouterList
ns_list_foreach(thread_network_data_context_entry_t, curRoute, &curPrefix->contextList) {
uint8_t flags;
flags = curRoute->cid;
if (curRoute->compression) {
flags |= 0x10;
}
lowpan_context_update(&cur->lowpan_contexts, flags, 0xFFFF, curPrefix->servicesPrefix, curPrefix->servicesPrefixLen, curRoute->stableData);
}
} // for each localPrefixList
}
void thread_bootstrap_network_data_update(protocol_interface_info_entry_t *cur)
{
if (!cur || !cur->thread_info) {
return;
}
if (cur->thread_info->networkDataStorage.stableUpdatePushed) {
tr_debug("Stable Network Data Update");
} else if (cur->thread_info->networkDataStorage.temporaryUpdatePushed) {
tr_debug("Temporary Network Data Update");
} else {
// No changes in network data detected so no processing done
return;
}
tr_info("Network data updated");
thread_bootstrap_network_data_activate(cur);
thread_router_bootstrap_network_data_distribute(cur);
cur->thread_info->networkDataStorage.stableUpdatePushed = false;
cur->thread_info->networkDataStorage.temporaryUpdatePushed = false;
}
void thread_bootstrap_clear_neighbor_entries(protocol_interface_info_entry_t *cur)
{
if (cur == NULL || cur->thread_info == NULL) {
return;
}
// Remove registered entries in the IP neighbor cache
ns_list_foreach_safe(ipv6_neighbour_t, neighbour, &cur->ipv6_neighbour_cache.list) {
if (neighbour->type == IP_NEIGHBOUR_REGISTERED) {
ipv6_neighbour_entry_remove(&cur->ipv6_neighbour_cache, neighbour);
}
}
mac_neighbor_table_neighbor_list_clean(mac_neighbor_info(cur));
}
void thread_bootstrap_dynamic_configuration_save(protocol_interface_info_entry_t *cur)
{
uint32_t mac_frame_counter;
if (!cur->thread_info) {
return;
}
if (thread_i_am_router(cur)) {
/* Store information of our children to the dynamic storage */
mac_neighbor_table_list_t *mac_table_list = &mac_neighbor_info(cur)->neighbour_list;
ns_list_foreach_safe(mac_neighbor_table_entry_t, entry, mac_table_list) {
if (thread_addr_is_child(mac_helper_mac16_address_get(cur), entry->mac16)) {
thread_dynamic_storage_child_info_store(cur, entry);
}
}
}
link_configuration_s *linkConfiguration = thread_joiner_application_get_config(cur->id);
mac_helper_link_frame_counter_read(cur->id, &mac_frame_counter);
// in error situation this returns 0 !!!!
uint32_t mle_frame_counter = mle_service_security_get_frame_counter(cur->id);
if (linkConfiguration) {
thread_nvm_store_fast_data_check_and_write(mac_frame_counter, mle_frame_counter, linkConfiguration->key_sequence);
} else {
thread_nvm_store_frame_counters_check_and_write(mac_frame_counter, mle_frame_counter);
}
}
bool thread_bootstrap_link_create_check(protocol_interface_info_entry_t *interface, uint16_t short_address)
{
if (interface->thread_info->thread_device_mode == THREAD_DEVICE_MODE_SLEEPY_END_DEVICE ||
interface->thread_info->thread_device_mode == THREAD_DEVICE_MODE_END_DEVICE) {
// MED's never make any links so return false
return false;
}
if (mle_class_free_entry_count_get(interface) < 1) {
// We dont have room for any new links
tr_warn("Link ignore no room for addr:%x", short_address);
return false;
}
// TODO should check that we have enough room left for children
// TODO Need to drop links for bad routers if no room
if (interface->thread_info->thread_attached_state == THREAD_STATE_CONNECTED_ROUTER) {
//routers always make links
return true;
}
if (thread_is_router_addr(short_address) == false) {
//incoming advertisement is not from router so return false
return false;
}
if (mle_class_active_neigh_counter(interface) < THREAD_REED_AND_END_DEVICE_NEIGHBOR_LINKS + 1) {
return true;
}
return false;
}
bool thread_bootstrap_link_create_allowed(protocol_interface_info_entry_t *interface, uint16_t short_address, const uint8_t *src_addr)
{
#ifndef HAVE_DEBUG
(void) short_address;
#endif
// Add blacklist of routers that newer answer to us
if (mle_service_interface_tx_queue_size(interface->id) > THREAD_MAX_PARALLEL_MLE_LINK_REQUEST) {
// Maximum parallel link requests
tr_warn("Link ignore too many req addr:%x", short_address);
return false;
}
if (blacklist_reject(src_addr)) {
return false;
}
return true;
}
bool thread_bootstrap_should_register_address(protocol_interface_info_entry_t *cur)
{
// If we are a MTD, send requests to register addresses
// Neither REEDs nor FEDs need to register their addresses
uint8_t mode = thread_mode_get_by_interface_ptr(cur);
return (mode & MLE_DEV_MASK) == MLE_RFD_DEV;
}
#endif