/* * Copyright (c) 2014-2018, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "nsconfig.h" #include #include "ns_types.h" #include #include "eventOS_event.h" #include #include "ns_trace.h" #include "Core/include/ns_buffer.h" #include "common_functions.h" #include "randLIB.h" #include "thread_border_router_api.h" #include "thread_management_if.h" #include "NWK_INTERFACE/Include/protocol.h" #include "6LoWPAN/Thread/thread_config.h" #include "6LoWPAN/Thread/thread_common.h" #include "6LoWPAN/Thread/thread_extension_bbr.h" #include "6LoWPAN/Thread/thread_network_data_lib.h" #include "6LoWPAN/Thread/thread_network_data_storage.h" #include "6LoWPAN/Thread/thread_management_client.h" #include "6LoWPAN/Thread/thread_joiner_application.h" #include "6LoWPAN/Thread/thread_tmfcop_lib.h" #include "6LoWPAN/Thread/thread_border_router_api_internal.h" #include "6LoWPAN/Thread/thread_mdns.h" #include "6LoWPAN/Bootstraps/protocol_6lowpan.h" #include "6LoWPAN/MAC/mac_helper.h" #include "MLE/mle.h" #include "thread_meshcop_lib.h" #include "thread_network_data_lib.h" #include "coap_service_api.h" #define TRACE_GROUP "tBRa" #ifdef HAVE_THREAD_ROUTER /* * Structure containing IPv6 Router advertisement options for DNS configuration. */ typedef struct { uint16_t option_length; uint8_t *option_data; } dns_option_t; /* * Border router instance data. */ typedef struct { dns_option_t *dns_search_list_option; /* option_data encoded according to RFC6106, DNSSL */ dns_option_t *recursive_dns_server_option; /* option_data encoded according to RFC6106, RDNSS */ uint16_t nwk_data_resubmit_timer; /* network data resubmit timeout */ int8_t interface_id; int8_t coap_service_id; ns_list_link_t link; } thread_border_router_t; /* Neighbor discovery options according to RFC6106 (rfc4861) */ #define RFC6106_RECURSIVE_DNS_SERVER_OPTION 25 #define RFC6106_DNS_SEARCH_LIST_OPTION 31 static NS_LIST_DEFINE(border_router_instance_list, thread_border_router_t, link); static thread_border_router_t *thread_border_router_find_by_interface(int8_t interface_id) { thread_border_router_t *this = NULL; ns_list_foreach(thread_border_router_t, cur_br, &border_router_instance_list) { if (cur_br->interface_id == interface_id) { this = cur_br; break; } } return this; } static thread_border_router_t *thread_border_router_find_by_service(int8_t service_id) { thread_border_router_t *this = NULL; ns_list_foreach(thread_border_router_t, cur_br, &border_router_instance_list) { if (cur_br->coap_service_id == service_id) { this = cur_br; break; } } return this; } /* * Read Border Router TLV: DNS search list option. * DNSSL option data will be fetched from: * 1. User has provided static configuration by using method thread_border_router_dns_search_list_option_set. * 2. ICMP RA messages (if service is enabled and running in background). */ static uint8_t *thread_management_server_border_router_nd_dnssl_option_read(thread_border_router_t *this, uint16_t *resp_len) { if (this->dns_search_list_option) { *resp_len = this->dns_search_list_option->option_length; return (uint8_t *)&this->dns_search_list_option->option_data; } else { // TODO: Read DNSSL from stored ICMP RA messages. *resp_len = 0; return NULL; } } /* * Read Border Router TLV: Recursive DNS Server option. * RDNSS option data will be fetched from: * 1. User has provided static configuration by using method thread_border_router_recursive_dns_server_option_set. * 2. ICMP RA messages (if service is enabled and running in background). */ static uint8_t *thread_management_server_border_router_nd_rdnss_option_read(thread_border_router_t *this, uint16_t *resp_len) { if (this->recursive_dns_server_option) { *resp_len = this->recursive_dns_server_option->option_length; return (uint8_t *)&this->recursive_dns_server_option->option_data; } else { // TODO: Read RDNSS from stored ICMP RA messages. *resp_len = 0; return NULL; } } /** * Thread Neighbor discovery data request handler */ static int thread_border_router_neighbor_discovery_data_req_cb(int8_t service_id, uint8_t source_address[16], uint16_t source_port, sn_coap_hdr_s *request_ptr) { thread_border_router_t *this = thread_border_router_find_by_service(service_id); uint8_t *request_tlv_ptr; uint8_t *resp_payload_ptr; uint16_t options_len, rdnss_option_len, dnssl_option_len, payload_len; uint8_t options_data[4]; uint8_t *dns_server_tlv_ptr = NULL; uint8_t *dns_search_list_tlv_ptr = NULL; uint8_t *ptr; sn_coap_msg_code_e return_code = COAP_MSG_CODE_RESPONSE_CHANGED; (void) source_port; (void) source_address; tr_debug("neighbor data request"); if (!this) { return -1; } request_tlv_ptr = options_data; rdnss_option_len = dnssl_option_len = 0; options_len = thread_tmfcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_ND_OPTION, &request_tlv_ptr); while (options_len > 0) { switch (*request_tlv_ptr) { case RFC6106_RECURSIVE_DNS_SERVER_OPTION: dns_server_tlv_ptr = thread_management_server_border_router_nd_rdnss_option_read(this, &rdnss_option_len); break; case RFC6106_DNS_SEARCH_LIST_OPTION: dns_search_list_tlv_ptr = thread_management_server_border_router_nd_dnssl_option_read(this, &dnssl_option_len); break; } request_tlv_ptr++; options_len--; } payload_len = rdnss_option_len + dnssl_option_len; resp_payload_ptr = ptr = ns_dyn_mem_alloc(payload_len + 4); //reserve space also for type/length if (!resp_payload_ptr) { return_code = COAP_MSG_CODE_RESPONSE_INTERNAL_SERVER_ERROR; goto send_response; } *ptr++ = TMFCOP_TLV_ND_DATA; if (payload_len > 255) { *ptr++ = 0xff; ptr = common_write_16_bit(payload_len, ptr); } else { *ptr++ = payload_len; } if (dns_server_tlv_ptr) { memcpy(ptr, dns_server_tlv_ptr, rdnss_option_len); ptr += rdnss_option_len; } if (dns_search_list_tlv_ptr) { memcpy(ptr, dns_search_list_tlv_ptr, dnssl_option_len); ptr += dnssl_option_len; } send_response: coap_service_response_send(this->coap_service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, return_code, COAP_CT_OCTET_STREAM, resp_payload_ptr, ptr - resp_payload_ptr); ns_dyn_mem_free(resp_payload_ptr); return 0; } /* * Check that local prefix is available in received network data */ static bool thread_border_router_network_data_prefix_match(thread_network_data_cache_entry_t *network_data_ptr, thread_network_local_data_entry_t *local_prefix, uint16_t router_id) { ns_list_foreach(thread_network_data_prefix_cache_entry_t, nwk_prefix, &network_data_ptr->localPrefixList) { if (nwk_prefix->servicesPrefixLen != local_prefix->servicesPrefixLen) { continue; } if (!bitsequal(nwk_prefix->servicesPrefix, local_prefix->servicesPrefix, local_prefix->servicesPrefixLen)) { continue; } // check that prefix is hosted by this router if (!thread_nd_hosted_by_this_routerid(router_id, &nwk_prefix->routeList) && !thread_nd_hosted_by_this_routerid(router_id, &nwk_prefix->borderRouterList)) { return false; } return true; } return false; } /* * Check that local service is available in network data */ static bool thread_border_router_network_data_service_match(thread_network_data_cache_entry_t *network_data_ptr, thread_network_data_service_entry_t *local_service, uint16_t router_id) { thread_network_data_service_cache_entry_t *service_cache_entry = thread_network_data_service_entry_find(&network_data_ptr->service_list, local_service); if (service_cache_entry) { return thread_network_data_service_hosted_by_this_router_id(service_cache_entry, router_id); } return false; } static bool thread_border_router_local_network_data_prefix_match(thread_network_local_data_cache_entry_t *local_data, thread_network_data_prefix_cache_entry_t *prefix, uint16_t router_id) { bool instance_found = false; if (thread_nd_hosted_by_this_routerid(router_id, &prefix->routeList)) { instance_found = true; } if (thread_nd_hosted_by_this_routerid(router_id, &prefix->borderRouterList)) { instance_found = true; } if (!instance_found) { /* Router ID not in propagated network data; skip */ return true; } instance_found = false; ns_list_foreach(thread_network_local_data_entry_t, localPrefix, &local_data->prefix_list) { if (prefix->servicesPrefixLen != localPrefix->servicesPrefixLen) { continue; } if (bitsequal(prefix->servicesPrefix, localPrefix->servicesPrefix, localPrefix->servicesPrefixLen)) { /* Prefix exists in local data; return true */ instance_found = true; break; } } if (!instance_found) { tr_debug("Found missing prefix: %s", trace_array(prefix->servicesPrefix, 8)); return false; } return true; } static void thread_border_router_child_network_data_clean(protocol_interface_info_entry_t *cur, uint16_t child_id) { uint8_t addr16_buf[2]; common_write_16_bit(child_id, addr16_buf); if (mac_neighbor_table_address_discover(mac_neighbor_info(cur), addr16_buf, ADDR_802_15_4_SHORT)) { /* Child is available in mle, do nothing */ return; } // Child is not our child => network data contains data from lost children, remove it tr_debug("Remove nwk data from lost child: %04x", child_id); thread_management_client_network_data_unregister(cur->id, child_id); } static void thread_border_router_lost_children_nwk_data_validate(protocol_interface_info_entry_t *cur, uint16_t router_short_addr) { if (!thread_is_router_addr(router_short_addr)) { // not validating children nwk data return; } thread_network_data_cache_entry_t *network_data = &cur->thread_info->networkDataStorage; ns_list_foreach(thread_network_data_prefix_cache_entry_t, curLP, &network_data->localPrefixList) { /* Go throgh all routes */ ns_list_foreach(thread_network_server_data_entry_t, curRoute, &curLP->routeList) { if (thread_addr_is_child(router_short_addr, curRoute->routerID)) { // Router children found thread_border_router_child_network_data_clean(cur, curRoute->routerID); } } /* Go through all BR's */ ns_list_foreach(thread_network_server_data_entry_t, curBR, &curLP->borderRouterList) { if (thread_addr_is_child(router_short_addr, curBR->routerID)) { // Router children found thread_border_router_child_network_data_clean(cur, curBR->routerID); } } } /* Go throgh all services */ ns_list_foreach(thread_network_data_service_cache_entry_t, service, &network_data->service_list) { ns_list_foreach(thread_network_data_service_server_entry_t, server, &service->server_list) { if (thread_addr_is_child(router_short_addr, server->router_id)) { // Router children found thread_border_router_child_network_data_clean(cur, server->router_id); } } } } static bool thread_border_router_local_network_data_service_match(thread_network_local_data_cache_entry_t *local_data, thread_network_data_service_cache_entry_t *service, uint16_t router_id) { bool instance_found = false; ns_list_foreach(thread_network_data_service_server_entry_t, server, &service->server_list) { if (server->router_id == router_id) { instance_found = true; break; } } if (!instance_found) { /* Router ID not in propagated network data; skip */ return true; } ns_list_foreach(thread_network_data_service_entry_t, local_service, &local_data->service_list) { if (local_service->S_service_data_length != service->S_service_data_length) { continue; } if (memcmp(local_service->S_service_data, service->S_service_data, local_service->S_service_data_length) != 0) { continue; } if (local_service->S_enterprise_number != service->S_enterprise_number) { continue; } /* Service exists in local data; return true */ return true; } tr_debug("Found missing service: %s", trace_array(service->S_service_data, service->S_service_data_length)); return false; } /* * Check that local server data is available in network data */ static bool thread_border_router_local_srv_data_in_network_data_check(protocol_interface_info_entry_t *cur) { thread_network_data_cache_entry_t *network_data; thread_network_local_data_cache_entry_t *local_data; uint16_t router_id = cur->mac_parameters->mac_short_address; network_data = &cur->thread_info->networkDataStorage; local_data = &cur->thread_info->localServerDataBase; ns_list_foreach(thread_network_local_data_entry_t, localPrefix, &local_data->prefix_list) { // find matching prefix from network data if (thread_border_router_network_data_prefix_match(network_data, localPrefix, router_id) == false) { return false; } } ns_list_foreach(thread_network_data_service_entry_t, localService, &local_data->service_list) { // find matching service from network data if (thread_border_router_network_data_service_match(network_data, localService, router_id) == false) { return false; } } ns_list_foreach(thread_network_data_prefix_cache_entry_t, prefix, &network_data->localPrefixList) { if (thread_border_router_local_network_data_prefix_match(local_data, prefix, router_id) == false) { return false; } } ns_list_foreach(thread_network_data_service_cache_entry_t, service, &network_data->service_list) { if (thread_border_router_local_network_data_service_match(local_data, service, router_id) == false) { return false; } } thread_border_router_lost_children_nwk_data_validate(cur, router_id); return true; } #ifdef HAVE_THREAD_BORDER_ROUTER static int thread_border_router_recursive_dns_server_option_store(int8_t interface_id, uint8_t *recursive_dns_server_option, uint16_t recursive_dns_server_option_len) { thread_border_router_t *this = thread_border_router_find_by_interface(interface_id); if (!this) { return -1; } ns_dyn_mem_free(this->recursive_dns_server_option); if (recursive_dns_server_option) { this->recursive_dns_server_option = ns_dyn_mem_alloc(sizeof(dns_option_t) + recursive_dns_server_option_len); if (!this->recursive_dns_server_option) { return -2; } memcpy(&this->recursive_dns_server_option->option_data, recursive_dns_server_option, recursive_dns_server_option_len); this->recursive_dns_server_option->option_length = recursive_dns_server_option_len; } return 0; } #endif #ifdef HAVE_THREAD_BORDER_ROUTER static int thread_border_router_dns_search_list_option_store(int8_t interface_id, uint8_t *dns_search_list_option, uint16_t search_list_option_len) { thread_border_router_t *this = thread_border_router_find_by_interface(interface_id); if (!this) { return -1; } ns_dyn_mem_free(this->dns_search_list_option); if (dns_search_list_option) { this->dns_search_list_option = ns_dyn_mem_alloc(sizeof(dns_option_t) + search_list_option_len); if (!this->dns_search_list_option) { return -2; } memcpy(&this->dns_search_list_option->option_data, dns_search_list_option, search_list_option_len); this->dns_search_list_option->option_length = search_list_option_len; } return 0; } #endif int8_t thread_border_router_init(int8_t interface_id) { thread_border_router_t *this = thread_border_router_find_by_interface(interface_id); if (this) { return 0; } tr_debug("thread_border_router_init if=%d", interface_id); this = ns_dyn_mem_alloc(sizeof(thread_border_router_t)); if (!this) { return -2; } this->dns_search_list_option = NULL; this->recursive_dns_server_option = NULL; this->interface_id = interface_id; this->nwk_data_resubmit_timer = 0; this->coap_service_id = coap_service_initialize(this->interface_id, THREAD_MANAGEMENT_PORT, COAP_SERVICE_OPTIONS_NONE, NULL, NULL); if (this->coap_service_id < 0) { tr_warn("Thread border router coap init failed"); ns_dyn_mem_free(this); return -3; } // Register to Mesh CoAP URIs coap_service_register_uri(this->coap_service_id, THREAD_URI_NEIGHBOR_DISCOVERY_DATA_REQ, COAP_SERVICE_ACCESS_GET_ALLOWED, thread_border_router_neighbor_discovery_data_req_cb); ns_list_add_to_start(&border_router_instance_list, this); return 0; } void thread_border_router_delete(int8_t interface_id) { thread_border_router_t *this = thread_border_router_find_by_interface(interface_id); if (!this) { return; } coap_service_delete(this->coap_service_id); ns_list_remove(&border_router_instance_list, this); ns_dyn_mem_free(this->dns_search_list_option); ns_dyn_mem_free(this->recursive_dns_server_option); ns_dyn_mem_free(this); } void thread_border_router_seconds_timer(int8_t interface_id, uint32_t seconds) { thread_border_router_t *this = thread_border_router_find_by_interface(interface_id); if (!this) { return; } if (this->nwk_data_resubmit_timer) { if (this->nwk_data_resubmit_timer > seconds) { this->nwk_data_resubmit_timer -= seconds; } else { protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id); this->nwk_data_resubmit_timer = 0; if (cur) { if (!thread_border_router_local_srv_data_in_network_data_check(cur)) { tr_info("nwk data mismatch - resubmit"); thread_border_router_publish(cur->id); } } } } } void thread_border_router_resubmit_timer_set(int8_t interface_id, int16_t seconds) { thread_border_router_t *this = thread_border_router_find_by_interface(interface_id); if (!this) { return; } if (seconds >= 0) { this->nwk_data_resubmit_timer = seconds; } else { // re-init network data resubmit timer to default value this->nwk_data_resubmit_timer = THREAD_DATA_RESUBMIT_DELAY + randLIB_get_random_in_range(0, THREAD_DATA_RESUBMIT_DELAY / 10); } } void thread_border_router_network_data_appl_callback(protocol_interface_info_entry_t *cur) { if (cur->thread_info->network_data_tlv_cb) { uint16_t payload_len = thread_network_data_tlv_size(cur, true); uint8_t *payload_ptr = ns_dyn_mem_alloc(payload_len + 3); /* 3 => room is also needed for TLV ID and length */ if (payload_ptr) { thread_network_data_tlv_write(cur, payload_ptr, true); // Send Network data TLV to application without TLV ID and length cur->thread_info->network_data_tlv_cb(cur->thread_info->interface_id, payload_ptr + 2, payload_len); ns_dyn_mem_free(payload_ptr); } } } void thread_border_router_network_data_update_notify(protocol_interface_info_entry_t *cur) { thread_border_router_t *this = thread_border_router_find_by_interface(cur->thread_info->interface_id); if (!this) { return; } if (!thread_border_router_local_srv_data_in_network_data_check(cur)) { if (this->nwk_data_resubmit_timer == 0) { this->nwk_data_resubmit_timer = 5; tr_debug("Resubmitted timer trig"); } } else { tr_debug("All data registered"); this->nwk_data_resubmit_timer = 0; } thread_border_router_network_data_appl_callback(cur); } void thread_border_router_old_partition_data_clean(int8_t interface_id) { thread_border_router_t *this = thread_border_router_find_by_interface(interface_id); if (this) { coap_service_request_delete_by_service_id(this->coap_service_id); } thread_extension_bbr_old_partition_data_clean(interface_id); } #endif // HAVE_THREAD_ROUTER /*External APIs*/ int thread_border_router_prefix_add(int8_t interface_id, uint8_t *prefix_ptr, uint8_t prefix_len, thread_border_router_info_t *prefix_info_ptr) { #ifdef HAVE_THREAD_ROUTER protocol_interface_info_entry_t *cur; thread_prefix_tlv_t prefixTlv; thread_border_router_tlv_entry_t service; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur || !cur->thread_info || thread_attach_ready(cur) != 0) { return -1; } if (!prefix_info_ptr || !prefix_ptr) { return -2; } if (prefix_info_ptr->P_dhcp == true && prefix_info_ptr->P_slaac == true) { return -3;// Can not configure both services on } prefixTlv.domainId = 0; prefixTlv.Prefix = prefix_ptr; prefixTlv.PrefixLen = prefix_len; service.P_configure = prefix_info_ptr->P_configure; service.P_default_route = prefix_info_ptr->P_default_route; service.P_dhcp = prefix_info_ptr->P_dhcp; service.P_preferred = prefix_info_ptr->P_preferred; service.P_slaac = prefix_info_ptr->P_slaac; service.Prf = prefix_info_ptr->Prf; service.stableData = prefix_info_ptr->stableData; service.P_on_mesh = prefix_info_ptr->P_on_mesh; service.P_nd_dns = prefix_info_ptr->P_nd_dns; service.P_res1 = prefix_info_ptr->P_res1; return thread_local_server_list_add_on_mesh_server(&cur->thread_info->localServerDataBase, &prefixTlv, &service); #else (void) interface_id; (void) prefix_ptr; (void) prefix_len; (void) prefix_info_ptr; return -1; #endif } int thread_border_router_prefix_delete(int8_t interface_id, uint8_t *prefix_ptr, uint8_t prefix_len) { #ifdef HAVE_THREAD_ROUTER protocol_interface_info_entry_t *cur; thread_prefix_tlv_t prefixTlv; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur || !cur->thread_info || thread_attach_ready(cur) != 0) { return -1; } if (!prefix_ptr) { return -2; } prefixTlv.domainId = 0; prefixTlv.Prefix = prefix_ptr; prefixTlv.PrefixLen = prefix_len; return thread_local_server_list_del_on_mesh_server(&cur->thread_info->localServerDataBase, &prefixTlv); #else (void) interface_id; (void) prefix_ptr; (void) prefix_len; return -1; #endif } int thread_border_router_route_add(int8_t interface_id, uint8_t *prefix_ptr, uint8_t prefix_len, bool stable, int8_t prf) { #ifdef HAVE_THREAD_ROUTER thread_prefix_tlv_t prefixTlv; thread_border_router_tlv_entry_t route; protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return -1; } if (!cur->thread_info || thread_attach_ready(cur) != 0) { return -2; } prefixTlv.domainId = 0; prefixTlv.Prefix = prefix_ptr; prefixTlv.PrefixLen = prefix_len; memset(&route, 0, sizeof(thread_border_router_tlv_entry_t)); route.Prf = prf; route.stableData = stable; return thread_local_server_add_route(&cur->thread_info->localServerDataBase, &prefixTlv, &route); #else (void) interface_id; (void) prefix_ptr; (void) prefix_len; (void) stable; (void) prf; return -1; #endif } int thread_border_router_route_delete(int8_t interface_id, uint8_t *prefix_ptr, uint8_t prefix_len) { #ifdef HAVE_THREAD_ROUTER thread_prefix_tlv_t prefixTlv; protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return -1; } if (!cur->thread_info || thread_attach_ready(cur) != 0) { return -1; } prefixTlv.domainId = 0; prefixTlv.Prefix = prefix_ptr; prefixTlv.PrefixLen = prefix_len; return thread_local_server_del_route(&cur->thread_info->localServerDataBase, &prefixTlv); #else (void) interface_id; (void) prefix_ptr; (void) prefix_len; return -1; #endif } int thread_border_router_service_add(int8_t interface_id, uint8_t *service_data, uint8_t service_len, uint8_t sid, uint32_t enterprise_number, uint8_t *server_data, uint8_t server_data_len, bool stable) { #ifdef HAVE_THREAD_ROUTER protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur || !cur->thread_info || thread_attach_ready(cur) != 0) { return -1; } if (!service_data || !service_len) { return -2; } thread_network_data_service_entry_t service = { .T = false, .S_id = sid, .S_enterprise_number = enterprise_number, .S_service_data = service_data, .S_service_data_length = service_len, .S_server_data = server_data, .S_server_data_length = server_data_len, .S_stable = stable, }; if (enterprise_number == THREAD_ENTERPRISE_NUMBER) { service.T = true; } return thread_local_service_list_add(&cur->thread_info->localServerDataBase, &service); #else (void)interface_id; (void)service_data; (void)service_len; (void)sid; (void)enterprise_number; (void)server_data; (void)server_data_len; (void)stable; return -1; #endif } int thread_border_router_service_delete(int8_t interface_id, uint8_t *service_data, uint8_t service_len, uint32_t enterprise_number) { #ifdef HAVE_THREAD_ROUTER protocol_interface_info_entry_t *cur; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur || !cur->thread_info || thread_attach_ready(cur) != 0) { return -1; } if (!service_data || !service_len) { return -2; } thread_network_data_service_entry_t service = { .S_enterprise_number = enterprise_number, .S_service_data = service_data, .S_service_data_length = service_len, }; return thread_local_service_list_del(&cur->thread_info->localServerDataBase, &service); #else (void) interface_id; (void) service_data; (void) service_len; (void) enterprise_number; return -1; #endif } int thread_border_router_recursive_dns_server_option_set(int8_t interface_id, uint8_t *recursive_dns_server_option, uint16_t recursive_dns_server_option_len) { #ifdef HAVE_THREAD_BORDER_ROUTER return thread_border_router_recursive_dns_server_option_store(interface_id, recursive_dns_server_option, recursive_dns_server_option_len); #else (void)interface_id; (void)recursive_dns_server_option; (void)recursive_dns_server_option_len; return -1; #endif } int thread_border_router_dns_search_list_option_set(int8_t interface_id, uint8_t *dns_search_list_option, uint16_t search_list_option_len) { #ifdef HAVE_THREAD_BORDER_ROUTER return thread_border_router_dns_search_list_option_store(interface_id, dns_search_list_option, search_list_option_len); #else (void)interface_id; (void)dns_search_list_option; (void)search_list_option_len; return -1; #endif } /** Network data set response callback. * * callback to inform if network data was set to leader. * * /param status status of operation 0 success, -1 failure from leader received * /param data_ptr pointer to network data TLV that leader accepted. * /param data_len length of network data. * */ #ifdef HAVE_THREAD_ROUTER static void thread_tmf_client_network_data_set_cb(int8_t interface_id, int8_t status, uint8_t *data_ptr, uint16_t data_len) { protocol_interface_info_entry_t *cur; (void) data_len; (void) data_ptr; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return; } cur->thread_info->localServerDataBase.publish_coap_req_id = 0; tr_debug("BR a/sd response status: %s, addr: %x", status ? "Fail" : "OK", cur->thread_info->localServerDataBase.registered_rloc16); if (cur->thread_info->localServerDataBase.publish_pending) { cur->thread_info->localServerDataBase.publish_pending = false; thread_border_router_publish(cur->id); } if (status == 0) { // If request was successful, then update RLOC to new one. cur->thread_info->localServerDataBase.registered_rloc16 = mac_helper_mac16_address_get(cur); cur->thread_info->localServerDataBase.release_old_address = false; } } #endif int thread_border_router_publish(int8_t interface_id) { #ifdef HAVE_THREAD_ROUTER uint16_t network_data_len; uint8_t *payload_ptr; uint8_t *ptr; uint16_t rloc16; int ret_val; protocol_interface_info_entry_t *cur; tr_debug("Border router Publish Local Services"); cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return -1; } if (!cur->thread_info || thread_attach_ready(cur) != 0) { return -2; } rloc16 = mac_helper_mac16_address_get(cur); tr_debug("Border router old: %x, new: %x", cur->thread_info->localServerDataBase.registered_rloc16, rloc16); if (cur->thread_info->localServerDataBase.publish_coap_req_id) { if (rloc16 != cur->thread_info->localServerDataBase.registered_rloc16) { /* * Device short address has changed, cancel previous a/sd requests * and start resubmit timer * */ tr_debug("RLOC changed, kill pending a/sd request"); thread_management_client_coap_message_delete(cur->id, cur->thread_info->localServerDataBase.publish_coap_req_id); thread_border_router_resubmit_timer_set(interface_id, 5); } else { cur->thread_info->localServerDataBase.publish_pending = true; tr_debug("Activate pending status for publish"); } return 0; } //Allocate Memory for Data network_data_len = thread_nd_own_service_list_data_size(&cur->thread_info->localServerDataBase); // Room for RLOC16 Room for Network data TLV ptr = payload_ptr = ns_dyn_mem_temporary_alloc(network_data_len + 4 + 5); if (!ptr) { return -3; } ptr = thread_tmfcop_tlv_data_write_header(ptr, TMFCOP_TLV_NETWORK_DATA, network_data_len); ptr = thread_nd_own_service_list_data_write(&cur->thread_info->localServerDataBase, ptr, rloc16); if (cur->thread_info->localServerDataBase.registered_rloc16 != 0xffff && cur->thread_info->localServerDataBase.release_old_address && cur->thread_info->localServerDataBase.registered_rloc16 != rloc16) { // Our address has changed so we must register our network with new address and remove the old address tr_debug("BR address changed - remove old %x", cur->thread_info->localServerDataBase.registered_rloc16); ptr = thread_tmfcop_tlv_data_write_uint16(ptr, TMFCOP_TLV_RLOC16, cur->thread_info->localServerDataBase.registered_rloc16); } cur->thread_info->localServerDataBase.registered_rloc16 = rloc16; ret_val = thread_management_client_network_data_register(cur->id, payload_ptr, ptr - payload_ptr, thread_tmf_client_network_data_set_cb); if (payload_ptr) { ns_dyn_mem_free(payload_ptr); } if (ret_val > 0) { // a/sd request successful, save coap request id cur->thread_info->localServerDataBase.publish_coap_req_id = (uint16_t)ret_val; ret_val = 0 ; } thread_border_router_resubmit_timer_set(interface_id, -1); return ret_val; #else (void) interface_id; return -1; #endif } int thread_border_router_delete_all(int8_t interface_id) { #ifdef HAVE_THREAD_ROUTER protocol_interface_info_entry_t *cur; tr_debug("Border router Delete Local Service"); cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return -1; } if (!cur->thread_info || thread_attach_ready(cur) != 0) { return -2; } //Delete first Local List thread_network_local_data_free_and_clean(&cur->thread_info->localServerDataBase, interface_id); return 0; #else (void) interface_id; return -1; #endif } int thread_border_router_network_data_callback_register(int8_t interface_id, thread_network_data_tlv_cb *nwk_data_cb) { #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return -1; } if (!cur->thread_info || thread_attach_ready(cur) != 0) { return -2; } cur->thread_info->network_data_tlv_cb = nwk_data_cb; thread_border_router_network_data_appl_callback(cur); return 0; #else (void)interface_id; (void)nwk_data_cb; return -1; #endif } int thread_border_router_prefix_tlv_find(uint8_t *network_data_tlv, uint16_t network_data_tlv_length, uint8_t **prefix_tlv, bool *stable) { #ifdef HAVE_THREAD uint16_t tlv_length; if (!network_data_tlv || !network_data_tlv_length || !prefix_tlv) { return -1; } //tr_debug("thread_tlv_lib_prefix_find() len=%d, tlv=%s", network_data_tlv_length, trace_array(network_data_tlv, network_data_tlv_length)); *stable = true; tlv_length = thread_meshcop_tlv_find_next(network_data_tlv, network_data_tlv_length, THREAD_NWK_DATA_TYPE_PREFIX | THREAD_NWK_STABLE_DATA, prefix_tlv); if (tlv_length == 0) { tlv_length = thread_meshcop_tlv_find_next(network_data_tlv, network_data_tlv_length, THREAD_NWK_DATA_TYPE_PREFIX, prefix_tlv); *stable = false; } return tlv_length; #else (void)network_data_tlv; (void)network_data_tlv_length; (void)prefix_tlv; (void)stable; return -1; #endif } int thread_border_router_tlv_find(uint8_t *prefix_tlv, uint16_t prefix_tlv_length, uint8_t **border_router_tlv, bool *stable) { #ifdef HAVE_THREAD uint16_t tlv_length; if (!prefix_tlv || !prefix_tlv_length || !border_router_tlv) { return -1; } //tr_debug("thread_tlv_lib_border_router_find() len=%d, tlv=%s", prefix_tlv_length, trace_array(prefix_tlv, prefix_tlv_length)); uint8_t prefix_length = prefix_tlv[1]; uint8_t prefix_byte_len = prefixBits_to_bytes(prefix_length); prefix_tlv = prefix_tlv + 2 + prefix_byte_len; //2 = domain ID + prefix length prefix_tlv_length = prefix_tlv_length - prefix_byte_len - 2; // find stable prefix first and if not found return unstable data *stable = true; tlv_length = thread_meshcop_tlv_find_next(prefix_tlv, prefix_tlv_length, THREAD_NWK_DATA_TYPE_BORDER_ROUTER | THREAD_NWK_STABLE_DATA, border_router_tlv); if (tlv_length == 0) { tlv_length = thread_meshcop_tlv_find_next(prefix_tlv, prefix_tlv_length, THREAD_NWK_DATA_TYPE_BORDER_ROUTER, border_router_tlv); *stable = false; } return tlv_length; #else (void)prefix_tlv; (void)prefix_tlv_length; (void)border_router_tlv; (void)stable; return -1; #endif } int thread_border_router_prefix_context_id(uint8_t *prefix_tlv, uint16_t prefix_tlv_length) { #ifdef HAVE_THREAD if (!prefix_tlv || !prefix_tlv_length) { return -1; } uint16_t data_length = prefix_tlv_length; while (data_length) { uint8_t type = *prefix_tlv++; uint16_t len = *prefix_tlv++; data_length -= 2; type &= THREAD_NWK_DATA_TYPE_MASK; if (type == THREAD_NWK_DATA_TYPE_6LOWPAN_ID) { return (*prefix_tlv & 0x0f); } data_length -= len; prefix_tlv += len; } return -2; #else (void)prefix_tlv; (void)prefix_tlv_length; return -1; #endif } int thread_border_router_service_tlv_find(uint8_t *network_data_tlv, uint16_t network_data_tlv_length, uint8_t **service_tlv, bool *stable) { #ifdef HAVE_THREAD uint16_t tlv_length; if (!network_data_tlv || !network_data_tlv_length || !service_tlv) { return -1; } *stable = true; tlv_length = thread_meshcop_tlv_find_next(network_data_tlv, network_data_tlv_length, THREAD_NWK_DATA_TYPE_SERVICE_DATA | THREAD_NWK_STABLE_DATA, service_tlv); if (tlv_length == 0) { tlv_length = thread_meshcop_tlv_find_next(network_data_tlv, network_data_tlv_length, THREAD_NWK_DATA_TYPE_SERVICE_DATA, service_tlv); *stable = false; } return tlv_length; #else (void)network_data_tlv; (void)network_data_tlv_length; (void)service_tlv; (void)stable; return -1; #endif } int thread_border_router_server_tlv_find(uint8_t *service_tlv, uint16_t service_tlv_length, uint8_t **server_tlv, bool *stable) { #ifdef HAVE_THREAD uint16_t tlv_length; if (!service_tlv || !service_tlv_length || !server_tlv) { return -1; } uint8_t t_flag = service_tlv[0] >> 7; service_tlv += 1; if (!t_flag) { service_tlv_length -= 4; service_tlv += 4; } uint8_t service_data_len = *service_tlv; service_tlv += 1 + service_data_len; service_tlv_length = service_tlv_length - service_data_len - 2; *stable = true; tlv_length = thread_meshcop_tlv_find_next(service_tlv, service_tlv_length, THREAD_NWK_DATA_TYPE_SERVER_DATA | THREAD_NWK_STABLE_DATA, server_tlv); if (tlv_length == 0) { tlv_length = thread_meshcop_tlv_find_next(service_tlv, service_tlv_length, THREAD_NWK_DATA_TYPE_SERVER_DATA, server_tlv); *stable = false; } return tlv_length; #else (void)service_tlv; (void)service_tlv_length; (void)server_tlv; (void)stable; return -1; #endif } int thread_border_router_mdns_responder_start(int8_t interface_id, int8_t interface_id_mdns, const char *service_name) { #ifdef HAVE_THREAD_BORDER_ROUTER return thread_mdns_start(interface_id, interface_id_mdns, service_name); #else (void)interface_id; (void)interface_id_mdns; (void)service_name; return -1; #endif } int thread_border_router_mdns_responder_stop(void) { #ifdef HAVE_THREAD_BORDER_ROUTER return thread_mdns_stop(); #else return -1; #endif }