mirror of https://github.com/ARMmbed/mbed-os.git
2501 lines
87 KiB
C
Executable File
2501 lines
87 KiB
C
Executable File
/*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* \file thread_network_data_storage.c
|
|
* \brief Add short description about this file!!!
|
|
*
|
|
*/
|
|
|
|
#include "nsconfig.h"
|
|
#ifdef HAVE_THREAD
|
|
#include <string.h>
|
|
#include <ns_types.h>
|
|
#include <nsdynmemLIB.h>
|
|
#include "NWK_INTERFACE/Include/protocol.h"
|
|
#include "common_functions.h"
|
|
#include "ns_trace.h"
|
|
#include "ip6string.h"
|
|
#include "6LoWPAN/Thread/thread_common.h"
|
|
#include "6LoWPAN/Thread/thread_config.h"
|
|
#include "6LoWPAN/Thread/thread_extension.h"
|
|
#include "6LoWPAN/Thread/thread_joiner_application.h"
|
|
#include "6LoWPAN/Thread/thread_network_data_lib.h"
|
|
#include "6LoWPAN/Thread/thread_network_data_storage.h"
|
|
#include "DHCPv6_client/dhcpv6_client_api.h"
|
|
#include "6LoWPAN/MAC/mac_helper.h"
|
|
#include "thread_management_if.h"
|
|
#include "thread_meshcop_lib.h"
|
|
#include "libDHCPv6/libDHCPv6_server.h"
|
|
#include "DHCPv6_Server/DHCPv6_server_service.h"
|
|
|
|
#define TRACE_GROUP "thrd"
|
|
|
|
|
|
static void network_server_print(const thread_network_server_data_entry_t *route)
|
|
{
|
|
#ifndef HAVE_DEBUG
|
|
(void) route;
|
|
#endif
|
|
|
|
tr_info("%s Info: routerId:%04x",
|
|
(route->stableData ? "Stable" : "Temporary"),
|
|
route->routerID);
|
|
}
|
|
|
|
//@TODO seems that this function is called from application,
|
|
// is there better way than print data inside stack ?
|
|
void thread_nd_network_data_print(thread_network_data_cache_entry_t *networkData, uint16_t routerId)
|
|
{
|
|
// Route prefix is variable-length, so need to zero pad for ip6tos
|
|
uint8_t addr[16];
|
|
tr_info("Network Data:");
|
|
|
|
ns_list_foreach(thread_network_data_prefix_cache_entry_t, cur, &networkData->localPrefixList) {
|
|
memset(addr, 0, 16);
|
|
bitcopy(addr, cur->servicesPrefix, cur->servicesPrefixLen);
|
|
tr_info("Prefix: %40s/%-3u", trace_ipv6(addr), cur->servicesPrefixLen);
|
|
ns_list_foreach(thread_network_server_data_entry_t, curRoute, &cur->routeList) {
|
|
tr_info("Has Route:");
|
|
if (curRoute->routerID != routerId) {
|
|
network_server_print(curRoute);
|
|
} else {
|
|
tr_info("This Interface");
|
|
}
|
|
}
|
|
|
|
ns_list_foreach(thread_network_server_data_entry_t, curRoute, &cur->borderRouterList) {
|
|
if (curRoute->P_dhcp) {
|
|
tr_info("DHCPv6:");
|
|
}
|
|
if (curRoute->P_slaac || curRoute->P_preferred) {
|
|
tr_info("SLAAC:");
|
|
}
|
|
if (curRoute->routerID != routerId) {
|
|
network_server_print(curRoute);
|
|
} else {
|
|
tr_info("I'm Hosting");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8_t thread_nd_server_external_route_list_size(thread_network_server_data_list_t *list, bool stable)
|
|
{
|
|
|
|
uint8_t listSize = 0;
|
|
|
|
ns_list_foreach(thread_network_server_data_entry_t, cur, list) {
|
|
if (cur->stableData == stable) {
|
|
listSize += 3;
|
|
}
|
|
}
|
|
|
|
if (listSize) {
|
|
listSize += 2;
|
|
}
|
|
|
|
return listSize;
|
|
}
|
|
|
|
uint8_t thread_nd_border_routerserver_list_size(thread_network_server_data_list_t *list, bool stable)
|
|
{
|
|
uint8_t listSize = 0;
|
|
|
|
ns_list_foreach(thread_network_server_data_entry_t, cur, list) {
|
|
if (cur->stableData == stable) {
|
|
listSize += 4;
|
|
}
|
|
}
|
|
if (listSize) {
|
|
listSize += 2;
|
|
}
|
|
|
|
return listSize;
|
|
}
|
|
|
|
uint8_t thread_nd_context_list_size(thread_data_context_list_t *list, bool stable)
|
|
{
|
|
uint8_t listSize = 0;
|
|
|
|
ns_list_foreach(thread_network_data_context_entry_t, cur, list) {
|
|
if (cur->stableData == stable) {
|
|
listSize += 2;
|
|
}
|
|
}
|
|
|
|
if (listSize) {
|
|
listSize += 2;
|
|
}
|
|
return listSize;
|
|
}
|
|
|
|
uint8_t *thread_nd_context_list_write(thread_data_context_list_t *list, uint8_t *dataPtr, bool stableData)
|
|
{
|
|
uint8_t tlv_size = 0;
|
|
uint8_t *ptr = (dataPtr + 2); //Leave allways Type & Length free first
|
|
|
|
ns_list_foreach(thread_network_data_context_entry_t, cur, list) {
|
|
if (cur->stableData == stableData) {
|
|
tlv_size += 2;
|
|
*ptr++ = cur->cid | (cur->compression ? THREAD_NWK_CONTEXT_COMPRESS_ENABLED : 0);
|
|
*ptr++ = cur->contextPrefixLength;
|
|
}
|
|
}
|
|
if (tlv_size) {
|
|
uint8_t type = THREAD_NWK_DATA_TYPE_6LOWPAN_ID;
|
|
if (stableData) {
|
|
type |= THREAD_NWK_STABLE_DATA;
|
|
}
|
|
|
|
*dataPtr++ = type;
|
|
*dataPtr = tlv_size;
|
|
//SET Pointer back to end of data
|
|
dataPtr = ptr;
|
|
}
|
|
|
|
return dataPtr;
|
|
}
|
|
|
|
static uint16_t thread_nd_service_border_router_flags_read(thread_network_server_data_entry_t *cur)
|
|
{
|
|
uint16_t flags = (cur->Prf << THREAD_PRF_BIT_MOVE);
|
|
flags |= (cur->P_preferred << THREAD_P_PREFERRED_BIT_MOVE);
|
|
flags |= (cur->P_slaac << THREAD_P_SLAAC_BIT_MOVE);
|
|
flags |= (cur->P_dhcp << THREAD_P_DHCP_BIT_MOVE);
|
|
flags |= (cur->P_configure << THREAD_P_CONFIGURE_BIT_MOVE);
|
|
flags |= (cur->P_default_route << THREAD_P_DEF_ROUTE_BIT_MOVE);
|
|
flags |= (cur->P_on_mesh << THREAD_P_ON_MESH_BIT_MOVE);
|
|
flags |= (cur->P_nd_dns << THREAD_P_ND_DNS_BIT_MOVE);
|
|
flags |= (cur->P_res1 << THREAD_P_ND_RES_BIT_MOVE);
|
|
return flags;
|
|
}
|
|
|
|
uint8_t *thread_nd_server_list_write(thread_network_server_data_list_t *list, uint8_t *dataPtr, uint8_t type, bool stable)
|
|
{
|
|
uint8_t tlv_size = 0, tlv_length;
|
|
uint16_t flags;
|
|
uint8_t *ptr = (dataPtr + 2); //Leave allways Type & Length free first
|
|
uint16_t routerId;
|
|
|
|
if (type == THREAD_NWK_DATA_TYPE_BORDER_ROUTER) {
|
|
tlv_length = THREAD_BORDER_ROUTER_TLV_LENGTH;
|
|
} else {
|
|
tlv_length = THREAD_HAS_ROUTE_TLV_LENGTH;
|
|
}
|
|
|
|
ns_list_foreach(thread_network_server_data_entry_t, cur, list) {
|
|
if (cur->stableData == stable) {
|
|
tlv_size += tlv_length;
|
|
routerId = cur->routerID;
|
|
if (type == THREAD_NWK_DATA_TYPE_BORDER_ROUTER) {
|
|
flags = thread_nd_service_border_router_flags_read(cur);
|
|
ptr = thread_nd_network_data_border_router_tlv_write(ptr, routerId, flags);
|
|
} else {
|
|
uint8_t pref = (cur->Prf << THREAD_HAS_ROUTE_PRF_BIT_MOVE);
|
|
ptr = thread_nd_network_data_has_route_tlv_write(ptr, routerId, pref);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tlv_size) {
|
|
if (stable) {
|
|
type |= THREAD_NWK_STABLE_DATA;
|
|
}
|
|
*dataPtr++ = type;
|
|
*dataPtr = tlv_size;
|
|
//SET Pointer back to end of data
|
|
dataPtr = ptr;
|
|
}
|
|
|
|
return dataPtr;
|
|
}
|
|
|
|
uint8_t *thread_nd_service_server_list_write(thread_network_data_service_server_list_t *list, uint8_t *ptr, bool full_list)
|
|
{
|
|
ns_list_foreach(thread_network_data_service_server_entry_t, cur, list) {
|
|
if (!full_list && !cur->stable) {
|
|
continue;
|
|
}
|
|
|
|
if (cur->stable) {
|
|
*ptr++ = THREAD_NWK_DATA_TYPE_SERVER_DATA | THREAD_NWK_STABLE_DATA;
|
|
} else {
|
|
*ptr++ = THREAD_NWK_DATA_TYPE_SERVER_DATA;
|
|
}
|
|
|
|
*ptr++ = 2 + cur->server_data_length;
|
|
ptr = common_write_16_bit(cur->router_id, ptr);
|
|
|
|
memcpy(ptr, cur->server_data, cur->server_data_length);
|
|
ptr += cur->server_data_length;
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
uint8_t thread_nd_service_based_on_list_entry_size(thread_network_data_service_cache_entry_t *entry, bool fullList)
|
|
{
|
|
uint8_t tempTlvSize = 0;
|
|
|
|
ns_list_foreach(thread_network_data_service_server_entry_t, cur, &entry->server_list) {
|
|
if (!fullList && !cur->stable) {
|
|
continue;
|
|
}
|
|
tempTlvSize += 4; // Type, Length, and S_server_16
|
|
tempTlvSize += cur->server_data_length;
|
|
}
|
|
|
|
if (tempTlvSize) {
|
|
tempTlvSize += 2; // T + S_id + service data length
|
|
|
|
if (!entry->T) {
|
|
tempTlvSize += 4;
|
|
}
|
|
|
|
tempTlvSize += entry->S_service_data_length;
|
|
}
|
|
|
|
return tempTlvSize;
|
|
}
|
|
|
|
uint8_t thread_nd_prefix_based_on_list_entry_size(thread_network_data_prefix_cache_entry_t *entry, bool fullList)
|
|
{
|
|
uint8_t tempTlvSize = 0;
|
|
|
|
tempTlvSize += thread_nd_border_routerserver_list_size(&entry->borderRouterList, true);
|
|
tempTlvSize += thread_nd_server_external_route_list_size(&entry->routeList, true);
|
|
tempTlvSize += thread_nd_context_list_size(&entry->contextList, true);
|
|
if (fullList) {
|
|
tempTlvSize += thread_nd_border_routerserver_list_size(&entry->borderRouterList, false);
|
|
tempTlvSize += thread_nd_server_external_route_list_size(&entry->routeList, false);
|
|
tempTlvSize += thread_nd_context_list_size(&entry->contextList, false);
|
|
}
|
|
|
|
if (tempTlvSize) {
|
|
tempTlvSize += prefixBits_to_bytes(entry->servicesPrefixLen);
|
|
tempTlvSize += 2; //Domain ID & Length
|
|
}
|
|
return tempTlvSize;
|
|
}
|
|
|
|
int thread_nd_local_data_length_updated(thread_network_data_cache_entry_t *networkDataList)
|
|
{
|
|
int ret_val = -1;
|
|
uint16_t localDataLength = 0;
|
|
localDataLength = thread_network_data_prefix_set_size(networkDataList, true)
|
|
+ thread_network_data_service_set_size(networkDataList, true);
|
|
|
|
if (localDataLength != networkDataList->networkDataTlvSize) {
|
|
networkDataList->networkDataTlvSize = localDataLength;
|
|
ret_val = 0;
|
|
}
|
|
return ret_val;
|
|
}
|
|
|
|
|
|
thread_network_local_data_entry_t *thread_local_service_list_allocate(thread_prefix_tlv_t *prefixTlv)
|
|
{
|
|
thread_network_local_data_entry_t *newEntry = ns_dyn_mem_alloc(sizeof(thread_network_local_data_entry_t));
|
|
if (newEntry) {
|
|
memset(newEntry->servicesPrefix, 0, 16);
|
|
bitcopy(newEntry->servicesPrefix, prefixTlv->Prefix, prefixTlv->PrefixLen);
|
|
newEntry->servicesPrefixLen = prefixTlv->PrefixLen;
|
|
newEntry->domainId = prefixTlv->domainId;
|
|
newEntry->dhcpv6ServerActive = false;
|
|
newEntry->brDataStable = false;
|
|
newEntry->slaacServerActive = false;
|
|
newEntry->slaacPreferred = false;
|
|
newEntry->routeActive = false;
|
|
newEntry->routeDataStable = false;
|
|
newEntry->preference = 0;
|
|
newEntry->configure = false;
|
|
newEntry->defaultRoute = false;
|
|
newEntry->onMesh = false;
|
|
newEntry->ndDns = false;
|
|
newEntry->brActive = false;
|
|
newEntry->res1 = false;
|
|
}
|
|
return newEntry;
|
|
}
|
|
|
|
thread_network_data_prefix_cache_entry_t *thread_prefix_service_list_allocate(thread_prefix_tlv_t *prefixTlv)
|
|
{
|
|
thread_network_data_prefix_cache_entry_t *newEntry = ns_dyn_mem_alloc(sizeof(thread_network_data_prefix_cache_entry_t));
|
|
if (newEntry) {
|
|
memset(newEntry->servicesPrefix, 0, 16);
|
|
bitcopy(newEntry->servicesPrefix, prefixTlv->Prefix, prefixTlv->PrefixLen);
|
|
newEntry->servicesPrefixLen = prefixTlv->PrefixLen;
|
|
newEntry->domainId = prefixTlv->domainId;
|
|
ns_list_init(&newEntry->contextList);
|
|
ns_list_init(&newEntry->borderRouterList);
|
|
ns_list_init(&newEntry->routeList);
|
|
}
|
|
return newEntry;
|
|
}
|
|
|
|
thread_network_server_data_entry_t *thread_server_entry_allocate(thread_border_router_tlv_entry_t *service)
|
|
{
|
|
thread_network_server_data_entry_t *newEntry = ns_dyn_mem_alloc(sizeof(thread_network_server_data_entry_t));
|
|
if (newEntry) {
|
|
tr_debug("Create new server entry addr: %04x", service->routerID);
|
|
newEntry->routerID = service->routerID;
|
|
newEntry->stableData = service->stableData;
|
|
newEntry->P_configure = service->P_configure;
|
|
newEntry->P_default_route = service->P_default_route;
|
|
newEntry->P_dhcp = service->P_dhcp;
|
|
newEntry->P_preferred = service->P_preferred;
|
|
newEntry->P_slaac = service->P_slaac;
|
|
newEntry->Prf = service->Prf;
|
|
newEntry->P_on_mesh = service->P_on_mesh;
|
|
newEntry->P_nd_dns = service->P_nd_dns;
|
|
newEntry->P_res1 = service->P_res1;
|
|
newEntry->canDelete = false;
|
|
|
|
}
|
|
return newEntry;
|
|
}
|
|
|
|
thread_network_data_context_entry_t *thread_context_service_list_allocate(uint8_t contextLength, uint8_t cid, bool compersioEnabled)
|
|
{
|
|
thread_network_data_context_entry_t *newEntry = ns_dyn_mem_alloc(sizeof(thread_network_data_context_entry_t));
|
|
if (newEntry) {
|
|
newEntry->cid = cid;
|
|
newEntry->compression = compersioEnabled;
|
|
newEntry->contextPrefixLength = contextLength;
|
|
newEntry->canDelete = false;
|
|
newEntry->stableData = false;
|
|
}
|
|
return newEntry;
|
|
}
|
|
|
|
int thread_nd_verify_contex_id_is_free(thread_network_data_cache_entry_t *list, uint8_t *prefixPtr, thread_network_local_data_context_entry_t *context)
|
|
{
|
|
int retVal = 0;
|
|
ns_list_foreach(thread_network_data_prefix_cache_entry_t, cur, &list->localPrefixList) {
|
|
|
|
ns_list_foreach(thread_network_data_context_entry_t, curContetx, &cur->contextList) {
|
|
if (curContetx->cid == context->cid) {
|
|
if (context->contextPrefixLength == cur->servicesPrefixLen) {
|
|
if (!bitsequal(cur->servicesPrefix, prefixPtr, context->contextPrefixLength)) {
|
|
retVal = -1; //Do not accept same context anymore again if prefix is different
|
|
}
|
|
} else {
|
|
retVal = -1;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
}
|
|
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
thread_network_data_service_cache_entry_t *thread_network_data_service_entry_find(thread_network_data_service_cache_list_t *list, thread_network_data_service_entry_t *service)
|
|
{
|
|
ns_list_foreach(thread_network_data_service_cache_entry_t, cur, list) {
|
|
if (service->S_enterprise_number == cur->S_enterprise_number) {
|
|
if (service->S_service_data_length == cur->S_service_data_length) {
|
|
if (memcmp(service->S_service_data, cur->S_service_data, service->S_service_data_length) == 0) {
|
|
return cur;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
thread_network_data_service_entry_t *thread_local_service_entry_find(thread_network_data_service_list_t *list, thread_network_data_service_entry_t *service)
|
|
{
|
|
ns_list_foreach(thread_network_data_service_entry_t, cur, list) {
|
|
if (service->S_enterprise_number == cur->S_enterprise_number) {
|
|
if (service->S_service_data_length == cur->S_service_data_length) {
|
|
if (memcmp(service->S_service_data, cur->S_service_data, service->S_service_data_length) == 0) {
|
|
return cur;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
thread_network_data_service_entry_t *thread_local_service_entry_save(thread_network_data_service_list_t *list, thread_network_data_service_entry_t *service)
|
|
{
|
|
thread_network_data_service_entry_t *service_entry;
|
|
service_entry = thread_local_service_entry_find(list, service);
|
|
|
|
if (service_entry) {
|
|
service_entry->S_id = service->S_id;
|
|
service_entry->S_stable = service->S_stable;
|
|
|
|
if (service_entry->S_server_data) {
|
|
ns_dyn_mem_free(service_entry->S_server_data);
|
|
service_entry->S_server_data = NULL;
|
|
}
|
|
|
|
if (service->S_server_data_length) {
|
|
service_entry->S_server_data = ns_dyn_mem_alloc(service->S_server_data_length);
|
|
}
|
|
|
|
if (service_entry->S_server_data) {
|
|
service_entry->S_server_data_length = service->S_server_data_length;
|
|
memcpy(service_entry->S_server_data, service->S_server_data, service->S_server_data_length);
|
|
}
|
|
|
|
return service_entry;
|
|
}
|
|
|
|
service_entry = ns_dyn_mem_alloc(sizeof(thread_network_data_service_entry_t));
|
|
|
|
if (service_entry) {
|
|
memset(service_entry, 0, sizeof(thread_network_data_service_entry_t));
|
|
|
|
service_entry->T = service->T;
|
|
service_entry->S_id = service->S_id;
|
|
service_entry->S_stable = service->S_stable;
|
|
service_entry->S_enterprise_number = service->S_enterprise_number;
|
|
service_entry->S_service_data = ns_dyn_mem_alloc(service->S_service_data_length);
|
|
|
|
if (!service_entry->S_service_data) {
|
|
tr_error("Failed to allocate memory for service data!");
|
|
ns_dyn_mem_free(service_entry);
|
|
return NULL;
|
|
}
|
|
|
|
service_entry->S_service_data_length = service->S_service_data_length;
|
|
memcpy(service_entry->S_service_data, service->S_service_data, service->S_service_data_length);
|
|
|
|
if (service->S_server_data_length) {
|
|
service_entry->S_server_data = ns_dyn_mem_alloc(service->S_server_data_length);
|
|
|
|
if (!service_entry->S_server_data) {
|
|
tr_error("Failed to allocate memory for server data!");
|
|
ns_dyn_mem_free(service_entry->S_service_data);
|
|
ns_dyn_mem_free(service_entry);
|
|
return NULL;
|
|
}
|
|
|
|
service_entry->S_server_data_length = service->S_server_data_length;
|
|
memcpy(service_entry->S_server_data, service->S_server_data, service->S_server_data_length);
|
|
}
|
|
|
|
ns_list_add_to_end(list, service_entry);
|
|
|
|
tr_debug("Added service; enterprise number: %"PRIu32", service: %s", service_entry->S_enterprise_number,
|
|
trace_array(service_entry->S_service_data, service_entry->S_service_data_length));
|
|
}
|
|
|
|
return service_entry;
|
|
}
|
|
|
|
thread_network_local_data_entry_t *thread_local_prefix_entry_find(thread_network_data_prefix_list_t *list, thread_prefix_tlv_t *prefixTlv)
|
|
{
|
|
ns_list_foreach(thread_network_local_data_entry_t, cur, list) {
|
|
if (prefixTlv->domainId == cur->domainId) {
|
|
if (prefixTlv->PrefixLen == cur->servicesPrefixLen) {
|
|
if (bitsequal(cur->servicesPrefix, prefixTlv->Prefix, prefixTlv->PrefixLen)) {
|
|
return cur;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
thread_network_local_data_entry_t *thread_local_prefix_entry_get(thread_network_data_prefix_list_t *list, thread_prefix_tlv_t *prefixTlv)
|
|
{
|
|
thread_network_local_data_entry_t *prefix_entry;
|
|
|
|
//Check current Prefix List
|
|
prefix_entry = thread_local_prefix_entry_find(list, prefixTlv);
|
|
if (prefix_entry) {
|
|
return prefix_entry;
|
|
}
|
|
|
|
prefix_entry = thread_local_service_list_allocate(prefixTlv);
|
|
if (prefix_entry) {
|
|
bool inserted = false;
|
|
ns_list_foreach(thread_network_local_data_entry_t, cur, list) {
|
|
if (prefixTlv->PrefixLen >= cur->servicesPrefixLen) { //Add always longer or same length to before last one
|
|
//add before new
|
|
tr_debug("Add new Longer");
|
|
inserted = true;
|
|
ns_list_add_before(list, cur, prefix_entry);
|
|
break;
|
|
}
|
|
}
|
|
if (!inserted) {
|
|
tr_debug("Add to end");
|
|
ns_list_add_to_end(list, prefix_entry);
|
|
}
|
|
}
|
|
return prefix_entry;
|
|
}
|
|
|
|
uint8_t thread_service_next_free_service_type_id(thread_network_data_service_cache_list_t *list)
|
|
{
|
|
uint8_t sid;
|
|
|
|
for (sid = 0; sid < 16; ++sid) {
|
|
bool found = false;
|
|
|
|
ns_list_foreach(thread_network_data_service_cache_entry_t, cur, list) {
|
|
if (cur->S_id == sid) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
return sid;
|
|
}
|
|
}
|
|
|
|
return 0xff; // IDs exhausted
|
|
}
|
|
|
|
thread_network_data_prefix_cache_entry_t *thread_prefix_entry_find(thread_network_prefix_list_t *list, thread_prefix_tlv_t *prefixTlv)
|
|
{
|
|
ns_list_foreach(thread_network_data_prefix_cache_entry_t, cur, list) {
|
|
if (prefixTlv->domainId == cur->domainId) {
|
|
if (prefixTlv->PrefixLen == cur->servicesPrefixLen) {
|
|
if (bitsequal(cur->servicesPrefix, prefixTlv->Prefix, prefixTlv->PrefixLen)) {
|
|
return cur;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
thread_network_data_prefix_cache_entry_t *thread_prefix_entry_get(thread_network_prefix_list_t *list, thread_prefix_tlv_t *prefixTlv)
|
|
{
|
|
thread_network_data_prefix_cache_entry_t *main_list;
|
|
//Check current Prefix List
|
|
main_list = thread_prefix_entry_find(list, prefixTlv);
|
|
if (main_list) {
|
|
return main_list;
|
|
}
|
|
|
|
main_list = thread_prefix_service_list_allocate(prefixTlv);
|
|
tr_info("New prefix added");
|
|
|
|
if (main_list) {
|
|
bool inserted = false;
|
|
ns_list_foreach(thread_network_data_prefix_cache_entry_t, cur, list) {
|
|
if (prefixTlv->PrefixLen >= cur->servicesPrefixLen) { //Add always longer or same length to before last one
|
|
//add before new
|
|
tr_debug("Add new Longer");
|
|
inserted = true;
|
|
ns_list_add_before(list, cur, main_list);
|
|
break;
|
|
}
|
|
}
|
|
if (!inserted) {
|
|
tr_debug("Add to end");
|
|
ns_list_add_to_end(list, main_list);
|
|
}
|
|
}
|
|
|
|
return main_list;
|
|
}
|
|
|
|
thread_network_data_service_cache_entry_t *thread_service_entry_get(thread_network_data_service_cache_list_t *list, thread_network_data_service_entry_t *service)
|
|
{
|
|
thread_network_data_service_cache_entry_t *service_entry;
|
|
|
|
service_entry = thread_network_data_service_entry_find(list, service);
|
|
|
|
if (service_entry) {
|
|
return service_entry;
|
|
}
|
|
|
|
service_entry = ns_dyn_mem_alloc(sizeof(thread_network_data_service_cache_entry_t));
|
|
|
|
if (service_entry) {
|
|
memset(service_entry, 0, sizeof(thread_network_data_service_entry_t));
|
|
|
|
service_entry->T = service->T;
|
|
service_entry->S_stable = service->S_stable;
|
|
service_entry->S_enterprise_number = service->S_enterprise_number;
|
|
|
|
uint8_t sid = thread_service_next_free_service_type_id(list);
|
|
|
|
if (sid == 0xff) {
|
|
tr_error("All Service Type ID (S_id) values used!");
|
|
ns_dyn_mem_free(service_entry);
|
|
return NULL;
|
|
}
|
|
|
|
service_entry->S_id = sid;
|
|
service_entry->S_service_data = ns_dyn_mem_alloc(service->S_service_data_length);
|
|
|
|
if (!service_entry->S_service_data) {
|
|
tr_error("Failed to allocate memory for service data!");
|
|
ns_dyn_mem_free(service_entry);
|
|
return NULL;
|
|
}
|
|
|
|
service_entry->S_service_data_length = service->S_service_data_length;
|
|
memcpy(service_entry->S_service_data, service->S_service_data, service->S_service_data_length);
|
|
ns_list_init(&service_entry->server_list);
|
|
ns_list_add_to_end(list, service_entry);
|
|
}
|
|
|
|
return service_entry;
|
|
}
|
|
|
|
thread_network_server_data_entry_t *thread_server_entry_search(thread_network_server_data_list_t *list, uint16_t routerId)
|
|
{
|
|
ns_list_foreach(thread_network_server_data_entry_t, cur, list) {
|
|
if (cur->routerID == routerId) {
|
|
cur->canDelete = false;
|
|
return cur;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
thread_network_data_service_server_entry_t *thread_service_server_entry_search(thread_network_data_service_server_list_t *list, uint16_t routerId)
|
|
{
|
|
ns_list_foreach(thread_network_data_service_server_entry_t, cur, list) {
|
|
if (cur->router_id == routerId) {
|
|
cur->can_delete = false;
|
|
return cur;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
thread_network_server_data_entry_t *thread_server_entry_get(thread_network_server_data_list_t *list, thread_border_router_tlv_entry_t *service)
|
|
{
|
|
thread_network_server_data_entry_t *server_entry;
|
|
bool inserted = false;
|
|
|
|
//Check current Stable List
|
|
server_entry = thread_server_entry_search(list, service->routerID);
|
|
if (server_entry) {
|
|
return server_entry;
|
|
}
|
|
server_entry = thread_server_entry_allocate(service);
|
|
if (!server_entry) {
|
|
return NULL;
|
|
}
|
|
tr_info("New server entry made");
|
|
ns_list_foreach(thread_network_server_data_entry_t, cur, list) {
|
|
if (service->routerID < cur->routerID) { //Add always longer or same length to before last one
|
|
//add before new
|
|
tr_debug("Add smaller id");
|
|
inserted = true;
|
|
ns_list_add_before(list, cur, server_entry);
|
|
break;
|
|
}
|
|
}
|
|
if (!inserted) {
|
|
tr_debug("Add new Last");
|
|
ns_list_add_to_end(list, server_entry);
|
|
}
|
|
|
|
return server_entry;
|
|
}
|
|
|
|
thread_network_data_service_server_entry_t *thread_service_server_entry_get(thread_network_data_service_server_list_t *list, thread_network_data_service_server_entry_t *server)
|
|
{
|
|
thread_network_data_service_server_entry_t *server_entry;
|
|
|
|
server_entry = thread_service_server_entry_search(list, server->router_id);
|
|
|
|
if (server_entry) {
|
|
return server_entry;
|
|
}
|
|
|
|
server_entry = ns_dyn_mem_alloc(sizeof(thread_network_data_service_server_entry_t));
|
|
|
|
if (server_entry) {
|
|
memset(server_entry, 0, sizeof(thread_network_data_service_server_entry_t));
|
|
|
|
server_entry->can_delete = false;
|
|
server_entry->router_id = server->router_id;
|
|
server_entry->stable = server->stable;
|
|
server_entry->server_data = ns_dyn_mem_alloc(server->server_data_length);
|
|
|
|
if (!server_entry->server_data) {
|
|
tr_error("Failed to allocate memory for server data!");
|
|
ns_dyn_mem_free(server_entry);
|
|
return NULL;
|
|
}
|
|
|
|
server_entry->server_data_length = server->server_data_length;
|
|
memcpy(server_entry->server_data, server->server_data, server->server_data_length);
|
|
ns_list_add_to_end(list, server_entry);
|
|
}
|
|
|
|
return server_entry;
|
|
}
|
|
|
|
uint8_t thread_get_context_id_by_length(thread_data_context_list_t *list, uint8_t contextLength)
|
|
{
|
|
//Check current Stable List
|
|
ns_list_foreach(thread_network_data_context_entry_t, cur, list) {
|
|
if (cur->contextPrefixLength == contextLength) {
|
|
return cur->cid;
|
|
}
|
|
}
|
|
return 16;
|
|
}
|
|
|
|
thread_network_data_context_entry_t *thread_get_context_by_id(thread_data_context_list_t *list, uint8_t cid)
|
|
{
|
|
//Check current Stable List
|
|
ns_list_foreach(thread_network_data_context_entry_t, cur, list) {
|
|
if (cur->cid == cid) {
|
|
return cur;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
thread_network_server_data_entry_t *thread_get_dhcp_server_from_list(thread_network_server_data_list_t *list)
|
|
{
|
|
//Check current Stable List
|
|
ns_list_foreach(thread_network_server_data_entry_t, cur, list) {
|
|
if (cur->P_dhcp) {
|
|
return cur;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
thread_network_data_context_entry_t *thread_get_main_context_list(thread_data_context_list_t *list, thread_network_local_data_context_entry_t *context)
|
|
{
|
|
thread_network_data_context_entry_t *main_list;
|
|
|
|
//Check current Stable List
|
|
ns_list_foreach(thread_network_data_context_entry_t, cur, list) {
|
|
if (cur->cid == context->cid) {
|
|
return cur;
|
|
}
|
|
}
|
|
|
|
main_list = thread_context_service_list_allocate(context->contextPrefixLength, context->cid, context->compression);
|
|
if (main_list) {
|
|
tr_debug("Add New");
|
|
ns_list_add_to_end(list, main_list);
|
|
}
|
|
|
|
return main_list;
|
|
}
|
|
|
|
void thread_server_list_free(thread_network_server_data_list_t *listPtr)
|
|
{
|
|
ns_list_foreach_safe(thread_network_server_data_entry_t, cur, listPtr) {
|
|
ns_list_remove(listPtr, cur);
|
|
ns_dyn_mem_free(cur);
|
|
}
|
|
}
|
|
|
|
void thread_service_server_list_free(thread_network_data_service_server_list_t *listPtr)
|
|
{
|
|
ns_list_foreach_safe(thread_network_data_service_server_entry_t, cur, listPtr) {
|
|
if (cur->server_data) {
|
|
ns_dyn_mem_free(cur->server_data);
|
|
}
|
|
ns_list_remove(listPtr, cur);
|
|
ns_dyn_mem_free(cur);
|
|
}
|
|
}
|
|
|
|
static int thread_server_context_delete_mark(thread_data_context_list_t *listPtr)
|
|
{
|
|
int retVal = -1;
|
|
ns_list_foreach_safe(thread_network_data_context_entry_t, cur, listPtr) {
|
|
cur->canDelete = true;
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
static int thread_server_data_delete_mark_by_router_id(thread_network_server_data_list_t *listPtr, uint16_t routerID, bool subSet)
|
|
{
|
|
int retVal = -1;
|
|
ns_list_foreach_safe(thread_network_server_data_entry_t, cur, listPtr) {
|
|
if (!subSet) {
|
|
cur->canDelete = true;
|
|
}
|
|
if (cur->routerID == routerID) {
|
|
cur->canDelete = true;
|
|
}
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
static int thread_service_data_delete_mark_by_router_id(thread_network_data_service_server_list_t *listPtr, uint16_t routerID, bool subSet)
|
|
{
|
|
int retVal = -1;
|
|
ns_list_foreach_safe(thread_network_data_service_server_entry_t, cur, listPtr) {
|
|
if (!subSet) {
|
|
cur->can_delete = true;
|
|
}
|
|
if (cur->router_id == routerID) {
|
|
cur->can_delete = true;
|
|
}
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
static int thread_server_context_clean(int8_t id, thread_network_data_cache_entry_t *cachePtr, thread_data_context_list_t *listPtr, thread_network_data_prefix_cache_entry_t *prefixEntry, lowpan_context_list_t *context_list)
|
|
{
|
|
(void) id;
|
|
int retVal = -1;
|
|
(void) prefixEntry;
|
|
ns_list_foreach_safe(thread_network_data_context_entry_t, cur, listPtr) {
|
|
if (cur->canDelete) {
|
|
cachePtr->temporaryUpdatePushed = true;
|
|
if (cur->stableData) {
|
|
cachePtr->stableUpdatePushed = true;
|
|
}
|
|
// Set context lifetime to 0 to delete
|
|
lowpan_context_update(context_list, cur->cid, 0, NULL, 0, true);
|
|
ns_list_remove(listPtr, cur);
|
|
ns_dyn_mem_free(cur);
|
|
retVal = 0;
|
|
}
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
|
|
static bool thread_server_data_clean_by_router_id(thread_network_data_cache_entry_t *cachePtr, thread_network_server_data_list_t *listPtr, thread_network_data_prefix_cache_entry_t *prefixEntry, bool routeInfo, protocol_interface_info_entry_t *curInterface)
|
|
{
|
|
uint8_t addr[16];
|
|
uint8_t *nextHop;
|
|
bool address_removed = false;
|
|
|
|
uint16_t mac16 = mac_helper_mac16_address_get(curInterface);
|
|
ns_list_foreach_safe(thread_network_server_data_entry_t, cur, listPtr) {
|
|
if (cur->canDelete) {
|
|
tr_debug("Delete BR %s", trace_array(prefixEntry->servicesPrefix, 8));
|
|
cachePtr->temporaryUpdatePushed = true;
|
|
if (cur->stableData) {
|
|
cachePtr->stableUpdatePushed = true;
|
|
}
|
|
//Call Route and DHCPV6 Updates here
|
|
memcpy(addr, curInterface->thread_info->threadPrivatePrefixInfo.ulaPrefix, 8);
|
|
|
|
memcpy(&addr[8], ADDR_SHORT_ADR_SUFFIC, 6);
|
|
common_write_16_bit(cur->routerID, &addr[14]);
|
|
|
|
|
|
if (routeInfo) {
|
|
if (cur->routerID == mac16) {
|
|
nextHop = NULL;
|
|
} else {
|
|
nextHop = addr;
|
|
}
|
|
if (prefixEntry) {
|
|
ipv6_route_delete(prefixEntry->servicesPrefix, prefixEntry->servicesPrefixLen, curInterface->id, nextHop, ROUTE_THREAD);
|
|
} else {
|
|
ipv6_route_delete(NULL, 0, curInterface->id, nextHop, ROUTE_THREAD);
|
|
}
|
|
} else {
|
|
address_removed = true;
|
|
|
|
if (cur->P_dhcp) {
|
|
tr_debug("Delete DHCPv6 given address");
|
|
dhcp_client_global_address_delete(curInterface->id, addr, prefixEntry->servicesPrefix);
|
|
}
|
|
|
|
if (cur->P_slaac) {
|
|
tr_debug("Delete SLAAC address");
|
|
addr_delete_matching(curInterface, prefixEntry->servicesPrefix, 64, ADDR_SOURCE_SLAAC);
|
|
}
|
|
|
|
if (cur->P_res1) {
|
|
tr_debug("Delete thread domain address");
|
|
addr_delete_matching(curInterface, prefixEntry->servicesPrefix, 64, ADDR_SOURCE_THREAD_DOMAIN);
|
|
}
|
|
}
|
|
|
|
ns_list_remove(listPtr, cur);
|
|
ns_dyn_mem_free(cur);
|
|
}
|
|
}
|
|
return address_removed;
|
|
}
|
|
|
|
static void thread_service_data_clean_by_router_id(thread_network_data_cache_entry_t *cachePtr, thread_network_data_service_server_list_t *listPtr)
|
|
{
|
|
ns_list_foreach_safe(thread_network_data_service_server_entry_t, cur, listPtr) {
|
|
if (cur->can_delete) {
|
|
tr_debug("Deleting server %04x", cur->router_id);
|
|
cachePtr->temporaryUpdatePushed = true;
|
|
if (cur->stable) {
|
|
cachePtr->stableUpdatePushed = true;
|
|
}
|
|
if (cur->server_data) {
|
|
ns_dyn_mem_free(cur->server_data);
|
|
}
|
|
ns_list_remove(listPtr, cur);
|
|
ns_dyn_mem_free(cur);
|
|
}
|
|
}
|
|
}
|
|
|
|
void thread_context_service_list_free(thread_data_context_list_t *listPtr)
|
|
{
|
|
ns_list_foreach_safe(thread_network_data_context_entry_t, cur, listPtr) {
|
|
ns_list_remove(listPtr, cur);
|
|
ns_dyn_mem_free(cur);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialise Thread Network Data cache
|
|
*
|
|
* \param cachePtr Pointer to Network Data Structure which will be initialized
|
|
*
|
|
*/
|
|
void thread_network_data_base_init(thread_network_data_cache_entry_t *cachePtr)
|
|
{
|
|
ns_list_init(&cachePtr->localPrefixList);
|
|
ns_list_init(&cachePtr->service_list);
|
|
cachePtr->contex_id_reuse_timeout = THREAD_CONTEXT_ID_REUSE_TIMEOUT;
|
|
cachePtr->networkDataTlvSize = 0;
|
|
cachePtr->stableUpdatePushed = false;
|
|
cachePtr->temporaryUpdatePushed = false;
|
|
cachePtr->network_data_update_delay = 0;
|
|
cachePtr->network_data_len = 0;
|
|
}
|
|
|
|
void thread_network_local_server_data_base_init(thread_network_local_data_cache_entry_t *cachePtr)
|
|
{
|
|
ns_list_init(&cachePtr->prefix_list);
|
|
ns_list_init(&cachePtr->service_list);
|
|
cachePtr->registered_rloc16 = 0xffff;
|
|
cachePtr->release_old_address = false;
|
|
cachePtr->publish_coap_req_id = 0;
|
|
cachePtr->publish_pending = false;
|
|
}
|
|
|
|
void thread_network_data_router_id_mark_delete(thread_network_data_cache_entry_t *cachePtr, uint16_t routerID, bool subSet)
|
|
{
|
|
ns_list_foreach_safe(thread_network_data_prefix_cache_entry_t, cur, &cachePtr->localPrefixList) {
|
|
thread_server_data_delete_mark_by_router_id(&cur->borderRouterList, routerID, subSet);
|
|
thread_server_data_delete_mark_by_router_id(&cur->routeList, routerID, subSet);
|
|
if (!subSet) {
|
|
thread_server_context_delete_mark(&cur->contextList);
|
|
}
|
|
}
|
|
|
|
ns_list_foreach_safe(thread_network_data_service_cache_entry_t, cur, &cachePtr->service_list) {
|
|
thread_service_data_delete_mark_by_router_id(&cur->server_list, routerID, subSet);
|
|
}
|
|
}
|
|
bool thread_network_data_router_id_free(thread_network_data_cache_entry_t *cachePtr, bool is_leader, protocol_interface_info_entry_t *curInterface)
|
|
{
|
|
bool address_removed = false;
|
|
ns_list_foreach_safe(thread_network_data_prefix_cache_entry_t, cur, &cachePtr->localPrefixList) {
|
|
//GET Context
|
|
if (thread_server_data_clean_by_router_id(cachePtr, &cur->borderRouterList, cur, false, curInterface)) {
|
|
address_removed = true;
|
|
}
|
|
thread_server_data_clean_by_router_id(cachePtr, &cur->routeList, cur, true, curInterface);
|
|
if (!is_leader) {
|
|
thread_server_context_clean(curInterface->id, cachePtr, &cur->contextList, cur, &curInterface->lowpan_contexts);
|
|
}
|
|
|
|
if (ns_list_is_empty(&cur->borderRouterList)) {
|
|
//Delete On-Mesh Prefix
|
|
ipv6_route_delete(cur->servicesPrefix, cur->servicesPrefixLen, curInterface->id, NULL, ROUTE_THREAD);
|
|
}
|
|
|
|
if (!ns_list_is_empty(&cur->borderRouterList)) {
|
|
|
|
} else if (!ns_list_is_empty(&cur->routeList)) {
|
|
|
|
} else if (!ns_list_is_empty(&cur->contextList)) {
|
|
if (is_leader) {
|
|
ns_list_foreach_safe(thread_network_data_context_entry_t, curContext, &cur->contextList) {
|
|
cachePtr->temporaryUpdatePushed = true;
|
|
if (curContext->stableData) {
|
|
cachePtr->stableUpdatePushed = true;
|
|
}
|
|
if (curContext->compression) {
|
|
curContext->compression = false;
|
|
tr_debug("disable Cur Context and start timer");
|
|
curContext->context_reuse_delay = cachePtr->contex_id_reuse_timeout;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
//ALL Empty
|
|
ns_list_remove(&cachePtr->localPrefixList, cur);
|
|
ns_dyn_mem_free(cur);
|
|
}
|
|
}
|
|
|
|
ns_list_foreach_safe(thread_network_data_service_cache_entry_t, cur, &cachePtr->service_list) {
|
|
thread_service_data_clean_by_router_id(cachePtr, &cur->server_list);
|
|
if (ns_list_is_empty(&cur->server_list)) {
|
|
if (cur->S_service_data) {
|
|
ns_dyn_mem_free(cur->S_service_data);
|
|
}
|
|
ns_list_remove(&cachePtr->service_list, cur);
|
|
ns_dyn_mem_free(cur);
|
|
}
|
|
}
|
|
|
|
if (cachePtr->temporaryUpdatePushed || cachePtr->stableUpdatePushed) {
|
|
//Validate that Length will be always updated
|
|
cachePtr->networkDataTlvSize = thread_network_data_prefix_set_size(cachePtr, true)
|
|
+ thread_network_data_service_set_size(cachePtr, true);
|
|
}
|
|
return address_removed;
|
|
}
|
|
|
|
void thread_network_data_context_re_use_timer_update(int8_t id, thread_network_data_cache_entry_t *cachePtr, uint32_t ticks, lowpan_context_list_t *context_list)
|
|
{
|
|
(void) id;
|
|
ns_list_foreach_safe(thread_network_data_prefix_cache_entry_t, cur, &cachePtr->localPrefixList) {
|
|
ns_list_foreach_safe(thread_network_data_context_entry_t, curContext, &cur->contextList) {
|
|
if (!curContext->compression) {
|
|
if (curContext->context_reuse_delay > ticks) {
|
|
curContext->context_reuse_delay -= ticks;
|
|
} else {
|
|
ns_list_remove(&cur->contextList, curContext);
|
|
if (curContext->stableData) {
|
|
cachePtr->stableUpdatePushed = true;
|
|
} else {
|
|
cachePtr->temporaryUpdatePushed = true;
|
|
}
|
|
// Set context lifetime to 0 to delete
|
|
lowpan_context_update(context_list, curContext->cid, 0, NULL, 0, true);
|
|
ns_dyn_mem_free(curContext);
|
|
}
|
|
}
|
|
}
|
|
if (ns_list_is_empty(&cur->borderRouterList) &&
|
|
ns_list_is_empty(&cur->routeList) &&
|
|
ns_list_is_empty(&cur->contextList)) {
|
|
ns_list_remove(&cachePtr->localPrefixList, cur);
|
|
ns_dyn_mem_free(cur);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void thread_network_data_free_and_clean(thread_network_data_cache_entry_t *cachePtr)
|
|
{
|
|
if (!ns_list_is_empty(&cachePtr->localPrefixList)) {
|
|
ns_list_foreach_safe(thread_network_data_prefix_cache_entry_t, cur, &cachePtr->localPrefixList) {
|
|
thread_context_service_list_free(&cur->contextList);
|
|
thread_server_list_free(&cur->borderRouterList);
|
|
thread_server_list_free(&cur->routeList);
|
|
ns_list_remove(&cachePtr->localPrefixList, cur);
|
|
ns_dyn_mem_free(cur);
|
|
}
|
|
ns_list_init(&cachePtr->localPrefixList);
|
|
}
|
|
|
|
if (!ns_list_is_empty(&cachePtr->service_list)) {
|
|
ns_list_foreach_safe(thread_network_data_service_cache_entry_t, cur, &cachePtr->service_list) {
|
|
thread_service_server_list_free(&cur->server_list);
|
|
if (cur->S_service_data) {
|
|
ns_dyn_mem_free(cur->S_service_data);
|
|
}
|
|
ns_list_remove(&cachePtr->service_list, cur);
|
|
ns_dyn_mem_free(cur);
|
|
}
|
|
ns_list_init(&cachePtr->service_list);
|
|
}
|
|
|
|
cachePtr->networkDataTlvSize = 0;
|
|
cachePtr->stableUpdatePushed = false;
|
|
cachePtr->temporaryUpdatePushed = false;
|
|
cachePtr->network_data_update_delay = 0;
|
|
cachePtr->network_data_len = 0;
|
|
}
|
|
|
|
void thread_network_local_data_free_and_clean(thread_network_local_data_cache_entry_t *cachePtr, int8_t interface_id)
|
|
{
|
|
ns_list_foreach_safe(thread_network_local_data_entry_t, cur, &cachePtr->prefix_list) {
|
|
if (cur->dhcpv6ServerActive) {
|
|
DHCPv6_server_service_delete(interface_id, cur->servicesPrefix, true);
|
|
}
|
|
|
|
ns_list_remove(&cachePtr->prefix_list, cur);
|
|
ns_dyn_mem_free(cur);
|
|
}
|
|
|
|
ns_list_foreach_safe(thread_network_data_service_entry_t, cur, &cachePtr->service_list) {
|
|
ns_list_remove(&cachePtr->service_list, cur);
|
|
if (cur->S_service_data) {
|
|
ns_dyn_mem_free(cur->S_service_data);
|
|
}
|
|
if (cur->S_server_data) {
|
|
ns_dyn_mem_free(cur->S_server_data);
|
|
}
|
|
ns_dyn_mem_free(cur);
|
|
}
|
|
|
|
cachePtr->publish_pending = false;
|
|
cachePtr->publish_coap_req_id = 0;
|
|
cachePtr->release_old_address = false;
|
|
}
|
|
|
|
static bool thread_network_data_has_subtlv(uint8_t *network_data_ptr, uint16_t network_data_length, uint8_t *prefix, uint8_t prefix_length, uint8_t lookup_type)
|
|
{
|
|
uint8_t *dptr;
|
|
uint8_t length;
|
|
uint8_t type;
|
|
dptr = network_data_ptr;
|
|
while (network_data_length) {
|
|
if (network_data_length >= 2) {
|
|
type = *dptr++;
|
|
type &= THREAD_NWK_DATA_TYPE_MASK;
|
|
length = *dptr++;
|
|
|
|
if (length == 0) {
|
|
return false;
|
|
}
|
|
network_data_length -= 2;
|
|
if (network_data_length >= length) {
|
|
// Set length ready for next check
|
|
network_data_length -= length;
|
|
|
|
if (type == THREAD_NWK_DATA_TYPE_PREFIX) {
|
|
thread_prefix_tlv_t prefix_tlv;
|
|
prefix_tlv.domainId = *dptr++;
|
|
prefix_tlv.PrefixLen = *dptr++;
|
|
prefix_tlv.Prefix = dptr;
|
|
length -= 2;
|
|
|
|
uint8_t prefix_bytes_len = prefixBits_to_bytes(prefix_tlv.PrefixLen);
|
|
uint8_t prefix_bytes_length = prefixBits_to_bytes(prefix_length);
|
|
|
|
if (prefix_bytes_len > length) {
|
|
return false;
|
|
}
|
|
|
|
if (prefix_bytes_len != prefix_bytes_length ||
|
|
memcmp(prefix, prefix_tlv.Prefix, prefix_bytes_len) != 0) {
|
|
dptr += length;
|
|
continue;
|
|
}
|
|
|
|
length -= prefix_bytes_len;
|
|
dptr += prefix_bytes_len;
|
|
|
|
while (length > 2) {
|
|
type = *dptr++;
|
|
type &= THREAD_NWK_DATA_TYPE_MASK;
|
|
uint8_t subLength = *dptr++;
|
|
length -= 2;
|
|
|
|
if (subLength <= length) {
|
|
length -= subLength;
|
|
dptr += subLength;
|
|
|
|
if (lookup_type == type) {
|
|
return true;
|
|
}
|
|
} else {
|
|
tr_error("Length fail");
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
dptr += length;
|
|
}
|
|
} else {
|
|
tr_error("Length fail");
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static uint16_t thread_network_data_prefix_shrink_size(thread_network_data_cache_entry_t *networkDataStorage, uint8_t *network_data_ptr, uint16_t network_data_length, uint16_t router_id)
|
|
{
|
|
uint16_t network_data_len = 0;
|
|
|
|
ns_list_foreach(thread_network_data_prefix_cache_entry_t, cur, &networkDataStorage->localPrefixList) {
|
|
ns_list_foreach(thread_network_server_data_entry_t, br_cur, &cur->borderRouterList) {
|
|
if (br_cur->routerID == router_id) {
|
|
tr_debug("Checking prefix: %s", trace_ipv6_prefix(cur->servicesPrefix, cur->servicesPrefixLen));
|
|
if (!thread_network_data_has_subtlv(network_data_ptr, network_data_length, cur->servicesPrefix, cur->servicesPrefixLen, THREAD_NWK_DATA_TYPE_BORDER_ROUTER)) {
|
|
if (ns_list_count(&cur->borderRouterList) == 1) {
|
|
network_data_len += 2 + THREAD_BORDER_ROUTER_TLV_LENGTH;
|
|
} else {
|
|
network_data_len += THREAD_BORDER_ROUTER_TLV_LENGTH;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ns_list_foreach(thread_network_server_data_entry_t, route_cur, &cur->routeList) {
|
|
if (route_cur->routerID == router_id) {
|
|
if (!thread_network_data_has_subtlv(network_data_ptr, network_data_length, cur->servicesPrefix, cur->servicesPrefixLen, THREAD_NWK_DATA_TYPE_ROUTE)) {
|
|
if (ns_list_count(&cur->routeList) == 1) {
|
|
network_data_len += 2 + THREAD_HAS_ROUTE_TLV_LENGTH;
|
|
if (ns_list_count(&cur->borderRouterList) == 0) {
|
|
uint8_t prefix_bytes_len = prefixBits_to_bytes(cur->servicesPrefixLen);
|
|
network_data_len += 2 + 2 + prefix_bytes_len;
|
|
}
|
|
} else {
|
|
network_data_len += THREAD_HAS_ROUTE_TLV_LENGTH;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tr_debug("Shrink size: %d", network_data_len);
|
|
return network_data_len;
|
|
}
|
|
|
|
/**
|
|
* Calculates what the resulting size of prefixes for Network Data TLV would be.
|
|
*/
|
|
static int thread_network_data_resulting_prefix_set_size(thread_network_data_cache_entry_t *networkDataStorage, uint8_t *network_data_ptr, uint16_t network_data_length, uint16_t rid)
|
|
{
|
|
uint8_t *dptr;
|
|
uint8_t length;
|
|
uint8_t type;
|
|
|
|
if (!networkDataStorage || !network_data_ptr) {
|
|
return -1;
|
|
}
|
|
|
|
uint16_t network_data_len = thread_network_data_prefix_set_size(networkDataStorage, true);
|
|
tr_debug("Old network data length: %d", network_data_len);
|
|
|
|
/* Calculate how much removed network data would reduce the total size */
|
|
network_data_len -= thread_network_data_prefix_shrink_size(networkDataStorage, network_data_ptr, network_data_length, rid);
|
|
|
|
dptr = network_data_ptr;
|
|
while (network_data_length) {
|
|
if (network_data_length >= 2) {
|
|
type = *dptr++;
|
|
type &= THREAD_NWK_DATA_TYPE_MASK;
|
|
length = *dptr++;
|
|
|
|
if (length == 0) {
|
|
// 0 is not valid length for TLV
|
|
return -1;
|
|
}
|
|
|
|
network_data_length -= 2;
|
|
|
|
if (network_data_length >= length) {
|
|
// Set length ready for next check
|
|
network_data_length -= length;
|
|
|
|
if (type == THREAD_NWK_DATA_TYPE_PREFIX) {
|
|
thread_prefix_tlv_t prefix_tlv;
|
|
prefix_tlv.domainId = *dptr++;
|
|
prefix_tlv.PrefixLen = *dptr++;
|
|
prefix_tlv.Prefix = dptr;
|
|
length -= 2;
|
|
|
|
uint8_t prefix_bytes_len = prefixBits_to_bytes(prefix_tlv.PrefixLen);
|
|
|
|
if (prefix_bytes_len > length) {
|
|
return -1;
|
|
}
|
|
|
|
length -= prefix_bytes_len;
|
|
dptr += prefix_bytes_len;
|
|
|
|
tr_debug("Prefix: %s", trace_ipv6_prefix(prefix_tlv.Prefix, prefix_tlv.PrefixLen));
|
|
thread_network_data_prefix_cache_entry_t *prefix = thread_prefix_entry_find(&networkDataStorage->localPrefixList, &prefix_tlv);
|
|
|
|
if (!prefix) {
|
|
tr_debug("Adding new prefix!");
|
|
// This is a new prefix; add header length
|
|
network_data_len += 2 + 2 + prefix_bytes_len;
|
|
}
|
|
|
|
while (length > 2) {
|
|
type = *dptr++;
|
|
type &= THREAD_NWK_DATA_TYPE_MASK;
|
|
uint8_t subLength = *dptr++;
|
|
length -= 2;
|
|
|
|
tr_debug("SubType: %02x, %s", type, trace_array(dptr, subLength));
|
|
|
|
if (subLength <= length) {
|
|
length -= subLength;
|
|
|
|
if (type == THREAD_NWK_DATA_TYPE_BORDER_ROUTER) {
|
|
if (!prefix) {
|
|
// Add 6LoWPAN ID length (plus type & length)
|
|
network_data_len += 2 + THREAD_6LOWPAN_ID_TLV_LENGTH;
|
|
}
|
|
|
|
while (subLength) {
|
|
thread_network_server_data_entry_t *server_entry = NULL;
|
|
uint16_t router_id = common_read_16_bit(dptr);
|
|
dptr += 4;
|
|
|
|
if (prefix) {
|
|
server_entry = thread_server_entry_search(&prefix->borderRouterList, router_id);
|
|
}
|
|
|
|
if (!prefix || !server_entry) {
|
|
if (!prefix || ns_list_count(&prefix->borderRouterList) == 0) {
|
|
// Add Border Router type and length
|
|
network_data_len += 2;
|
|
}
|
|
// Add Border Router TLV length
|
|
network_data_len += THREAD_BORDER_ROUTER_TLV_LENGTH;
|
|
}
|
|
|
|
subLength -= THREAD_BORDER_ROUTER_TLV_LENGTH;
|
|
}
|
|
|
|
tr_debug("Type is BORDER ROUTER");
|
|
} else if (type == THREAD_NWK_DATA_TYPE_ROUTE) {
|
|
while (subLength) {
|
|
thread_network_server_data_entry_t *server_entry = NULL;
|
|
uint16_t router_id = common_read_16_bit(dptr);
|
|
dptr += 3;
|
|
|
|
if (prefix) {
|
|
server_entry = thread_server_entry_search(&prefix->routeList, router_id);
|
|
}
|
|
|
|
if (!prefix || !server_entry) {
|
|
if (!prefix || ns_list_count(&prefix->routeList) == 0) {
|
|
// Add Has Route type and length
|
|
network_data_len += 2;
|
|
}
|
|
// Add Has Route TLV length
|
|
network_data_len += THREAD_HAS_ROUTE_TLV_LENGTH;
|
|
}
|
|
|
|
subLength -= THREAD_HAS_ROUTE_TLV_LENGTH;
|
|
}
|
|
|
|
tr_debug("Type is HAS ROUTE");
|
|
} else {
|
|
dptr += subLength;
|
|
}
|
|
} else {
|
|
tr_error("Length fail");
|
|
return -1;
|
|
}
|
|
}
|
|
} else {
|
|
dptr += length;
|
|
}
|
|
} else {
|
|
tr_error("Length fail");
|
|
return -1;
|
|
}
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return network_data_len;
|
|
}
|
|
|
|
/**
|
|
* Calculates what the future size of Network Data TLV would be if new data was
|
|
* incorporated to the old data. NOTE: Use of this function should be replaced
|
|
* by some sophisticated logic to analyze if new data or part of it should be
|
|
* given priority. Now we simply limit by the total size.
|
|
*/
|
|
int thread_network_data_resulting_tlv_size(thread_network_data_cache_entry_t *networkDataStorage, uint8_t *network_data_ptr, uint16_t network_data_length, uint16_t router_id)
|
|
{
|
|
int network_data_len = thread_network_data_resulting_prefix_set_size(networkDataStorage, network_data_ptr, network_data_length, router_id);
|
|
|
|
// TODO: Add support for other TLVs than Prefix here
|
|
|
|
tr_debug("Size of the prefix set: %d", network_data_len);
|
|
|
|
if (network_data_len >= 0) {
|
|
// Add maximum Commission TLV size
|
|
network_data_len += THREAD_MAX_COMM_DATA_TLV_LEN;
|
|
}
|
|
|
|
return network_data_len;
|
|
}
|
|
|
|
/**
|
|
* Add new route information to route List
|
|
*
|
|
* \param networkDataList Pointer main network data structure
|
|
* \param prefixTlv Prefix TLV (domainID, Prefix, PrefixLen)
|
|
* \param service Route TLV
|
|
*
|
|
* return 0, ADD OK
|
|
* return <0 Add Not OK
|
|
*/
|
|
int thread_nd_local_list_add_route(thread_network_data_cache_entry_t *networkDataList, thread_prefix_tlv_t *prefixTlv, thread_border_router_tlv_entry_t *route)
|
|
{
|
|
thread_network_server_data_entry_t *entry;
|
|
tr_debug("Add Route: %s", trace_ipv6_prefix(prefixTlv->Prefix, prefixTlv->PrefixLen));
|
|
|
|
route->P_configure = false;
|
|
route->P_default_route = false;
|
|
route->P_dhcp = false;
|
|
route->P_preferred = false;
|
|
route->P_slaac = false;
|
|
|
|
if (!networkDataList) {
|
|
return -1;
|
|
}
|
|
|
|
thread_network_data_prefix_cache_entry_t *prefix_entry = thread_prefix_entry_get(&networkDataList->localPrefixList, prefixTlv);
|
|
if (!prefix_entry) {
|
|
return -1;
|
|
}
|
|
|
|
entry = thread_server_entry_get(&prefix_entry->routeList, route);
|
|
if (!entry) {
|
|
return -1;
|
|
}
|
|
|
|
entry->canDelete = false;
|
|
if ((thread_nd_local_data_length_updated(networkDataList) == 0)) {
|
|
if (route->stableData) {
|
|
networkDataList->stableUpdatePushed = true;
|
|
} else {
|
|
networkDataList->temporaryUpdatePushed = true;
|
|
}
|
|
} else if (entry->Prf != route->Prf) {
|
|
entry->Prf = route->Prf;
|
|
if (route->stableData) {
|
|
networkDataList->stableUpdatePushed = true;
|
|
} else {
|
|
networkDataList->temporaryUpdatePushed = true;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int thread_nd_local_list_add_service(thread_network_data_cache_entry_t *networkDataList, thread_network_data_service_entry_t *service, thread_network_data_service_server_entry_t *server)
|
|
{
|
|
bool changed = false;
|
|
tr_debug("Add Service: %s", trace_array(service->S_service_data, service->S_service_data_length));
|
|
|
|
if (!networkDataList) {
|
|
return -1;
|
|
}
|
|
|
|
thread_network_data_service_cache_entry_t *service_entry = thread_service_entry_get(&networkDataList->service_list, service);
|
|
if (!service_entry) {
|
|
return -1;
|
|
}
|
|
|
|
thread_network_data_service_server_entry_t *entry = thread_service_server_entry_get(&service_entry->server_list, server);
|
|
if (!entry) {
|
|
return -1;
|
|
}
|
|
|
|
if (entry->server_data_length != server->server_data_length || memcmp(entry->server_data, server->server_data, server->server_data_length) != 0) {
|
|
// Server data changed
|
|
if (entry->server_data_length != server->server_data_length) {
|
|
ns_dyn_mem_free(entry->server_data);
|
|
entry->server_data = ns_dyn_mem_alloc(server->server_data_length);
|
|
if (!entry->server_data) {
|
|
return -1;
|
|
}
|
|
}
|
|
memcpy(entry->server_data, server->server_data, server->server_data_length);
|
|
entry->server_data_length = server->server_data_length;
|
|
changed = true;
|
|
}
|
|
entry->can_delete = false;
|
|
|
|
if (changed || (thread_nd_local_data_length_updated(networkDataList) == 0)) {
|
|
if (server->stable) {
|
|
networkDataList->stableUpdatePushed = true;
|
|
} else {
|
|
networkDataList->temporaryUpdatePushed = true;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Add new BorderRouter Server information to route List
|
|
*
|
|
* \param networkDataList Pointer main network data structure
|
|
* \param prefixTlv Prefix TLv
|
|
* \param service Border Router TLV
|
|
*
|
|
* return 0, ADD OK
|
|
* return <0 Add Not OK
|
|
*/
|
|
int thread_nd_local_list_add_on_mesh_prefix(thread_network_data_cache_entry_t *networkDataList, thread_prefix_tlv_t *prefixTlv, thread_border_router_tlv_entry_t *service)
|
|
{
|
|
thread_network_data_prefix_cache_entry_t *prefix_entry;
|
|
thread_network_server_data_entry_t *server_entry;
|
|
bool trigDataPropagate = false;
|
|
|
|
tr_debug("Add %s%s%s prefix:%s server: %04x", service->P_dhcp ? "DHCPv6" : "", service->P_slaac ? "SLAAC " : "", service->P_res1 ? "P_res1 " : "",
|
|
trace_ipv6_prefix(prefixTlv->Prefix, prefixTlv->PrefixLen), service->routerID);
|
|
|
|
if (!networkDataList) {
|
|
return -1;
|
|
}
|
|
|
|
if (!prefixTlv->PrefixLen) {
|
|
return -1;
|
|
}
|
|
|
|
prefix_entry = thread_prefix_entry_get(&networkDataList->localPrefixList, prefixTlv);
|
|
if (!prefix_entry) {
|
|
return -1;
|
|
}
|
|
|
|
server_entry = thread_server_entry_get(&prefix_entry->borderRouterList, service);
|
|
|
|
if (!server_entry) {
|
|
return -1;
|
|
}
|
|
tr_debug("update current BR");
|
|
|
|
server_entry->canDelete = false;
|
|
if (thread_nd_local_data_length_updated(networkDataList) == 0) {
|
|
trigDataPropagate = true;
|
|
} else {
|
|
if (server_entry->P_configure != service->P_configure) {
|
|
server_entry->P_configure = service->P_configure;
|
|
trigDataPropagate = true;
|
|
}
|
|
|
|
if (server_entry->P_default_route != service->P_default_route) {
|
|
server_entry->P_default_route = service->P_default_route;
|
|
trigDataPropagate = true;
|
|
}
|
|
|
|
if (server_entry->Prf != service->Prf) {
|
|
server_entry->Prf = service->Prf;
|
|
trigDataPropagate = true;
|
|
}
|
|
|
|
if (server_entry->P_preferred != service->P_preferred || server_entry->P_slaac != service->P_slaac) {
|
|
server_entry->P_preferred = service->P_preferred;
|
|
server_entry->P_slaac = service->P_slaac;
|
|
trigDataPropagate = true;
|
|
}
|
|
|
|
if (server_entry->P_dhcp != service->P_dhcp) {
|
|
server_entry->P_dhcp = service->P_dhcp;
|
|
trigDataPropagate = true;
|
|
}
|
|
|
|
if (server_entry->P_on_mesh != service->P_on_mesh) {
|
|
server_entry->P_on_mesh = service->P_on_mesh;
|
|
trigDataPropagate = true;
|
|
}
|
|
|
|
if (server_entry->P_nd_dns != service->P_nd_dns) {
|
|
server_entry->P_nd_dns = service->P_nd_dns;
|
|
trigDataPropagate = true;
|
|
}
|
|
|
|
if (server_entry->P_res1 != service->P_res1) {
|
|
server_entry->P_res1 = service->P_res1;
|
|
trigDataPropagate = true;
|
|
}
|
|
}
|
|
|
|
if (trigDataPropagate) {
|
|
if (service->stableData) {
|
|
networkDataList->stableUpdatePushed = true;
|
|
} else {
|
|
networkDataList->temporaryUpdatePushed = true;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Del DHCPv6 Server information to route List
|
|
*
|
|
* \param networkDataList Pointer main network data structure
|
|
* \param prefixTlv Prefix TLV (domainID, Prefix, PrefixLen)
|
|
* \param service On Mesh prefix TLV
|
|
*
|
|
* return 0, Del OK
|
|
* return <0 Del Not OK
|
|
*/
|
|
int thread_nd_local_list_del_on_mesh_server(thread_network_data_cache_entry_t *networkDataList, thread_prefix_tlv_t *prefixTlv, thread_border_router_tlv_entry_t *service)
|
|
{
|
|
thread_network_data_prefix_cache_entry_t *main_list;
|
|
thread_network_server_data_entry_t *entry;
|
|
|
|
if (!networkDataList) {
|
|
return -1;
|
|
}
|
|
|
|
if (prefixTlv->PrefixLen) {
|
|
return -1;
|
|
}
|
|
|
|
tr_debug("Del %s%s%s prefix: %s", service->P_dhcp ? "DHCPv6" : "", service->P_slaac ? "SLAAC " : "", service->P_res1 ? "P_res1 " : "",
|
|
trace_array(prefixTlv->Prefix, prefixBits_to_bytes(prefixTlv->PrefixLen)));
|
|
|
|
main_list = thread_prefix_entry_get(&networkDataList->localPrefixList, prefixTlv);
|
|
if (!main_list) {
|
|
return -1;
|
|
}
|
|
|
|
entry = thread_server_entry_search(&main_list->borderRouterList, service->routerID);
|
|
|
|
if (!entry) {
|
|
return -1;
|
|
}
|
|
|
|
entry->canDelete = true;
|
|
if (entry->stableData) {
|
|
networkDataList->stableUpdatePushed = true;
|
|
} else {
|
|
networkDataList->temporaryUpdatePushed = true;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Add new Local DHCPv6 Server information to route List
|
|
*
|
|
* \param networkDataList Pointer main network data structure
|
|
* \param prefixTlv Prefix TLV (domainID, Prefix, PrefixLen)
|
|
* \param service On Mesh prefix TLV
|
|
*
|
|
* return 0, ADD OK
|
|
* return <0 Add Not OK
|
|
*/
|
|
int thread_local_server_list_add_on_mesh_server(thread_network_local_data_cache_entry_t *networkDataList, thread_prefix_tlv_t *prefixTlv, thread_border_router_tlv_entry_t *service)
|
|
{
|
|
int retVal = -1;
|
|
tr_debug("Add prefix: %s prf:%d %s%s%s%s%s%s", trace_ipv6_prefix(prefixTlv->Prefix, prefixTlv->PrefixLen), service->Prf,
|
|
service->P_default_route ? "Default Route " : "", service->P_dhcp ? "DHCPv6 Server " : "", service->P_configure ? "DHCPv6 Configuration " : "",
|
|
service->P_slaac ? "SLAAC " : "", service->P_preferred ? "Preferred " : "", service->P_res1 ? "P_res1 " : "");
|
|
|
|
if (networkDataList) {
|
|
thread_network_local_data_entry_t *prefix_entry = thread_local_prefix_entry_get(&networkDataList->prefix_list, prefixTlv);
|
|
if (prefix_entry) {
|
|
prefix_entry->brDataStable = service->stableData;
|
|
prefix_entry->preference = service->Prf;
|
|
prefix_entry->configure = service->P_configure;
|
|
prefix_entry->defaultRoute = service->P_default_route;
|
|
prefix_entry->onMesh = service->P_on_mesh;
|
|
prefix_entry->ndDns = service->P_nd_dns;
|
|
prefix_entry->res1 = service->P_res1;
|
|
prefix_entry->dhcpv6ServerActive = service->P_dhcp;
|
|
prefix_entry->slaacServerActive = service->P_slaac;
|
|
prefix_entry->slaacPreferred = service->P_preferred;
|
|
|
|
if (service->P_dhcp ||
|
|
service->P_slaac ||
|
|
service->P_res1) {
|
|
prefix_entry->brActive = true;
|
|
}
|
|
|
|
if (prefixTlv->PrefixLen == 0) {
|
|
// Adding as default route ::/0
|
|
prefix_entry->routeActive = true;
|
|
prefix_entry->routeDataStable = service->stableData;
|
|
}
|
|
retVal = 0;
|
|
}
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
/**
|
|
* Del Local DHCPv6 Server information to route List
|
|
*
|
|
* \param networkDataList Pointer main network data structure
|
|
* \param prefixTlv Prefix TLV (domainID, Prefix, PrefixLen)
|
|
*
|
|
* return 0, Del OK
|
|
* return <0 Del Not OK
|
|
*/
|
|
int thread_local_server_list_del_on_mesh_server(thread_network_local_data_cache_entry_t *networkDataList, thread_prefix_tlv_t *prefixTlv)
|
|
{
|
|
thread_network_local_data_entry_t *prefix_entry;
|
|
|
|
if (!networkDataList) {
|
|
return -1;
|
|
}
|
|
|
|
tr_debug("Delete prefix %s", trace_array(prefixTlv->Prefix, prefixBits_to_bytes(prefixTlv->PrefixLen)));
|
|
|
|
prefix_entry = thread_local_prefix_entry_find(&networkDataList->prefix_list, prefixTlv);
|
|
if (!prefix_entry) {
|
|
return -1;
|
|
}
|
|
|
|
tr_debug("Prefix deleted");
|
|
//Delete Entry
|
|
ns_list_remove(&networkDataList->prefix_list, prefix_entry);
|
|
ns_dyn_mem_free(prefix_entry);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Modify or add new service information to the local service list.
|
|
*
|
|
* \param networkDataList Pointer main network data structure.
|
|
* \param service Service information to store to the list.
|
|
*
|
|
* return 0, Operation OK
|
|
* return <0 Operation not OK
|
|
*/
|
|
int thread_local_service_list_add(thread_network_local_data_cache_entry_t *networkDataList, thread_network_data_service_entry_t *service)
|
|
{
|
|
if (!networkDataList) {
|
|
return -1;
|
|
}
|
|
|
|
tr_debug("Add service %"PRIu32": %s", service->S_enterprise_number, trace_array(service->S_service_data, service->S_service_data_length));
|
|
|
|
thread_network_data_service_entry_t *service_entry = thread_local_service_entry_save(&networkDataList->service_list, service);
|
|
|
|
if (!service_entry) {
|
|
return -1;
|
|
}
|
|
|
|
tr_debug("Service added");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Delete service information from the local service list.
|
|
*
|
|
* \param networkDataList Pointer main network data structure
|
|
* \param service Service information to use in deletion.
|
|
*
|
|
* return 0, Delete OK
|
|
* return <0 Delete not OK
|
|
*/
|
|
int thread_local_service_list_del(thread_network_local_data_cache_entry_t *networkDataList, thread_network_data_service_entry_t *service)
|
|
{
|
|
thread_network_data_service_entry_t *service_entry;
|
|
|
|
if (!networkDataList || !service) {
|
|
return -1;
|
|
}
|
|
|
|
tr_debug("Delete service %"PRIu32": %s", service->S_enterprise_number, trace_array(service->S_service_data, service->S_service_data_length));
|
|
|
|
service_entry = thread_local_service_entry_find(&networkDataList->service_list, service);
|
|
|
|
if (!service_entry) {
|
|
return -1;
|
|
}
|
|
|
|
tr_debug("Service deleted");
|
|
|
|
ns_list_remove(&networkDataList->service_list, service_entry);
|
|
|
|
ns_dyn_mem_free(service_entry->S_service_data);
|
|
|
|
if (service_entry->S_server_data) {
|
|
ns_dyn_mem_free(service_entry->S_server_data);
|
|
}
|
|
|
|
ns_dyn_mem_free(service_entry);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Add new local route information to route List
|
|
*
|
|
* \param networkDataList Pointer main network data structure
|
|
* \param routePrefixPtr pointer to route prefix
|
|
* \param prefixLength indicate prefix pointer valid information in bits
|
|
* \param stableService Boolean true generate stable service, false temporary
|
|
*
|
|
* return 0, ADD OK
|
|
* return <0 Add Not OK
|
|
*/
|
|
int thread_local_server_add_route(thread_network_local_data_cache_entry_t *networkDataList, thread_prefix_tlv_t *prefixTlv, thread_border_router_tlv_entry_t *route)
|
|
{
|
|
tr_debug("Add Route: %s", trace_ipv6_prefix(prefixTlv->Prefix, prefixTlv->PrefixLen));
|
|
|
|
if (!networkDataList) {
|
|
return -1;
|
|
}
|
|
|
|
thread_network_local_data_entry_t *prefix_entry = thread_local_prefix_entry_get(&networkDataList->prefix_list, prefixTlv);
|
|
if (!prefix_entry) {
|
|
return -1;
|
|
}
|
|
|
|
prefix_entry->routeActive = true;
|
|
prefix_entry->routeDataStable = route->stableData;
|
|
prefix_entry->preference = route->Prf;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Del local route information to route List
|
|
*
|
|
* \param networkDataList Pointer main network data structure
|
|
* \param prefixTlv Prefix TLV (domainID, Prefix, PrefixLen)
|
|
*
|
|
* return 0, Del OK
|
|
* return <0 Del Not OK
|
|
*/
|
|
int thread_local_server_del_route(thread_network_local_data_cache_entry_t *networkDataList, thread_prefix_tlv_t *prefixTlv)
|
|
{
|
|
thread_network_local_data_entry_t *prefix_entry;
|
|
tr_debug("Del Route: %s",
|
|
trace_array(prefixTlv->Prefix, prefixBits_to_bytes(prefixTlv->PrefixLen)));
|
|
|
|
if (!networkDataList) {
|
|
return -1;
|
|
}
|
|
|
|
prefix_entry = thread_local_prefix_entry_find(&networkDataList->prefix_list, prefixTlv);
|
|
if (!prefix_entry) {
|
|
return -1;
|
|
}
|
|
|
|
prefix_entry->routeActive = false;
|
|
if (prefix_entry->brActive) {
|
|
return 0;
|
|
}
|
|
|
|
tr_debug("Free Entry");
|
|
//Delete Entry
|
|
ns_list_remove(&networkDataList->prefix_list, prefix_entry);
|
|
ns_dyn_mem_free(prefix_entry);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool thread_nd_public_contex_id_allocated(thread_network_data_cache_entry_t *networkDataList, uint8_t tempId)
|
|
{
|
|
ns_list_foreach(thread_network_data_prefix_cache_entry_t, cur, &networkDataList->localPrefixList) {
|
|
if (thread_get_context_by_id(&cur->contextList, tempId)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Add new 6LoWPAN contexts information to Network Data list
|
|
*
|
|
* \param networkDataList Network Interface
|
|
* \param prefixPtr pointer 6LoWPAN Contexts
|
|
* \param prefixLength indicate prefix pointer valid information in bits
|
|
*
|
|
* return 0, ADD OK
|
|
* return <0 Add Not OK
|
|
*/
|
|
uint8_t thread_nd_context_id_allocate(thread_network_data_cache_entry_t *networkDataList, thread_network_local_data_cache_entry_t *localDataList, uint8_t *prefixPtr, uint8_t prefixLength)
|
|
{
|
|
uint8_t cid = 16;
|
|
uint8_t tempId;
|
|
|
|
tr_debug("Disvover Context id for: %s",
|
|
trace_ipv6_prefix(prefixPtr, prefixLength));
|
|
|
|
if (!networkDataList || !localDataList) {
|
|
return cid;
|
|
} else if (!prefixLength) {
|
|
return cid;
|
|
}
|
|
|
|
//Check first is context already allocated
|
|
|
|
//Check Fisrt Public list
|
|
ns_list_foreach(thread_network_data_prefix_cache_entry_t, cur, &networkDataList->localPrefixList) {
|
|
if (cur->servicesPrefixLen >= prefixLength) {
|
|
if (bitsequal(cur->servicesPrefix, prefixPtr, prefixLength)) {
|
|
|
|
//Check Prefix context List
|
|
tempId = thread_get_context_id_by_length(&cur->contextList, prefixLength);
|
|
if (tempId != 16) {
|
|
return tempId;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Allocate Free context id
|
|
if (thread_extension_version_check(thread_version)) {
|
|
tempId = 2;
|
|
} else {
|
|
tempId = 1;
|
|
}
|
|
for (; tempId < 16; tempId++) {
|
|
if (thread_nd_public_contex_id_allocated(networkDataList, tempId)) {
|
|
//Allocated
|
|
} else {
|
|
cid = tempId;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return cid;
|
|
}
|
|
|
|
|
|
/**
|
|
* Add new 6LoWPAN contexts information to Network Data list
|
|
*
|
|
* \param networkDataList Network Data structure pointer
|
|
* \param prefixTlv Prefix TLV (domainID, Prefix, PrefixLen)
|
|
* \param context Context TLV
|
|
*
|
|
* return 0, ADD OK
|
|
* return <0 Add Not OK
|
|
*/
|
|
int thread_nd_local_list_add_contexts(thread_network_data_cache_entry_t *networkDataList, thread_prefix_tlv_t *prefixTlv, thread_network_local_data_context_entry_t *context)
|
|
{
|
|
thread_network_data_prefix_cache_entry_t *main_list;
|
|
thread_network_data_context_entry_t *cur;
|
|
tr_debug("Add Public Context: %s", trace_ipv6_prefix(prefixTlv->Prefix, prefixTlv->PrefixLen));
|
|
|
|
if (!networkDataList) {
|
|
return -1;
|
|
}
|
|
|
|
if (!prefixTlv->PrefixLen) {
|
|
return -1;
|
|
}
|
|
|
|
main_list = thread_prefix_entry_get(&networkDataList->localPrefixList, prefixTlv);
|
|
if (!main_list) {
|
|
return -1;
|
|
}
|
|
|
|
cur = thread_get_main_context_list(&main_list->contextList, context);
|
|
if (!cur) {
|
|
return -1;
|
|
}
|
|
cur->canDelete = false;
|
|
if (context->stableData == true) {
|
|
cur->stableData = true;
|
|
}
|
|
cur->compression = context->compression;
|
|
cur->contextPrefixLength = context->contextPrefixLength;
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint8_t thread_server_prefix_length(thread_network_local_data_entry_t *cur)
|
|
{
|
|
uint8_t tempLength = 0;
|
|
|
|
|
|
if (cur->routeActive) {
|
|
if (!(cur->brActive && cur->defaultRoute)) {
|
|
// HasRoute is added if BorderRouter TLV does not have default route bit
|
|
tempLength += 5;
|
|
}
|
|
}
|
|
|
|
if (cur->brActive) {
|
|
tempLength += 6;
|
|
}
|
|
|
|
if (tempLength) {
|
|
tempLength += prefixBits_to_bytes(cur->servicesPrefixLen);
|
|
tempLength += 2; //Length &domainId
|
|
}
|
|
|
|
return tempLength;
|
|
}
|
|
|
|
static uint8_t thread_service_length(thread_network_data_service_entry_t *cur)
|
|
{
|
|
uint8_t length = 0;
|
|
|
|
length += 1; // T + S_id
|
|
|
|
if (!cur->T) {
|
|
length += 4; // S_enterprise_number
|
|
}
|
|
|
|
// Service data length + service data
|
|
length += 1 + cur->S_service_data_length;
|
|
|
|
if (cur->S_server_data) {
|
|
length += 2; // Sub-TLV's type & length
|
|
length += 2; // S_server_16
|
|
length += cur->S_server_data_length;
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
uint16_t thread_nd_own_service_list_data_size(thread_network_local_data_cache_entry_t *serverDataList)
|
|
{
|
|
uint16_t localDataLength = 0;
|
|
uint16_t tempLength;
|
|
|
|
ns_list_foreach(thread_network_data_service_entry_t, cur, &serverDataList->service_list) {
|
|
tempLength = thread_service_length(cur);
|
|
if (tempLength) {
|
|
localDataLength += tempLength + 2; //Type & Length for service
|
|
}
|
|
}
|
|
|
|
ns_list_foreach(thread_network_local_data_entry_t, cur, &serverDataList->prefix_list) {
|
|
tempLength = thread_server_prefix_length(cur);
|
|
if (tempLength) {
|
|
localDataLength += tempLength + 2; //Type & Length for prefix
|
|
}
|
|
}
|
|
|
|
return localDataLength;
|
|
}
|
|
|
|
static bool thread_check_local_data_prefix_stable_boolean(thread_network_local_data_entry_t *dataList)
|
|
{
|
|
if (dataList->brActive && dataList->brDataStable) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static uint8_t *thread_nd_hosted_prefix_header_write(uint8_t *ptr, uint8_t length, thread_network_local_data_entry_t *cur)
|
|
{
|
|
uint8_t prefixBytesLen = prefixBits_to_bytes(cur->servicesPrefixLen);
|
|
if (thread_check_local_data_prefix_stable_boolean(cur)) {
|
|
*ptr++ = THREAD_NWK_DATA_TYPE_PREFIX | THREAD_NWK_STABLE_DATA;
|
|
} else {
|
|
*ptr++ = THREAD_NWK_DATA_TYPE_PREFIX;
|
|
}
|
|
*ptr++ = length;
|
|
*ptr++ = cur->domainId;
|
|
*ptr++ = cur->servicesPrefixLen;
|
|
if (prefixBytesLen) {
|
|
memset(ptr, 0, prefixBytesLen);
|
|
bitcopy(ptr, cur->servicesPrefix, cur->servicesPrefixLen);
|
|
ptr += prefixBytesLen;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static uint8_t *thread_nd_hosted_service_header_write(uint8_t *ptr, uint8_t length, thread_network_data_service_entry_t *cur)
|
|
{
|
|
if (cur->S_stable) {
|
|
*ptr++ = THREAD_NWK_DATA_TYPE_SERVICE_DATA | THREAD_NWK_STABLE_DATA;
|
|
} else {
|
|
*ptr++ = THREAD_NWK_DATA_TYPE_SERVICE_DATA;
|
|
}
|
|
|
|
*ptr++ = length;
|
|
*ptr++ = (cur->T << 7) | cur->S_id;
|
|
|
|
if (!cur->T) {
|
|
ptr = common_write_32_bit(cur->S_enterprise_number, ptr);
|
|
}
|
|
|
|
*ptr++ = cur->S_service_data_length;
|
|
|
|
memcpy(ptr, cur->S_service_data, cur->S_service_data_length);
|
|
ptr += cur->S_service_data_length;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
static bool thread_nd_network_data_prefix_stable(thread_network_data_prefix_cache_entry_t *dataList)
|
|
{
|
|
ns_list_foreach(thread_network_server_data_entry_t, cur, &dataList->borderRouterList) {
|
|
if (cur->stableData) {
|
|
return true;
|
|
}
|
|
}
|
|
ns_list_foreach(thread_network_server_data_entry_t, cur, &dataList->routeList) {
|
|
if (cur->stableData) {
|
|
return true;
|
|
}
|
|
}
|
|
ns_list_foreach(thread_network_data_context_entry_t, cur, &dataList->contextList) {
|
|
if (cur->stableData) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static uint8_t *thread_nd_prefix_header_write(uint8_t *ptr, thread_network_data_prefix_cache_entry_t *cur, uint8_t tlvLength)
|
|
{
|
|
uint8_t prefixBytesLen = prefixBits_to_bytes(cur->servicesPrefixLen);
|
|
if (thread_nd_network_data_prefix_stable(cur)) {
|
|
*ptr++ = THREAD_NWK_DATA_TYPE_PREFIX | THREAD_NWK_STABLE_DATA;
|
|
} else {
|
|
*ptr++ = THREAD_NWK_DATA_TYPE_PREFIX;
|
|
}
|
|
*ptr++ = tlvLength;
|
|
*ptr++ = cur->domainId;
|
|
*ptr++ = cur->servicesPrefixLen;
|
|
if (prefixBytesLen) {
|
|
memcpy(ptr, cur->servicesPrefix, prefixBytesLen);
|
|
ptr += prefixBytesLen;
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
static bool thread_nd_network_data_service_stable(thread_network_data_service_cache_entry_t *dataList)
|
|
{
|
|
ns_list_foreach(thread_network_data_service_server_entry_t, cur, &dataList->server_list) {
|
|
if (cur->stable) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static uint8_t *thread_nd_service_header_write(uint8_t *ptr, thread_network_data_service_cache_entry_t *cur, uint8_t tlvLength)
|
|
{
|
|
if (thread_nd_network_data_service_stable(cur)) {
|
|
*ptr++ = THREAD_NWK_DATA_TYPE_SERVICE_DATA | THREAD_NWK_STABLE_DATA;
|
|
} else {
|
|
*ptr++ = THREAD_NWK_DATA_TYPE_SERVICE_DATA;
|
|
}
|
|
|
|
*ptr++ = tlvLength;
|
|
*ptr++ = (cur->T << 7) | cur->S_id;
|
|
|
|
if (!cur->T) {
|
|
ptr = common_write_32_bit(cur->S_enterprise_number, ptr);
|
|
}
|
|
|
|
*ptr++ = cur->S_service_data_length;
|
|
|
|
memcpy(ptr, cur->S_service_data, cur->S_service_data_length);
|
|
ptr += cur->S_service_data_length;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
static uint8_t *thread_nd_hosted_service_server_write(uint8_t *ptr, uint16_t router_id, thread_network_data_service_entry_t *cur)
|
|
{
|
|
if (cur->S_stable) {
|
|
*ptr++ = THREAD_NWK_DATA_TYPE_SERVER_DATA | THREAD_NWK_STABLE_DATA;
|
|
} else {
|
|
*ptr++ = THREAD_NWK_DATA_TYPE_SERVER_DATA;
|
|
}
|
|
|
|
*ptr++ = 2 + cur->S_server_data_length;
|
|
ptr = common_write_16_bit(router_id, ptr);
|
|
|
|
memcpy(ptr, cur->S_server_data, cur->S_server_data_length);
|
|
ptr += cur->S_server_data_length;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
static uint8_t *thread_service_border_router_tlv_write(uint8_t *ptr, uint8_t tlvType, uint16_t routerId, uint16_t flags)
|
|
{
|
|
*ptr++ = tlvType;
|
|
*ptr++ = THREAD_BORDER_ROUTER_TLV_LENGTH;
|
|
ptr = thread_nd_network_data_border_router_tlv_write(ptr, routerId, flags);
|
|
return ptr;
|
|
}
|
|
|
|
static uint8_t *thread_service_has_route_tlv_write(uint8_t *ptr, uint8_t tlvType, uint16_t routerId, uint8_t preference)
|
|
{
|
|
*ptr++ = tlvType;
|
|
*ptr++ = THREAD_HAS_ROUTE_TLV_LENGTH;
|
|
ptr = thread_nd_network_data_has_route_tlv_write(ptr, routerId, preference);
|
|
return ptr;
|
|
}
|
|
|
|
uint8_t *thread_nd_own_service_list_data_write(thread_network_local_data_cache_entry_t *serverDataList, uint8_t *ptr, uint16_t routerID)
|
|
{
|
|
uint8_t tlvType;
|
|
uint8_t servicesLen;
|
|
|
|
ns_list_foreach(thread_network_data_service_entry_t, cur, &serverDataList->service_list) {
|
|
servicesLen = thread_service_length(cur);
|
|
if (servicesLen) {
|
|
ptr = thread_nd_hosted_service_header_write(ptr, servicesLen, cur);
|
|
if (cur->S_server_data) {
|
|
ptr = thread_nd_hosted_service_server_write(ptr, routerID, cur);
|
|
}
|
|
}
|
|
}
|
|
|
|
ns_list_foreach(thread_network_local_data_entry_t, cur, &serverDataList->prefix_list) {
|
|
servicesLen = thread_server_prefix_length(cur);
|
|
if (servicesLen) {
|
|
ptr = thread_nd_hosted_prefix_header_write(ptr, servicesLen, cur);
|
|
if (cur->routeActive) {
|
|
if (!(cur->brActive && cur->defaultRoute)) {
|
|
// HasRoute is added if BorderRouter TLV does not have default route bit
|
|
uint8_t preference = 0;
|
|
tlvType = THREAD_NWK_DATA_TYPE_ROUTE;
|
|
if (cur->routeDataStable) {
|
|
tlvType |= THREAD_NWK_STABLE_DATA;
|
|
}
|
|
if (cur->preference) {
|
|
preference |= cur->preference << THREAD_HAS_ROUTE_PRF_BIT_MOVE;
|
|
}
|
|
ptr = thread_service_has_route_tlv_write(ptr, tlvType, routerID, preference);
|
|
}
|
|
}
|
|
|
|
if (cur->brActive) {
|
|
uint16_t flags = 0;
|
|
tlvType = THREAD_NWK_DATA_TYPE_BORDER_ROUTER;
|
|
if (cur->brDataStable) {
|
|
tlvType |= THREAD_NWK_STABLE_DATA;
|
|
}
|
|
if (cur->slaacServerActive) {
|
|
flags |= 1 << THREAD_P_SLAAC_BIT_MOVE;
|
|
}
|
|
if (cur->dhcpv6ServerActive) {
|
|
flags |= 1 << THREAD_P_DHCP_BIT_MOVE;
|
|
}
|
|
if (cur->slaacPreferred) {
|
|
flags |= 1 << THREAD_P_PREFERRED_BIT_MOVE;
|
|
}
|
|
if (cur->preference) {
|
|
flags |= cur->preference << THREAD_PRF_BIT_MOVE;
|
|
}
|
|
if (cur->configure) {
|
|
flags |= 1 << THREAD_P_CONFIGURE_BIT_MOVE;
|
|
}
|
|
if (cur->defaultRoute) {
|
|
flags |= 1 << THREAD_P_DEF_ROUTE_BIT_MOVE;
|
|
}
|
|
if (cur->onMesh) {
|
|
flags |= 1 << THREAD_P_ON_MESH_BIT_MOVE;
|
|
}
|
|
if (cur->ndDns) {
|
|
flags |= 1 << THREAD_P_ND_DNS_BIT_MOVE;
|
|
}
|
|
if (cur->res1) {
|
|
flags |= 1 << THREAD_P_ND_RES_BIT_MOVE;
|
|
}
|
|
ptr = thread_service_border_router_tlv_write(ptr, tlvType, routerID, flags);
|
|
} // slaac or dhcp
|
|
}
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
bool thread_nd_dhcp_anycast_address_mapping_from_network_data(thread_network_data_cache_entry_t *networkDataList, uint16_t *rlocAddress, uint8_t contexId)
|
|
{
|
|
ns_list_foreach(thread_network_data_prefix_cache_entry_t, cur, &networkDataList->localPrefixList) {
|
|
if (thread_get_context_by_id(&cur->contextList, contexId)) {
|
|
//Check current Stable List
|
|
thread_network_server_data_entry_t *server = thread_get_dhcp_server_from_list(&cur->borderRouterList);
|
|
if (server) {
|
|
*rlocAddress = server->routerID;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool thread_nd_service_anycast_address_mapping_from_network_data(thread_network_data_cache_entry_t *networkDataList, uint16_t *rlocAddress, uint8_t S_id)
|
|
{
|
|
ns_list_foreach(thread_network_data_service_cache_entry_t, curService, &networkDataList->service_list) {
|
|
// Go through all services
|
|
if (curService->S_id != S_id) {
|
|
continue;
|
|
}
|
|
/* any server will do - take first from the list */
|
|
thread_network_data_service_server_entry_t *curServiceServer = ns_list_get_first(&curService->server_list);
|
|
if (curServiceServer) {
|
|
*rlocAddress = curServiceServer->router_id;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool thread_nd_on_mesh_address_valid(thread_network_server_data_entry_t *curRoute)
|
|
{
|
|
bool onMeshActive = false;
|
|
if (curRoute->P_dhcp || curRoute->P_slaac || curRoute->P_preferred || curRoute->P_on_mesh) {
|
|
onMeshActive = true;
|
|
}
|
|
|
|
return onMeshActive;
|
|
}
|
|
|
|
thread_network_server_data_entry_t *thread_nd_hosted_by_this_routerid(uint16_t routerId, thread_network_server_data_list_t *list)
|
|
{
|
|
ns_list_foreach(thread_network_server_data_entry_t, curInfo, list) {
|
|
if (curInfo->routerID == routerId) {
|
|
return curInfo;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
bool thread_network_data_services_registered(thread_network_data_cache_entry_t *cachePtr, uint16_t routerID)
|
|
{
|
|
ns_list_foreach(thread_network_data_prefix_cache_entry_t, cur, &cachePtr->localPrefixList) {
|
|
if (thread_nd_hosted_by_this_routerid(routerID, &cur->borderRouterList)) {
|
|
return true;
|
|
}
|
|
if (thread_nd_hosted_by_this_routerid(routerID, &cur->routeList)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint16_t thread_network_data_service_set_size(thread_network_data_cache_entry_t *networkDataList, bool fullList)
|
|
{
|
|
uint16_t localDataLength = 0;
|
|
uint16_t tempLength;
|
|
|
|
ns_list_foreach(thread_network_data_service_cache_entry_t, cur, &networkDataList->service_list) {
|
|
tempLength = thread_nd_service_based_on_list_entry_size(cur, fullList);
|
|
if (tempLength) {
|
|
localDataLength += tempLength + 2; //Type & Length for service
|
|
}
|
|
}
|
|
|
|
return localDataLength;
|
|
}
|
|
|
|
uint16_t thread_network_data_prefix_set_size(thread_network_data_cache_entry_t *networkDataList, bool fullList)
|
|
{
|
|
uint16_t localDataLength = 0;
|
|
uint16_t tempLength;
|
|
|
|
ns_list_foreach(thread_network_data_prefix_cache_entry_t, cur, &networkDataList->localPrefixList) {
|
|
tempLength = thread_nd_prefix_based_on_list_entry_size(cur, fullList);
|
|
if (tempLength) {
|
|
localDataLength += tempLength + 2; //Type & Length for prefix
|
|
}
|
|
}
|
|
|
|
return localDataLength;
|
|
}
|
|
|
|
uint8_t *thread_network_data_prefix_set_write(thread_network_data_cache_entry_t *networkDataList, uint8_t *ptr)
|
|
{
|
|
uint8_t tlvLength;
|
|
|
|
//Add always Prefix based data first and Border Router List after That
|
|
ns_list_foreach(thread_network_data_prefix_cache_entry_t, cur, &networkDataList->localPrefixList) {
|
|
tlvLength = thread_nd_prefix_based_on_list_entry_size(cur, true);
|
|
if (tlvLength) {
|
|
ptr = thread_nd_prefix_header_write(ptr, cur, tlvLength);
|
|
tr_debug("Prefix: %s", trace_ipv6_prefix(cur->servicesPrefix, cur->servicesPrefixLen));
|
|
|
|
//Follow canonical order first temporary data and then stable
|
|
//Stable & Temporary subTLVS need to be smallest first also
|
|
//SET Route always to 1. THREAD_NWK_DATA_TYPE_ROUTE
|
|
//DHCPv6 Services 2. THREAD_NWK_DATA_TYPE_BORDER_ROUTER
|
|
//SET Context always to 3. THREAD_NWK_DATA_TYPE_6LOWPAN_ID
|
|
|
|
//write Temporary List this includes the stable and unstable servers with correct address.
|
|
ptr = thread_nd_server_list_write(&cur->routeList, ptr, THREAD_NWK_DATA_TYPE_ROUTE, false);
|
|
ptr = thread_nd_server_list_write(&cur->borderRouterList, ptr, THREAD_NWK_DATA_TYPE_BORDER_ROUTER, false);
|
|
ptr = thread_nd_context_list_write(&cur->contextList, ptr, false);
|
|
|
|
ptr = thread_nd_server_list_write(&cur->routeList, ptr, THREAD_NWK_DATA_TYPE_ROUTE, true);
|
|
ptr = thread_nd_server_list_write(&cur->borderRouterList, ptr, THREAD_NWK_DATA_TYPE_BORDER_ROUTER, true);
|
|
ptr = thread_nd_context_list_write(&cur->contextList, ptr, true);
|
|
}
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
uint8_t *thread_network_data_service_set_write(thread_network_data_cache_entry_t *networkDataList, uint8_t *ptr)
|
|
{
|
|
uint8_t tlvLength;
|
|
|
|
ns_list_foreach(thread_network_data_service_cache_entry_t, cur, &networkDataList->service_list) {
|
|
tlvLength = thread_nd_service_based_on_list_entry_size(cur, true);
|
|
if (tlvLength) {
|
|
ptr = thread_nd_service_header_write(ptr, cur, tlvLength);
|
|
tr_debug("Service: sid:%d e:%"PRIu32" data:%s", cur->S_id, cur->S_enterprise_number, trace_array(cur->S_service_data, cur->S_service_data_length));
|
|
ptr = thread_nd_service_server_list_write(&cur->server_list, ptr, true);
|
|
}
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
bool thread_network_data_service_hosted_by_this_router_id(thread_network_data_service_cache_entry_t *dataList, uint16_t router_id)
|
|
{
|
|
ns_list_foreach(thread_network_data_service_server_entry_t, cur, &dataList->server_list) {
|
|
if (cur->router_id == router_id) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint16_t thread_network_data_service_child_id_from_networkdata_get(thread_network_data_cache_entry_t *networkDataList, uint16_t router_short_addr)
|
|
{
|
|
ns_list_foreach(thread_network_data_service_cache_entry_t, service, &networkDataList->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)) {
|
|
return server->router_id;
|
|
}
|
|
}
|
|
}
|
|
|
|
ns_list_foreach(thread_network_data_prefix_cache_entry_t, cur, &networkDataList->localPrefixList) {
|
|
ns_list_foreach(thread_network_server_data_entry_t, curRoute, &cur->routeList) {
|
|
if (thread_addr_is_child(router_short_addr, curRoute->routerID)) {
|
|
return curRoute->routerID;
|
|
}
|
|
}
|
|
|
|
ns_list_foreach(thread_network_server_data_entry_t, curBR, &cur->borderRouterList) {
|
|
if (thread_addr_is_child(router_short_addr, curBR->routerID)) {
|
|
return curBR->routerID;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|