mbed-os/connectivity/nanostack/sal-stack-nanostack/source/6LoWPAN/Thread/thread_bbr_commercial.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, &registrar_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