mirror of https://github.com/ARMmbed/mbed-os.git
1397 lines
48 KiB
C
1397 lines
48 KiB
C
/*
|
|
* Copyright (c) 2016-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.
|
|
*/
|
|
|
|
#include "nsconfig.h"
|
|
#ifdef HAVE_THREAD
|
|
#include "ns_types.h"
|
|
#include "eventOS_event.h"
|
|
#include "eventOS_event_timer.h"
|
|
#include "thread_config.h"
|
|
#include "common_functions.h"
|
|
#include <string.h>
|
|
#include "ns_trace.h"
|
|
#include "ns_list.h"
|
|
#include "randLIB.h"
|
|
#include <nsdynmemLIB.h>
|
|
#include "thread_discovery.h"
|
|
#include "NWK_INTERFACE/Include/protocol.h"
|
|
#include "6LoWPAN/Thread/thread_common.h"
|
|
#include "6LoWPAN/Thread/thread_management_server.h"
|
|
#include "6LoWPAN/Thread/thread_joiner_application.h"
|
|
#include "6LoWPAN/Thread/thread_management_internal.h"
|
|
#include "6LoWPAN/Thread/thread_bootstrap.h"
|
|
#include "6LoWPAN/Thread/thread_extension.h"
|
|
#include "Service_Libs/mle_service/mle_service_api.h"
|
|
#include "MLE/mle.h"
|
|
#include "MLE/mle_tlv.h"
|
|
#include "thread_tmfcop_lib.h"
|
|
#include "thread_meshcop_lib.h"
|
|
#include "6LoWPAN/MAC/mac_helper.h"
|
|
#include "mac_api.h"
|
|
#include "6LoWPAN/MAC/mac_data_poll.h"
|
|
|
|
typedef enum {
|
|
THREAD_DISCOVER_INIT = 0,
|
|
THREAD_DISCOVER_TIMER
|
|
} thread_discover_event_id_e;
|
|
|
|
typedef struct {
|
|
uint8_t type;
|
|
uint8_t *data;
|
|
uint16_t length;
|
|
} mescop_tlv_t;
|
|
|
|
#define TRACE_GROUP "tdis"
|
|
|
|
static int8_t thread_discover_tasklet_id = -1;
|
|
static bool thread_discover_timer_active = false;
|
|
|
|
#define discover_optional_start_pointer(x) (&(x)->filter_tlv_data[0])
|
|
|
|
//Discovery request class
|
|
typedef struct {
|
|
uint16_t active_timer;
|
|
uint32_t channel_mask;
|
|
uint16_t random_panid;
|
|
uint8_t temporary_mac64[8];
|
|
thread_discovery_ready_cb *response_cb;
|
|
uint8_t active_channel;
|
|
uint8_t channel_page;
|
|
bool waiting_response: 1;
|
|
bool joiner_flag: 1;
|
|
bool native_commisioner_scan: 1;
|
|
uint8_t filter_tlv_length; //Optional Filter data length
|
|
uint8_t filter_tlv_data[]; //Do not anything after this definition
|
|
} thread_discovery_request_info_t;
|
|
|
|
|
|
//Discovery request class
|
|
typedef struct {
|
|
uint16_t active_timer;
|
|
uint32_t channel_mask;
|
|
uint8_t active_channel;
|
|
uint16_t pan_id;
|
|
uint64_t active_time_stamp;
|
|
thread_announce_scan_ready_cb *response_cb;
|
|
announce_discovery_response_t *network;
|
|
bool waiting_response: 1;
|
|
} thread_announce_request_info_t;
|
|
|
|
|
|
typedef struct {
|
|
uint8_t extentedAddress[8];
|
|
uint16_t panId;
|
|
uint8_t timer;
|
|
bool randomJitter;
|
|
ns_list_link_t link;
|
|
} thread_discovery_response_msg_t;
|
|
|
|
typedef NS_LIST_HEAD(thread_discovery_response_msg_t, link) thread_discovery_response_msg_list;
|
|
|
|
typedef struct {
|
|
struct protocol_interface_info_entry *interface;
|
|
thread_discovery_request_info_t *discovery_request;
|
|
thread_announce_request_info_t *thread_announce_request;
|
|
thread_discovery_response_msg_list srv_respose_msg_list;
|
|
thread_discovery_response_msg_list srv_respose_msg_buffers;
|
|
thread_discovery_response_msg_t *msg_buffers;
|
|
thread_nwk_discovery_response_list_t discovered_network;
|
|
int8_t interface_id;
|
|
uint8_t version;
|
|
bool discovery_server_active;
|
|
ns_list_link_t link;
|
|
} thread_discovery_class_t;
|
|
|
|
#define THREAD_DISCOVER_TIMER_PERIOD 50
|
|
|
|
|
|
static NS_LIST_DEFINE(thread_discovery_class_list, thread_discovery_class_t, link);
|
|
|
|
static bool thread_discovery_timer_update(void);
|
|
static void thread_discover_timer_trig(void);
|
|
static discovery_response_list_t *thread_discover_response_msg_allocate(void);
|
|
static discovery_response_list_t *thread_discover_response_msg_get_discover_from_list(thread_nwk_discovery_response_list_t *list, uint8_t channel, uint16_t panId);
|
|
|
|
|
|
static int stringlen(const char *s, int n)
|
|
{
|
|
char *end = memchr(s, 0, n);
|
|
return end ? end - s : n;
|
|
}
|
|
|
|
static void thread_discover_timer_trig(void)
|
|
{
|
|
if (thread_discover_timer_active || thread_discover_tasklet_id == -1) {
|
|
|
|
}
|
|
eventOS_event_timer_request(3, THREAD_DISCOVER_TIMER, thread_discover_tasklet_id, THREAD_DISCOVER_TIMER_PERIOD);
|
|
thread_discover_timer_active = true;
|
|
}
|
|
|
|
static thread_discovery_request_info_t *thread_discovery_request_allocate(thread_discover_reques_t *scan_request, thread_discovery_ready_cb *response_cb)
|
|
{
|
|
thread_discovery_request_info_t *discover_request = ns_dyn_mem_temporary_alloc(sizeof(thread_discovery_request_info_t) + scan_request->filter_tlv_length);
|
|
if (discover_request) {
|
|
discover_request->waiting_response = false;
|
|
discover_request->active_timer = 0;
|
|
discover_request->channel_mask = scan_request->channel_mask;
|
|
discover_request->joiner_flag = scan_request->joiner_flag;
|
|
discover_request->native_commisioner_scan = scan_request->native_commisioner;
|
|
discover_request->filter_tlv_length = scan_request->filter_tlv_length;
|
|
discover_request->random_panid = randLIB_get_random_in_range(1, 0xfffd); //Generate random pan-id
|
|
randLIB_get_n_bytes_random(discover_request->temporary_mac64, 8); //Generate random temporary mac64
|
|
|
|
discover_request->response_cb = response_cb;
|
|
if (discover_request->filter_tlv_length) {
|
|
memcpy(discover_optional_start_pointer(discover_request), scan_request->filter_tlv_data, discover_request->filter_tlv_length);
|
|
}
|
|
}
|
|
return discover_request;
|
|
}
|
|
|
|
|
|
static thread_announce_request_info_t *thread_announce_discovery_request_allocate(thread_announce_discover_reques_t *scan_request, thread_announce_scan_ready_cb *response_cb)
|
|
{
|
|
thread_announce_request_info_t *discover_request = ns_dyn_mem_temporary_alloc(sizeof(thread_announce_request_info_t));
|
|
if (discover_request) {
|
|
discover_request->waiting_response = false;
|
|
discover_request->active_timer = 0;
|
|
discover_request->channel_mask = scan_request->channel_mask;
|
|
discover_request->pan_id = scan_request->pan_id;
|
|
discover_request->active_time_stamp = scan_request->active_timestamp;
|
|
discover_request->response_cb = response_cb;
|
|
discover_request->network = NULL;
|
|
}
|
|
return discover_request;
|
|
}
|
|
|
|
static void thread_discovery_server_msg_clean(thread_discovery_class_t *class)
|
|
{
|
|
class->discovery_server_active = false;
|
|
ns_list_foreach_safe(thread_discovery_response_msg_t, cur, &class->srv_respose_msg_list) {
|
|
ns_list_remove(&class->srv_respose_msg_list, cur);
|
|
ns_list_add_to_start(&class->srv_respose_msg_buffers, cur);
|
|
}
|
|
}
|
|
|
|
static bool thread_discovery_server_msg_buffer_allocate(thread_discovery_class_t *class)
|
|
{
|
|
thread_discovery_response_msg_t *msg_buffer = ns_dyn_mem_alloc(sizeof(thread_discovery_class_t) * 4);
|
|
if (!msg_buffer) {
|
|
return false;
|
|
}
|
|
class->msg_buffers = msg_buffer;
|
|
for (uint8_t i = 0; i < 4; i++) {
|
|
ns_list_add_to_start(&class->srv_respose_msg_buffers, msg_buffer++);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void thread_discovery_server_msg_buffer_free(thread_discovery_class_t *class)
|
|
{
|
|
thread_discovery_server_msg_clean(class);
|
|
ns_dyn_mem_free(class->msg_buffers);
|
|
class->msg_buffers = NULL;
|
|
}
|
|
|
|
static bool thread_discovery_response_pending_at_list(thread_discovery_response_msg_list *list, uint8_t *extended_address)
|
|
{
|
|
ns_list_foreach(thread_discovery_response_msg_t, cur, list) {
|
|
if (memcmp(cur->extentedAddress, extended_address, 8) == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static thread_discovery_response_msg_t *thread_discovery_response_allocate(thread_discovery_response_msg_list *list)
|
|
{
|
|
thread_discovery_response_msg_t *entry = ns_list_get_first(list);
|
|
if (entry) {
|
|
//Remove from the list
|
|
ns_list_remove(list, entry);
|
|
// -1 because timer accuracy is 50ms and message sending must happen before 300ms
|
|
entry->timer = randLIB_get_random_in_range(1, THREAD_DISCOVERY_MAX_JITTER / THREAD_DISCOVER_TIMER_PERIOD - 1);
|
|
entry->randomJitter = true;
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
|
|
static void thread_discovery_response_trig(thread_discovery_class_t *class, uint8_t *ll64, uint16_t dstPanId)
|
|
{
|
|
//Start DoS verify
|
|
if (thread_discovery_response_pending_at_list(&class->srv_respose_msg_list, ll64 + 8)) {
|
|
tr_debug("Too Agressive Discovery");
|
|
return;
|
|
}
|
|
|
|
//Allocate new response
|
|
thread_discovery_response_msg_t *entry = thread_discovery_response_allocate(&class->srv_respose_msg_buffers);
|
|
if (!entry) {
|
|
tr_debug("Too much discovery");
|
|
return;
|
|
}
|
|
//end DoS verify
|
|
/* Add response messaged trigger to list */
|
|
memcpy(entry->extentedAddress, ll64 + 8, 8);
|
|
entry->panId = dstPanId;
|
|
//Add to list
|
|
ns_list_add_to_end(&class->srv_respose_msg_list, entry);
|
|
thread_discover_timer_trig();
|
|
|
|
}
|
|
|
|
static discovery_response_list_t *thread_discover_response_msg_get_discover_from_list(thread_nwk_discovery_response_list_t *list, uint8_t channel, uint16_t panId)
|
|
{
|
|
//Get last
|
|
discovery_response_list_t *entry = ns_list_get_last(list);
|
|
while (entry) {
|
|
if (entry->channel == channel && entry->pan_id == panId) {
|
|
return entry;
|
|
}
|
|
entry = ns_list_get_previous(list, entry);
|
|
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static discovery_response_list_t *thread_discover_response_msg_allocate(void)
|
|
{
|
|
discovery_response_list_t *msg = ns_dyn_mem_temporary_alloc(sizeof(discovery_response_list_t));
|
|
if (msg) {
|
|
memset(msg, 0, sizeof(discovery_response_list_t));
|
|
}
|
|
return msg;
|
|
}
|
|
|
|
static void thread_discovery_request_free(thread_discovery_class_t *class)
|
|
{
|
|
if (!class->discovery_request) {
|
|
return;
|
|
}
|
|
|
|
ns_dyn_mem_free(class->discovery_request);
|
|
class->discovery_request = NULL;
|
|
|
|
}
|
|
|
|
static void thread_announce_discovery_request_free(thread_discovery_class_t *class)
|
|
{
|
|
if (!class->thread_announce_request) {
|
|
return;
|
|
}
|
|
ns_dyn_mem_free(class->thread_announce_request->network);
|
|
ns_dyn_mem_free(class->thread_announce_request);
|
|
class->thread_announce_request = NULL;
|
|
|
|
}
|
|
|
|
static void thread_discovery_prepare(protocol_interface_info_entry_t *interface, thread_discovery_request_info_t *discovery)
|
|
{
|
|
mac_helper_default_security_level_set(interface, SEC_ENC_MIC32);
|
|
mac_helper_beacon_payload_reallocate(interface, 0); // No beacons for thread
|
|
mac_data_poll_init(interface);
|
|
mac_helper_mac16_address_set(interface, 0xffff);
|
|
mac_helper_mac64_set(interface, discovery->temporary_mac64);
|
|
thread_set_link_local_address(interface); // only to generate IID
|
|
discovery->active_timer = 1;
|
|
discovery->waiting_response = false;
|
|
thread_discover_timer_trig();
|
|
}
|
|
|
|
static void thread_announce_discovery_prepare(protocol_interface_info_entry_t *interface, thread_announce_request_info_t *discovery)
|
|
{
|
|
mac_helper_default_security_level_set(interface, SEC_ENC_MIC32);
|
|
if (thread_info(interface)->thread_device_mode == THREAD_DEVICE_MODE_SLEEPY_END_DEVICE) {
|
|
thread_end_device_mode_set(interface, false);
|
|
}
|
|
mac_data_poll_init(interface);
|
|
mac_helper_mac16_address_set(interface, 0xffff);
|
|
discovery->active_timer = 1;
|
|
discovery->waiting_response = false;
|
|
thread_discover_timer_trig();
|
|
}
|
|
|
|
static bool thread_discovery_proces_ready(thread_discovery_request_info_t *discovery)
|
|
{
|
|
|
|
//Get next free channel
|
|
uint8_t i;
|
|
uint32_t mask = 1;
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
if (discovery->channel_mask & mask) {
|
|
discovery->channel_mask &= ~mask;
|
|
discovery->active_channel = i;
|
|
discovery->channel_page = 0; //Support only chnnel page 0
|
|
return false;
|
|
}
|
|
mask <<= 1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool thread_announce_discovery_process_ready(thread_announce_request_info_t *discovery)
|
|
{
|
|
|
|
//Get next free channel
|
|
uint8_t i;
|
|
uint32_t mask = 0x80000000;
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
if (discovery->channel_mask & mask) {
|
|
discovery->channel_mask &= ~mask;
|
|
discovery->active_channel = i;
|
|
return false;
|
|
}
|
|
mask >>= 1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void thread_discovery_process_end(protocol_interface_info_entry_t *interface)
|
|
{
|
|
mlme_reset_t reset;
|
|
reset.SetDefaultPIB = true;
|
|
interface->mac_api->mlme_req(interface->mac_api, MLME_RESET, &reset);
|
|
}
|
|
|
|
/**
|
|
* Activate mac to selected channel and pan-id
|
|
*/
|
|
static void thread_discovery_link_activate(protocol_interface_info_entry_t *interface, uint16_t pan_id, uint8_t channel, uint8_t channel_page)
|
|
{
|
|
mlme_start_t start_req;
|
|
memset(&start_req, 0, sizeof(mlme_start_t));
|
|
|
|
interface->mac_parameters->pan_id = pan_id;
|
|
interface->mac_parameters->mac_channel = channel;
|
|
|
|
start_req.PANId = pan_id;
|
|
start_req.LogicalChannel = channel;
|
|
start_req.ChannelPage = channel_page;
|
|
start_req.BeaconOrder = 0x0f;
|
|
start_req.SuperframeOrder = 0x0f;
|
|
|
|
if (interface->mac_api) {
|
|
interface->mac_api->mlme_req(interface->mac_api, MLME_START, (void *)&start_req);
|
|
}
|
|
}
|
|
|
|
static uint16_t thread_discover_tlv_get(uint8_t version, bool dynamic_bit)
|
|
{
|
|
uint16_t thread_discover_tlv = 0;
|
|
|
|
thread_discover_tlv |= (uint16_t)(version << 12);
|
|
|
|
if (dynamic_bit) {
|
|
thread_discover_tlv |= (uint16_t)(1 << 11);
|
|
}
|
|
|
|
return thread_discover_tlv;
|
|
}
|
|
|
|
|
|
static int thread_discovery_request_send(thread_discovery_class_t *class, thread_discovery_request_info_t *discovery)
|
|
{
|
|
protocol_interface_info_entry_t *cur = class->interface;
|
|
|
|
uint16_t buf_id = mle_service_msg_allocate(cur->id, 4 + discovery->filter_tlv_length + 2, false, MLE_COMMAND_DISCOVERY_REQUEST);
|
|
if (buf_id == 0) {
|
|
return -1;
|
|
}
|
|
|
|
mle_service_set_msg_destination_address(buf_id, ADDR_LINK_LOCAL_ALL_NODES);
|
|
mle_service_msg_update_security_params(buf_id, 0, 0, 0);
|
|
//Enable link layer security
|
|
mle_service_set_msg_link_layer_security_mode(buf_id, true);
|
|
uint8_t *ptr = mle_service_get_data_pointer(buf_id);
|
|
uint16_t discover_request_tlv = thread_discover_tlv_get(class->version, discovery->joiner_flag);
|
|
|
|
*ptr++ = MLE_TYPE_DISCOVERY;
|
|
*ptr++ = 4 + discovery->filter_tlv_length;
|
|
|
|
ptr = thread_meshcop_tlv_data_write_uint16(ptr, MESHCOP_TLV_DISCOVERY_REQUEST, discover_request_tlv);
|
|
if (discovery->filter_tlv_length) {
|
|
memcpy(ptr, discover_optional_start_pointer(discovery), discovery->filter_tlv_length);
|
|
ptr += discovery->filter_tlv_length;
|
|
}
|
|
|
|
if (mle_service_update_length_by_ptr(buf_id, ptr) != 0) {
|
|
tr_debug("Buffer overflow at message write");
|
|
}
|
|
|
|
mle_message_timeout_params_t timeout;
|
|
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);
|
|
discovery->waiting_response = true;
|
|
return 0;
|
|
}
|
|
|
|
static int thread_discovery_announce_request_send(thread_discovery_class_t *class, thread_announce_request_info_t *discovery)
|
|
{
|
|
protocol_interface_info_entry_t *cur = class->interface;
|
|
|
|
|
|
uint16_t buf_id = mle_service_msg_allocate(cur->id, 19, false, MLE_COMMAND_DATASET_ANNOUNCE);
|
|
if (buf_id == 0) {
|
|
return -1;
|
|
}
|
|
|
|
mle_service_set_msg_destination_address(buf_id, ADDR_LINK_LOCAL_ALL_NODES);
|
|
uint32_t keySequence;
|
|
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);
|
|
uint8_t *ptr = mle_service_get_data_pointer(buf_id);
|
|
|
|
uint8_t channel_tlv[3];
|
|
ptr = thread_meshcop_tlv_data_write_uint64(ptr, MLE_TYPE_ACTIVE_TIMESTAMP, discovery->active_time_stamp);
|
|
ptr = thread_meshcop_tlv_data_write_uint16(ptr, MLE_TYPE_PANID, discovery->pan_id);
|
|
channel_tlv[0] = 0;
|
|
common_write_16_bit(discovery->active_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");
|
|
}
|
|
|
|
mle_message_timeout_params_t timeout;
|
|
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);
|
|
discovery->waiting_response = true;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
static int thread_discovery_response_send(thread_discovery_class_t *class, thread_discovery_response_msg_t *msg_buffers)
|
|
{
|
|
link_configuration_s *linkConfiguration = thread_joiner_application_get_config(class->interface_id);
|
|
if (!linkConfiguration) {
|
|
return -1;
|
|
}
|
|
thread_management_server_data_t server_data;
|
|
if (thread_management_server_commisoner_data_get(class->interface_id, &server_data) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
protocol_interface_info_entry_t *cur = class->interface;
|
|
|
|
// Calculate length
|
|
uint16_t message_length = 16; // Default data to response always
|
|
message_length += stringlen((char *)&linkConfiguration->name, 16);
|
|
|
|
// [Joiner UDP Port TLV]
|
|
if (server_data.joiner_router_enabled && server_data.joiner_router_port) {
|
|
message_length += 4;
|
|
// [Steering Data TLV]
|
|
if (cur->thread_info->registered_commissioner.commissioner_valid && cur->thread_info->registered_commissioner.steering_data_len) {
|
|
message_length += cur->thread_info->registered_commissioner.steering_data_len + 2;
|
|
}
|
|
}
|
|
|
|
// [Commissioner UDP Port TLV]
|
|
if (linkConfiguration->securityPolicy & SECURITY_POLICY_NATIVE_COMMISSIONING_ALLOWED) {
|
|
message_length += 4;
|
|
}
|
|
|
|
message_length += thread_extension_discover_response_len(cur);
|
|
|
|
uint16_t buf_id = mle_service_msg_allocate(class->interface_id, message_length + 2, false, MLE_COMMAND_DISCOVERY_RESPONSE);
|
|
if (buf_id == 0) {
|
|
return -1;
|
|
}
|
|
|
|
//SET ll64 from euid64
|
|
uint8_t *ll64 = mle_service_get_msg_destination_address_pointer(buf_id);
|
|
memcpy(ll64, ADDR_LINK_LOCAL_PREFIX, 8);
|
|
memcpy(ll64 + 8, msg_buffers->extentedAddress, 8);
|
|
//No link layer security and no mle security.
|
|
mle_service_msg_update_security_params(buf_id, 0, 0, 0);
|
|
uint8_t *ptr = mle_service_get_data_pointer(buf_id);
|
|
|
|
*ptr++ = MLE_TYPE_DISCOVERY;
|
|
*ptr++ = message_length;
|
|
uint16_t discover_response_tlv = thread_discover_tlv_get(class->version, (linkConfiguration->securityPolicy & SECURITY_POLICY_NATIVE_COMMISSIONING_ALLOWED));
|
|
|
|
thread_extension_discover_response_tlv_write(&discover_response_tlv, class->version, linkConfiguration->securityPolicy);
|
|
|
|
ptr = thread_meshcop_tlv_data_write_uint16(ptr, MESHCOP_TLV_DISCOVERY_RESPONSE, discover_response_tlv);
|
|
ptr = thread_meshcop_tlv_data_write(ptr, MESHCOP_TLV_XPANID, 8, linkConfiguration->extented_pan_id);
|
|
ptr = thread_meshcop_tlv_data_write(ptr, MESHCOP_TLV_NETWORK_NAME, stringlen((char *)&linkConfiguration->name, 16), linkConfiguration->name);
|
|
//Optional Part
|
|
|
|
if (linkConfiguration->securityPolicy & SECURITY_POLICY_NATIVE_COMMISSIONING_ALLOWED) {
|
|
ptr = thread_meshcop_tlv_data_write_uint16(ptr, MESHCOP_TLV_COMMISSIONER_UDP_PORT, server_data.commissioner_port);
|
|
}
|
|
|
|
if (server_data.joiner_router_enabled && server_data.joiner_router_port) {
|
|
ptr = thread_meshcop_tlv_data_write_uint16(ptr, MESHCOP_TLV_JOINER_UDP_PORT, server_data.joiner_router_port);
|
|
if (cur->thread_info->registered_commissioner.commissioner_valid && cur->thread_info->registered_commissioner.steering_data_len) {
|
|
ptr = thread_meshcop_tlv_data_write(ptr, MESHCOP_TLV_STEERING_DATA, cur->thread_info->registered_commissioner.steering_data_len, cur->thread_info->registered_commissioner.steering_data);
|
|
}
|
|
}
|
|
|
|
ptr = thread_extension_discover_response_write(cur, ptr);
|
|
|
|
if (mle_service_update_length_by_ptr(buf_id, ptr) != 0) {
|
|
tr_debug("Buffer overflow at message write");
|
|
}
|
|
|
|
mle_message_timeout_params_t timeout;
|
|
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, msg_buffers->panId);
|
|
mle_service_send_message(buf_id);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static bool thread_discovery_request_timer_update(thread_discovery_class_t *class)
|
|
{
|
|
class->discovery_request->active_timer--;
|
|
if (class->discovery_request->active_timer) {
|
|
return true;
|
|
}
|
|
|
|
if (thread_discovery_proces_ready(class->discovery_request)) {
|
|
thread_discovery_process_end(class->interface);
|
|
class->discovery_request->response_cb(class->interface, &class->discovered_network);
|
|
//Free Entry
|
|
thread_discovery_request_free(class);
|
|
return false;
|
|
}
|
|
|
|
//Switch channel and set pan-id
|
|
thread_discovery_link_activate(class->interface, class->discovery_request->random_panid, class->discovery_request->active_channel, class->discovery_request->channel_page);
|
|
tr_debug("Discover channel %u", class->discovery_request->active_channel);
|
|
//Process packet
|
|
thread_discovery_request_send(class, class->discovery_request);
|
|
//Set timer
|
|
class->discovery_request->active_timer = THREAD_DISCOVERY_TIMEOUT / THREAD_DISCOVER_TIMER_PERIOD;
|
|
return true;
|
|
|
|
}
|
|
|
|
static bool thread_announce_discovery_request_timer_update(thread_discovery_class_t *class)
|
|
{
|
|
class->thread_announce_request->active_timer--;
|
|
if (class->thread_announce_request->active_timer) {
|
|
return true;
|
|
}
|
|
|
|
if (thread_announce_discovery_process_ready(class->thread_announce_request)) {
|
|
thread_discovery_process_end(class->interface);
|
|
announce_discovery_response_t *result = class->thread_announce_request->network;
|
|
thread_announce_scan_ready_cb *response_cb = class->thread_announce_request->response_cb;
|
|
class->thread_announce_request->network = NULL;
|
|
thread_announce_discovery_request_free(class);
|
|
response_cb(class->interface, result);
|
|
return false;
|
|
}
|
|
|
|
//Switch channel and set pan-id
|
|
thread_discovery_link_activate(class->interface, class->thread_announce_request->pan_id, class->thread_announce_request->active_channel, 0);
|
|
tr_debug("Announce Discover channel %u", class->thread_announce_request->active_channel);
|
|
//Process packet
|
|
thread_discovery_announce_request_send(class, class->thread_announce_request);
|
|
//Set timer
|
|
class->thread_announce_request->active_timer = THREAD_DISCOVERY_TIMEOUT / THREAD_DISCOVER_TIMER_PERIOD;
|
|
return true;
|
|
|
|
}
|
|
|
|
static bool thread_discovery_response_jitter_timer_update(thread_discovery_class_t *class)
|
|
{
|
|
bool pending_list = false;
|
|
ns_list_foreach_safe(thread_discovery_response_msg_t, cur, &class->srv_respose_msg_list) {
|
|
cur->timer--;
|
|
if (!cur->timer) {
|
|
//Send a packet
|
|
thread_discovery_response_send(class, cur);
|
|
ns_list_remove(&class->srv_respose_msg_list, cur);
|
|
ns_list_add_to_start(&class->srv_respose_msg_buffers, cur);
|
|
} else {
|
|
pending_list = true;
|
|
}
|
|
}
|
|
|
|
return pending_list;
|
|
|
|
}
|
|
|
|
static thread_discovery_class_t *thread_discovery_class_get(int8_t interface_id)
|
|
{
|
|
ns_list_foreach(thread_discovery_class_t, cur_class, &thread_discovery_class_list) {
|
|
if (cur_class->interface_id == interface_id) {
|
|
return cur_class;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void thread_discovery_free_discovered_networks(thread_nwk_discovery_response_list_t *list)
|
|
{
|
|
ns_list_foreach_safe(discovery_response_list_t, cur, list) {
|
|
ns_list_remove(list, cur);
|
|
ns_dyn_mem_free(cur);
|
|
}
|
|
}
|
|
|
|
static thread_discovery_class_t *thread_discovery_class_allocate(bool reedDevice)
|
|
{
|
|
thread_discovery_class_t *class_ptr = ns_dyn_mem_alloc(sizeof(thread_discovery_class_t));
|
|
|
|
if (class_ptr) {
|
|
ns_list_init(&class_ptr->srv_respose_msg_list);
|
|
ns_list_init(&class_ptr->srv_respose_msg_buffers);
|
|
ns_list_init(&class_ptr->discovered_network);
|
|
|
|
if (reedDevice) {
|
|
if (!thread_discovery_server_msg_buffer_allocate(class_ptr)) {
|
|
ns_dyn_mem_free(class_ptr);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
class_ptr->msg_buffers = NULL;
|
|
}
|
|
class_ptr->discovery_request = NULL;
|
|
class_ptr->thread_announce_request = NULL;
|
|
ns_list_add_to_start(&thread_discovery_class_list, class_ptr);
|
|
}
|
|
return class_ptr;
|
|
}
|
|
|
|
static void thread_discovery_class_free(thread_discovery_class_t *class_ptr)
|
|
{
|
|
if (!class_ptr) {
|
|
return;
|
|
}
|
|
ns_list_remove(&thread_discovery_class_list, class_ptr);
|
|
|
|
thread_discovery_server_msg_buffer_free(class_ptr);
|
|
thread_discovery_free_discovered_networks(&class_ptr->discovered_network);
|
|
thread_discovery_request_free(class_ptr);
|
|
thread_announce_discovery_request_free(class_ptr);
|
|
ns_dyn_mem_free(class_ptr);
|
|
}
|
|
|
|
bool thread_discovery_tlv_spesific_data_discover(const uint8_t *ptr, uint16_t length, const mescop_tlv_t *compare_tlv)
|
|
{
|
|
const uint8_t *p;
|
|
if (!ptr || length < 2 || !compare_tlv || !compare_tlv->data || !compare_tlv->length) {
|
|
return false;
|
|
}
|
|
|
|
p = ptr;
|
|
while (p != NULL) {
|
|
const uint8_t *tlv_data_ptr;
|
|
uint16_t tlv_data_length;
|
|
//tr_info("tlv_find first check");
|
|
// check if we have enough length for normal length tlv
|
|
if (p + 2 > ptr + length) {
|
|
break; //must have at least type and short length
|
|
}
|
|
|
|
if (p[1] == 0xff) {
|
|
// Long length format
|
|
if (p + 4 > ptr + length) {
|
|
break; // check if enough length for long length
|
|
}
|
|
|
|
tlv_data_length = common_read_16_bit(&p[2]);
|
|
tlv_data_ptr = p + 4;
|
|
} else {
|
|
tlv_data_length = p[1];
|
|
tlv_data_ptr = p + 2;
|
|
}
|
|
//tr_info("tlv_find check: %d, type: %d", tlv_data_length, *p);
|
|
|
|
// check if length of tlv is correct
|
|
if (tlv_data_ptr + tlv_data_length > ptr + length) {
|
|
break; //length goes past the data block
|
|
}
|
|
|
|
if (*p == compare_tlv->type) {
|
|
if (tlv_data_length == compare_tlv->length) {
|
|
if (memcmp(tlv_data_ptr, compare_tlv->data, tlv_data_length) == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
p = tlv_data_ptr + tlv_data_length;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool thread_discovery_request_filter_validate(link_configuration_s *linkConfiguration, uint8_t *data_ptr, uint16_t length)
|
|
{
|
|
//Validate PAN-ID
|
|
uint8_t pan_id[2];
|
|
|
|
mescop_tlv_t compare_tlv;
|
|
//Validate PAN-id, ExtentedPAN-id & Network name
|
|
|
|
compare_tlv.data = pan_id;
|
|
compare_tlv.length = 2;
|
|
compare_tlv.type = MESHCOP_TLV_PANID;
|
|
|
|
common_write_16_bit(linkConfiguration->panId, compare_tlv.data);
|
|
|
|
if (thread_discovery_tlv_spesific_data_discover(data_ptr, length, &compare_tlv)) {
|
|
return false;
|
|
}
|
|
|
|
compare_tlv.data = linkConfiguration->extented_pan_id;
|
|
compare_tlv.length = 8;
|
|
compare_tlv.type = MESHCOP_TLV_XPANID;
|
|
|
|
if (thread_discovery_tlv_spesific_data_discover(data_ptr, length, &compare_tlv)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
static void thread_discovery_request_msg_handler(thread_discovery_class_t *discovery_class, mle_message_t *mle_msg)
|
|
{
|
|
//Validate that server is enabled
|
|
if (!discovery_class->discovery_server_active) {
|
|
return;
|
|
}
|
|
|
|
link_configuration_s *linkConfiguration = thread_joiner_application_get_config(discovery_class->interface_id);
|
|
if (!linkConfiguration) {
|
|
return;
|
|
}
|
|
tr_debug("Thread discovery request message RX");
|
|
|
|
// Check if we have room for new neighbor
|
|
if (mle_class_free_entry_count_get(discovery_class->interface) < 1) {
|
|
tr_debug("MLE table full, skip request");
|
|
return;
|
|
}
|
|
|
|
//validate message
|
|
mle_tlv_info_t discovery_tlv;
|
|
//Parse Message
|
|
uint16_t discover_req_tlv;
|
|
|
|
//Discover MLE_TYPE_DISCOVERY
|
|
if (mle_tlv_option_discover(mle_msg->data_ptr, mle_msg->data_length, MLE_TYPE_DISCOVERY, &discovery_tlv) < 4) {
|
|
return;
|
|
}
|
|
|
|
if (thread_meshcop_tlv_data_get_uint16(discovery_tlv.dataPtr, discovery_tlv.tlvLen, MESHCOP_TLV_DISCOVERY_REQUEST, &discover_req_tlv) < 2) {
|
|
tr_debug("discover response not include all mandatory TLV's");
|
|
return;
|
|
}
|
|
//Validate Version
|
|
uint8_t version = (uint8_t)(discover_req_tlv >> 12);
|
|
if (discovery_class->version < version) {
|
|
tr_debug("Dropped by version %u != %u", discovery_class->version, version);
|
|
return;
|
|
}
|
|
|
|
bool joiner_flag = (discover_req_tlv >> 11) & 1;
|
|
|
|
if (joiner_flag) {
|
|
//Can we respond
|
|
thread_management_server_data_t joiner_router_info;
|
|
if (0 != thread_management_server_commisoner_data_get(discovery_class->interface_id, &joiner_router_info) ||
|
|
!joiner_router_info.joiner_router_enabled) {
|
|
if (!thread_extension_joining_enabled(discovery_class->interface_id)) {
|
|
tr_debug("Drop by Joining disabled");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Validate possible blacklist
|
|
if (!thread_discovery_request_filter_validate(linkConfiguration, discovery_tlv.dataPtr, discovery_tlv.tlvLen)) {
|
|
tr_debug("Dropped by filter");
|
|
return;
|
|
}
|
|
|
|
//Trig response
|
|
thread_discovery_response_trig(discovery_class, mle_msg->packet_src_address, mle_msg->src_pan_id);
|
|
}
|
|
|
|
static bool thread_seering_data_accept_any(uint8_t length, uint8_t *data)
|
|
{
|
|
if (length == 1 && *data == 0xff) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void thread_discovery_nwk_push_to_list_by_lqi(thread_nwk_discovery_response_list_t *result_list, discovery_response_list_t *nwk_info)
|
|
{
|
|
if (ns_list_count(result_list)) {
|
|
ns_list_foreach_safe(discovery_response_list_t, cur_entry, result_list) {
|
|
if (nwk_info->dbm > cur_entry->dbm) {
|
|
|
|
ns_list_add_before(result_list, cur_entry, nwk_info);
|
|
return;
|
|
}
|
|
}
|
|
ns_list_add_to_end(result_list, nwk_info);
|
|
} else {
|
|
ns_list_add_to_end(result_list, nwk_info);
|
|
}
|
|
}
|
|
|
|
static void thread_discovery_joiner_set(thread_nwk_discovery_response_list_t *result_list, discovery_response_list_t *nwk_info, bool new_accept_any)
|
|
{
|
|
if (ns_list_count(result_list)) {
|
|
|
|
bool cur_acept_any;
|
|
ns_list_foreach_safe(discovery_response_list_t, cur_entry, result_list) {
|
|
|
|
cur_acept_any = thread_seering_data_accept_any(cur_entry->steering_data_valid, cur_entry->steering_data);
|
|
|
|
if (!cur_acept_any && !new_accept_any) {
|
|
if (nwk_info->dbm > cur_entry->dbm) {
|
|
ns_list_add_before(result_list, cur_entry, nwk_info);
|
|
return;
|
|
}
|
|
} else {
|
|
if (!new_accept_any) {
|
|
//add this before cur
|
|
ns_list_add_before(result_list, cur_entry, nwk_info);
|
|
return;
|
|
} else if (nwk_info->dbm > cur_entry->dbm) {
|
|
ns_list_add_before(result_list, cur_entry, nwk_info);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
ns_list_add_to_end(result_list, nwk_info);
|
|
} else {
|
|
ns_list_add_to_end(result_list, nwk_info);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void thread_discovery_response_msg_handler(thread_discovery_class_t *discovery_class, mle_message_t *mle_msg)
|
|
{
|
|
if (!discovery_class->discovery_request || !discovery_class->discovery_request->waiting_response) {
|
|
return;
|
|
}
|
|
|
|
mle_tlv_info_t discovery_tlv;
|
|
//Parse Message
|
|
uint16_t discover_response_tlv;
|
|
uint8_t *nwk_name, *extented_panid;
|
|
uint8_t nwk_name_length;
|
|
|
|
//Discover MLE_TYPE_DISCOVERY
|
|
if (mle_tlv_option_discover(mle_msg->data_ptr, mle_msg->data_length, MLE_TYPE_DISCOVERY, &discovery_tlv) < 4) {
|
|
return;
|
|
}
|
|
|
|
nwk_name_length = thread_meshcop_tlv_find(discovery_tlv.dataPtr, discovery_tlv.tlvLen, MESHCOP_TLV_NETWORK_NAME, &nwk_name);
|
|
|
|
if (thread_meshcop_tlv_data_get_uint16(discovery_tlv.dataPtr, discovery_tlv.tlvLen, MESHCOP_TLV_DISCOVERY_RESPONSE, &discover_response_tlv) < 2
|
|
|| thread_meshcop_tlv_find(discovery_tlv.dataPtr, discovery_tlv.tlvLen, MESHCOP_TLV_XPANID, &extented_panid) < 8
|
|
|| nwk_name_length > 16) {
|
|
|
|
tr_debug("discover response not include all mandatory TLV's");
|
|
return;
|
|
}
|
|
|
|
tr_debug("Thread discovery response message RX");
|
|
|
|
uint8_t version = (uint8_t)(discover_response_tlv >> 12);
|
|
if (discovery_class->version > version) {
|
|
tr_debug("Dropped by version %u != %u", discovery_class->version, version);
|
|
return;
|
|
}
|
|
|
|
if (discovery_class->discovery_request->native_commisioner_scan) {
|
|
bool native_commioner_bit = (discover_response_tlv >> 11) & 1;
|
|
if (!native_commioner_bit) {
|
|
tr_debug("Native commisioner not supported");
|
|
return;
|
|
}
|
|
}
|
|
uint16_t pan_id = mle_msg->src_pan_id;
|
|
uint8_t *steering_data;
|
|
uint16_t joiner_port;
|
|
bool joiner_port_valid;
|
|
|
|
uint8_t steerin_data_length = thread_meshcop_tlv_find(discovery_tlv.dataPtr, discovery_tlv.tlvLen, MESHCOP_TLV_STEERING_DATA, &steering_data);
|
|
if (steerin_data_length > 16) {
|
|
steerin_data_length = 0;
|
|
}
|
|
|
|
if (thread_meshcop_tlv_data_get_uint16(discovery_tlv.dataPtr, discovery_tlv.tlvLen, MESHCOP_TLV_JOINER_UDP_PORT, &joiner_port) >= 2) {
|
|
joiner_port_valid = true;
|
|
} else {
|
|
joiner_port_valid = false;
|
|
}
|
|
|
|
if (discovery_class->discovery_request->joiner_flag && (!joiner_port_valid || steerin_data_length == 0)) {
|
|
if (thread_extension_version_check(discovery_class->interface->thread_info->version)) {
|
|
if (!discovery_class->interface->thread_info->extension_credentials_ptr) {
|
|
tr_debug("Dropped, no joiner info");
|
|
}
|
|
} else {
|
|
tr_debug("Dropped by no valid joiner info %u %u", joiner_port_valid, steerin_data_length);
|
|
return;
|
|
}
|
|
}
|
|
|
|
discovery_response_list_t *nwk_info = thread_discover_response_msg_get_discover_from_list(
|
|
&discovery_class->discovered_network,
|
|
discovery_class->discovery_request->active_channel, pan_id);
|
|
if (nwk_info) {
|
|
if (nwk_info->dbm < mle_msg->dbm) {
|
|
goto save_optional_data;
|
|
}
|
|
return;
|
|
}
|
|
|
|
nwk_info = thread_discover_response_msg_allocate();
|
|
if (!nwk_info) {
|
|
return;
|
|
}
|
|
//Set parameters
|
|
nwk_info->version = (discover_response_tlv >> 12);
|
|
nwk_info->dbm = mle_msg->dbm;
|
|
nwk_info->channel = discovery_class->discovery_request->active_channel;
|
|
|
|
nwk_info->pan_id = pan_id;
|
|
memcpy(nwk_info->extented_pan_id, extented_panid, 8);
|
|
|
|
memset(nwk_info->network_name, 0, 16);
|
|
memcpy(nwk_info->network_name, nwk_name, nwk_name_length);
|
|
|
|
thread_meshcop_tlv_data_get_uint16(discovery_tlv.dataPtr, discovery_tlv.tlvLen, MESHCOP_TLV_COMMISSIONER_UDP_PORT, &nwk_info->commissioner_port);
|
|
|
|
thread_extension_discover_response_read(nwk_info, discover_response_tlv, discovery_tlv.dataPtr, discovery_tlv.tlvLen);
|
|
|
|
//Add to last
|
|
if (discovery_class->discovery_request->native_commisioner_scan) {
|
|
thread_discovery_nwk_push_to_list_by_lqi(&discovery_class->discovered_network, nwk_info);
|
|
} else {
|
|
//Validate is steering data
|
|
thread_discovery_joiner_set(&discovery_class->discovered_network, nwk_info, thread_seering_data_accept_any(nwk_info->steering_data_valid, nwk_info->steering_data));
|
|
}
|
|
|
|
|
|
save_optional_data:
|
|
memcpy(nwk_info->extented_mac, mle_msg->packet_src_address + 8, 8);
|
|
nwk_info->extented_mac[0] ^= 2;
|
|
if (steerin_data_length) {
|
|
memcpy(nwk_info->steering_data, steering_data, steerin_data_length);
|
|
nwk_info->steering_data_valid = steerin_data_length;
|
|
}
|
|
|
|
if (joiner_port_valid) {
|
|
nwk_info->joiner_port = joiner_port;
|
|
}
|
|
|
|
}
|
|
|
|
static void thread_announce_discovery_message_receiver_cb(int8_t interface_id, mle_message_t *mle_msg)
|
|
{
|
|
if (mle_msg->message_type != MLE_COMMAND_DATASET_ANNOUNCE) {
|
|
return;
|
|
}
|
|
thread_discovery_class_t *discovery_class = thread_discovery_class_get(interface_id);
|
|
if (!discovery_class || !discovery_class->thread_announce_request) {
|
|
return;
|
|
}
|
|
|
|
tr_debug("MLE ANNOUNCE RX");
|
|
uint64_t timestamp;
|
|
uint16_t panid;
|
|
uint8_t *ptr;
|
|
uint16_t channel;
|
|
|
|
tr_debug("Host Recv Dataset Announce %s", trace_ipv6(mle_msg->packet_src_address));
|
|
if (8 > thread_tmfcop_tlv_data_get_uint64(mle_msg->data_ptr, mle_msg->data_length, MLE_TYPE_ACTIVE_TIMESTAMP, ×tamp)) {
|
|
tr_error("Missing timestamp TLV");
|
|
return;
|
|
}
|
|
if (2 > thread_tmfcop_tlv_data_get_uint16(mle_msg->data_ptr, mle_msg->data_length, MLE_TYPE_PANID, &panid)) {
|
|
tr_error("Missing Panid TLV");
|
|
return;
|
|
}
|
|
if (3 > thread_tmfcop_tlv_find(mle_msg->data_ptr, mle_msg->data_length, MLE_TYPE_CHANNEL, &ptr)) {
|
|
tr_error("Missing Channel TLV");
|
|
return;
|
|
}
|
|
channel = common_read_16_bit(&ptr[1]);
|
|
|
|
if (timestamp <= discovery_class->thread_announce_request->active_time_stamp) {
|
|
tr_debug("Drop by timestamp");
|
|
return;
|
|
}
|
|
|
|
//Validate
|
|
announce_discovery_response_t *response = NULL;
|
|
if (discovery_class->thread_announce_request->network) {
|
|
response = discovery_class->thread_announce_request->network;
|
|
if (timestamp <= discovery_class->thread_announce_request->network->active_timestamp) {
|
|
response = NULL;
|
|
}
|
|
} else {
|
|
discovery_class->thread_announce_request->network = ns_dyn_mem_temporary_alloc(sizeof(announce_discovery_response_t));
|
|
response = discovery_class->thread_announce_request->network;
|
|
}
|
|
|
|
if (response) {
|
|
tr_debug("Save data");
|
|
response->active_timestamp = timestamp;
|
|
response->channel = channel;
|
|
response->pan_id = panid;
|
|
}
|
|
}
|
|
|
|
|
|
static void thread_announce_discover_receive_cb(int8_t interface_id, mle_message_t *mle_msg, mle_security_header_t *security_headers)
|
|
{
|
|
//Verify security
|
|
(void)security_headers;
|
|
|
|
/* Check that message is from link-local scope */
|
|
if (!addr_is_ipv6_link_local(mle_msg->packet_src_address)) {
|
|
return;
|
|
}
|
|
|
|
thread_announce_discovery_message_receiver_cb(interface_id, mle_msg);
|
|
}
|
|
|
|
static void thread_discovery_message_receiver_cb(int8_t interface_id, mle_message_t *mle_msg)
|
|
{
|
|
//Discovery interface get
|
|
thread_discovery_class_t *discovery_class = thread_discovery_class_get(interface_id);
|
|
if (!discovery_class) {
|
|
return;
|
|
}
|
|
|
|
switch (mle_msg->message_type) {
|
|
case MLE_COMMAND_DISCOVERY_REQUEST:
|
|
//call message handler
|
|
thread_discovery_request_msg_handler(discovery_class, mle_msg);
|
|
break;
|
|
|
|
case MLE_COMMAND_DISCOVERY_RESPONSE:
|
|
//call message handler
|
|
thread_discovery_response_msg_handler(discovery_class, mle_msg);
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
static void thread_discover_event_handler(arm_event_s *event)
|
|
{
|
|
switch (event->event_type) {
|
|
case THREAD_DISCOVER_INIT:
|
|
thread_discover_timer_trig();
|
|
break;
|
|
|
|
case THREAD_DISCOVER_TIMER:
|
|
//Do list in future for each of mle user
|
|
thread_discover_timer_active = false;
|
|
if (thread_discovery_timer_update()) {
|
|
//Request new timer
|
|
thread_discover_timer_trig();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int8_t thread_discover_class_event_handler_init(void)
|
|
{
|
|
if (thread_discover_tasklet_id == -1) {
|
|
//GENERATE TASKLET
|
|
thread_discover_tasklet_id = eventOS_event_handler_create(&thread_discover_event_handler, THREAD_DISCOVER_INIT);
|
|
}
|
|
return thread_discover_tasklet_id;
|
|
}
|
|
|
|
|
|
int thread_discovery_init(int8_t interface_id, struct protocol_interface_info_entry *cur_interface, uint8_t version, bool reedDevice)
|
|
{
|
|
if (!cur_interface) {
|
|
return -2;
|
|
}
|
|
|
|
thread_discovery_class_t *discovery_class = thread_discovery_class_get(interface_id);
|
|
if (discovery_class) {
|
|
//Verify reed boolean
|
|
|
|
thread_discovery_request_free(discovery_class);
|
|
thread_announce_discovery_request_free(discovery_class);
|
|
thread_discovery_server_msg_buffer_free(discovery_class);
|
|
if (reedDevice) {
|
|
if (!thread_discovery_server_msg_buffer_allocate(discovery_class)) {
|
|
thread_discovery_class_free(discovery_class);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
goto return_ok;
|
|
}
|
|
|
|
if (thread_discover_class_event_handler_init() < 0) {
|
|
return -1;
|
|
}
|
|
|
|
//Allocate new entry
|
|
discovery_class = thread_discovery_class_allocate(reedDevice);
|
|
if (!discovery_class) {
|
|
mle_service_interface_receiver_bypass_handler_update(interface_id, NULL);
|
|
return -1;
|
|
}
|
|
discovery_class->interface_id = interface_id;
|
|
|
|
return_ok:
|
|
discovery_class->discovery_server_active = false;
|
|
discovery_class->interface = cur_interface;
|
|
discovery_class->version = version;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Reset discovery class state to idle
|
|
*/
|
|
int thread_discovery_reset(int8_t interface_id)
|
|
{
|
|
thread_discovery_class_t *discovery_class = thread_discovery_class_get(interface_id);
|
|
if (!discovery_class) {
|
|
return -1;
|
|
}
|
|
thread_discovery_request_free(discovery_class);
|
|
thread_announce_discovery_request_free(discovery_class);
|
|
thread_discovery_server_msg_clean(discovery_class);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Update discovery timer state's
|
|
*/
|
|
static bool thread_discovery_timer_update(void)
|
|
{
|
|
bool keep_timer_active = false;
|
|
ns_list_foreach(thread_discovery_class_t, cur_class, &thread_discovery_class_list) {
|
|
if (cur_class->discovery_server_active) {
|
|
if (thread_discovery_response_jitter_timer_update(cur_class)) {
|
|
keep_timer_active = true;
|
|
}
|
|
} else if (cur_class->discovery_request) {
|
|
if (thread_discovery_request_timer_update(cur_class)) {
|
|
keep_timer_active = true;
|
|
}
|
|
} else if (cur_class->thread_announce_request) {
|
|
if (thread_announce_discovery_request_timer_update(cur_class)) {
|
|
keep_timer_active = true;
|
|
}
|
|
}
|
|
}
|
|
return keep_timer_active;
|
|
}
|
|
|
|
/**
|
|
* Enable thread discovery request response support
|
|
*/
|
|
int thread_discovery_responser_enable(int8_t interface_id, bool enable_service)
|
|
{
|
|
thread_discovery_class_t *discovery_class = thread_discovery_class_get(interface_id);
|
|
if (!discovery_class || !discovery_class->msg_buffers) {
|
|
return -1;
|
|
}
|
|
|
|
|
|
//Clean server message list always
|
|
thread_discovery_server_msg_clean(discovery_class);
|
|
if (mle_service_interface_receiver_bypass_handler_update(interface_id, thread_discovery_message_receiver_cb) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
discovery_class->discovery_server_active = enable_service;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void thread_discovery_normal_receive_cb(int8_t interface_id, mle_message_t *mle_msg, mle_security_header_t *security_headers)
|
|
{
|
|
if (security_headers->securityLevel == 0) {
|
|
thread_discovery_message_receiver_cb(interface_id, mle_msg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start Thread network discovery
|
|
*/
|
|
int thread_discovery_network_scan(struct protocol_interface_info_entry *cur_interface, thread_discover_reques_t *scan_request, thread_discovery_ready_cb *ready_cb)
|
|
{
|
|
|
|
thread_discovery_class_t *discovery_class = thread_discovery_class_get(cur_interface->id);
|
|
if (!discovery_class || !ready_cb || !scan_request) {
|
|
return -1;
|
|
}
|
|
|
|
//Check Is discovery started already
|
|
if (discovery_class->discovery_request) {
|
|
return -2;
|
|
}
|
|
|
|
if (!scan_request->channel_mask || (scan_request->filter_tlv_length > 0 && !scan_request->filter_tlv_data)) {
|
|
return -1;
|
|
}
|
|
|
|
discovery_class->discovery_request = thread_discovery_request_allocate(scan_request, ready_cb);
|
|
|
|
if (!discovery_class->discovery_request) {
|
|
return -3;
|
|
}
|
|
|
|
if (mle_service_interface_register(cur_interface->id, cur_interface, thread_discovery_normal_receive_cb, discovery_class->discovery_request->temporary_mac64, 8) != 0) {
|
|
thread_discovery_request_free(discovery_class);
|
|
return -1;
|
|
}
|
|
|
|
if (mle_service_interface_receiver_bypass_handler_update(cur_interface->id, thread_discovery_message_receiver_cb) != 0) {
|
|
thread_discovery_request_free(discovery_class);
|
|
return -1;
|
|
}
|
|
|
|
//Free old networks
|
|
thread_discovery_free_discovered_networks(&discovery_class->discovered_network);
|
|
//Set temporary mac and generate ll64
|
|
thread_discovery_prepare(discovery_class->interface, discovery_class->discovery_request);
|
|
return 0;
|
|
}
|
|
|
|
int thread_discovery_announce_network_scan(int8_t interface_id, thread_announce_discover_reques_t *scan_request, thread_announce_scan_ready_cb *ready_cb)
|
|
{
|
|
thread_discovery_class_t *discovery_class = thread_discovery_class_get(interface_id);
|
|
if (!discovery_class || !ready_cb || !scan_request) {
|
|
return -1;
|
|
}
|
|
|
|
//Check Is discovery started already
|
|
if (discovery_class->discovery_request || discovery_class->thread_announce_request) {
|
|
return -2;
|
|
}
|
|
|
|
if (!scan_request->channel_mask) {
|
|
return -1;
|
|
}
|
|
|
|
discovery_class->thread_announce_request = thread_announce_discovery_request_allocate(scan_request, ready_cb);
|
|
|
|
if (!discovery_class->thread_announce_request) {
|
|
return -3;
|
|
}
|
|
|
|
//Update receiver callback
|
|
if (mle_service_interface_receiver_handler_update(interface_id, thread_announce_discover_receive_cb) != 0) {
|
|
thread_announce_discovery_request_free(discovery_class);
|
|
return -1;
|
|
}
|
|
|
|
if (mle_service_interface_receiver_bypass_handler_update(interface_id, thread_announce_discovery_message_receiver_cb) != 0) {
|
|
thread_discovery_request_free(discovery_class);
|
|
return -1;
|
|
}
|
|
|
|
//Set temporary mac and generate ll64
|
|
thread_announce_discovery_prepare(discovery_class->interface, discovery_class->thread_announce_request);
|
|
return 0;
|
|
}
|
|
|
|
discovery_response_list_t *thread_discovery_network_description_get(int8_t interface_id)
|
|
{
|
|
thread_discovery_class_t *discovery_class = thread_discovery_class_get(interface_id);
|
|
if (!discovery_class) {
|
|
return NULL;
|
|
}
|
|
|
|
discovery_response_list_t *entry = ns_list_get_first(&discovery_class->discovered_network);
|
|
if (entry) {
|
|
ns_list_remove(&discovery_class->discovered_network, entry);
|
|
}
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
#if 0
|
|
/**
|
|
* Free all allocated memory and free class
|
|
* Not used API function, flagged away. This should be taken in use when refactoring ifdown - functionality.
|
|
*/
|
|
int thread_discovery_free(int8_t interface_id)
|
|
{
|
|
thread_discovery_class_t *discovery_class = thread_discovery_class_get(interface_id);
|
|
if (!discovery_class) {
|
|
return -1;
|
|
}
|
|
|
|
thread_discovery_class_free(discovery_class);
|
|
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
#endif
|