/* * 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 #include #include #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_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_version >= THREAD_VERSION_1_2) { 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