mbed-os/features/nanostack/sal-stack-nanostack/source/6LoWPAN/Thread/thread_mdns.c

358 lines
12 KiB
C
Raw Normal View History

/*
* 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
}