mirror of https://github.com/ARMmbed/mbed-os.git
1571 lines
61 KiB
C
1571 lines
61 KiB
C
/*
|
|
* Copyright (c) 2017-2019, 2021, Pelion 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 <ns_types.h>
|
|
#include <string.h>
|
|
#include <nsdynmemLIB.h>
|
|
#include "ns_list.h"
|
|
#include "ns_trace.h"
|
|
#include "randLIB.h"
|
|
#include "common_functions.h"
|
|
|
|
#include "thread_config.h"
|
|
#include "coap_service_api.h"
|
|
#include "thread_bbr_api.h"
|
|
#include "6LoWPAN/Thread/thread_common.h"
|
|
#include "6LoWPAN/Thread/thread_bootstrap.h"
|
|
#include "6LoWPAN/Thread/thread_network_data_lib.h"
|
|
#include "6LoWPAN/Thread/thread_management_client.h"
|
|
#include "6LoWPAN/Thread/thread_tmfcop_lib.h"
|
|
#include "6LoWPAN/Thread/thread_joiner_application.h"
|
|
#include "6LoWPAN/Thread/thread_management_internal.h"
|
|
#include "6LoWPAN/Thread/thread_discovery.h"
|
|
#include "6LoWPAN/Thread/thread_bbr_api_internal.h"
|
|
#include "6LoWPAN/Thread/thread_resolution_client.h"
|
|
#include "6LoWPAN/Thread/thread_bbr_commercial.h"
|
|
#include "6LoWPAN/Thread/thread_ccm.h"
|
|
#include "6LoWPAN/MAC/mac_helper.h"
|
|
#include "NWK_INTERFACE/Include/protocol.h"
|
|
#include "Common_Protocols/ipv6.h"
|
|
|
|
|
|
#if defined(HAVE_THREAD_V2) && defined(HAVE_THREAD_BORDER_ROUTER)
|
|
#define TRACE_GROUP "pBBR"
|
|
|
|
static uint8_t dua_response_status = 0xff;
|
|
static uint8_t dua_status_count = 1;
|
|
static uint8_t ba_response_status_count = 0;
|
|
|
|
/*
|
|
* Border router instance data.
|
|
*/
|
|
typedef struct {
|
|
uint8_t pbbr_multicast_address[16];
|
|
uint8_t tri_address[16];
|
|
uint8_t registrar_address[16];
|
|
uint8_t domain_prefix[8];
|
|
uint32_t mlr_timeout;
|
|
uint32_t delay_timer;
|
|
uint32_t dua_timeout;
|
|
uint32_t dua_timer_ticks;
|
|
uint16_t pbbr_port; /* primary BBR listening port */
|
|
uint16_t tri_port; /* Thread Registrar Interface listening port */
|
|
uint16_t joiner_router_rloc;
|
|
uint8_t sequence_number;
|
|
int8_t interface_id;
|
|
int8_t coap_service_id;
|
|
int8_t coap_nmkp_virtual_service_id;
|
|
int8_t backbone_interface_id;
|
|
int8_t br_bb_service_id;
|
|
bool pbbr_started: 1;
|
|
ns_list_link_t link;
|
|
} thread_pbbr_t;
|
|
|
|
typedef struct {
|
|
int8_t interface_id;
|
|
uint8_t source_address[16];
|
|
uint8_t ml_eid[8];
|
|
uint8_t target_eid[16];
|
|
uint16_t ttl;
|
|
uint8_t dua_dad_repeat;
|
|
ns_list_link_t link;
|
|
} duplicate_dua_tr_t;
|
|
|
|
|
|
static NS_LIST_DEFINE(pbbr_instance_list, thread_pbbr_t, link);
|
|
static NS_LIST_DEFINE(duplicate_dua_tr_list, duplicate_dua_tr_t, link);
|
|
|
|
#define THREAD_BBR_MLR_REGISTRATION_TIMEOUT 3600 //<* Default MLR timeout in seconds
|
|
#define THREAD_BBR_DUA_REGISTRATION_TIMEOUT 3600*24 //<* 24 hours and we refresh the sequence number
|
|
#define THREAD_BBR_DUA_REGISTRATION_DELAY 5000 // 5 seconds in ms
|
|
#define THREAD_BBR_BACKBONE_PORT 5683 //<* Backbone border router
|
|
#define THREAD_BBR_DUA_DAD_QUERY_TIMEOUT 1 // wait period for Duplicate Address Detection
|
|
#define THREAD_BBR_DUA_DAD_REPEATS 2 // multicast repeated as part of DUA
|
|
|
|
static void thread_border_router_multicast_store_add(thread_pbbr_t *this, uint8_t *destination_addr_ptr);
|
|
static void thread_border_router_multicast_store_del(thread_pbbr_t *this, uint8_t *destination_addr_ptr);
|
|
static void thread_border_router_multicast_store_activate(thread_pbbr_t *this);
|
|
|
|
static int stringlen(const char *s, int n)
|
|
{
|
|
char *end = memchr(s, 0, n);
|
|
return end ? end - s : n;
|
|
}
|
|
|
|
static thread_pbbr_t *thread_bbr_find_by_interface(int8_t interface_id)
|
|
{
|
|
thread_pbbr_t *this = NULL;
|
|
ns_list_foreach(thread_pbbr_t, cur_br, &pbbr_instance_list) {
|
|
if (cur_br->interface_id == interface_id) {
|
|
this = cur_br;
|
|
break;
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
static thread_pbbr_t *thread_border_router_find_by_service(int8_t service_id)
|
|
{
|
|
thread_pbbr_t *this = NULL;
|
|
ns_list_foreach(thread_pbbr_t, cur_br, &pbbr_instance_list) {
|
|
if (cur_br->coap_service_id == service_id ||
|
|
cur_br->br_bb_service_id == service_id || cur_br->coap_nmkp_virtual_service_id == service_id) {
|
|
this = cur_br;
|
|
break;
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
static duplicate_dua_tr_t *thread_border_router_dup_tr_create(int8_t interface_id, uint8_t *source_addr_ptr, uint8_t *target_eid_ptr, uint8_t *ml_eid_ptr)
|
|
{
|
|
duplicate_dua_tr_t *this = ns_dyn_mem_alloc(sizeof(duplicate_dua_tr_t));
|
|
|
|
if (!this) {
|
|
return NULL;
|
|
}
|
|
ns_list_add_to_start(&duplicate_dua_tr_list, this);
|
|
this->interface_id = interface_id;
|
|
memcpy(this->target_eid, target_eid_ptr, 16);
|
|
memcpy(this->source_address, source_addr_ptr, 16);
|
|
memcpy(this->ml_eid, ml_eid_ptr, 8);
|
|
this->dua_dad_repeat = THREAD_BBR_DUA_DAD_REPEATS;
|
|
this->ttl = THREAD_BBR_DUA_DAD_QUERY_TIMEOUT;
|
|
return this;
|
|
}
|
|
|
|
static void thread_border_router_dup_tr_delete(duplicate_dua_tr_t *this)
|
|
{
|
|
if (!this) {
|
|
return;
|
|
}
|
|
ns_list_remove(&duplicate_dua_tr_list, this);
|
|
ns_dyn_mem_free(this);
|
|
return;
|
|
}
|
|
|
|
static duplicate_dua_tr_t *thread_border_router_dup_tr_find(int8_t interface_id, uint8_t *target_eid_ptr)
|
|
{
|
|
duplicate_dua_tr_t *this = NULL;
|
|
ns_list_foreach(duplicate_dua_tr_t, cur_dup_tr, &duplicate_dua_tr_list) {
|
|
if (cur_dup_tr->interface_id == interface_id && memcmp(cur_dup_tr->target_eid, target_eid_ptr, 16) == 0) {
|
|
this = cur_dup_tr;
|
|
break;
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/*
|
|
* Target EID TLV
|
|
* ML-EID TLV
|
|
* Time Since Last Transaction TLV
|
|
* DUA Registration Sequence Number TLV
|
|
* Network Name TLV
|
|
*/
|
|
static int thread_border_router_bb_ans_send(thread_pbbr_t *this, uint8_t *destination_addr_ptr, uint8_t *target_eid_ptr, uint8_t *ml_eid_ptr, uint32_t last_transaction_time, uint8_t *network_name_ptr, uint16_t *rloc_ptr)
|
|
{
|
|
uint8_t *payload_ptr, *ptr;
|
|
sn_coap_msg_type_e coap_msg_type = COAP_MSG_TYPE_CONFIRMABLE;
|
|
|
|
tr_debug("Thread BBR BB_ANS.ntf send");
|
|
|
|
payload_ptr = ptr = ns_dyn_mem_alloc(64);
|
|
if (!payload_ptr) {
|
|
tr_error("BB_ANS.ntf alloc failed!");
|
|
return -1;
|
|
}
|
|
|
|
ptr = thread_meshcop_tlv_data_write(ptr, TMFCOP_TLV_TARGET_EID, 16, target_eid_ptr);
|
|
ptr = thread_meshcop_tlv_data_write(ptr, TMFCOP_TLV_ML_EID, 8, ml_eid_ptr);
|
|
ptr = thread_meshcop_tlv_data_write_uint32(ptr, TMFCOP_TLV_LAST_TRANSACTION_TIME, last_transaction_time);
|
|
ptr = thread_meshcop_tlv_data_write(ptr, TMFCOP_TLV_NETWORK_NAME, stringlen((char *)network_name_ptr, 16), network_name_ptr);
|
|
if (rloc_ptr) {
|
|
ptr = thread_meshcop_tlv_data_write_uint16(ptr, TMFCOP_TLV_RLOC16, *rloc_ptr);
|
|
}
|
|
|
|
if (addr_is_ipv6_multicast(destination_addr_ptr)) {
|
|
coap_msg_type = COAP_MSG_TYPE_NON_CONFIRMABLE;
|
|
}
|
|
|
|
coap_service_request_send(this->br_bb_service_id, COAP_REQUEST_OPTIONS_NONE, destination_addr_ptr, this->pbbr_port,
|
|
coap_msg_type, COAP_MSG_CODE_REQUEST_POST, THREAD_URI_BBR_BB_ANS_NTF, COAP_CT_OCTET_STREAM, payload_ptr, ptr - payload_ptr, NULL);
|
|
|
|
ns_dyn_mem_free(payload_ptr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void thread_border_router_bb_qry_send(thread_pbbr_t *this, const uint8_t *target_eid_ptr, uint16_t *rloc_ptr)
|
|
{
|
|
uint8_t *ptr;
|
|
uint8_t payload[22];
|
|
|
|
tr_info("Thread BBR BB_QRY.ntf send if:%d, service:%d", this->backbone_interface_id, this->br_bb_service_id);
|
|
|
|
ptr = payload;
|
|
if (target_eid_ptr) {
|
|
ptr = thread_meshcop_tlv_data_write(ptr, TMFCOP_TLV_TARGET_EID, 16, target_eid_ptr);
|
|
}
|
|
if (rloc_ptr) {
|
|
ptr = thread_meshcop_tlv_data_write_uint16(ptr, TMFCOP_TLV_RLOC16, *rloc_ptr);
|
|
}
|
|
|
|
/* UDP Encapsulation TLV */
|
|
coap_service_request_send(this->br_bb_service_id, COAP_REQUEST_OPTIONS_NONE, this->pbbr_multicast_address, this->pbbr_port,
|
|
COAP_MSG_TYPE_NON_CONFIRMABLE, COAP_MSG_CODE_REQUEST_POST, THREAD_URI_BBR_BB_QRY_NTF, COAP_CT_OCTET_STREAM, payload, ptr - payload, NULL);
|
|
|
|
return;
|
|
}
|
|
static int thread_pbbr_relay_from_tri_cb(int8_t service_id, uint8_t source_address[16], uint16_t source_port, sn_coap_hdr_s *request_ptr)
|
|
{
|
|
(void)source_address;
|
|
(void)source_port;
|
|
thread_pbbr_t *this = thread_border_router_find_by_service(service_id);
|
|
uint8_t destination_address[16];
|
|
uint16_t short_address;
|
|
|
|
tr_info("Thread BBR relay from TRI to JR");
|
|
|
|
if (!this) {
|
|
return -1;
|
|
}
|
|
|
|
// security check? we should compare source address to TRI address
|
|
|
|
if (thread_management_get_ml_prefix_112(this->interface_id, destination_address) != 0 ||
|
|
2 > thread_meshcop_tlv_data_get_uint16(request_ptr->payload_ptr, request_ptr->payload_len, MESHCOP_TLV_JOINER_ROUTER_LOCATOR, &short_address)) {
|
|
tr_warn("No joiner router address");
|
|
return -1;
|
|
}
|
|
common_write_16_bit(short_address, &destination_address[14]);
|
|
|
|
//buffer length is limited to 10 characters
|
|
|
|
coap_service_request_send(this->coap_service_id, COAP_REQUEST_OPTIONS_NONE, destination_address, THREAD_MANAGEMENT_PORT,
|
|
COAP_MSG_TYPE_NON_CONFIRMABLE, COAP_MSG_CODE_REQUEST_POST, THREAD_URI_BBR_TRI_TX_NTF, COAP_CT_OCTET_STREAM, request_ptr->payload_ptr, request_ptr->payload_len, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int thread_pbbr_relay_to_tri_cb(int8_t service_id, uint8_t source_address[16], uint16_t source_port, sn_coap_hdr_s *request_ptr)
|
|
{
|
|
(void)source_address;
|
|
(void)source_port;
|
|
thread_pbbr_t *this = thread_border_router_find_by_service(service_id);
|
|
|
|
tr_info("Thread BBR relay to TRI");
|
|
|
|
if (!this) {
|
|
return -1;
|
|
}
|
|
|
|
coap_service_request_send(this->br_bb_service_id, COAP_REQUEST_OPTIONS_NONE, this->tri_address, this->tri_port,
|
|
COAP_MSG_TYPE_NON_CONFIRMABLE, COAP_MSG_CODE_REQUEST_POST, THREAD_URI_TRI_RX_NTF, COAP_CT_OCTET_STREAM, request_ptr->payload_ptr, request_ptr->payload_len, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int thread_pbbr_data_req_recv_cb(int8_t service_id, uint8_t source_address[static 16], uint16_t source_port, sn_coap_hdr_s *request_ptr)
|
|
{
|
|
(void) source_address;
|
|
(void) source_port;
|
|
|
|
uint8_t payload_ptr[18] = {0};
|
|
uint8_t *address_ptr;
|
|
uint8_t *request_tlv_ptr;
|
|
uint16_t request_tlv_len;
|
|
uint8_t *ptr = payload_ptr;
|
|
thread_pbbr_t *this = thread_border_router_find_by_service(service_id);
|
|
|
|
if (!this || !request_ptr) {
|
|
return -1;
|
|
}
|
|
|
|
tr_debug("MGMT_BBR_GET.req received");
|
|
|
|
request_tlv_len = thread_tmfcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, MESHCOP_TLV_GET, &request_tlv_ptr);
|
|
|
|
if (0 == request_tlv_len) {
|
|
//error handling
|
|
return -1;
|
|
}
|
|
|
|
if (thread_meshcop_tlv_list_type_available(request_tlv_ptr, request_tlv_len, MESHCOP_TLV_REGISTRAR_IPV6_ADDRESS)) {
|
|
tr_debug("Registrar IPv6 address requested");
|
|
// If registrar address is not set, return TRI address
|
|
if (addr_is_ipv6_unspecified(this->registrar_address)) {
|
|
address_ptr = this->tri_address;
|
|
} else {
|
|
address_ptr = this->registrar_address;
|
|
}
|
|
|
|
ptr = thread_meshcop_tlv_data_write(ptr, MESHCOP_TLV_REGISTRAR_IPV6_ADDRESS, 16, address_ptr);
|
|
}
|
|
|
|
coap_service_response_send(this->coap_service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, COAP_MSG_CODE_RESPONSE_CHANGED, COAP_CT_OCTET_STREAM, payload_ptr, ptr - payload_ptr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int thread_pbbr_data_set_recv_cb(int8_t service_id, uint8_t source_address[static 16], uint16_t source_port, sn_coap_hdr_s *request_ptr)
|
|
{
|
|
(void) source_address;
|
|
(void) source_port;
|
|
|
|
uint8_t response[3] = {0};
|
|
uint8_t *ptr = response;
|
|
int8_t response_code = -1;
|
|
uint8_t *registrar_addr = NULL;
|
|
thread_pbbr_t *this = thread_border_router_find_by_service(service_id);
|
|
|
|
if (!request_ptr || !this) {
|
|
return -1;
|
|
}
|
|
|
|
tr_debug("MGMT_BBR_SET.req received");
|
|
|
|
if (16 == thread_meshcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, MESHCOP_TLV_REGISTRAR_IPV6_ADDRESS, ®istrar_addr)) {
|
|
memcpy(this->registrar_address, registrar_addr, 16);
|
|
response_code = 1;
|
|
}
|
|
|
|
ptr = thread_meshcop_tlv_data_write_uint8(response, MESHCOP_TLV_STATE, response_code);
|
|
|
|
coap_service_response_send(service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, COAP_MSG_CODE_RESPONSE_CHANGED, COAP_CT_OCTET_STREAM, response, ptr - response);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int thread_pbbr_sec_data_set_recv_cb(int8_t service_id, uint8_t source_address[static 16], uint16_t source_port, sn_coap_hdr_s *request_ptr)
|
|
{
|
|
(void) source_address;
|
|
(void) source_port;
|
|
|
|
uint8_t response[3] = {0};
|
|
uint8_t *ptr = response;
|
|
|
|
// This is not yet supported, thus return state TLV rejected
|
|
ptr = thread_meshcop_tlv_data_write_uint8(response, MESHCOP_TLV_STATE, 0xff);
|
|
|
|
coap_service_response_send(service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, COAP_MSG_CODE_RESPONSE_CHANGED, COAP_CT_OCTET_STREAM, response, ptr - response);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int thread_pbbr_nmkp_relay_rx_recv_cb(int8_t service_id, uint8_t source_address[static 16], uint16_t source_port, sn_coap_hdr_s *request_ptr)
|
|
{
|
|
thread_pbbr_t *this = thread_border_router_find_by_service(service_id);
|
|
uint8_t joiner_address[16] = { 0xfe, 0x80 };
|
|
uint8_t *joiner_iid_ptr;
|
|
uint8_t *udp_ptr;
|
|
uint16_t udp_len;
|
|
uint16_t joiner_port;
|
|
uint16_t joiner_router_rloc;
|
|
(void) source_port;
|
|
|
|
if (!this || !source_address || !request_ptr) {
|
|
return -1; // goto error response
|
|
}
|
|
|
|
if ((0 == (udp_len = thread_meshcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, MESHCOP_TLV_JOINER_ENCAPSULATION, &udp_ptr))) ||
|
|
(8 > thread_meshcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, MESHCOP_TLV_JOINER_IID, &joiner_iid_ptr)) ||
|
|
(2 > thread_meshcop_tlv_data_get_uint16(request_ptr->payload_ptr, request_ptr->payload_len, MESHCOP_TLV_JOINER_UDP_PORT, &joiner_port)) ||
|
|
(2 > thread_meshcop_tlv_data_get_uint16(request_ptr->payload_ptr, request_ptr->payload_len, MESHCOP_TLV_JOINER_ROUTER_LOCATOR, &joiner_router_rloc))) {
|
|
tr_warn("Corrupted relay packet received");
|
|
return -1;
|
|
}
|
|
|
|
memcpy(&joiner_address[8], joiner_iid_ptr, 8);
|
|
tr_debug("BBR Relay RX for NMKP from (%x) addr %s, port %d, len:%d", joiner_router_rloc, trace_ipv6(joiner_address), joiner_port, udp_len);
|
|
|
|
this->joiner_router_rloc = joiner_router_rloc;
|
|
coap_service_virtual_socket_recv(this->coap_nmkp_virtual_service_id, joiner_address, joiner_port, udp_ptr, udp_len);
|
|
return 1; // no response sent
|
|
}
|
|
|
|
static int thread_pbbr_nmkp_virtual_socket_send_cb(int8_t service_id, uint8_t destination_addr_ptr[static 16], uint16_t port, const uint8_t *data_ptr, uint16_t data_len)
|
|
{
|
|
thread_pbbr_t *this = thread_border_router_find_by_service(service_id);
|
|
uint8_t destination_address[16];
|
|
uint8_t *payload_ptr;
|
|
uint8_t *ptr;
|
|
uint16_t payload_len;
|
|
|
|
tr_debug("virtual socket send to addr %s, port %d, len:%d", trace_ipv6(destination_addr_ptr), port, data_len);
|
|
if (!this || !destination_addr_ptr || !data_ptr) {
|
|
return -1;
|
|
}
|
|
|
|
payload_len = 4 + data_len + 4 + 8 + 4 + 2;
|
|
payload_ptr = ns_dyn_mem_alloc(payload_len);
|
|
if (!payload_ptr) {
|
|
return -3;
|
|
}
|
|
thread_management_get_ml_prefix_112(this->interface_id, destination_address);
|
|
common_write_16_bit(this->joiner_router_rloc, &destination_address[14]);
|
|
|
|
tr_debug("Relay destination %s:%d", trace_ipv6(destination_address), THREAD_MANAGEMENT_PORT);
|
|
ptr = payload_ptr;
|
|
ptr = thread_meshcop_tlv_data_write(ptr, MESHCOP_TLV_JOINER_ENCAPSULATION, data_len, data_ptr);
|
|
ptr = thread_meshcop_tlv_data_write(ptr, MESHCOP_TLV_JOINER_IID, 8, &destination_addr_ptr[8]);
|
|
ptr = thread_meshcop_tlv_data_write_uint16(ptr, MESHCOP_TLV_JOINER_UDP_PORT, port);
|
|
ptr = thread_meshcop_tlv_data_write_uint16(ptr, MESHCOP_TLV_JOINER_ROUTER_LOCATOR, this->joiner_router_rloc);
|
|
|
|
coap_service_request_send(this->coap_service_id, COAP_REQUEST_OPTIONS_NONE, destination_address, THREAD_MANAGEMENT_PORT,
|
|
COAP_MSG_TYPE_NON_CONFIRMABLE, COAP_MSG_CODE_REQUEST_POST, THREAD_URI_BBR_NMK_TX_NTF, COAP_CT_OCTET_STREAM, payload_ptr, ptr - payload_ptr, NULL);
|
|
|
|
ns_dyn_mem_free(payload_ptr);
|
|
|
|
return 0;
|
|
}
|
|
static int thread_pbbr_pskd_security_start_cb(int8_t service_id, uint8_t address[static 16], uint16_t port, uint8_t *pw, uint8_t *pw_len)
|
|
{
|
|
thread_pbbr_t *this = thread_border_router_find_by_service(service_id);
|
|
device_configuration_s *configuration_ptr;
|
|
(void)address;
|
|
(void)port;
|
|
|
|
if (!this) {
|
|
return -1;
|
|
}
|
|
|
|
configuration_ptr = thread_joiner_application_get_device_config(this->interface_id);
|
|
if (!configuration_ptr) {
|
|
return -1;
|
|
}
|
|
tr_info("Thread BBR PSKd security start pskd:%s", trace_array(configuration_ptr->PSKd_ptr, configuration_ptr->PSKd_len));
|
|
*pw_len = configuration_ptr->PSKd_len;
|
|
memcpy(pw, configuration_ptr->PSKd_ptr, *pw_len);
|
|
return 0;
|
|
}
|
|
static int thread_pbbr_nmkp_req_recv_cb(int8_t service_id, uint8_t source_address[static 16], uint16_t source_port, sn_coap_hdr_s *request_ptr)
|
|
{
|
|
thread_pbbr_t *this = thread_border_router_find_by_service(service_id);
|
|
protocol_interface_info_entry_t *cur;
|
|
uint8_t source_addr[16];
|
|
uint8_t *payload_ptr;
|
|
uint8_t *ptr;
|
|
uint16_t len;
|
|
|
|
(void) source_port;
|
|
|
|
tr_debug("Thread BBR NMKP recv addr %s", trace_ipv6(source_address));
|
|
if (!this || !source_address || !request_ptr) {
|
|
return 0;
|
|
}
|
|
cur = protocol_stack_interface_info_get_by_id(this->interface_id);
|
|
if (!cur) {
|
|
return -1;
|
|
}
|
|
len = 3 + 10; // state,timestamp
|
|
len += thread_joiner_application_active_config_length(this->interface_id, NULL, 0, NULL, 0);
|
|
// TODO implement processing
|
|
payload_ptr = ns_dyn_mem_temporary_alloc(len);
|
|
if (!payload_ptr) {
|
|
tr_error("out of resources");
|
|
return -1;
|
|
}
|
|
ptr = payload_ptr;
|
|
ptr = thread_meshcop_tlv_data_write_uint8(ptr, MESHCOP_TLV_STATE, 1);
|
|
ptr = thread_joiner_application_active_config_write(this->interface_id, ptr, NULL, 0, NULL, 0);
|
|
ptr = thread_active_timestamp_write(cur, ptr);
|
|
|
|
memcpy(source_addr, source_address, 16);
|
|
|
|
coap_service_response_send(service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, COAP_MSG_CODE_RESPONSE_CHANGED, COAP_CT_OCTET_STREAM, payload_ptr, ptr - payload_ptr);
|
|
ns_dyn_mem_free(payload_ptr);
|
|
coap_service_close_secure_connection(service_id, source_addr, source_port);
|
|
return 0;
|
|
}
|
|
|
|
static int thread_pbbr_bb_qry_cb(int8_t service_id, uint8_t source_address[16], uint16_t source_port, sn_coap_hdr_s *request_ptr)
|
|
{
|
|
(void)source_port;
|
|
uint16_t addr_len;
|
|
uint8_t *addr_data_ptr;
|
|
uint8_t *ml_eid_ptr;
|
|
uint16_t rloc;
|
|
uint16_t *rloc_ptr = NULL;
|
|
uint32_t last_transaction_time;
|
|
tr_info("Thread BBR BB_QRY.ntf Received");
|
|
|
|
thread_pbbr_t *this = thread_border_router_find_by_service(service_id);
|
|
|
|
if (!this) {
|
|
return -1;
|
|
}
|
|
|
|
link_configuration_s *link_configuration_ptr = thread_joiner_application_get_config(this->interface_id);
|
|
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(this->interface_id);
|
|
protocol_interface_info_entry_t *backbone_if = protocol_stack_interface_info_get_by_id(this->backbone_interface_id);
|
|
|
|
if (!link_configuration_ptr || !cur || !backbone_if) {
|
|
return -1;
|
|
}
|
|
if (addr_interface_address_compare(backbone_if, source_address) == 0) {
|
|
// Received from own address no need to process
|
|
return -1;
|
|
}
|
|
|
|
addr_len = thread_meshcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_TARGET_EID, &addr_data_ptr);
|
|
if (addr_len < 16) {
|
|
tr_warn("Invalid BB_QRY.ntf message");
|
|
return -1;
|
|
}
|
|
|
|
// Test code for b/ba response override
|
|
if (ba_response_status_count) {
|
|
device_configuration_s *device_config = thread_joiner_application_get_device_config(this->interface_id);
|
|
if (!device_config) {
|
|
return -1;
|
|
}
|
|
ml_eid_ptr = device_config->eui64;
|
|
last_transaction_time = protocol_core_monotonic_time;
|
|
ba_response_status_count--;
|
|
goto send_response;
|
|
}
|
|
|
|
ipv6_route_t *route = ipv6_route_choose_next_hop(addr_data_ptr, this->interface_id, NULL);
|
|
if (!route || route->prefix_len < 128 || !route->on_link || route->info.source != ROUTE_THREAD_PROXIED_DUA_HOST || !route->info.info) {
|
|
//address not in mesh
|
|
return -1;
|
|
}
|
|
if (thread_meshcop_tlv_data_get_uint16(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_RLOC16, &rloc) > 1) {
|
|
rloc_ptr = &rloc;
|
|
}
|
|
|
|
last_transaction_time = protocol_core_monotonic_time - ((thread_pbbr_dua_info_t *)route->info.info)->last_contact_time;
|
|
ml_eid_ptr = ((thread_pbbr_dua_info_t *)route->info.info)->mleid_ptr;
|
|
|
|
send_response:
|
|
// This address is valid in our MESH
|
|
return thread_border_router_bb_ans_send(this, source_address, addr_data_ptr, ml_eid_ptr, last_transaction_time, link_configuration_ptr->name, rloc_ptr);
|
|
}
|
|
|
|
static void thread_pbbr_pro_bb_ntf_process(protocol_interface_info_entry_t *cur, uint8_t *network_name_ptr, uint8_t network_name_len, uint8_t *ml_eid_ptr, uint8_t *addr_data_ptr, uint32_t last_transaction_time)
|
|
{
|
|
(void) network_name_ptr;
|
|
(void) network_name_len;
|
|
thread_pbbr_dua_info_t *mleid_dua_map;
|
|
thread_pbbr_t *this = thread_bbr_find_by_interface(cur->id);
|
|
if (!this) {
|
|
return;
|
|
}
|
|
ipv6_route_t *route = ipv6_route_lookup_with_info(addr_data_ptr, 128, this->interface_id, NULL, ROUTE_THREAD_PROXIED_DUA_HOST, NULL, 0);
|
|
if (!route || !route->info.info) {
|
|
return;
|
|
}
|
|
mleid_dua_map = route->info.info;
|
|
if (memcmp(mleid_dua_map->mleid_ptr, ml_eid_ptr, 8) == 0) {
|
|
// matching ml-eid present in our dua map, device has roamed
|
|
if (last_transaction_time <= protocol_core_monotonic_time - mleid_dua_map->last_contact_time) {
|
|
// remove entry
|
|
goto remove_entry;
|
|
} else {
|
|
thread_border_router_bb_ans_send(this, this->pbbr_multicast_address, addr_data_ptr, ml_eid_ptr, protocol_core_monotonic_time - mleid_dua_map->last_contact_time, thread_joiner_application_network_name_get(cur->id), NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// duplicate dua address detected
|
|
// Address should be ML-prefix + MLeid TODO create spec issue
|
|
uint8_t destination_address[16];
|
|
thread_management_get_ml_prefix(cur->id, destination_address);
|
|
memcpy(&destination_address[8], mleid_dua_map->mleid_ptr, 8);
|
|
thread_resolution_client_address_error(cur->id, destination_address, addr_data_ptr, mleid_dua_map->mleid_ptr);
|
|
|
|
remove_entry:
|
|
tr_info("Remove dua registration for %s", trace_ipv6(addr_data_ptr));
|
|
ipv6_route_delete(addr_data_ptr, 128, cur->id, NULL, ROUTE_THREAD_PROXIED_DUA_HOST);
|
|
return;
|
|
}
|
|
|
|
static int thread_pbbr_dua_duplicate_address_detection(int8_t service_id, uint8_t *addr_data_ptr, uint8_t *ml_eid_ptr)
|
|
{
|
|
thread_pbbr_t *this = thread_border_router_find_by_service(service_id);
|
|
|
|
if (!this) {
|
|
return -1;
|
|
}
|
|
|
|
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(this->interface_id);
|
|
duplicate_dua_tr_t *tr_ptr = thread_border_router_dup_tr_find(this->interface_id, addr_data_ptr);
|
|
|
|
if (!cur || !tr_ptr) {
|
|
return -1;
|
|
}
|
|
|
|
ipv6_route_t *route = ipv6_route_choose_next_hop(addr_data_ptr, this->interface_id, NULL);
|
|
if (!route || route->prefix_len != 128 || !route->on_link || route->info.source != ROUTE_THREAD_PROXIED_DUA_HOST) {
|
|
// Not found
|
|
tr_debug("route not found");
|
|
return -1;
|
|
}
|
|
|
|
// We have pending request and received answer
|
|
if (memcmp(ml_eid_ptr, tr_ptr->ml_eid, 8) != 0) {
|
|
// Different ml_eid but same address means duplicate address detected
|
|
thread_resolution_client_address_error(this->interface_id, tr_ptr->source_address, tr_ptr->target_eid, ml_eid_ptr);
|
|
ipv6_neighbour_t *neighbour_entry;
|
|
neighbour_entry = ipv6_neighbour_lookup(&cur->ipv6_neighbour_cache, tr_ptr->target_eid);
|
|
if (neighbour_entry) {
|
|
tr_debug("Remove from neigh Cache: %s", tr_ipv6(tr_ptr->target_eid));
|
|
ipv6_neighbour_entry_remove(&cur->ipv6_neighbour_cache, neighbour_entry);
|
|
}
|
|
ipv6_route_delete(route->prefix, route->prefix_len, this->interface_id, NULL, ROUTE_THREAD_PROXIED_DUA_HOST);
|
|
thread_border_router_dup_tr_delete(tr_ptr);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int thread_pbbr_bb_ans_cb(int8_t service_id, uint8_t source_address[16], uint16_t source_port, sn_coap_hdr_s *request_ptr)
|
|
{
|
|
(void)service_id;
|
|
(void)source_port;
|
|
(void)request_ptr;
|
|
uint16_t addr_len, ml_eid_len, last_transaction_time_len;
|
|
uint8_t *addr_data_ptr;
|
|
uint16_t rloc16;
|
|
uint8_t destination_address[16] = {0};
|
|
uint8_t *ml_eid_ptr;
|
|
uint32_t last_transaction_time;
|
|
uint8_t *network_name_ptr;
|
|
uint8_t network_name_len;
|
|
tr_info("Thread BBR BB_ANS.ntf receive");
|
|
thread_pbbr_t *this = thread_border_router_find_by_service(service_id);
|
|
|
|
if (!this) {
|
|
return -1;
|
|
}
|
|
link_configuration_s *link_configuration_ptr = thread_joiner_application_get_config(this->interface_id);
|
|
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(this->interface_id);
|
|
protocol_interface_info_entry_t *backbone_if = protocol_stack_interface_info_get_by_id(this->backbone_interface_id);
|
|
|
|
if (!link_configuration_ptr || !cur || !backbone_if) {
|
|
return -1;
|
|
}
|
|
if (addr_interface_address_compare(backbone_if, source_address) == 0) {
|
|
// Received from own address no need to process
|
|
return -1;
|
|
}
|
|
addr_len = thread_meshcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_TARGET_EID, &addr_data_ptr);
|
|
ml_eid_len = thread_meshcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_ML_EID, &ml_eid_ptr);
|
|
last_transaction_time_len = thread_meshcop_tlv_data_get_uint32(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_LAST_TRANSACTION_TIME, &last_transaction_time);
|
|
network_name_len = thread_meshcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_NETWORK_NAME, &network_name_ptr);
|
|
|
|
if (addr_len < 16 || ml_eid_len < 8 || last_transaction_time_len < 4) {
|
|
tr_err("Invalid message");
|
|
// send confirmation with not acceptable status code
|
|
if (request_ptr->msg_type == COAP_MSG_TYPE_CONFIRMABLE) {
|
|
coap_service_response_send(service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, COAP_MSG_CODE_RESPONSE_NOT_ACCEPTABLE, COAP_CT_OCTET_STREAM, NULL, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// when we process unicast b/ba req send confirmation
|
|
if (request_ptr->msg_type == COAP_MSG_TYPE_CONFIRMABLE) {
|
|
coap_service_response_send(service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, COAP_MSG_CODE_RESPONSE_CHANGED, COAP_CT_OCTET_STREAM, NULL, 0);
|
|
}
|
|
|
|
if ((thread_pbbr_dua_duplicate_address_detection(service_id, addr_data_ptr, ml_eid_ptr) == 0)) {
|
|
return 0;
|
|
}
|
|
|
|
// If rloc16 is present then a/an is sent to the thread device with the rloc
|
|
if (thread_tmfcop_tlv_data_get_uint16(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_RLOC16, &rloc16) != 2) {
|
|
thread_pbbr_pro_bb_ntf_process(cur, network_name_ptr, network_name_len, ml_eid_ptr, addr_data_ptr, last_transaction_time);
|
|
return 0;
|
|
}
|
|
|
|
// form the destination address to which the a/an is sent
|
|
thread_addr_write_mesh_local_16(destination_address, rloc16, cur->thread_info);
|
|
|
|
// Address query pending send address notification, include tlvs: target eid, rloc16 tlv of bbr, mleid, and last transaction time
|
|
uint8_t payload[16 + 2 + 8 + 4]; // Target eid + Rloc16 + MLEID + transaction time
|
|
uint8_t *ptr;
|
|
ptr = payload;
|
|
ptr = thread_tmfcop_tlv_data_write(ptr, TMFCOP_TLV_TARGET_EID, 16, addr_data_ptr);
|
|
ptr = thread_tmfcop_tlv_data_write_uint16(ptr, TMFCOP_TLV_RLOC16, cur->thread_info->routerShortAddress);
|
|
ptr = thread_tmfcop_tlv_data_write(ptr, TMFCOP_TLV_ML_EID, 8, ml_eid_ptr);
|
|
ptr = thread_tmfcop_tlv_data_write_uint32(ptr, TMFCOP_TLV_LAST_TRANSACTION_TIME, last_transaction_time);
|
|
|
|
// XXX "Accepted" response?
|
|
|
|
/* We don't require a response, so we don't specify a callback. Library
|
|
* should retry itself until it gets an ACK.
|
|
*/
|
|
coap_service_request_send(this->coap_service_id, COAP_REQUEST_OPTIONS_ADDRESS_SHORT,
|
|
destination_address, THREAD_MANAGEMENT_PORT,
|
|
COAP_MSG_TYPE_CONFIRMABLE, COAP_MSG_CODE_REQUEST_POST,
|
|
THREAD_URI_ADDRESS_NOTIFICATION, COAP_CT_OCTET_STREAM,
|
|
payload, ptr - payload, NULL);
|
|
return 0;
|
|
}
|
|
static int thread_bbr_commercial_bmlr_req_send(int8_t service_id, const uint8_t br_addr[16], const uint8_t *address_ptr, uint8_t addr_len, uint32_t timeout)
|
|
{
|
|
uint8_t payload[2 + 16 + 2 + 4 + 2];
|
|
uint8_t *ptr;
|
|
|
|
if (!br_addr || !address_ptr) {
|
|
return -1;
|
|
}
|
|
|
|
ptr = payload;
|
|
ptr = thread_meshcop_tlv_data_write(ptr, TMFCOP_TLV_IPV6_ADDRESS, addr_len, address_ptr);
|
|
ptr = thread_meshcop_tlv_data_write_uint32(ptr, TMFCOP_TLV_TIMEOUT, timeout);
|
|
|
|
tr_debug("thread BMLR.ntf send %s; timeout: %"PRIu32, trace_ipv6(address_ptr), timeout);
|
|
|
|
coap_service_request_send(service_id, COAP_REQUEST_OPTIONS_NONE, br_addr, THREAD_MANAGEMENT_PORT,
|
|
COAP_MSG_TYPE_NON_CONFIRMABLE, COAP_MSG_CODE_REQUEST_POST, THREAD_URI_BBR_BMLR_NTF, COAP_CT_OCTET_STREAM, payload, ptr - payload, NULL);
|
|
return 0;
|
|
}
|
|
|
|
static int thread_bbr_commercial_mlr_cb(int8_t service_id, uint8_t source_address[16], uint16_t source_port, sn_coap_hdr_s *request_ptr)
|
|
{
|
|
(void)source_address;
|
|
(void)source_port;
|
|
|
|
protocol_interface_info_entry_t *cur;
|
|
sn_coap_msg_code_e response_code = COAP_MSG_CODE_RESPONSE_CHANGED;
|
|
uint16_t addr_len;
|
|
uint16_t session_id;
|
|
uint8_t *addr_data_ptr;
|
|
uint8_t *invalid_addr_ptr = NULL;
|
|
uint32_t timeout_value;
|
|
bool commissioner = false;
|
|
uint8_t bbr_rloc_addr[16];
|
|
uint8_t bbr_status = THREAD_ST_DUA_SUCCESS;
|
|
uint8_t payload[4 + 18];
|
|
uint8_t *ptr;
|
|
|
|
tr_info("Thread BBR MLR.ntf receive");
|
|
|
|
thread_pbbr_t *this = thread_border_router_find_by_service(service_id);
|
|
if (!this) {
|
|
return -1;
|
|
}
|
|
|
|
cur = protocol_stack_interface_info_get_by_id(this->interface_id);
|
|
if (!cur) {
|
|
return -1;
|
|
}
|
|
|
|
ptr = payload;
|
|
|
|
// Set default value if not specified in message.
|
|
timeout_value = this->mlr_timeout;
|
|
|
|
addr_len = thread_meshcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_IPV6_ADDRESS, &addr_data_ptr);
|
|
|
|
//check if commissioner
|
|
if (2 <= thread_meshcop_tlv_data_get_uint16(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_COMMISSIONER_SESSION_ID, &session_id)) {
|
|
// Session id present must be valid
|
|
tr_info("message from commissioner");
|
|
if (cur->thread_info->registered_commissioner.session_id != session_id) {
|
|
tr_warn("Invalid commissioner session id");
|
|
bbr_status = THREAD_ST_DUA_GENERAL_FAILURE;
|
|
goto send_response;
|
|
}
|
|
thread_meshcop_tlv_data_get_uint32(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_TIMEOUT, &timeout_value);
|
|
commissioner = true;
|
|
}
|
|
|
|
if (!commissioner) {
|
|
// check if we are not primary bbr respond status 5
|
|
if (0 != thread_common_primary_bbr_get(cur, bbr_rloc_addr, NULL, NULL, NULL) ||
|
|
!addr_get_entry(cur, bbr_rloc_addr)) {
|
|
// Primary BBR not present or I am not BBR
|
|
bbr_status = THREAD_ST_DUA_BBR_NOT_PRIMARY;
|
|
goto send_response;
|
|
}
|
|
}
|
|
|
|
|
|
int16_t remaining = addr_len;
|
|
uint8_t *addr_ptr = addr_data_ptr;
|
|
|
|
if (addr_len < 16 ||
|
|
addr_len % 16 != 0) {
|
|
// Address must be more than 16 and can be multible
|
|
tr_err("Invalid /n/mr message");
|
|
if (addr_len >= 16) {
|
|
invalid_addr_ptr = addr_ptr;
|
|
}
|
|
bbr_status = THREAD_ST_DUA_INVALID;
|
|
goto send_response;
|
|
}
|
|
|
|
while (remaining > 0) {
|
|
//Go through all addresses
|
|
if (!addr_is_ipv6_multicast(addr_ptr)) {
|
|
tr_err("Invalid /n/mr not multicast address");
|
|
bbr_status = THREAD_ST_DUA_INVALID;
|
|
if (!invalid_addr_ptr) {
|
|
invalid_addr_ptr = addr_ptr;
|
|
}
|
|
} else {
|
|
if (timeout_value == 0) {
|
|
//tr_debug("Multicast address: %s delete", trace_ipv6(addr_ptr));
|
|
addr_multicast_fwd_remove(cur, addr_ptr);
|
|
// TODO remove address from NVM
|
|
thread_border_router_multicast_store_del(this, addr_ptr);
|
|
} else {
|
|
//tr_debug("Multicast address: %s Timeout value: %"PRIu32, trace_ipv6(addr_ptr),timeout_value);
|
|
multicast_fwd_add(this->interface_id, addr_ptr, timeout_value);
|
|
if (timeout_value == 0xffffffff) {
|
|
thread_border_router_multicast_store_add(this, addr_ptr);
|
|
}
|
|
// send BMLR.ntf message to backend
|
|
thread_bbr_commercial_bmlr_req_send(this->br_bb_service_id, this->pbbr_multicast_address, addr_data_ptr, 16, timeout_value);
|
|
}
|
|
}
|
|
addr_ptr += 16;
|
|
remaining -= 16;
|
|
}
|
|
|
|
send_response:
|
|
|
|
if (request_ptr->msg_type == COAP_MSG_TYPE_CONFIRMABLE) {
|
|
ptr = thread_tmfcop_tlv_data_write_uint8(ptr, TMFCOP_TLV_STATUS, bbr_status);
|
|
if (invalid_addr_ptr) {
|
|
ptr = thread_meshcop_tlv_data_write(ptr, TMFCOP_TLV_IPV6_ADDRESS, 16, invalid_addr_ptr);
|
|
}
|
|
coap_service_response_send(this->coap_service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, response_code, COAP_CT_OCTET_STREAM, payload, ptr - payload);
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
static int thread_bbr_commercial_bmlr_cb(int8_t service_id, uint8_t source_address[16], uint16_t source_port, sn_coap_hdr_s *request_ptr)
|
|
{
|
|
(void)source_address;
|
|
(void)source_port;
|
|
(void)service_id;
|
|
(void)request_ptr;
|
|
|
|
tr_info("Thread BBR BMLR.ntf receive");
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int thread_bbr_commercial_dua_cb(int8_t service_id, uint8_t source_address[16], uint16_t source_port, sn_coap_hdr_s *request_ptr)
|
|
{
|
|
(void)source_port;
|
|
uint16_t addr_len, ml_eid_len;
|
|
uint8_t *addr_data_ptr = NULL;
|
|
uint8_t *ml_eid_ptr;
|
|
uint8_t payload[2 + 16 + 2 + 2 + 1 + 8];
|
|
uint8_t bbr_rloc_addr[16];
|
|
uint8_t *ptr;
|
|
bool entry_keep_alive = false;
|
|
uint8_t bbr_status = THREAD_ST_DUA_SUCCESS;
|
|
|
|
tr_info("Thread BBR DUA.req received");
|
|
|
|
thread_pbbr_t *this = thread_border_router_find_by_service(service_id);
|
|
sn_coap_msg_code_e response_code = COAP_MSG_CODE_RESPONSE_CHANGED;
|
|
|
|
if (!this) {
|
|
return -1;
|
|
}
|
|
link_configuration_s *link_configuration_ptr = thread_joiner_application_get_config(this->interface_id);
|
|
if (!link_configuration_ptr) {
|
|
return -1;
|
|
}
|
|
|
|
/*if (this->backbone_interface_id< 0){
|
|
//backbone not valid
|
|
tr_warn("Backbone link not available");
|
|
bbr_status = THREAD_BBR_STATUS_BB_LINK_NOT_OPERATIONAL;
|
|
goto send_response;
|
|
}
|
|
*/
|
|
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(this->interface_id);
|
|
if (!cur) {
|
|
return -1;
|
|
}
|
|
|
|
if (0 == thread_common_primary_bbr_get(cur, bbr_rloc_addr, NULL, NULL, NULL) &&
|
|
!addr_get_entry(cur, bbr_rloc_addr)) {
|
|
// Primary pBBR present and I am not the pBBR
|
|
bbr_status = THREAD_ST_DUA_BBR_NOT_PRIMARY;
|
|
goto send_response;
|
|
}
|
|
|
|
addr_len = thread_meshcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_TARGET_EID, &addr_data_ptr);
|
|
ml_eid_len = thread_meshcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_ML_EID, &ml_eid_ptr);
|
|
|
|
if (addr_len < 16 || ml_eid_len < 8) {
|
|
tr_err("Invalid /n/dr message");
|
|
bbr_status = THREAD_ST_DUA_GENERAL_FAILURE;
|
|
goto send_response;
|
|
}
|
|
|
|
if (!bitsequal(addr_data_ptr, this->domain_prefix, 64)) {
|
|
// Prefix did not match
|
|
tr_warn("Registration failed invalid address");
|
|
bbr_status = THREAD_ST_DUA_INVALID;
|
|
goto send_response;
|
|
}
|
|
|
|
tr_debug("DUA.req addr:%s ml_eid:%s", trace_array(addr_data_ptr, addr_len), trace_array(ml_eid_ptr, ml_eid_len));
|
|
|
|
// Test code for dua response override
|
|
if (dua_response_status != 0xff && dua_status_count) {
|
|
bbr_status = dua_response_status;
|
|
dua_status_count--;
|
|
goto send_response;
|
|
}
|
|
|
|
entry_keep_alive = false;
|
|
ipv6_route_t *route = ipv6_route_lookup_with_info(addr_data_ptr, 128, this->interface_id, NULL, ROUTE_THREAD_PROXIED_DUA_HOST, NULL, 0);
|
|
|
|
// new device has generated duplicate dua
|
|
if (route != NULL && route->info.info != NULL && memcmp(((thread_pbbr_dua_info_t *)route->info.info)->mleid_ptr, ml_eid_ptr, 8) != 0) {
|
|
bbr_status = THREAD_ST_DUA_DUPLICATE;
|
|
goto send_response;
|
|
}
|
|
if (route != NULL && route->info.info != NULL && memcmp(((thread_pbbr_dua_info_t *)route->info.info)->mleid_ptr, ml_eid_ptr, 8) == 0) {
|
|
((thread_pbbr_dua_info_t *)route->info.info)->last_contact_time = protocol_core_monotonic_time;
|
|
entry_keep_alive = true;
|
|
}
|
|
if (thread_bbr_dua_entry_add(this->interface_id, addr_data_ptr, this->dua_timeout * 2, ml_eid_ptr) != 0) {
|
|
bbr_status = THREAD_ST_DUA_NO_RESOURCES;
|
|
goto send_response;
|
|
}
|
|
/*if (route && memcmp(route->info.next_hop_addr, ml_addr,16) != 0) {
|
|
// MLEID not matching duplicate detected
|
|
tr_warn("Registration failed duplicate detected");
|
|
bbr_status = THREAD_BBR_STATUS_DUA_ALREADY_IN_USE;
|
|
goto send_response;
|
|
}
|
|
*/
|
|
|
|
if (entry_keep_alive) {
|
|
// send proactive BB_ans.ntf
|
|
thread_border_router_bb_ans_send(this, this->pbbr_multicast_address, addr_data_ptr, ml_eid_ptr, 0, link_configuration_ptr->name, NULL);
|
|
} else {
|
|
// send BB.qry
|
|
// TODO Create duplicate Transaction to wait answers from Backbone
|
|
thread_border_router_dup_tr_create(this->interface_id, source_address, addr_data_ptr, ml_eid_ptr);
|
|
thread_border_router_bb_qry_send(this, addr_data_ptr, NULL);
|
|
|
|
}
|
|
|
|
send_response:
|
|
|
|
ptr = payload;
|
|
ptr = thread_tmfcop_tlv_data_write_uint8(ptr, TMFCOP_TLV_STATUS, bbr_status);
|
|
if (addr_data_ptr) {
|
|
ptr = thread_meshcop_tlv_data_write(ptr, TMFCOP_TLV_TARGET_EID, 16, addr_data_ptr);
|
|
}
|
|
coap_service_response_send(this->coap_service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, response_code, COAP_CT_OCTET_STREAM, payload, ptr - payload);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*POC for multicast NVM*/
|
|
#define MULTICAST_ADDRESS_STORE_AMOUNT 3
|
|
|
|
typedef struct {
|
|
uint8_t addr[16];
|
|
} multicast_addr_t;
|
|
static multicast_addr_t multicast_store[MULTICAST_ADDRESS_STORE_AMOUNT] = {{{0}}};
|
|
|
|
static void thread_border_router_multicast_store_add(thread_pbbr_t *this, uint8_t *destination_addr_ptr)
|
|
{
|
|
(void)this;
|
|
tr_info("Store multicast address to NVM");
|
|
for (int n = 0; n < MULTICAST_ADDRESS_STORE_AMOUNT; n++) {
|
|
if (memcmp(multicast_store[n].addr, ADDR_UNSPECIFIED, 16) == 0) {
|
|
memcpy(multicast_store[n].addr, destination_addr_ptr, 16);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void thread_border_router_multicast_store_del(thread_pbbr_t *this, uint8_t *destination_addr_ptr)
|
|
{
|
|
(void)this;
|
|
tr_info("delete multicast address from NVM");
|
|
for (int n = 0; n < MULTICAST_ADDRESS_STORE_AMOUNT; n++) {
|
|
if (memcmp(multicast_store[n].addr, destination_addr_ptr, 16) == 0) {
|
|
memcpy(multicast_store[n].addr, ADDR_UNSPECIFIED, 16);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void thread_border_router_multicast_store_activate(thread_pbbr_t *this)
|
|
{
|
|
(void)this;
|
|
for (int n = 0; n < MULTICAST_ADDRESS_STORE_AMOUNT; n++) {
|
|
if (memcmp(multicast_store[n].addr, ADDR_UNSPECIFIED, 16) != 0) {
|
|
multicast_fwd_add(this->interface_id, multicast_store[n].addr, 0xffffffff);
|
|
thread_bbr_commercial_bmlr_req_send(this->br_bb_service_id, this->pbbr_multicast_address, multicast_store[n].addr, 16, 0xffffffff);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static bool thread_bbr_commercial_downgrade_to_secondary(struct protocol_interface_info_entry *cur)
|
|
{
|
|
uint16_t rloc16 = mac_helper_mac16_address_get(cur);
|
|
uint16_t highest_rloc16 = rloc16;
|
|
uint8_t highest_seq = 0;
|
|
|
|
|
|
ns_list_foreach(thread_network_data_service_cache_entry_t, service, &cur->thread_info->networkDataStorage.service_list) {
|
|
|
|
if (service->S_enterprise_number != THREAD_ENTERPRISE_NUMBER) {
|
|
// Not Thread service
|
|
continue;
|
|
}
|
|
if (service->S_service_data_length < 1 &&
|
|
service->S_service_data[0] != THREAD_SERVICE_DATA_BBR) {
|
|
//Not pBBR service
|
|
continue;
|
|
}
|
|
|
|
ns_list_foreach(thread_network_data_service_server_entry_t, service_instance, &service->server_list) {
|
|
if (service_instance->router_id > 0xfffe) {
|
|
continue;
|
|
}
|
|
if (service_instance->server_data_length < 1) {
|
|
continue;
|
|
}
|
|
if (service_instance->server_data[0] > highest_seq) {
|
|
highest_seq = service_instance->server_data[0];
|
|
highest_rloc16 = service_instance->router_id;
|
|
}
|
|
}
|
|
}
|
|
if (highest_rloc16 != rloc16) {
|
|
// We are not highest serquence number we
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
static int thread_border_router_pbbr_nw_data_register(thread_pbbr_t *this)
|
|
{
|
|
// Create Primary BBR network data and send to leader
|
|
uint8_t service_data[1];
|
|
uint8_t server_data[7];
|
|
uint8_t *ptr = server_data;
|
|
|
|
service_data[0] = THREAD_SERVICE_DATA_BBR;
|
|
*ptr++ = this->sequence_number;
|
|
ptr = common_write_16_bit(this->delay_timer, ptr);
|
|
ptr = common_write_32_bit(this->mlr_timeout, ptr);
|
|
|
|
if (thread_border_router_service_add(this->interface_id, service_data,
|
|
1, 0, THREAD_ENTERPRISE_NUMBER,
|
|
server_data, 7, true) != 0) {
|
|
tr_error("Adding service data failed!");
|
|
return -1;
|
|
}
|
|
|
|
if (memcmp(this->domain_prefix, ADDR_UNSPECIFIED, 8) != 0) {
|
|
thread_border_router_info_t br_info = {0};
|
|
br_info.P_on_mesh = true;
|
|
br_info.stableData = true;
|
|
br_info.P_res1 = true;
|
|
if (thread_border_router_prefix_add(this->interface_id, this->domain_prefix, 64, &br_info)) {
|
|
tr_error("Adding domain prefix failed!");
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int thread_bbr_commercial_pbbr_stop(thread_pbbr_t *this)
|
|
{
|
|
// Create Primary BBR network data and send to leader
|
|
uint8_t service_data[1];
|
|
|
|
service_data[0] = THREAD_SERVICE_DATA_BBR;
|
|
thread_border_router_service_delete(this->interface_id, service_data, 1, THREAD_ENTERPRISE_NUMBER);
|
|
thread_border_router_prefix_delete(this->interface_id, this->domain_prefix, 64);
|
|
multicast_free_address(this->pbbr_multicast_address);
|
|
multicast_fwd_full_for_scope(this->interface_id, IPV6_SCOPE_SITE_LOCAL);
|
|
|
|
ipv6_route_table_remove_info(this->backbone_interface_id, ROUTE_THREAD_BBR, NULL);
|
|
|
|
coap_service_unregister_uri(this->coap_service_id, THREAD_URI_BBR_TRI_RX_NTF);
|
|
coap_service_unregister_uri(this->coap_service_id, THREAD_URI_BBR_NMK_RX_NTF);
|
|
|
|
coap_service_delete(this->br_bb_service_id);
|
|
this->br_bb_service_id = -1;
|
|
|
|
coap_service_delete(this->coap_nmkp_virtual_service_id);
|
|
this->coap_nmkp_virtual_service_id = -1;
|
|
|
|
this->pbbr_started = false;
|
|
return 0;
|
|
}
|
|
|
|
static int thread_bbr_commercial_pbbr_start(thread_pbbr_t *this)
|
|
{
|
|
// Create Primary BBR network data and send to leader
|
|
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(this->interface_id);
|
|
if (!cur || !cur->thread_info) {
|
|
return -1;
|
|
}
|
|
|
|
this->sequence_number++;
|
|
|
|
thread_border_router_pbbr_nw_data_register(this);
|
|
// Start ticker for DUA refresh
|
|
thread_border_router_publish(this->interface_id);
|
|
this->dua_timer_ticks = this->dua_timeout;
|
|
if (this->pbbr_started) {
|
|
//If started only refresh network data
|
|
return 0;
|
|
}
|
|
// Register to backbone CoAP service
|
|
this->br_bb_service_id = coap_service_initialize(this->backbone_interface_id, this->pbbr_port, COAP_SERVICE_OPTIONS_SELECT_SOCKET_IF, NULL, NULL);
|
|
if (this->br_bb_service_id < 0) {
|
|
tr_error("pBBR Commercial service start failed");
|
|
thread_bbr_commercial_pbbr_stop(this);
|
|
return -1;
|
|
}
|
|
// Allow only filtered addresses to be forwarded
|
|
multicast_fwd_full_for_scope(this->interface_id, 0x10);
|
|
multicast_fwd_full_for_scope(this->backbone_interface_id, 0);
|
|
|
|
// Generate pbbr multicast address
|
|
memset(this->pbbr_multicast_address, 0, 16);
|
|
this->pbbr_multicast_address[0] = 0xff;
|
|
this->pbbr_multicast_address[1] = 0x32; //Thread specification says p and t bits are 1
|
|
this->pbbr_multicast_address[2] = 0x00; //Reserved
|
|
this->pbbr_multicast_address[3] = 0x40; //Prefix length 64 bits
|
|
memcpy(&this->pbbr_multicast_address[4], this->domain_prefix, 8);
|
|
this->pbbr_multicast_address[15] = 3;
|
|
|
|
tr_info("pBBR service started (Commercial network)");
|
|
// Register Primary BBR backbone multicast address
|
|
multicast_add_address(this->pbbr_multicast_address, false);
|
|
this->pbbr_started = true;
|
|
thread_border_router_multicast_store_activate(this);
|
|
|
|
// Register Backbone commercial features
|
|
coap_service_register_uri(this->br_bb_service_id, THREAD_URI_BBR_BB_QRY_NTF, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_pbbr_bb_qry_cb);
|
|
coap_service_register_uri(this->br_bb_service_id, THREAD_URI_BBR_BB_ANS_NTF, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_pbbr_bb_ans_cb);
|
|
coap_service_register_uri(this->br_bb_service_id, THREAD_URI_TRI_TX_NTF, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_pbbr_relay_from_tri_cb);
|
|
coap_service_register_uri(this->br_bb_service_id, THREAD_URI_BBR_BMLR_NTF, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_bbr_commercial_bmlr_cb);
|
|
|
|
// Register Mesh side relay URI
|
|
coap_service_register_uri(this->coap_service_id, THREAD_URI_BBR_TRI_RX_NTF, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_pbbr_relay_to_tri_cb);
|
|
coap_service_register_uri(this->coap_service_id, THREAD_URI_BBR_NMK_RX_NTF, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_pbbr_nmkp_relay_rx_recv_cb);
|
|
|
|
// create secure service for Network master key provisioning
|
|
this->coap_nmkp_virtual_service_id = coap_service_initialize(this->interface_id, THREAD_MANAGEMENT_PORT, COAP_SERVICE_OPTIONS_SECURE | COAP_SERVICE_OPTIONS_VIRTUAL_SOCKET, thread_pbbr_pskd_security_start_cb, NULL);
|
|
// SET certificates
|
|
thread_ccm_network_certificate_enable(cur, this->coap_nmkp_virtual_service_id);
|
|
coap_service_register_uri(this->coap_nmkp_virtual_service_id, THREAD_URI_BBR_NMKP_REQ, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_pbbr_nmkp_req_recv_cb);
|
|
coap_service_virtual_socket_set_cb(this->coap_nmkp_virtual_service_id, thread_pbbr_nmkp_virtual_socket_send_cb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int8_t thread_bbr_commercial_init(int8_t interface_id, int8_t backbone_interface_id)
|
|
{
|
|
thread_pbbr_t *this = thread_bbr_find_by_interface(interface_id);
|
|
if (this) {
|
|
return 0;
|
|
}
|
|
if (thread_version < THREAD_VERSION_1_2) {
|
|
return 0;
|
|
}
|
|
tr_debug("thread_border_router_init if=%d", interface_id);
|
|
|
|
this = ns_dyn_mem_alloc(sizeof(thread_pbbr_t));
|
|
if (!this) {
|
|
return -2;
|
|
}
|
|
memset(this, 0, sizeof(thread_pbbr_t));
|
|
this->interface_id = interface_id;
|
|
|
|
this->backbone_interface_id = backbone_interface_id;
|
|
this->sequence_number = randLIB_get_8bit();
|
|
this->mlr_timeout = THREAD_BBR_MLR_REGISTRATION_TIMEOUT;
|
|
this->dua_timeout = THREAD_BBR_DUA_REGISTRATION_TIMEOUT;
|
|
this->delay_timer = THREAD_BBR_DUA_REGISTRATION_DELAY;
|
|
this->pbbr_started = false;
|
|
memcpy(this->pbbr_multicast_address, ADDR_LINK_LOCAL_ALL_ROUTERS, 16);
|
|
this->pbbr_port = THREAD_BBR_BACKBONE_PORT;
|
|
memset(this->registrar_address, 0, 16);
|
|
memcpy(this->tri_address, ADDR_LINK_LOCAL_ALL_ROUTERS, 16);
|
|
this->tri_port = THREAD_BBR_BACKBONE_PORT;
|
|
this->joiner_router_rloc = 0xffff;
|
|
this->coap_nmkp_virtual_service_id = -1;
|
|
this->br_bb_service_id = -1;
|
|
memset(this->domain_prefix, 0, sizeof(this->domain_prefix));
|
|
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;
|
|
}
|
|
ns_list_add_to_start(&pbbr_instance_list, this);
|
|
|
|
// Set the partition weight to be 72
|
|
thread_management_partition_weighting_set(this->interface_id, 72);
|
|
// Register Mesh address registration URI
|
|
coap_service_register_uri(this->coap_service_id, THREAD_URI_BBR_MCAST_LISTENER_REPORT, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_bbr_commercial_mlr_cb);
|
|
coap_service_register_uri(this->coap_service_id, THREAD_URI_BBR_DOMAIN_ADDRESS_REGISTRATION, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_bbr_commercial_dua_cb);
|
|
// Register BBR data request URI
|
|
coap_service_register_uri(this->coap_service_id, THREAD_URI_BBR_DATA_REQ, COAP_SERVICE_ACCESS_GET_ALLOWED, thread_pbbr_data_req_recv_cb);
|
|
coap_service_register_uri(this->coap_service_id, THREAD_URI_BBR_DATA_SET, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_pbbr_data_set_recv_cb);
|
|
coap_service_register_uri(this->coap_service_id, THREAD_URI_MGMT_SEC_PENDING_SET, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_pbbr_sec_data_set_recv_cb);
|
|
|
|
// add features to secure commissioner port
|
|
int8_t commissioner_service_id = thread_bbr_get_commissioner_service(this->interface_id);
|
|
coap_service_register_uri(commissioner_service_id, THREAD_URI_BBR_DATA_REQ, COAP_SERVICE_ACCESS_GET_ALLOWED, thread_pbbr_data_req_recv_cb);
|
|
coap_service_register_uri(commissioner_service_id, THREAD_URI_BBR_DATA_SET, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_pbbr_data_set_recv_cb);
|
|
coap_service_register_uri(commissioner_service_id, THREAD_URI_MGMT_SEC_PENDING_SET, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_pbbr_sec_data_set_recv_cb);
|
|
coap_service_register_uri(commissioner_service_id, THREAD_URI_BBR_MCAST_LISTENER_REPORT, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_bbr_commercial_mlr_cb);
|
|
return 0;
|
|
}
|
|
|
|
void thread_bbr_commercial_delete(int8_t interface_id)
|
|
{
|
|
thread_pbbr_t *this = thread_bbr_find_by_interface(interface_id);
|
|
if (!this) {
|
|
return;
|
|
}
|
|
thread_bbr_commercial_pbbr_stop(this);
|
|
int8_t commissioner_service_id = thread_bbr_get_commissioner_service(this->interface_id);
|
|
coap_service_unregister_uri(commissioner_service_id, THREAD_URI_BBR_DATA_REQ);
|
|
coap_service_unregister_uri(commissioner_service_id, THREAD_URI_BBR_DATA_SET);
|
|
coap_service_unregister_uri(commissioner_service_id, THREAD_URI_MGMT_SEC_PENDING_SET);
|
|
coap_service_unregister_uri(commissioner_service_id, THREAD_URI_BBR_MCAST_LISTENER_REPORT);
|
|
|
|
coap_service_delete(this->coap_service_id);
|
|
|
|
ns_list_remove(&pbbr_instance_list, this);
|
|
ns_dyn_mem_free(this);
|
|
}
|
|
|
|
bool thread_bbr_commercial_nd_query_process(protocol_interface_info_entry_t *cur, const uint8_t *target_addr, uint16_t rloc)
|
|
{
|
|
if (thread_version < THREAD_VERSION_1_2) {
|
|
return false;
|
|
}
|
|
thread_pbbr_t *this = thread_bbr_find_by_interface(cur->id);
|
|
if (!this) {
|
|
return false;
|
|
}
|
|
if (!this->pbbr_started) {
|
|
return false;
|
|
}
|
|
// if we do not have DUA addressing enabled
|
|
if (!bitsequal(this->domain_prefix, target_addr, 64)) {
|
|
return false;
|
|
}
|
|
|
|
// SEND BB_QRY
|
|
thread_border_router_bb_qry_send(this, target_addr, &rloc);
|
|
return true;
|
|
|
|
}
|
|
|
|
static void thread_bbr_commercial_dad_process(protocol_interface_info_entry_t *cur, thread_pbbr_t *this, uint32_t seconds)
|
|
{
|
|
if (!this) {
|
|
return;
|
|
}
|
|
ns_list_foreach_safe(duplicate_dua_tr_t, cur_dup_tr, &duplicate_dua_tr_list) {
|
|
if (cur_dup_tr->interface_id != cur->id) {
|
|
continue;
|
|
}
|
|
if (cur_dup_tr->ttl > seconds) {
|
|
cur_dup_tr->ttl -= seconds;
|
|
} else {
|
|
cur_dup_tr->dua_dad_repeat--;
|
|
// repeat dad for one more time
|
|
if (cur_dup_tr->dua_dad_repeat > 0) {
|
|
cur_dup_tr->ttl = THREAD_BBR_DUA_DAD_QUERY_TIMEOUT;
|
|
thread_border_router_bb_qry_send(this, cur_dup_tr->target_eid, NULL);
|
|
} else {
|
|
// dad completed
|
|
// send PRO_BB.ntf and delete dad entry
|
|
thread_border_router_bb_ans_send(this, this->pbbr_multicast_address, cur_dup_tr->target_eid, cur_dup_tr->ml_eid, 0, thread_joiner_application_network_name_get(cur->id), NULL);
|
|
thread_bbr_na_send(this->backbone_interface_id, cur_dup_tr->target_eid);
|
|
thread_border_router_dup_tr_delete(cur_dup_tr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void thread_bbr_commercial_route_update(protocol_interface_info_entry_t *cur)
|
|
{
|
|
// if we have DUA prefix in settings
|
|
// if we are pbbr delete dua on-mesh route
|
|
// if we are pbbr add dua route to backbone
|
|
// else
|
|
// Remove all DUA routes using type
|
|
uint8_t addr[16];
|
|
if (0 != thread_common_primary_bbr_get(cur, addr, NULL, NULL, NULL)) {
|
|
// BBR not present
|
|
return;
|
|
}
|
|
if (!addr_is_assigned_to_interface(cur, addr)) {
|
|
return;
|
|
}
|
|
|
|
thread_pbbr_t *this = thread_bbr_find_by_interface(cur->id);
|
|
if (!this) {
|
|
return;
|
|
}
|
|
|
|
// remove with info and add valid domain prefix again to back bone interface
|
|
ipv6_route_table_remove_info(this->backbone_interface_id, ROUTE_THREAD_BBR, NULL);
|
|
|
|
if (memcmp(this->domain_prefix, ADDR_UNSPECIFIED, 8) != 0) {
|
|
// add dua route to backbone, delete dua from on-mesh route
|
|
ipv6_route_add_with_info(this->domain_prefix, 64, this->backbone_interface_id, NULL, ROUTE_THREAD_BBR, NULL, 0, 0xffffffff, 0);
|
|
tr_info("Hosting pBBR for DUA prefix");
|
|
ipv6_route_delete(this->domain_prefix, 64, cur->id, NULL, ROUTE_THREAD);
|
|
}
|
|
}
|
|
|
|
void thread_bbr_commercial_seconds_timer(int8_t interface_id, uint32_t seconds)
|
|
{
|
|
thread_pbbr_t *this = thread_bbr_find_by_interface(interface_id);
|
|
if (!this) {
|
|
return;
|
|
}
|
|
if (thread_version < THREAD_VERSION_1_2) {
|
|
return;
|
|
}
|
|
if (this->backbone_interface_id < 0) {
|
|
// pbbr Not enabled
|
|
return;
|
|
}
|
|
|
|
protocol_interface_info_entry_t *cur;
|
|
cur = protocol_stack_interface_info_get_by_id(this->interface_id);
|
|
if (!cur) {
|
|
return;
|
|
}
|
|
if (!this->pbbr_started) {
|
|
// Check if I am valid to start as pBBR
|
|
if (thread_attach_ready(cur) != 0) {
|
|
//not yet attached
|
|
return;
|
|
}
|
|
// Check if there is already pbbr if present dont enable
|
|
if (0 == thread_common_primary_bbr_get(cur, NULL, NULL, NULL, NULL)) {
|
|
// Primary bbr present in network I am secondary
|
|
return;
|
|
}
|
|
if (!thread_am_router(cur)) {
|
|
// I am not router
|
|
return;
|
|
}
|
|
// Should we have backend routing check?
|
|
// If we are leader we should send immediately even without it?
|
|
//if (!thread_bbr_routing_enabled(cur)) {
|
|
// return;
|
|
// }
|
|
|
|
thread_bbr_commercial_pbbr_start(this);
|
|
} else {
|
|
if (thread_bbr_commercial_downgrade_to_secondary(cur)) {
|
|
tr_info("pbbr downgraded");
|
|
thread_bbr_commercial_pbbr_stop(this);
|
|
}
|
|
|
|
}
|
|
// Check secondary state if we need to drop
|
|
|
|
// Check pending dua registrations
|
|
thread_bbr_commercial_dad_process(cur, this, seconds);
|
|
|
|
if (this->dua_timer_ticks) {
|
|
if (this->dua_timer_ticks > seconds) {
|
|
this->dua_timer_ticks -= seconds;
|
|
} else {
|
|
//Clear commissioner session from the border router
|
|
tr_info("Refreshing Primary BBR information");
|
|
thread_bbr_commercial_pbbr_start(this);
|
|
}
|
|
}
|
|
// If domain prefix is changed in configuration we need to restart the pbbr
|
|
}
|
|
|
|
int thread_bbr_commercial_timeout_set(int8_t interface_id, uint32_t timeout_a, uint32_t timeout_b, uint32_t delay)
|
|
{
|
|
thread_pbbr_t *this = thread_bbr_find_by_interface(interface_id);
|
|
if (!this) {
|
|
return -1;
|
|
}
|
|
if (timeout_a) {
|
|
this->mlr_timeout = timeout_a;
|
|
}
|
|
if (timeout_b) {
|
|
this->dua_timeout = timeout_b;
|
|
}
|
|
if (delay) {
|
|
this->delay_timer = delay;
|
|
}
|
|
if (this->backbone_interface_id < 0 || !this->pbbr_started) {
|
|
// it is ok to change default values before the start of pbbr
|
|
return 0;
|
|
}
|
|
thread_bbr_commercial_pbbr_start(this);
|
|
return 0;
|
|
}
|
|
|
|
int thread_bbr_commercial_prefix_set(int8_t interface_id, uint8_t *prefix)
|
|
{
|
|
thread_pbbr_t *this = thread_bbr_find_by_interface(interface_id);
|
|
if (!this || !prefix) {
|
|
return -1;
|
|
}
|
|
thread_border_router_prefix_delete(this->interface_id, this->domain_prefix, 64);
|
|
memcpy(this->domain_prefix, prefix, 8);
|
|
if (!this->pbbr_started) {
|
|
return 0;
|
|
}
|
|
|
|
if (memcmp(this->domain_prefix, ADDR_UNSPECIFIED, 8) != 0) {
|
|
thread_border_router_info_t br_info = {0};
|
|
br_info.P_on_mesh = true;
|
|
br_info.stableData = true;
|
|
br_info.P_res1 = true;
|
|
if (thread_border_router_prefix_add(this->interface_id, this->domain_prefix, 64, &br_info)) {
|
|
tr_error("Adding domain prefix failed!");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
thread_border_router_publish(this->interface_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int thread_bbr_commercial_address_set(int8_t interface_id, const uint8_t *addr_ptr, uint16_t port)
|
|
{
|
|
|
|
thread_pbbr_t *this = thread_bbr_find_by_interface(interface_id);
|
|
|
|
if (!this) {
|
|
return -1;
|
|
}
|
|
if (addr_ptr) {
|
|
memcpy(this->tri_address, addr_ptr, 16);
|
|
}
|
|
if (port) {
|
|
this->tri_port = port;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int thread_bbr_commercial_sequence_number_set(int8_t interface_id, uint8_t seq_number)
|
|
{
|
|
thread_pbbr_t *this = thread_bbr_find_by_interface(interface_id);
|
|
if (!this) {
|
|
return -1;
|
|
}
|
|
if (seq_number) {
|
|
this->sequence_number = --seq_number;
|
|
}
|
|
if (this->backbone_interface_id < 0 || !this->pbbr_started) {
|
|
// it is ok to change seq number before the start of pbbr
|
|
return 0;
|
|
}
|
|
thread_bbr_commercial_pbbr_start(this);
|
|
return 0;
|
|
}
|
|
|
|
void thread_bbr_commercial_old_partition_data_clean(int8_t interface_id)
|
|
{
|
|
thread_pbbr_t *this = thread_bbr_find_by_interface(interface_id);
|
|
if (this) {
|
|
coap_service_request_delete_by_service_id(this->coap_service_id);
|
|
}
|
|
}
|
|
|
|
void thread_bbr_commercial_status_override_get(uint8_t *dua_status, uint8_t *dua_count, uint8_t *ba_failure_count)
|
|
{
|
|
if (*dua_status) {
|
|
*dua_status = dua_response_status;
|
|
}
|
|
if (*dua_count) {
|
|
*dua_count = dua_status_count;
|
|
}
|
|
if (*ba_failure_count) {
|
|
*ba_failure_count = ba_response_status_count;
|
|
}
|
|
}
|
|
|
|
void thread_bbr_commercial_status_override_set(uint8_t dua_status, uint8_t dua_count, uint8_t ba_failure_count)
|
|
{
|
|
dua_response_status = dua_status;
|
|
dua_status_count = dua_count;
|
|
ba_response_status_count = ba_failure_count;
|
|
}
|
|
|
|
void thread_bbr_commercial_mcast_fwd_check(int8_t interface_id, bool *multicast_fwd)
|
|
{
|
|
thread_pbbr_t *this = thread_bbr_find_by_interface(interface_id);
|
|
|
|
if (!this || !multicast_fwd) {
|
|
return;
|
|
}
|
|
|
|
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(this->interface_id);
|
|
if (!cur) {
|
|
return;
|
|
}
|
|
|
|
if (this->pbbr_started) {
|
|
//We are Primary BBR so we always forward multicast
|
|
*multicast_fwd = true;
|
|
return;
|
|
}
|
|
|
|
if (0 == thread_common_primary_bbr_get(cur, NULL, NULL, NULL, NULL)) {
|
|
// We are secondary BBR we newer forward
|
|
*multicast_fwd = false;
|
|
return;
|
|
}
|
|
// No modification made
|
|
}
|
|
|
|
#endif //HAVE_THREAD_BORDER_ROUTER && HAVE_THREAD_V2
|