mirror of https://github.com/ARMmbed/mbed-os.git
358 lines
12 KiB
C
358 lines
12 KiB
C
|
/*
|
|||
|
* Copyright (c) 2017, Arm Limited and affiliates.
|
|||
|
* SPDX-License-Identifier: BSD-3-Clause
|
|||
|
*
|
|||
|
* Redistribution and use in source and binary forms, with or without
|
|||
|
* modification, are permitted provided that the following conditions are met:
|
|||
|
*
|
|||
|
* 1. Redistributions of source code must retain the above copyright
|
|||
|
* notice, this list of conditions and the following disclaimer.
|
|||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|||
|
* notice, this list of conditions and the following disclaimer in the
|
|||
|
* documentation and/or other materials provided with the distribution.
|
|||
|
* 3. Neither the name of the copyright holder nor the
|
|||
|
* names of its contributors may be used to endorse or promote products
|
|||
|
* derived from this software without specific prior written permission.
|
|||
|
*
|
|||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|||
|
* POSSIBILITY OF SUCH DAMAGE.
|
|||
|
*/
|
|||
|
|
|||
|
#include "nsconfig.h"
|
|||
|
|
|||
|
#ifdef HAVE_THREAD_BORDER_ROUTER
|
|||
|
|
|||
|
#include <string.h>
|
|||
|
#include "ns_types.h"
|
|||
|
#include <nsdynmemLIB.h>
|
|||
|
#include "ns_trace.h"
|
|||
|
#include "common_functions.h"
|
|||
|
#include "6LoWPAN/Thread/thread_config.h"
|
|||
|
#include "6LoWPAN/Thread/thread_common.h"
|
|||
|
#include "6LoWPAN/Thread/thread_joiner_application.h"
|
|||
|
#include "ns_sha256.h"
|
|||
|
#include "ns_mdns_api.h"
|
|||
|
#include "Service_Libs/utils/ns_crc.h"
|
|||
|
|
|||
|
#define TRACE_GROUP "tmDNS"
|
|||
|
|
|||
|
/*
|
|||
|
* mDNS data structure
|
|||
|
*
|
|||
|
*/
|
|||
|
typedef struct {
|
|||
|
ns_mdns_t server_instance;
|
|||
|
ns_mdns_service_t service_instance;
|
|||
|
ns_mdns_service_param_t service_params;
|
|||
|
uint8_t *txt_record_buffer;
|
|||
|
uint16_t txt_value_crc;
|
|||
|
uint8_t txt_record_buffer_len;
|
|||
|
int8_t interface_id;
|
|||
|
int8_t interface_id_mdns;
|
|||
|
} thread_mdns_t;
|
|||
|
|
|||
|
static thread_mdns_t thread_mdns = {
|
|||
|
.server_instance = NULL,
|
|||
|
.service_instance = NULL,
|
|||
|
.txt_record_buffer = NULL,
|
|||
|
.txt_value_crc = 0,
|
|||
|
.txt_record_buffer_len = 0,
|
|||
|
.interface_id = -1,
|
|||
|
.interface_id_mdns = -1,
|
|||
|
};
|
|||
|
|
|||
|
static thread_mdns_t *thread_mdns_ptr = &thread_mdns;
|
|||
|
|
|||
|
/* mDNS constants */
|
|||
|
#define THREAD_MDNS_TIME_TO_LIVE 4500 /* Time to Live in seconds */
|
|||
|
#define THREAD_MDNS_TIME_TO_LIVE_HOP_COUNT 120 /* Time to Live in hops */
|
|||
|
|
|||
|
/* mDNS TXT record State Bitmap */
|
|||
|
#define THREAD_MDNS_TXT_SB_CONNECTION_MODE_BM 0x07 /* Mask 0000 0111 */
|
|||
|
#define THREAD_MDNS_TXT_SB_INTERFACE_STATUS_BM 0x18 /* Mask 0001 1000 */
|
|||
|
#define THREAD_MDNS_TXT_SB_AVAILABILITY_HIGH 0x20 /* Value 0010 0000 */
|
|||
|
|
|||
|
static uint8_t *thread_mdns_txt_record_sb_get(int interface_id, uint8_t *buf, uint32_t *state_bm)
|
|||
|
{
|
|||
|
uint32_t state_bitmap = 0;
|
|||
|
uint8_t *ptr = buf;
|
|||
|
|
|||
|
const uint8_t sb_string[] = {7, 's', 'b', '='};
|
|||
|
link_configuration_s *link_configuration = thread_management_configuration_get(interface_id);
|
|||
|
|
|||
|
if (link_configuration) {
|
|||
|
if (link_configuration->securityPolicy & SECURITY_POLICY_EXTERNAL_COMMISSIONER_ALLOWED) {
|
|||
|
/* connection mode:
|
|||
|
* 1: DTLS connection to Border Agent allowed with a user chosen network Commissioner Credential and shared PSKc for the active Thread Network Partition
|
|||
|
* 2, DTLS connection to Border Agent allowed using the Border Agent Device Passphrase (PSKd) as the Commissioner Credential
|
|||
|
* */
|
|||
|
state_bitmap = 0x01;
|
|||
|
}
|
|||
|
|
|||
|
/* Thread Interface Status
|
|||
|
* 2, Thread interface is initialized with a set of valid operational parameters and is actively part of a Thread Network Partition
|
|||
|
* */
|
|||
|
state_bitmap |= 0x02 << 3;
|
|||
|
|
|||
|
/* Availability
|
|||
|
* 1: High availability – The Border Agent device and its Thread interface are part of stable, always-on network infrastructure
|
|||
|
* */
|
|||
|
state_bitmap |= THREAD_MDNS_TXT_SB_AVAILABILITY_HIGH;
|
|||
|
} else {
|
|||
|
/* connection mode:
|
|||
|
* -bits 0-2: 0=DTLS connection to Border Agent is not allowed
|
|||
|
*
|
|||
|
* Thread Interface Status
|
|||
|
* -bits 3-4: 0=Thread interface is not active and is not initialized with a set of valid operational network parameters
|
|||
|
*
|
|||
|
* Availability
|
|||
|
* -bits 5-6: 0= Infrequent availability - Thread interface may become inactive when the Border Agent device is not in use
|
|||
|
* */
|
|||
|
state_bitmap = 0x00;
|
|||
|
}
|
|||
|
|
|||
|
*state_bm = state_bitmap;
|
|||
|
|
|||
|
memcpy(ptr, sb_string, sizeof(sb_string));
|
|||
|
ptr += sizeof(sb_string);
|
|||
|
|
|||
|
ptr = common_write_32_bit(state_bitmap, ptr);
|
|||
|
|
|||
|
return ptr;
|
|||
|
}
|
|||
|
|
|||
|
static uint8_t *thread_mdns_txt_record_nn_key_fill(int8_t interface_id, uint8_t *buf, uint32_t state_bitmap)
|
|||
|
{
|
|||
|
uint8_t *ptr = buf;
|
|||
|
int i;
|
|||
|
uint8_t length;
|
|||
|
const char nn_string[4] = "nn=";
|
|||
|
|
|||
|
if (!(state_bitmap & THREAD_MDNS_TXT_SB_CONNECTION_MODE_BM)) {
|
|||
|
// if connection mode is not allowed, elide nn-key
|
|||
|
return ptr;
|
|||
|
}
|
|||
|
|
|||
|
ptr++; /* make room for length */
|
|||
|
length = sizeof(nn_string) - 1;
|
|||
|
memcpy(ptr, nn_string, length);
|
|||
|
ptr += length;
|
|||
|
|
|||
|
if (state_bitmap & THREAD_MDNS_TXT_SB_AVAILABILITY_HIGH) {
|
|||
|
link_configuration_s *link_configuration = thread_management_configuration_get(interface_id);
|
|||
|
if (!link_configuration) {
|
|||
|
return ptr;
|
|||
|
}
|
|||
|
|
|||
|
for (i = 0; i < 16 && link_configuration->name[i] != 0; i++) {
|
|||
|
*ptr++ = link_configuration->name[i];
|
|||
|
}
|
|||
|
|
|||
|
length += i; /* update length */
|
|||
|
} else {
|
|||
|
// Thread interface seldomly available, use border agent product or model name
|
|||
|
memcpy(ptr, THREAD_VENDOR_MODEL, sizeof(THREAD_VENDOR_MODEL) - 1);
|
|||
|
ptr += sizeof(THREAD_VENDOR_MODEL) - 1;
|
|||
|
length += sizeof(THREAD_VENDOR_MODEL) - 1;
|
|||
|
}
|
|||
|
|
|||
|
*buf = length; // update length
|
|||
|
|
|||
|
return ptr;
|
|||
|
}
|
|||
|
|
|||
|
static uint8_t *thread_mdns_txt_record_xp_key_fill(int8_t interface_id, uint8_t *buf, uint32_t state_bitmap)
|
|||
|
{
|
|||
|
uint8_t *ptr = buf;
|
|||
|
int length;
|
|||
|
const uint8_t xp_string[4] = { 11, 'x', 'p', '=' };
|
|||
|
|
|||
|
if (!(state_bitmap & THREAD_MDNS_TXT_SB_CONNECTION_MODE_BM)) {
|
|||
|
// if connection mode not allowed, skip xb-key
|
|||
|
return ptr;
|
|||
|
}
|
|||
|
|
|||
|
length = sizeof xp_string;
|
|||
|
memcpy(ptr, xp_string, length);
|
|||
|
ptr += length;
|
|||
|
|
|||
|
if (state_bitmap & THREAD_MDNS_TXT_SB_INTERFACE_STATUS_BM) {
|
|||
|
link_configuration_s *link_configuration = thread_management_configuration_get(interface_id);
|
|||
|
if (!link_configuration) {
|
|||
|
tr_error("Failed to read link configuration");
|
|||
|
return ptr;
|
|||
|
}
|
|||
|
memcpy(ptr, link_configuration->extented_pan_id, 8);
|
|||
|
} else {
|
|||
|
device_configuration_s *device_configuration = thread_management_device_configuration_get(interface_id);
|
|||
|
if (!device_configuration) {
|
|||
|
tr_error("Failed to read device configuration");
|
|||
|
return ptr;
|
|||
|
}
|
|||
|
ns_sha256_nbits(device_configuration->eui64, 8, ptr, 64);
|
|||
|
}
|
|||
|
|
|||
|
ptr += 8;
|
|||
|
|
|||
|
return ptr;
|
|||
|
}
|
|||
|
|
|||
|
static uint8_t *thread_mdns_txt_record_fill(int8_t interface_id, uint8_t *buf)
|
|||
|
{
|
|||
|
static const char mDNS_thread_version[9] = "tv=1.1.1";
|
|||
|
uint8_t *ptr = buf;
|
|||
|
uint32_t state_bitmap;
|
|||
|
|
|||
|
/* rv */
|
|||
|
*ptr++ = 0x04; /* rv length */
|
|||
|
memcpy(ptr, "rv=1", 4);
|
|||
|
ptr += 4;
|
|||
|
|
|||
|
/* tv */
|
|||
|
*ptr++ = sizeof(mDNS_thread_version) - 1; /* tv length */
|
|||
|
memcpy(ptr, mDNS_thread_version, sizeof(mDNS_thread_version) - 1);
|
|||
|
ptr += sizeof(mDNS_thread_version) - 1;
|
|||
|
|
|||
|
/* state bitmap */
|
|||
|
ptr = thread_mdns_txt_record_sb_get(interface_id, ptr, &state_bitmap);
|
|||
|
|
|||
|
tr_debug("state_bitmap %"PRIx32, state_bitmap);
|
|||
|
|
|||
|
/* nn-key */
|
|||
|
ptr = thread_mdns_txt_record_nn_key_fill(interface_id, ptr, state_bitmap);
|
|||
|
|
|||
|
/* xp-key */
|
|||
|
ptr = thread_mdns_txt_record_xp_key_fill(interface_id, ptr, state_bitmap);
|
|||
|
|
|||
|
*ptr++ = 0;
|
|||
|
|
|||
|
return ptr;
|
|||
|
}
|
|||
|
|
|||
|
/* callback from mDNS to fill TXT record */
|
|||
|
static const uint8_t *thread_mdns_txt_record_callback(void)
|
|||
|
{
|
|||
|
uint8_t buffer[200]; /* 200 bytes enough space for TXT record */
|
|||
|
uint8_t *ptr;
|
|||
|
size_t new_record_length;
|
|||
|
|
|||
|
ptr = thread_mdns_txt_record_fill(thread_mdns_ptr->interface_id, buffer);
|
|||
|
|
|||
|
new_record_length = ptr - buffer;
|
|||
|
|
|||
|
if (thread_mdns_ptr->txt_record_buffer_len < new_record_length) {
|
|||
|
// not enough space in current buffer
|
|||
|
ns_dyn_mem_free(thread_mdns_ptr->txt_record_buffer);
|
|||
|
thread_mdns_ptr->txt_record_buffer = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (thread_mdns_ptr->txt_record_buffer == NULL) {
|
|||
|
thread_mdns_ptr->txt_record_buffer = ns_dyn_mem_alloc(new_record_length);
|
|||
|
}
|
|||
|
|
|||
|
if (!thread_mdns_ptr->txt_record_buffer) {
|
|||
|
tr_error("mDNS record not allocated");
|
|||
|
return (uint8_t *)'\0';
|
|||
|
}
|
|||
|
|
|||
|
memcpy(thread_mdns_ptr->txt_record_buffer, buffer, new_record_length);
|
|||
|
thread_mdns_ptr->txt_record_buffer_len = new_record_length;
|
|||
|
|
|||
|
tr_debug("mDNS TXT record created, %d bytes", thread_mdns_ptr->txt_record_buffer_len);
|
|||
|
|
|||
|
return thread_mdns_ptr->txt_record_buffer;
|
|||
|
}
|
|||
|
|
|||
|
static uint16_t thread_mdns_txt_values_crc_get(void)
|
|||
|
{
|
|||
|
link_configuration_s *link_configuration;
|
|||
|
uint16_t txt_values_crc = 0;
|
|||
|
|
|||
|
if (!thread_mdns_ptr->server_instance) {
|
|||
|
// mdns not active
|
|||
|
return txt_values_crc;
|
|||
|
}
|
|||
|
|
|||
|
link_configuration = thread_joiner_application_get_config(thread_mdns_ptr->interface_id);
|
|||
|
if (link_configuration) {
|
|||
|
// calculate CRC for values in mDNS TXT record that may change
|
|||
|
txt_values_crc = crc16_ccitt(link_configuration->name, 16);
|
|||
|
txt_values_crc |= crc16_ccitt(link_configuration->extented_pan_id, 8);
|
|||
|
txt_values_crc |= crc16_ccitt(&link_configuration->securityPolicy, 1);
|
|||
|
}
|
|||
|
|
|||
|
return txt_values_crc;
|
|||
|
}
|
|||
|
|
|||
|
int thread_mdns_stop(void)
|
|||
|
{
|
|||
|
if (thread_mdns_ptr->server_instance == NULL) {
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
ns_mdns_server_stop(thread_mdns_ptr->server_instance);
|
|||
|
|
|||
|
thread_mdns_ptr->server_instance = NULL;
|
|||
|
ns_dyn_mem_free(thread_mdns_ptr->txt_record_buffer);
|
|||
|
thread_mdns_ptr->txt_record_buffer = NULL;
|
|||
|
thread_mdns_ptr->txt_record_buffer_len = 0;
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
int thread_mdns_start(int8_t interface_id, int8_t interface_id_mdns, const char *service_name)
|
|||
|
{
|
|||
|
static const char *mDNS_service_type = "_meshcop._udp";
|
|||
|
|
|||
|
if (thread_mdns_ptr->server_instance != NULL) {
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
thread_mdns_ptr->server_instance = ns_mdns_server_start(service_name, THREAD_MDNS_TIME_TO_LIVE, THREAD_MDNS_TIME_TO_LIVE_HOP_COUNT, interface_id_mdns);
|
|||
|
if (thread_mdns_ptr->server_instance == NULL) {
|
|||
|
tr_error("mDNS server initialization failed");
|
|||
|
return -2;
|
|||
|
}
|
|||
|
|
|||
|
thread_mdns_ptr->interface_id = interface_id;
|
|||
|
thread_mdns_ptr->interface_id_mdns = interface_id_mdns;
|
|||
|
thread_mdns_ptr->service_params.service_type = mDNS_service_type;
|
|||
|
thread_mdns_ptr->service_params.service_port = THREAD_COMMISSIONING_PORT;
|
|||
|
thread_mdns_ptr->service_params.service_get_txt = thread_mdns_txt_record_callback;
|
|||
|
thread_mdns_ptr->service_instance = ns_mdns_service_register(thread_mdns_ptr->server_instance, &thread_mdns_ptr->service_params);
|
|||
|
thread_mdns_ptr->txt_value_crc = thread_mdns_txt_values_crc_get();
|
|||
|
|
|||
|
if (thread_mdns_ptr->service_instance == NULL) {
|
|||
|
tr_error("mDNS service init failed");
|
|||
|
thread_mdns_stop();
|
|||
|
return -3;
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
#endif //HAVE_THREAD_BORDER_ROUTER
|
|||
|
|
|||
|
void thread_mdns_network_data_update_notify(void)
|
|||
|
{
|
|||
|
#ifdef HAVE_THREAD_BORDER_ROUTER
|
|||
|
uint16_t txt_value_crc;
|
|||
|
txt_value_crc = thread_mdns_txt_values_crc_get();
|
|||
|
if (txt_value_crc != thread_mdns_ptr->txt_value_crc) {
|
|||
|
// Values for TXT-record have changed, send mDNS announcement
|
|||
|
thread_mdns_ptr->txt_value_crc = txt_value_crc;
|
|||
|
ns_mdns_announcement_send(thread_mdns_ptr->server_instance);
|
|||
|
}
|
|||
|
#endif //HAVE_THREAD_BORDER_ROUTER
|
|||
|
}
|
|||
|
|