mirror of https://github.com/ARMmbed/mbed-os.git
1170 lines
39 KiB
C
1170 lines
39 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
#include "nsconfig.h"
|
|
|
|
#include <string.h>
|
|
#include "ns_types.h"
|
|
#include <nsdynmemLIB.h>
|
|
#include "eventOS_event.h"
|
|
#include <ns_list.h>
|
|
#include "ns_trace.h"
|
|
#include "Core/include/ns_buffer.h"
|
|
#include "common_functions.h"
|
|
#include "randLIB.h"
|
|
#include "thread_border_router_api.h"
|
|
#include "thread_management_if.h"
|
|
#include "NWK_INTERFACE/Include/protocol.h"
|
|
#include "6LoWPAN/Thread/thread_config.h"
|
|
#include "6LoWPAN/Thread/thread_common.h"
|
|
#include "6LoWPAN/Thread/thread_extension_bbr.h"
|
|
#include "6LoWPAN/Thread/thread_network_data_lib.h"
|
|
#include "6LoWPAN/Thread/thread_network_data_storage.h"
|
|
#include "6LoWPAN/Thread/thread_management_client.h"
|
|
#include "6LoWPAN/Thread/thread_joiner_application.h"
|
|
#include "6LoWPAN/Thread/thread_tmfcop_lib.h"
|
|
#include "6LoWPAN/Thread/thread_border_router_api_internal.h"
|
|
#include "6LoWPAN/Thread/thread_mdns.h"
|
|
#include "6LoWPAN/Bootstraps/protocol_6lowpan.h"
|
|
#include "6LoWPAN/MAC/mac_helper.h"
|
|
#include "MLE/mle.h"
|
|
#include "thread_meshcop_lib.h"
|
|
#include "thread_network_data_lib.h"
|
|
#include "coap_service_api.h"
|
|
|
|
#define TRACE_GROUP "tBRa"
|
|
|
|
#ifdef HAVE_THREAD_ROUTER
|
|
/*
|
|
* Structure containing IPv6 Router advertisement options for DNS configuration.
|
|
*/
|
|
typedef struct {
|
|
uint16_t option_length;
|
|
uint8_t *option_data;
|
|
} dns_option_t;
|
|
|
|
/*
|
|
* Border router instance data.
|
|
*/
|
|
typedef struct {
|
|
dns_option_t *dns_search_list_option; /* option_data encoded according to RFC6106, DNSSL */
|
|
dns_option_t *recursive_dns_server_option; /* option_data encoded according to RFC6106, RDNSS */
|
|
uint16_t nwk_data_resubmit_timer; /* network data resubmit timeout */
|
|
int8_t interface_id;
|
|
int8_t coap_service_id;
|
|
ns_list_link_t link;
|
|
} thread_border_router_t;
|
|
|
|
/* Neighbor discovery options according to RFC6106 (rfc4861) */
|
|
#define RFC6106_RECURSIVE_DNS_SERVER_OPTION 25
|
|
#define RFC6106_DNS_SEARCH_LIST_OPTION 31
|
|
|
|
static NS_LIST_DEFINE(border_router_instance_list, thread_border_router_t, link);
|
|
|
|
|
|
static thread_border_router_t *thread_border_router_find_by_interface(int8_t interface_id)
|
|
{
|
|
thread_border_router_t *this = NULL;
|
|
ns_list_foreach(thread_border_router_t, cur_br, &border_router_instance_list) {
|
|
if (cur_br->interface_id == interface_id) {
|
|
this = cur_br;
|
|
break;
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
static thread_border_router_t *thread_border_router_find_by_service(int8_t service_id)
|
|
{
|
|
thread_border_router_t *this = NULL;
|
|
ns_list_foreach(thread_border_router_t, cur_br, &border_router_instance_list) {
|
|
if (cur_br->coap_service_id == service_id) {
|
|
this = cur_br;
|
|
break;
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/*
|
|
* Read Border Router TLV: DNS search list option.
|
|
* DNSSL option data will be fetched from:
|
|
* 1. User has provided static configuration by using method thread_border_router_dns_search_list_option_set.
|
|
* 2. ICMP RA messages (if service is enabled and running in background).
|
|
*/
|
|
static uint8_t *thread_management_server_border_router_nd_dnssl_option_read(thread_border_router_t *this, uint16_t *resp_len)
|
|
{
|
|
if (this->dns_search_list_option) {
|
|
*resp_len = this->dns_search_list_option->option_length;
|
|
return (uint8_t *)&this->dns_search_list_option->option_data;
|
|
} else {
|
|
// TODO: Read DNSSL from stored ICMP RA messages.
|
|
*resp_len = 0;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Read Border Router TLV: Recursive DNS Server option.
|
|
* RDNSS option data will be fetched from:
|
|
* 1. User has provided static configuration by using method thread_border_router_recursive_dns_server_option_set.
|
|
* 2. ICMP RA messages (if service is enabled and running in background).
|
|
*/
|
|
static uint8_t *thread_management_server_border_router_nd_rdnss_option_read(thread_border_router_t *this, uint16_t *resp_len)
|
|
{
|
|
if (this->recursive_dns_server_option) {
|
|
*resp_len = this->recursive_dns_server_option->option_length;
|
|
return (uint8_t *)&this->recursive_dns_server_option->option_data;
|
|
} else {
|
|
// TODO: Read RDNSS from stored ICMP RA messages.
|
|
*resp_len = 0;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Thread Neighbor discovery data request handler
|
|
*/
|
|
static int thread_border_router_neighbor_discovery_data_req_cb(int8_t service_id, uint8_t source_address[16], uint16_t source_port, sn_coap_hdr_s *request_ptr)
|
|
{
|
|
thread_border_router_t *this = thread_border_router_find_by_service(service_id);
|
|
uint8_t *request_tlv_ptr;
|
|
uint8_t *resp_payload_ptr;
|
|
uint16_t options_len, rdnss_option_len, dnssl_option_len, payload_len;
|
|
uint8_t options_data[4];
|
|
uint8_t *dns_server_tlv_ptr = NULL;
|
|
uint8_t *dns_search_list_tlv_ptr = NULL;
|
|
uint8_t *ptr;
|
|
sn_coap_msg_code_e return_code = COAP_MSG_CODE_RESPONSE_CHANGED;
|
|
(void) source_port;
|
|
(void) source_address;
|
|
|
|
tr_debug("neighbor data request");
|
|
|
|
if (!this) {
|
|
return -1;
|
|
}
|
|
|
|
request_tlv_ptr = options_data;
|
|
rdnss_option_len = dnssl_option_len = 0;
|
|
|
|
options_len = thread_tmfcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_ND_OPTION, &request_tlv_ptr);
|
|
|
|
while (options_len > 0) {
|
|
switch (*request_tlv_ptr) {
|
|
case RFC6106_RECURSIVE_DNS_SERVER_OPTION:
|
|
dns_server_tlv_ptr = thread_management_server_border_router_nd_rdnss_option_read(this, &rdnss_option_len);
|
|
break;
|
|
case RFC6106_DNS_SEARCH_LIST_OPTION:
|
|
dns_search_list_tlv_ptr = thread_management_server_border_router_nd_dnssl_option_read(this, &dnssl_option_len);
|
|
break;
|
|
}
|
|
request_tlv_ptr++;
|
|
options_len--;
|
|
}
|
|
|
|
payload_len = rdnss_option_len + dnssl_option_len;
|
|
|
|
resp_payload_ptr = ptr = ns_dyn_mem_alloc(payload_len + 4); //reserve space also for type/length
|
|
if (!resp_payload_ptr) {
|
|
return_code = COAP_MSG_CODE_RESPONSE_INTERNAL_SERVER_ERROR;
|
|
goto send_response;
|
|
}
|
|
|
|
*ptr++ = TMFCOP_TLV_ND_DATA;
|
|
if (payload_len > 255) {
|
|
*ptr++ = 0xff;
|
|
ptr = common_write_16_bit(payload_len, ptr);
|
|
} else {
|
|
*ptr++ = payload_len;
|
|
}
|
|
|
|
if (dns_server_tlv_ptr) {
|
|
memcpy(ptr, dns_server_tlv_ptr, rdnss_option_len);
|
|
ptr += rdnss_option_len;
|
|
}
|
|
|
|
if (dns_search_list_tlv_ptr) {
|
|
memcpy(ptr, dns_search_list_tlv_ptr, dnssl_option_len);
|
|
ptr += dnssl_option_len;
|
|
}
|
|
|
|
send_response:
|
|
coap_service_response_send(this->coap_service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, return_code, COAP_CT_OCTET_STREAM, resp_payload_ptr, ptr - resp_payload_ptr);
|
|
ns_dyn_mem_free(resp_payload_ptr);
|
|
return 0;
|
|
}
|
|
/*
|
|
* Check that local prefix is available in received network data
|
|
*/
|
|
static bool thread_border_router_network_data_prefix_match(thread_network_data_cache_entry_t *network_data_ptr, thread_network_local_data_entry_t *local_prefix, uint16_t router_id)
|
|
{
|
|
ns_list_foreach(thread_network_data_prefix_cache_entry_t, nwk_prefix, &network_data_ptr->localPrefixList) {
|
|
if (nwk_prefix->servicesPrefixLen != local_prefix->servicesPrefixLen) {
|
|
continue;
|
|
}
|
|
|
|
if (!bitsequal(nwk_prefix->servicesPrefix, local_prefix->servicesPrefix, local_prefix->servicesPrefixLen)) {
|
|
continue;
|
|
}
|
|
|
|
// check that prefix is hosted by this router
|
|
if (!thread_nd_hosted_by_this_routerid(router_id, &nwk_prefix->routeList) &&
|
|
!thread_nd_hosted_by_this_routerid(router_id, &nwk_prefix->borderRouterList)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Check that local service is available in network data
|
|
*/
|
|
static bool thread_border_router_network_data_service_match(thread_network_data_cache_entry_t *network_data_ptr, thread_network_data_service_entry_t *local_service, uint16_t router_id)
|
|
{
|
|
thread_network_data_service_cache_entry_t *service_cache_entry = thread_network_data_service_entry_find(&network_data_ptr->service_list, local_service);
|
|
|
|
if (service_cache_entry) {
|
|
return thread_network_data_service_hosted_by_this_router_id(service_cache_entry, router_id);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool thread_border_router_local_network_data_prefix_match(thread_network_local_data_cache_entry_t *local_data, thread_network_data_prefix_cache_entry_t *prefix, uint16_t router_id)
|
|
{
|
|
bool instance_found = false;
|
|
|
|
if (thread_nd_hosted_by_this_routerid(router_id, &prefix->routeList)) {
|
|
instance_found = true;
|
|
}
|
|
|
|
if (thread_nd_hosted_by_this_routerid(router_id, &prefix->borderRouterList)) {
|
|
instance_found = true;
|
|
}
|
|
|
|
if (!instance_found) {
|
|
/* Router ID not in propagated network data; skip */
|
|
return true;
|
|
}
|
|
|
|
instance_found = false;
|
|
|
|
ns_list_foreach(thread_network_local_data_entry_t, localPrefix, &local_data->prefix_list) {
|
|
if (prefix->servicesPrefixLen != localPrefix->servicesPrefixLen) {
|
|
continue;
|
|
}
|
|
|
|
if (bitsequal(prefix->servicesPrefix, localPrefix->servicesPrefix, localPrefix->servicesPrefixLen)) {
|
|
/* Prefix exists in local data; return true */
|
|
instance_found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!instance_found) {
|
|
tr_debug("Found missing prefix: %s", trace_array(prefix->servicesPrefix, 8));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void thread_border_router_child_network_data_clean(protocol_interface_info_entry_t *cur, uint16_t child_id)
|
|
{
|
|
uint8_t addr16_buf[2];
|
|
|
|
common_write_16_bit(child_id, addr16_buf);
|
|
if (mac_neighbor_table_address_discover(mac_neighbor_info(cur), addr16_buf, ADDR_802_15_4_SHORT)) {
|
|
/* Child is available in mle, do nothing */
|
|
return;
|
|
}
|
|
|
|
// Child is not our child => network data contains data from lost children, remove it
|
|
tr_debug("Remove nwk data from lost child: %04x", child_id);
|
|
thread_management_client_network_data_unregister(cur->id, child_id);
|
|
}
|
|
|
|
static void thread_border_router_lost_children_nwk_data_validate(protocol_interface_info_entry_t *cur, uint16_t router_short_addr)
|
|
{
|
|
if (!thread_is_router_addr(router_short_addr)) {
|
|
// not validating children nwk data
|
|
return;
|
|
}
|
|
|
|
thread_network_data_cache_entry_t *network_data = &cur->thread_info->networkDataStorage;
|
|
|
|
ns_list_foreach(thread_network_data_prefix_cache_entry_t, curLP, &network_data->localPrefixList) {
|
|
/* Go throgh all routes */
|
|
ns_list_foreach(thread_network_server_data_entry_t, curRoute, &curLP->routeList) {
|
|
if (thread_addr_is_child(router_short_addr, curRoute->routerID)) {
|
|
// Router children found
|
|
thread_border_router_child_network_data_clean(cur, curRoute->routerID);
|
|
}
|
|
}
|
|
|
|
/* Go through all BR's */
|
|
ns_list_foreach(thread_network_server_data_entry_t, curBR, &curLP->borderRouterList) {
|
|
if (thread_addr_is_child(router_short_addr, curBR->routerID)) {
|
|
// Router children found
|
|
thread_border_router_child_network_data_clean(cur, curBR->routerID);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Go throgh all services */
|
|
ns_list_foreach(thread_network_data_service_cache_entry_t, service, &network_data->service_list) {
|
|
ns_list_foreach(thread_network_data_service_server_entry_t, server, &service->server_list) {
|
|
if (thread_addr_is_child(router_short_addr, server->router_id)) {
|
|
// Router children found
|
|
thread_border_router_child_network_data_clean(cur, server->router_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool thread_border_router_local_network_data_service_match(thread_network_local_data_cache_entry_t *local_data, thread_network_data_service_cache_entry_t *service, uint16_t router_id)
|
|
{
|
|
bool instance_found = false;
|
|
|
|
ns_list_foreach(thread_network_data_service_server_entry_t, server, &service->server_list) {
|
|
if (server->router_id == router_id) {
|
|
instance_found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!instance_found) {
|
|
/* Router ID not in propagated network data; skip */
|
|
return true;
|
|
}
|
|
|
|
ns_list_foreach(thread_network_data_service_entry_t, local_service, &local_data->service_list) {
|
|
if (local_service->S_service_data_length != service->S_service_data_length) {
|
|
continue;
|
|
}
|
|
|
|
if (memcmp(local_service->S_service_data, service->S_service_data, local_service->S_service_data_length) != 0) {
|
|
continue;
|
|
}
|
|
|
|
if (local_service->S_enterprise_number != service->S_enterprise_number) {
|
|
continue;
|
|
}
|
|
|
|
/* Service exists in local data; return true */
|
|
return true;
|
|
}
|
|
|
|
tr_debug("Found missing service: %s", trace_array(service->S_service_data, service->S_service_data_length));
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Check that local server data is available in network data
|
|
*/
|
|
static bool thread_border_router_local_srv_data_in_network_data_check(protocol_interface_info_entry_t *cur)
|
|
{
|
|
thread_network_data_cache_entry_t *network_data;
|
|
thread_network_local_data_cache_entry_t *local_data;
|
|
uint16_t router_id = cur->mac_parameters->mac_short_address;
|
|
|
|
network_data = &cur->thread_info->networkDataStorage;
|
|
local_data = &cur->thread_info->localServerDataBase;
|
|
|
|
ns_list_foreach(thread_network_local_data_entry_t, localPrefix, &local_data->prefix_list) {
|
|
// find matching prefix from network data
|
|
if (thread_border_router_network_data_prefix_match(network_data, localPrefix, router_id) == false) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ns_list_foreach(thread_network_data_service_entry_t, localService, &local_data->service_list) {
|
|
// find matching service from network data
|
|
if (thread_border_router_network_data_service_match(network_data, localService, router_id) == false) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ns_list_foreach(thread_network_data_prefix_cache_entry_t, prefix, &network_data->localPrefixList) {
|
|
if (thread_border_router_local_network_data_prefix_match(local_data, prefix, router_id) == false) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ns_list_foreach(thread_network_data_service_cache_entry_t, service, &network_data->service_list) {
|
|
if (thread_border_router_local_network_data_service_match(local_data, service, router_id) == false) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
thread_border_router_lost_children_nwk_data_validate(cur, router_id);
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef HAVE_THREAD_BORDER_ROUTER
|
|
static int thread_border_router_recursive_dns_server_option_store(int8_t interface_id, uint8_t *recursive_dns_server_option, uint16_t recursive_dns_server_option_len)
|
|
{
|
|
thread_border_router_t *this = thread_border_router_find_by_interface(interface_id);
|
|
if (!this) {
|
|
return -1;
|
|
}
|
|
|
|
ns_dyn_mem_free(this->recursive_dns_server_option);
|
|
|
|
if (recursive_dns_server_option) {
|
|
this->recursive_dns_server_option = ns_dyn_mem_alloc(sizeof(dns_option_t) + recursive_dns_server_option_len);
|
|
if (!this->recursive_dns_server_option) {
|
|
return -2;
|
|
}
|
|
memcpy(&this->recursive_dns_server_option->option_data, recursive_dns_server_option, recursive_dns_server_option_len);
|
|
this->recursive_dns_server_option->option_length = recursive_dns_server_option_len;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_THREAD_BORDER_ROUTER
|
|
static int thread_border_router_dns_search_list_option_store(int8_t interface_id, uint8_t *dns_search_list_option, uint16_t search_list_option_len)
|
|
{
|
|
thread_border_router_t *this = thread_border_router_find_by_interface(interface_id);
|
|
if (!this) {
|
|
return -1;
|
|
}
|
|
|
|
ns_dyn_mem_free(this->dns_search_list_option);
|
|
if (dns_search_list_option) {
|
|
this->dns_search_list_option = ns_dyn_mem_alloc(sizeof(dns_option_t) + search_list_option_len);
|
|
if (!this->dns_search_list_option) {
|
|
return -2;
|
|
}
|
|
memcpy(&this->dns_search_list_option->option_data, dns_search_list_option, search_list_option_len);
|
|
this->dns_search_list_option->option_length = search_list_option_len;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int8_t thread_border_router_init(int8_t interface_id)
|
|
{
|
|
thread_border_router_t *this = thread_border_router_find_by_interface(interface_id);
|
|
if (this) {
|
|
return 0;
|
|
}
|
|
|
|
tr_debug("thread_border_router_init if=%d", interface_id);
|
|
|
|
this = ns_dyn_mem_alloc(sizeof(thread_border_router_t));
|
|
if (!this) {
|
|
return -2;
|
|
}
|
|
this->dns_search_list_option = NULL;
|
|
this->recursive_dns_server_option = NULL;
|
|
this->interface_id = interface_id;
|
|
this->nwk_data_resubmit_timer = 0;
|
|
this->coap_service_id = coap_service_initialize(this->interface_id, THREAD_MANAGEMENT_PORT, COAP_SERVICE_OPTIONS_NONE, NULL, NULL);
|
|
if (this->coap_service_id < 0) {
|
|
tr_warn("Thread border router coap init failed");
|
|
ns_dyn_mem_free(this);
|
|
return -3;
|
|
}
|
|
// Register to Mesh CoAP URIs
|
|
coap_service_register_uri(this->coap_service_id, THREAD_URI_NEIGHBOR_DISCOVERY_DATA_REQ, COAP_SERVICE_ACCESS_GET_ALLOWED, thread_border_router_neighbor_discovery_data_req_cb);
|
|
|
|
ns_list_add_to_start(&border_router_instance_list, this);
|
|
return 0;
|
|
}
|
|
|
|
void thread_border_router_delete(int8_t interface_id)
|
|
{
|
|
thread_border_router_t *this = thread_border_router_find_by_interface(interface_id);
|
|
if (!this) {
|
|
return;
|
|
}
|
|
|
|
coap_service_delete(this->coap_service_id);
|
|
|
|
ns_list_remove(&border_router_instance_list, this);
|
|
ns_dyn_mem_free(this->dns_search_list_option);
|
|
ns_dyn_mem_free(this->recursive_dns_server_option);
|
|
ns_dyn_mem_free(this);
|
|
}
|
|
|
|
void thread_border_router_seconds_timer(int8_t interface_id, uint32_t seconds)
|
|
{
|
|
thread_border_router_t *this = thread_border_router_find_by_interface(interface_id);
|
|
if (!this) {
|
|
return;
|
|
}
|
|
|
|
if (this->nwk_data_resubmit_timer) {
|
|
if (this->nwk_data_resubmit_timer > seconds) {
|
|
this->nwk_data_resubmit_timer -= seconds;
|
|
} else {
|
|
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
|
|
this->nwk_data_resubmit_timer = 0;
|
|
if (cur) {
|
|
if (!thread_border_router_local_srv_data_in_network_data_check(cur)) {
|
|
tr_info("nwk data mismatch - resubmit");
|
|
thread_border_router_publish(cur->id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void thread_border_router_resubmit_timer_set(int8_t interface_id, int16_t seconds)
|
|
{
|
|
thread_border_router_t *this = thread_border_router_find_by_interface(interface_id);
|
|
|
|
if (!this) {
|
|
return;
|
|
}
|
|
|
|
if (seconds >= 0) {
|
|
this->nwk_data_resubmit_timer = seconds;
|
|
} else {
|
|
// re-init network data resubmit timer to default value
|
|
this->nwk_data_resubmit_timer = THREAD_DATA_RESUBMIT_DELAY + randLIB_get_random_in_range(0, THREAD_DATA_RESUBMIT_DELAY / 10);
|
|
}
|
|
}
|
|
|
|
void thread_border_router_network_data_appl_callback(protocol_interface_info_entry_t *cur)
|
|
{
|
|
if (cur->thread_info->network_data_tlv_cb) {
|
|
uint16_t payload_len = thread_network_data_tlv_size(cur, true);
|
|
uint8_t *payload_ptr = ns_dyn_mem_alloc(payload_len + 3); /* 3 => room is also needed for TLV ID and length */
|
|
if (payload_ptr) {
|
|
thread_network_data_tlv_write(cur, payload_ptr, true);
|
|
// Send Network data TLV to application without TLV ID and length
|
|
cur->thread_info->network_data_tlv_cb(cur->thread_info->interface_id, payload_ptr + 2, payload_len);
|
|
ns_dyn_mem_free(payload_ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void thread_border_router_network_data_update_notify(protocol_interface_info_entry_t *cur)
|
|
{
|
|
thread_border_router_t *this = thread_border_router_find_by_interface(cur->thread_info->interface_id);
|
|
|
|
if (!this) {
|
|
return;
|
|
}
|
|
if (!thread_border_router_local_srv_data_in_network_data_check(cur)) {
|
|
if (this->nwk_data_resubmit_timer == 0) {
|
|
this->nwk_data_resubmit_timer = 5;
|
|
tr_debug("Resubmitted timer trig");
|
|
}
|
|
|
|
} else {
|
|
tr_debug("All data registered");
|
|
this->nwk_data_resubmit_timer = 0;
|
|
}
|
|
|
|
thread_border_router_network_data_appl_callback(cur);
|
|
}
|
|
|
|
void thread_border_router_old_partition_data_clean(int8_t interface_id)
|
|
{
|
|
thread_border_router_t *this = thread_border_router_find_by_interface(interface_id);
|
|
if (this) {
|
|
coap_service_request_delete_by_service_id(this->coap_service_id);
|
|
}
|
|
thread_extension_bbr_old_partition_data_clean(interface_id);
|
|
}
|
|
#endif // HAVE_THREAD_ROUTER
|
|
|
|
/*External APIs*/
|
|
|
|
int thread_border_router_prefix_add(int8_t interface_id, uint8_t *prefix_ptr, uint8_t prefix_len, thread_border_router_info_t *prefix_info_ptr)
|
|
{
|
|
#ifdef HAVE_THREAD_ROUTER
|
|
protocol_interface_info_entry_t *cur;
|
|
thread_prefix_tlv_t prefixTlv;
|
|
thread_border_router_tlv_entry_t service;
|
|
cur = protocol_stack_interface_info_get_by_id(interface_id);
|
|
if (!cur || !cur->thread_info || thread_attach_ready(cur) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (!prefix_info_ptr || !prefix_ptr) {
|
|
return -2;
|
|
}
|
|
if (prefix_info_ptr->P_dhcp == true && prefix_info_ptr->P_slaac == true) {
|
|
return -3;// Can not configure both services on
|
|
}
|
|
|
|
prefixTlv.domainId = 0;
|
|
prefixTlv.Prefix = prefix_ptr;
|
|
prefixTlv.PrefixLen = prefix_len;
|
|
|
|
service.P_configure = prefix_info_ptr->P_configure;
|
|
service.P_default_route = prefix_info_ptr->P_default_route;
|
|
service.P_dhcp = prefix_info_ptr->P_dhcp;
|
|
service.P_preferred = prefix_info_ptr->P_preferred;
|
|
service.P_slaac = prefix_info_ptr->P_slaac;
|
|
service.Prf = prefix_info_ptr->Prf;
|
|
service.stableData = prefix_info_ptr->stableData;
|
|
service.P_on_mesh = prefix_info_ptr->P_on_mesh;
|
|
service.P_nd_dns = prefix_info_ptr->P_nd_dns;
|
|
service.P_res1 = prefix_info_ptr->P_res1;
|
|
|
|
return thread_local_server_list_add_on_mesh_server(&cur->thread_info->localServerDataBase, &prefixTlv, &service);
|
|
#else
|
|
(void) interface_id;
|
|
(void) prefix_ptr;
|
|
(void) prefix_len;
|
|
(void) prefix_info_ptr;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int thread_border_router_prefix_delete(int8_t interface_id, uint8_t *prefix_ptr, uint8_t prefix_len)
|
|
{
|
|
#ifdef HAVE_THREAD_ROUTER
|
|
protocol_interface_info_entry_t *cur;
|
|
thread_prefix_tlv_t prefixTlv;
|
|
cur = protocol_stack_interface_info_get_by_id(interface_id);
|
|
if (!cur || !cur->thread_info || thread_attach_ready(cur) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (!prefix_ptr) {
|
|
return -2;
|
|
}
|
|
prefixTlv.domainId = 0;
|
|
prefixTlv.Prefix = prefix_ptr;
|
|
prefixTlv.PrefixLen = prefix_len;
|
|
|
|
return thread_local_server_list_del_on_mesh_server(&cur->thread_info->localServerDataBase, &prefixTlv);
|
|
#else
|
|
(void) interface_id;
|
|
(void) prefix_ptr;
|
|
(void) prefix_len;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int thread_border_router_route_add(int8_t interface_id, uint8_t *prefix_ptr, uint8_t prefix_len, bool stable, int8_t prf)
|
|
{
|
|
#ifdef HAVE_THREAD_ROUTER
|
|
thread_prefix_tlv_t prefixTlv;
|
|
thread_border_router_tlv_entry_t route;
|
|
protocol_interface_info_entry_t *cur;
|
|
|
|
cur = protocol_stack_interface_info_get_by_id(interface_id);
|
|
if (!cur) {
|
|
return -1;
|
|
}
|
|
|
|
if (!cur->thread_info || thread_attach_ready(cur) != 0) {
|
|
return -2;
|
|
}
|
|
prefixTlv.domainId = 0;
|
|
prefixTlv.Prefix = prefix_ptr;
|
|
prefixTlv.PrefixLen = prefix_len;
|
|
|
|
memset(&route, 0, sizeof(thread_border_router_tlv_entry_t));
|
|
route.Prf = prf;
|
|
route.stableData = stable;
|
|
|
|
return thread_local_server_add_route(&cur->thread_info->localServerDataBase, &prefixTlv, &route);
|
|
#else
|
|
(void) interface_id;
|
|
(void) prefix_ptr;
|
|
(void) prefix_len;
|
|
(void) stable;
|
|
(void) prf;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int thread_border_router_route_delete(int8_t interface_id, uint8_t *prefix_ptr, uint8_t prefix_len)
|
|
{
|
|
#ifdef HAVE_THREAD_ROUTER
|
|
thread_prefix_tlv_t prefixTlv;
|
|
protocol_interface_info_entry_t *cur;
|
|
|
|
cur = protocol_stack_interface_info_get_by_id(interface_id);
|
|
if (!cur) {
|
|
return -1;
|
|
}
|
|
|
|
if (!cur->thread_info || thread_attach_ready(cur) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
prefixTlv.domainId = 0;
|
|
prefixTlv.Prefix = prefix_ptr;
|
|
prefixTlv.PrefixLen = prefix_len;
|
|
|
|
return thread_local_server_del_route(&cur->thread_info->localServerDataBase, &prefixTlv);
|
|
#else
|
|
(void) interface_id;
|
|
(void) prefix_ptr;
|
|
(void) prefix_len;
|
|
return -1;
|
|
#endif
|
|
|
|
}
|
|
|
|
int thread_border_router_service_add(int8_t interface_id, uint8_t *service_data, uint8_t service_len, uint8_t sid, uint32_t enterprise_number, uint8_t *server_data, uint8_t server_data_len, bool stable)
|
|
{
|
|
#ifdef HAVE_THREAD_ROUTER
|
|
protocol_interface_info_entry_t *cur;
|
|
cur = protocol_stack_interface_info_get_by_id(interface_id);
|
|
|
|
if (!cur || !cur->thread_info || thread_attach_ready(cur) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (!service_data || !service_len) {
|
|
return -2;
|
|
}
|
|
|
|
thread_network_data_service_entry_t service = {
|
|
.T = false,
|
|
.S_id = sid,
|
|
.S_enterprise_number = enterprise_number,
|
|
.S_service_data = service_data,
|
|
.S_service_data_length = service_len,
|
|
.S_server_data = server_data,
|
|
.S_server_data_length = server_data_len,
|
|
.S_stable = stable,
|
|
};
|
|
|
|
if (enterprise_number == THREAD_ENTERPRISE_NUMBER) {
|
|
service.T = true;
|
|
}
|
|
|
|
return thread_local_service_list_add(&cur->thread_info->localServerDataBase, &service);
|
|
#else
|
|
(void)interface_id;
|
|
(void)service_data;
|
|
(void)service_len;
|
|
(void)sid;
|
|
(void)enterprise_number;
|
|
(void)server_data;
|
|
(void)server_data_len;
|
|
(void)stable;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int thread_border_router_service_delete(int8_t interface_id, uint8_t *service_data, uint8_t service_len, uint32_t enterprise_number)
|
|
{
|
|
#ifdef HAVE_THREAD_ROUTER
|
|
protocol_interface_info_entry_t *cur;
|
|
cur = protocol_stack_interface_info_get_by_id(interface_id);
|
|
|
|
if (!cur || !cur->thread_info || thread_attach_ready(cur) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (!service_data || !service_len) {
|
|
return -2;
|
|
}
|
|
|
|
thread_network_data_service_entry_t service = {
|
|
.S_enterprise_number = enterprise_number,
|
|
.S_service_data = service_data,
|
|
.S_service_data_length = service_len,
|
|
};
|
|
|
|
return thread_local_service_list_del(&cur->thread_info->localServerDataBase, &service);
|
|
#else
|
|
(void) interface_id;
|
|
(void) service_data;
|
|
(void) service_len;
|
|
(void) enterprise_number;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int thread_border_router_recursive_dns_server_option_set(int8_t interface_id, uint8_t *recursive_dns_server_option, uint16_t recursive_dns_server_option_len)
|
|
{
|
|
#ifdef HAVE_THREAD_BORDER_ROUTER
|
|
return thread_border_router_recursive_dns_server_option_store(interface_id, recursive_dns_server_option, recursive_dns_server_option_len);
|
|
#else
|
|
(void)interface_id;
|
|
(void)recursive_dns_server_option;
|
|
(void)recursive_dns_server_option_len;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int thread_border_router_dns_search_list_option_set(int8_t interface_id, uint8_t *dns_search_list_option, uint16_t search_list_option_len)
|
|
{
|
|
#ifdef HAVE_THREAD_BORDER_ROUTER
|
|
return thread_border_router_dns_search_list_option_store(interface_id, dns_search_list_option, search_list_option_len);
|
|
#else
|
|
(void)interface_id;
|
|
(void)dns_search_list_option;
|
|
(void)search_list_option_len;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
/** Network data set response callback.
|
|
*
|
|
* callback to inform if network data was set to leader.
|
|
*
|
|
* /param status status of operation 0 success, -1 failure from leader received
|
|
* /param data_ptr pointer to network data TLV that leader accepted.
|
|
* /param data_len length of network data.
|
|
*
|
|
*/
|
|
#ifdef HAVE_THREAD_ROUTER
|
|
static void thread_tmf_client_network_data_set_cb(int8_t interface_id, int8_t status, uint8_t *data_ptr, uint16_t data_len)
|
|
{
|
|
protocol_interface_info_entry_t *cur;
|
|
(void) data_len;
|
|
(void) data_ptr;
|
|
|
|
cur = protocol_stack_interface_info_get_by_id(interface_id);
|
|
if (!cur) {
|
|
return;
|
|
}
|
|
|
|
cur->thread_info->localServerDataBase.publish_coap_req_id = 0;
|
|
|
|
tr_debug("BR a/sd response status: %s, addr: %x", status ? "Fail" : "OK", cur->thread_info->localServerDataBase.registered_rloc16);
|
|
|
|
if (cur->thread_info->localServerDataBase.publish_pending) {
|
|
cur->thread_info->localServerDataBase.publish_pending = false;
|
|
thread_border_router_publish(cur->id);
|
|
}
|
|
|
|
if (status == 0) {
|
|
// If request was successful, then update RLOC to new one.
|
|
cur->thread_info->localServerDataBase.registered_rloc16 = mac_helper_mac16_address_get(cur);
|
|
cur->thread_info->localServerDataBase.release_old_address = false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
int thread_border_router_publish(int8_t interface_id)
|
|
{
|
|
#ifdef HAVE_THREAD_ROUTER
|
|
uint16_t network_data_len;
|
|
uint8_t *payload_ptr;
|
|
uint8_t *ptr;
|
|
uint16_t rloc16;
|
|
int ret_val;
|
|
protocol_interface_info_entry_t *cur;
|
|
|
|
tr_debug("Border router Publish Local Services");
|
|
cur = protocol_stack_interface_info_get_by_id(interface_id);
|
|
if (!cur) {
|
|
return -1;
|
|
}
|
|
|
|
if (!cur->thread_info || thread_attach_ready(cur) != 0) {
|
|
return -2;
|
|
}
|
|
|
|
rloc16 = mac_helper_mac16_address_get(cur);
|
|
tr_debug("Border router old: %x, new: %x", cur->thread_info->localServerDataBase.registered_rloc16, rloc16);
|
|
|
|
if (cur->thread_info->localServerDataBase.publish_coap_req_id) {
|
|
if (rloc16 != cur->thread_info->localServerDataBase.registered_rloc16) {
|
|
/*
|
|
* Device short address has changed, cancel previous a/sd requests
|
|
* and start resubmit timer
|
|
* */
|
|
tr_debug("RLOC changed, kill pending a/sd request");
|
|
thread_management_client_coap_message_delete(cur->id, cur->thread_info->localServerDataBase.publish_coap_req_id);
|
|
thread_border_router_resubmit_timer_set(interface_id, 5);
|
|
} else {
|
|
cur->thread_info->localServerDataBase.publish_pending = true;
|
|
tr_debug("Activate pending status for publish");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//Allocate Memory for Data
|
|
network_data_len = thread_nd_own_service_list_data_size(&cur->thread_info->localServerDataBase);
|
|
|
|
// Room for RLOC16 Room for Network data TLV
|
|
ptr = payload_ptr = ns_dyn_mem_temporary_alloc(network_data_len + 4 + 5);
|
|
if (!ptr) {
|
|
return -3;
|
|
}
|
|
|
|
ptr = thread_tmfcop_tlv_data_write_header(ptr, TMFCOP_TLV_NETWORK_DATA, network_data_len);
|
|
ptr = thread_nd_own_service_list_data_write(&cur->thread_info->localServerDataBase, ptr, rloc16);
|
|
|
|
if (cur->thread_info->localServerDataBase.registered_rloc16 != 0xffff &&
|
|
cur->thread_info->localServerDataBase.release_old_address &&
|
|
cur->thread_info->localServerDataBase.registered_rloc16 != rloc16) {
|
|
// Our address has changed so we must register our network with new address and remove the old address
|
|
tr_debug("BR address changed - remove old %x", cur->thread_info->localServerDataBase.registered_rloc16);
|
|
ptr = thread_tmfcop_tlv_data_write_uint16(ptr, TMFCOP_TLV_RLOC16, cur->thread_info->localServerDataBase.registered_rloc16);
|
|
}
|
|
|
|
cur->thread_info->localServerDataBase.registered_rloc16 = rloc16;
|
|
ret_val = thread_management_client_network_data_register(cur->id, payload_ptr, ptr - payload_ptr, thread_tmf_client_network_data_set_cb);
|
|
if (payload_ptr) {
|
|
ns_dyn_mem_free(payload_ptr);
|
|
}
|
|
if (ret_val > 0) {
|
|
// a/sd request successful, save coap request id
|
|
cur->thread_info->localServerDataBase.publish_coap_req_id = (uint16_t)ret_val;
|
|
ret_val = 0 ;
|
|
}
|
|
|
|
thread_border_router_resubmit_timer_set(interface_id, -1);
|
|
|
|
return ret_val;
|
|
#else
|
|
(void) interface_id;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int thread_border_router_delete_all(int8_t interface_id)
|
|
{
|
|
#ifdef HAVE_THREAD_ROUTER
|
|
protocol_interface_info_entry_t *cur;
|
|
tr_debug("Border router Delete Local Service");
|
|
cur = protocol_stack_interface_info_get_by_id(interface_id);
|
|
if (!cur) {
|
|
return -1;
|
|
}
|
|
|
|
if (!cur->thread_info || thread_attach_ready(cur) != 0) {
|
|
return -2;
|
|
}
|
|
|
|
//Delete first Local List
|
|
thread_network_local_data_free_and_clean(&cur->thread_info->localServerDataBase, interface_id);
|
|
|
|
return 0;
|
|
#else
|
|
(void) interface_id;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int thread_border_router_network_data_callback_register(int8_t interface_id, thread_network_data_tlv_cb *nwk_data_cb)
|
|
{
|
|
#ifdef HAVE_THREAD
|
|
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
|
|
if (!cur) {
|
|
return -1;
|
|
}
|
|
|
|
if (!cur->thread_info || thread_attach_ready(cur) != 0) {
|
|
return -2;
|
|
}
|
|
|
|
cur->thread_info->network_data_tlv_cb = nwk_data_cb;
|
|
|
|
thread_border_router_network_data_appl_callback(cur);
|
|
|
|
return 0;
|
|
#else
|
|
(void)interface_id;
|
|
(void)nwk_data_cb;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int thread_border_router_prefix_tlv_find(uint8_t *network_data_tlv, uint16_t network_data_tlv_length, uint8_t **prefix_tlv, bool *stable)
|
|
{
|
|
#ifdef HAVE_THREAD
|
|
uint16_t tlv_length;
|
|
if (!network_data_tlv || !network_data_tlv_length || !prefix_tlv) {
|
|
return -1;
|
|
}
|
|
//tr_debug("thread_tlv_lib_prefix_find() len=%d, tlv=%s", network_data_tlv_length, trace_array(network_data_tlv, network_data_tlv_length));
|
|
*stable = true;
|
|
tlv_length = thread_meshcop_tlv_find_next(network_data_tlv, network_data_tlv_length, THREAD_NWK_DATA_TYPE_PREFIX | THREAD_NWK_STABLE_DATA, prefix_tlv);
|
|
if (tlv_length == 0) {
|
|
tlv_length = thread_meshcop_tlv_find_next(network_data_tlv, network_data_tlv_length, THREAD_NWK_DATA_TYPE_PREFIX, prefix_tlv);
|
|
*stable = false;
|
|
}
|
|
return tlv_length;
|
|
#else
|
|
(void)network_data_tlv;
|
|
(void)network_data_tlv_length;
|
|
(void)prefix_tlv;
|
|
(void)stable;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int thread_border_router_tlv_find(uint8_t *prefix_tlv, uint16_t prefix_tlv_length, uint8_t **border_router_tlv, bool *stable)
|
|
{
|
|
#ifdef HAVE_THREAD
|
|
uint16_t tlv_length;
|
|
if (!prefix_tlv || !prefix_tlv_length || !border_router_tlv) {
|
|
return -1;
|
|
}
|
|
|
|
//tr_debug("thread_tlv_lib_border_router_find() len=%d, tlv=%s", prefix_tlv_length, trace_array(prefix_tlv, prefix_tlv_length));
|
|
uint8_t prefix_length = prefix_tlv[1];
|
|
uint8_t prefix_byte_len = prefixBits_to_bytes(prefix_length);
|
|
prefix_tlv = prefix_tlv + 2 + prefix_byte_len; //2 = domain ID + prefix length
|
|
prefix_tlv_length = prefix_tlv_length - prefix_byte_len - 2;
|
|
|
|
// find stable prefix first and if not found return unstable data
|
|
*stable = true;
|
|
tlv_length = thread_meshcop_tlv_find_next(prefix_tlv, prefix_tlv_length, THREAD_NWK_DATA_TYPE_BORDER_ROUTER | THREAD_NWK_STABLE_DATA, border_router_tlv);
|
|
if (tlv_length == 0) {
|
|
tlv_length = thread_meshcop_tlv_find_next(prefix_tlv, prefix_tlv_length, THREAD_NWK_DATA_TYPE_BORDER_ROUTER, border_router_tlv);
|
|
*stable = false;
|
|
}
|
|
return tlv_length;
|
|
#else
|
|
(void)prefix_tlv;
|
|
(void)prefix_tlv_length;
|
|
(void)border_router_tlv;
|
|
(void)stable;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int thread_border_router_prefix_context_id(uint8_t *prefix_tlv, uint16_t prefix_tlv_length)
|
|
{
|
|
#ifdef HAVE_THREAD
|
|
if (!prefix_tlv || !prefix_tlv_length) {
|
|
return -1;
|
|
}
|
|
|
|
uint16_t data_length = prefix_tlv_length;
|
|
|
|
while (data_length) {
|
|
uint8_t type = *prefix_tlv++;
|
|
uint16_t len = *prefix_tlv++;
|
|
data_length -= 2;
|
|
|
|
type &= THREAD_NWK_DATA_TYPE_MASK;
|
|
|
|
if (type == THREAD_NWK_DATA_TYPE_6LOWPAN_ID) {
|
|
return (*prefix_tlv & 0x0f);
|
|
}
|
|
|
|
data_length -= len;
|
|
prefix_tlv += len;
|
|
}
|
|
|
|
return -2;
|
|
#else
|
|
(void)prefix_tlv;
|
|
(void)prefix_tlv_length;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int thread_border_router_service_tlv_find(uint8_t *network_data_tlv, uint16_t network_data_tlv_length, uint8_t **service_tlv, bool *stable)
|
|
{
|
|
#ifdef HAVE_THREAD
|
|
uint16_t tlv_length;
|
|
if (!network_data_tlv || !network_data_tlv_length || !service_tlv) {
|
|
return -1;
|
|
}
|
|
|
|
*stable = true;
|
|
tlv_length = thread_meshcop_tlv_find_next(network_data_tlv, network_data_tlv_length, THREAD_NWK_DATA_TYPE_SERVICE_DATA | THREAD_NWK_STABLE_DATA, service_tlv);
|
|
if (tlv_length == 0) {
|
|
tlv_length = thread_meshcop_tlv_find_next(network_data_tlv, network_data_tlv_length, THREAD_NWK_DATA_TYPE_SERVICE_DATA, service_tlv);
|
|
*stable = false;
|
|
}
|
|
return tlv_length;
|
|
#else
|
|
(void)network_data_tlv;
|
|
(void)network_data_tlv_length;
|
|
(void)service_tlv;
|
|
(void)stable;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int thread_border_router_server_tlv_find(uint8_t *service_tlv, uint16_t service_tlv_length, uint8_t **server_tlv, bool *stable)
|
|
{
|
|
#ifdef HAVE_THREAD
|
|
uint16_t tlv_length;
|
|
if (!service_tlv || !service_tlv_length || !server_tlv) {
|
|
return -1;
|
|
}
|
|
|
|
uint8_t t_flag = service_tlv[0] >> 7;
|
|
service_tlv += 1;
|
|
|
|
if (!t_flag) {
|
|
service_tlv_length -= 4;
|
|
service_tlv += 4;
|
|
}
|
|
|
|
uint8_t service_data_len = *service_tlv;
|
|
service_tlv += 1 + service_data_len;
|
|
service_tlv_length = service_tlv_length - service_data_len - 2;
|
|
|
|
*stable = true;
|
|
tlv_length = thread_meshcop_tlv_find_next(service_tlv, service_tlv_length, THREAD_NWK_DATA_TYPE_SERVER_DATA | THREAD_NWK_STABLE_DATA, server_tlv);
|
|
if (tlv_length == 0) {
|
|
tlv_length = thread_meshcop_tlv_find_next(service_tlv, service_tlv_length, THREAD_NWK_DATA_TYPE_SERVER_DATA, server_tlv);
|
|
*stable = false;
|
|
}
|
|
return tlv_length;
|
|
#else
|
|
(void)service_tlv;
|
|
(void)service_tlv_length;
|
|
(void)server_tlv;
|
|
(void)stable;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int thread_border_router_mdns_responder_start(int8_t interface_id, int8_t interface_id_mdns, const char *service_name)
|
|
{
|
|
#ifdef HAVE_THREAD_BORDER_ROUTER
|
|
return thread_mdns_start(interface_id, interface_id_mdns, service_name);
|
|
#else
|
|
(void)interface_id;
|
|
(void)interface_id_mdns;
|
|
(void)service_name;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int thread_border_router_mdns_responder_stop(void)
|
|
{
|
|
#ifdef HAVE_THREAD_BORDER_ROUTER
|
|
return thread_mdns_stop();
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|