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

358 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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