/*
 * 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 "coap_service_api.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_bbr_commercial.h"
#include "6LoWPAN/Thread/thread_management_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_ccm.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);

#ifdef HAVE_THREAD_V2

static bool enabled = false;
static uint32_t addr_notificastion_timer;
static uint32_t mlr_timer;

static void thread_bootstrap_pbbr_network_data_process(struct protocol_interface_info_entry *cur);
static void thread_bootstrap_pbbr_update_done(struct protocol_interface_info_entry *cur);

#else
#define thread_bootstrap_pbbr_network_data_process(cur)
#define thread_bootstrap_pbbr_update_done(cur)

#endif

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_neigh_remove_free_tx_tables(cur, entry_ptr);

    thread_reset_neighbour_info(cur, entry_ptr);
    //Removes ETX neighbor
    etx_neighbor_remove(cur->id, entry_ptr->index, entry_ptr->mac64);
    //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_common_ccm_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_bbr_commercial_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;
    }

    mac_helper_mac_device_description_pan_id_update(cur->id, linkConfiguration->panId);

    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, DHCPV6_DUID_HARDWARE_EUI64_TYPE);
    dhcp_client_configure(cur->id, false, false, false);
    thread_management_client_init(cur->id);
    thread_bootstrap_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.probe_avoided_routers = 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 = 8 * cur->thread_info->host_link_timeout / 10;
    } else {
        tr_debug("Set End node Mode");
        cur->thread_info->thread_device_mode = THREAD_DEVICE_MODE_END_DEVICE;
    }

    if (cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_ROUTER) {
        // set router neighbour cache
        ipv6_neighbour_cache_configure(THREAD_ROUTER_IPV6_NEIGHBOUR_CACHE_SIZE,
                                       THREAD_ROUTER_IPV6_NEIGHBOUR_CACHE_SHORT_TERM,
                                       THREAD_ROUTER_IPV6_NEIGHBOUR_CACHE_LONG_TERM,
                                       THREAD_ROUTER_IPV6_NEIGHBOUR_CACHE_LIFETIME);
        // set router destination cache
        ipv6_destination_cache_configure(THREAD_ROUTER_IPV6_DESTINATION_CACHE_SIZE,
                                         THREAD_ROUTER_IPV6_DESTINATION_CACHE_SHORT_TERM,
                                         THREAD_ROUTER_IPV6_DESTINATION_CACHE_LONG_TERM,
                                         THREAD_ROUTER_IPV6_DESTINATION_CACHE_LIFETIME);
    } else {
        // device is some sort of end device
        ipv6_neighbour_cache_configure(THREAD_END_DEVICE_IPV6_NEIGHBOUR_CACHE_SIZE,
                                       THREAD_END_DEVICE_IPV6_NEIGHBOUR_CACHE_SHORT_TERM,
                                       THREAD_END_DEVICE_IPV6_NEIGHBOUR_CACHE_LONG_TERM,
                                       THREAD_END_DEVICE_IPV6_NEIGHBOUR_CACHE_LIFETIME);

        ipv6_destination_cache_configure(THREAD_END_DEVICE_IPV6_DESTINATION_CACHE_SIZE,
                                         THREAD_END_DEVICE_IPV6_DESTINATION_CACHE_SHORT_TERM,
                                         THREAD_END_DEVICE_IPV6_DESTINATION_CACHE_LONG_TERM,
                                         THREAD_END_DEVICE_IPV6_DESTINATION_CACHE_LIFETIME);
    }

    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->ipv6_neighbour_cache.probe_avoided_routers = false;
    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_ccm_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_ccm_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_bootstrap_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 ? 0xffffffff : 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_management_server_ccm_service_init(cur->id);

    // 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_bootstrap_pbbr_network_data_process(cur);
    thread_border_router_network_data_update_notify(cur);
    thread_bbr_network_data_update_notify(cur);

    thread_maintenance_timer_set(cur);

    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, 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) {
                    tr_debug("Invalidate router ID: %04x", curBorderRouter->routerID);
                    curBorderRouter->routerID = 0xfffe;
                }
            }

            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) {
                if (!thread_dhcpv6_address_entry_available(curPrefix->servicesPrefix, &cur->ip_addresses)) {
                    thread_bootstrap_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;
}

/**
 * Thread 1.2 support
 */

#ifdef HAVE_THREAD_V2

static void thread_bootstrap_mcast_subscrition_change(protocol_interface_info_entry_t *interface)
{
    uint8_t *addr;
    uint8_t *ptr;
    uint8_t addr_len;
    uint8_t br_ml_addr[16];

    if (0 != thread_common_primary_bbr_get(interface, br_ml_addr, NULL, NULL, NULL)) {
        return;
    }
    addr = ns_dyn_mem_temporary_alloc(16 * thread_max_mcast_addr + 2);
    if (!addr) {
        return;
    }
    // MLR is sent only for primary BBR for now, but this might change
    ptr = addr;
    addr_len = 0;
    *ptr++ = TMFCOP_TLV_IPV6_ADDRESS;
    *ptr++ = 0; // Length will be updated later
    ns_list_foreach(if_group_entry_t, cur, &interface->ip_groups) {
        if (addr_ipv6_multicast_scope(cur->group) < IPV6_SCOPE_ADMIN_LOCAL) {
            continue;
        }
        if (addr_ipv6_equal(cur->group, ADDR_SITE_LOCAL_ALL_ROUTERS)) {
            continue;
        }
        memcpy(ptr, cur->group, 16);
        ptr += 16;
        addr_len = ptr - addr;
        addr[1] = addr_len - 2;// Fill in the length
        if (addr_len > 16 * thread_max_mcast_addr) {
            // Send first patch
            thread_management_client_mlr_req_send(interface->id, br_ml_addr, addr, addr_len);
            // Reset the packet to start
            ptr = addr;
            ptr += 2;
            addr_len = 0;
        }
    }
    if (addr_len != 0) {
        // Send rest
        thread_management_client_mlr_req_send(interface->id, br_ml_addr, addr, addr_len);
    }
    ns_dyn_mem_free(addr);
}
static int thread_bootstrap_dua_registration_cb(int8_t service_id, uint8_t source_address[static 16], uint16_t source_port, sn_coap_hdr_s *response_ptr)
{
    (void) source_address;
    (void) source_port;

    uint16_t addr_len;
    uint8_t bbr_status;
    uint8_t *addr_data_ptr = NULL;

    protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(thread_management_client_get_interface_id_by_service_id(service_id));

    if (!cur) {
        return -1;
    }

    if (!response_ptr) {
        tr_warn("DUA.resp failed");
        return -2;
    }

    if (1 > thread_meshcop_tlv_data_get_uint8(response_ptr->payload_ptr, response_ptr->payload_len, TMFCOP_TLV_STATUS, &bbr_status)) {
        return 0;
    }

    addr_len = thread_meshcop_tlv_find(response_ptr->payload_ptr, response_ptr->payload_len, TMFCOP_TLV_TARGET_EID, &addr_data_ptr);

    if (addr_len < 16) {
        tr_warn("Invalid target eid in DUA.rsp cb message");
        return 0;
    }

    if_address_entry_t *addr_entry = addr_get_entry(cur, addr_data_ptr);
    if (addr_entry) {
        // Own processing
        if (bbr_status == THREAD_ST_DUA_SUCCESS) {
            addr_entry->preferred_lifetime = 0xffffffff;
        } else if (bbr_status == THREAD_ST_DUA_DUPLICATE) {
            cur->dad_failures++;
            thread_bootstrap_dua_address_generate(cur, addr_data_ptr, 64);
        } else if (bbr_status == THREAD_ST_DUA_INVALID) {
            addr_delete(cur, addr_data_ptr);
        } else {
            addr_entry->preferred_lifetime = 0;
        }
        return 0;
    }

    // processing for MTD children
    ipv6_neighbour_t *neighbour_entry;
    uint16_t nce_short_addr;
    uint8_t destination_address[16] = {0};

    if (bbr_status == THREAD_ST_DUA_SUCCESS) {
        // registration successful
        return 0;
    }

    neighbour_entry = ipv6_neighbour_lookup(&cur->ipv6_neighbour_cache, addr_data_ptr);
    if (!neighbour_entry) {
        return 0;
    }

    nce_short_addr = common_read_16_bit(neighbour_entry->ll_address + 2);
    if (!thread_addr_is_child(cur->thread_info->routerShortAddress, nce_short_addr)) {
        ipv6_neighbour_entry_remove(&cur->ipv6_neighbour_cache, neighbour_entry);
        return 0;
    }

    if (bbr_status == THREAD_ST_DUA_DUPLICATE || bbr_status == THREAD_ST_DUA_INVALID) {
        // remove invalid or duplicate child entry
        ipv6_neighbour_entry_remove(&cur->ipv6_neighbour_cache, neighbour_entry);
    }

    thread_addr_write_mesh_local_16(destination_address, nce_short_addr, cur->thread_info);

    thread_management_client_addr_ntf_send(cur->id, destination_address, addr_data_ptr, bbr_status);

    return 0;
}

static void thread_bootstrap_dua_registration_send(protocol_interface_info_entry_t *cur, const uint8_t address[16], const uint8_t ml_eid[8], const uint8_t dst_addr[16])
{

    tr_debug("domain address registration (DUA.req send)");
    thread_bootstrap_pbbr_update_done(cur);

    uint8_t payload[2 + 16 + 2 + 8];
    uint8_t *ptr;

    ptr = payload;
    ptr = thread_tmfcop_tlv_data_write(ptr, TMFCOP_TLV_TARGET_EID, 16, address);
    ptr = thread_tmfcop_tlv_data_write(ptr, TMFCOP_TLV_ML_EID, 8, ml_eid);

    coap_service_request_send(thread_management_server_service_id_get(cur->id), COAP_REQUEST_OPTIONS_NONE,
                              dst_addr, THREAD_MANAGEMENT_PORT,
                              COAP_MSG_TYPE_CONFIRMABLE, COAP_MSG_CODE_REQUEST_POST,
                              THREAD_URI_BBR_DOMAIN_ADDRESS_REGISTRATION, COAP_CT_OCTET_STREAM,
                              payload, ptr - payload, thread_bootstrap_dua_registration_cb);
}

static int thread_bootstrap_pbbr_update_needed(struct protocol_interface_info_entry *cur, uint16_t rloc, uint8_t seq)
{
    if (!cur->thread_info->ccm_info) {
        return 0;
    }
    if (cur->thread_info->ccm_info->update_needed  ||
            seq != cur->thread_info->ccm_info->sequence_number ||
            rloc != cur->thread_info->ccm_info->rloc) {
        return 1;
    }
    return 0;
}

static void thread_bootstrap_pbbr_update_done(struct protocol_interface_info_entry *cur)
{
    // Check if network data changed or bbr info and send proactive an if needed
    uint8_t addr[16];
    uint8_t sequence_number;
    uint32_t delay_timer;
    uint32_t br_mlr_timer;
    if (!cur->thread_info->ccm_info) {
        return;
    }
    if (0 == thread_common_primary_bbr_get(cur, addr, &sequence_number, &br_mlr_timer, &delay_timer)) {
        // BBR updated
        cur->thread_info->ccm_info->rloc = common_read_16_bit(&addr[14]);
        cur->thread_info->ccm_info->sequence_number = sequence_number;
        cur->thread_info->ccm_info->delay_timer = delay_timer;
        cur->thread_info->ccm_info->mlr_timer = br_mlr_timer;
        cur->thread_info->ccm_info->update_needed = false;
    }
}

static void thread_bootstrap_pbbr_network_data_process(struct protocol_interface_info_entry *cur)
{
    // Check if network data changed or bbr info and send proactive an if needed
    uint8_t addr[16];
    uint8_t sequence_number;
    uint32_t delay_timer;
    uint32_t mlr_timeout;

    if (0 != thread_common_primary_bbr_get(cur, addr, &sequence_number, &mlr_timeout, &delay_timer)) {
        // BBR not present
        return;
    }
    if (1 == thread_bootstrap_pbbr_update_needed(cur, common_read_16_bit(&addr[14]), sequence_number)) {
        thread_bootstrap_address_registration_timer_set(cur, 1 + randLIB_get_random_in_range(0, delay_timer / 1000), randLIB_get_random_in_range(1, 5));
    }
}

static void thread_bootstrap_address_registration_trigger(protocol_interface_info_entry_t *interface)
{
    // Address notification is sent only for DUA address
    thread_bootstrap_pbbr_update_done(interface);
    // Send notification on behalf of children
    ns_list_foreach(ipv6_neighbour_t, n, &interface->ipv6_neighbour_cache.list) {
        if (n->type == IP_NEIGHBOUR_REGISTERED && addr_ipv6_scope(n->ip_address, interface) > IPV6_SCOPE_REALM_LOCAL) {
            mac_neighbor_table_entry_t *entry = mac_neighbor_table_address_discover(mac_neighbor_info(interface), &n->ll_address[2], ADDR_802_15_4_SHORT);
            if (entry) {
                thread_bootstrap_address_registration(interface, n->ip_address, entry->mac64, true, false);
            }
        }
    }

    // Send own Address
    ns_list_foreach(if_address_entry_t, e, &interface->ip_addresses) {
        if (addr_ipv6_scope(e->address, interface) > IPV6_SCOPE_REALM_LOCAL) {
            thread_bootstrap_address_registration(interface, e->address, NULL, false, false);
        }
    }
}

void thread_bootstrap_address_registration_init()
{
    enabled = true;
    addr_notificastion_timer = THREAD_PROACTIVE_AN_INTERVAL;
    mlr_timer = 0;
}

bool thread_bootstrap_address_registration_running()
{
    return enabled;
}

void thread_bootstrap_address_registration_deinit()
{
    enabled = false;
}

bool thread_bootstrap_is_domain_prefix(protocol_interface_info_entry_t *interface, const uint8_t *addr)
{
    uint8_t address[16];
    memcpy(address, addr, 16);
    thread_prefix_tlv_t prefixTlv;
    prefixTlv.domainId = 0;
    prefixTlv.Prefix = address;
    prefixTlv.PrefixLen = 64;
    thread_network_data_prefix_cache_entry_t *prefix_entry = NULL;
    thread_network_data_cache_entry_t *networkDataCache = &interface->thread_info->networkDataStorage;
    prefix_entry = thread_prefix_entry_find(&networkDataCache->localPrefixList, &prefixTlv);
    if (!prefix_entry) {
        return false;
    }
    ns_list_foreach(thread_network_server_data_entry_t, brEntry, &prefix_entry->borderRouterList) {
        if (brEntry->P_res1) {
            return true;
        }
    }
    return false;
}

void thread_bootstrap_dua_address_generate(protocol_interface_info_entry_t *cur, const uint8_t *domain_prefix, uint8_t domain_prefix_len)
{
    if_address_entry_t *def_address = NULL;
    uint8_t domain_address[16];
    if (thread_info(cur)->version < THREAD_VERSION_1_2) {
        return;
    }
    addr_delete_matching(cur, NULL, 0, ADDR_SOURCE_THREAD_DOMAIN);
    memcpy(domain_address, domain_prefix, 8);
    def_address = icmpv6_slaac_address_add(cur, domain_address, domain_prefix_len, 0xffffffff, 0xffffffff, true, SLAAC_IID_DEFAULT);
    if (def_address) {
        tr_info("Generated domain address 64: %s", trace_ipv6(def_address->address));
        def_address->source = ADDR_SOURCE_THREAD_DOMAIN;
    } else {
        tr_error("Domain address creation failed");
    }
}

void thread_bootstrap_address_registration(struct protocol_interface_info_entry *interface, const uint8_t *addr, const uint8_t *child_mac64, bool refresh_child_entry, bool duplicate_child_detected)
{
    uint8_t *ml_eid;
    uint8_t br_ml_addr[16];
    uint8_t seq;
    uint32_t delay_timer;

    if (thread_info(interface)->version < THREAD_VERSION_1_2) {
        return;
    }

    if (!thread_bootstrap_is_domain_prefix(interface, addr)) {
        return;
    }
    tr_debug("domain prefix address");
    if (0 != thread_common_primary_bbr_get(interface, br_ml_addr, &seq, &delay_timer, NULL)) {
        // No pBBR present
        return;
    }
    tr_info("domain prefix Primary bbr present");

    if (child_mac64) {
        mac_neighbor_table_entry_t *entry = mac_neighbor_table_address_discover(mac_neighbor_info(interface), child_mac64, ADDR_802_15_4_LONG);

        if (!entry) {
            tr_error("No MLE entry.");
            return;
        }

        ml_eid = thread_neighbor_class_get_mleid(&interface->thread_info->neighbor_class, entry->index);
        if (!ml_eid) {
            tr_error("No Thread neighbor.");
            return;
        }

        if (duplicate_child_detected) {
            uint8_t dest_address[16] = {0};
            thread_addr_write_mesh_local_16(dest_address, entry->mac16, interface->thread_info);
            thread_management_client_addr_ntf_send(interface->id, dest_address, addr, 3);
            return;
        }
        if (!refresh_child_entry) {
            // don't send n/dr for existing entry
            return;
        }
    } else {
        link_configuration_s *link_config = thread_joiner_application_get_config(interface->id);

        if (!link_config) {
            tr_error("No link configuration.");
            return;
        }
        ml_eid = thread_joiner_application_ml_eid_get(interface->id);
    }

    // Register the DUA address
    thread_bootstrap_dua_registration_send(interface, addr, ml_eid, br_ml_addr);

    return;
}


void thread_bootstrap_address_registration_timer_set(protocol_interface_info_entry_t *interface, uint16_t dua_delay_seconds, uint16_t mlr_refresh_seconds)
{
    (void)interface;
    if (dua_delay_seconds != 0) {
        addr_notificastion_timer = dua_delay_seconds;
    }
    if (mlr_refresh_seconds != 0) {
        mlr_timer = mlr_refresh_seconds;
    }

}
void thread_bootstrap_address_registration_timer(protocol_interface_info_entry_t *interface, uint16_t seconds)
{
    uint32_t mlr_timeout;
    uint32_t delay_timer;
    if (!enabled || interface->thread_info->version < THREAD_VERSION_1_2) {
        return;
    }
    if (thread_bootstrap_should_register_address(interface)) {
        if (!interface->thread_info->thread_endnode_parent) {
            // We dont have parent so skip
            return;
        }
        if (interface->thread_info->thread_endnode_parent->version >= THREAD_VERSION_1_2) {
            // Parent supports all features
            return;
        }
    }

    if (0 != thread_common_primary_bbr_get(interface, NULL, NULL, &mlr_timeout, &delay_timer)) {
        // BBR not present
        return;
    }
    /* Update multicast addresses */
    if (mlr_timer > seconds) {
        mlr_timer -= seconds;
    } else {
        mlr_timer = mlr_timeout - randLIB_get_random_in_range(30, 50);
        thread_bootstrap_mcast_subscrition_change(interface);
    }
    /* Update global scope addresses (children's as well) */
    if (addr_notificastion_timer > seconds) {
        addr_notificastion_timer -= seconds;
    } else {
        addr_notificastion_timer = THREAD_PROACTIVE_AN_INTERVAL + randLIB_get_random_in_range(0, delay_timer / 1000);
        thread_bootstrap_address_registration_trigger(interface);
    }

}
void thread_bootstrap_child_address_registration_response_process(struct protocol_interface_info_entry *interface)
{
    //  if we are version 3 and parent is lower we need to send registrations for them

    if (interface->thread_info->version < THREAD_VERSION_1_2) {
        thread_bootstrap_address_registration_deinit();
        return;
    }
    if (!interface->thread_info->thread_endnode_parent) {
        // We dont have parent return
        thread_bootstrap_address_registration_deinit();
        return;
    }
    if (interface->thread_info->thread_endnode_parent->version >= THREAD_VERSION_1_2) {
        //parent supports 1.2
        thread_bootstrap_address_registration_deinit();
        return;
    }
    // We will start address registration timers
    if (!thread_bootstrap_address_registration_running()) {
        uint32_t dua_delay;
        if (0 != thread_common_primary_bbr_get(interface, NULL, NULL, NULL, &dua_delay)) {
            dua_delay = randLIB_get_random_in_range(1, 5);
        }
        thread_bootstrap_address_registration_init();
        thread_bootstrap_address_registration_timer_set(interface, 1 + randLIB_get_random_in_range(0, dua_delay / 1000), randLIB_get_random_in_range(1, 5));
    }
}


#endif
#endif
