/* * Copyright (c) 2018-2020, Pelion and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "nsconfig.h" #include "string.h" #include "ns_types.h" #include "ns_trace.h" #include "common_functions.h" #include "nsdynmemLIB.h" #include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h" #include "Core/include/ns_address_internal.h" #include "platform/topo_trace.h" mac_neighbor_table_t *mac_neighbor_table_create(uint8_t table_size, neighbor_entry_remove_notify *remove_cb, neighbor_entry_nud_notify *nud_cb, void *user_indentifier) { mac_neighbor_table_t *table_class = ns_dyn_mem_alloc(sizeof(mac_neighbor_table_t) + sizeof(mac_neighbor_table_entry_t) * table_size); if (!table_class) { return NULL; } memset(table_class, 0, sizeof(mac_neighbor_table_t)); mac_neighbor_table_entry_t *cur_ptr = &table_class->neighbor_entry_buffer[0]; table_class->list_total_size = table_size; table_class->table_user_identifier = user_indentifier; table_class->user_nud_notify_cb = nud_cb; table_class->user_remove_notify_cb = remove_cb; ns_list_init(&table_class->neighbour_list); ns_list_init(&table_class->free_list); for (uint8_t i = 0; i < table_size; i++) { memset(cur_ptr, 0, sizeof(mac_neighbor_table_entry_t)); cur_ptr->index = i; //Add to list ns_list_add_to_end(&table_class->free_list, cur_ptr); cur_ptr++; } return table_class; } void mac_neighbor_table_delete(mac_neighbor_table_t *table_class) { mac_neighbor_table_neighbor_list_clean(table_class); ns_dyn_mem_free(table_class); } static void neighbor_table_class_remove_entry(mac_neighbor_table_t *table_class, mac_neighbor_table_entry_t *entry) { ns_list_remove(&table_class->neighbour_list, entry); table_class->neighbour_list_size--; if (entry->nud_active) { entry->nud_active = false; table_class->active_nud_process--; } if (table_class->user_remove_notify_cb) { table_class->user_remove_notify_cb(entry, table_class->table_user_identifier); } topo_trace(TOPOLOGY_MLE, entry->mac64, TOPO_REMOVE); uint8_t index = entry->index; memset(entry, 0, sizeof(mac_neighbor_table_entry_t)); entry->index = index; ns_list_add_to_end(&table_class->free_list, entry); } void mac_neighbor_table_neighbor_list_clean(mac_neighbor_table_t *table_class) { if (!table_class) { return; } ns_list_foreach_safe(mac_neighbor_table_entry_t, cur, &table_class->neighbour_list) { neighbor_table_class_remove_entry(table_class, cur); } topo_trace(TOPOLOGY_MLE, NULL, TOPO_CLEAR); } void mac_neighbor_table_neighbor_timeout_update(mac_neighbor_table_t *table_class, uint32_t time_update) { if (!table_class) { return; } ns_list_foreach_safe(mac_neighbor_table_entry_t, cur, &table_class->neighbour_list) { if (cur->lifetime > time_update) { if (cur->lifetime == 0xffffffff && cur->link_lifetime == 0xffffffff) { continue; //Infinite Lifetime too not touch } cur->lifetime -= time_update; if (!table_class->user_nud_notify_cb || table_class->active_nud_process > ACTIVE_NUD_PROCESS_MAX || cur->nud_active || !cur->rx_on_idle) { continue; } if (table_class->user_nud_notify_cb(cur, table_class->table_user_identifier)) { table_class->active_nud_process++; cur->nud_active = true; } } else { neighbor_table_class_remove_entry(table_class, cur); } } } mac_neighbor_table_entry_t *mac_neighbor_table_entry_allocate(mac_neighbor_table_t *table_class, const uint8_t *mac64) { if (!table_class) { return NULL; } mac_neighbor_table_entry_t *entry = ns_list_get_first(&table_class->free_list); if (!entry) { return NULL; } //Remove from the list ns_list_remove(&table_class->free_list, entry); //Add to list ns_list_add_to_end(&table_class->neighbour_list, entry); table_class->neighbour_list_size++; memcpy(entry->mac64, mac64, 8); entry->mac16 = 0xffff; entry->rx_on_idle = true; entry->ffd_device = true; entry->nud_active = false; entry->advertisment = false; entry->connected_device = false; entry->trusted_device = false; entry->lifetime = NEIGHBOR_CLASS_LINK_DEFAULT_LIFETIME; entry->link_lifetime = NEIGHBOR_CLASS_LINK_DEFAULT_LIFETIME; entry->link_role = NORMAL_NEIGHBOUR; topo_trace(TOPOLOGY_MLE, mac64, TOPO_ADD); return entry; } static mac_neighbor_table_entry_t *neighbor_table_class_entry_validate(mac_neighbor_table_t *table_class, mac_neighbor_table_entry_t *neighbor_entry) { ns_list_foreach(mac_neighbor_table_entry_t, cur, &table_class->neighbour_list) { if (cur == neighbor_entry) { return cur; } } return NULL; } void mac_neighbor_table_neighbor_remove(mac_neighbor_table_t *table_class, mac_neighbor_table_entry_t *neighbor_entry) { mac_neighbor_table_entry_t *entry = neighbor_table_class_entry_validate(table_class, neighbor_entry); if (entry) { neighbor_table_class_remove_entry(table_class, entry); } } void mac_neighbor_table_neighbor_refresh(mac_neighbor_table_t *table_class, mac_neighbor_table_entry_t *neighbor_entry, uint32_t life_time) { neighbor_entry->lifetime = life_time; neighbor_entry->link_lifetime = life_time; if (neighbor_entry->nud_active) { neighbor_entry->nud_active = false; table_class->active_nud_process--; } } void mac_neighbor_table_neighbor_connected(mac_neighbor_table_t *table_class, mac_neighbor_table_entry_t *neighbor_entry) { (void)table_class; neighbor_entry->connected_device = true; } void mac_neighbor_table_trusted_neighbor(mac_neighbor_table_t *table_class, mac_neighbor_table_entry_t *neighbor_entry, bool trusted_device) { (void)table_class; if (!neighbor_entry->trusted_device && trusted_device) { neighbor_entry->lifetime = neighbor_entry->link_lifetime; } neighbor_entry->trusted_device = trusted_device; } mac_neighbor_table_entry_t *mac_neighbor_table_address_discover(mac_neighbor_table_t *table_class, const uint8_t *address, uint8_t address_type) { if (!table_class) { return NULL; } uint16_t short_address; if (address_type == ADDR_802_15_4_SHORT) { short_address = common_read_16_bit(address); } else if (address_type == ADDR_802_15_4_LONG) { } else { return NULL; } ns_list_foreach(mac_neighbor_table_entry_t, cur, &table_class->neighbour_list) { if (address_type == ADDR_802_15_4_SHORT) { if (cur->mac16 != 0xffff && cur->mac16 == short_address) { return cur; } } else { if (memcmp(cur->mac64, address, 8) == 0) { return cur; } } } return NULL; } mac_neighbor_table_entry_t *mac_neighbor_table_attribute_discover(mac_neighbor_table_t *table_class, uint8_t index) { ns_list_foreach(mac_neighbor_table_entry_t, cur, &table_class->neighbour_list) { if (cur->index == index) { return cur; } } return NULL; } mac_neighbor_table_entry_t *mac_neighbor_entry_get_by_ll64(mac_neighbor_table_t *table_class, const uint8_t *ipv6Address, bool allocateNew, bool *new_entry_allocated) { // Check it really is LL64 (not LL16) if (memcmp(ipv6Address, ADDR_LINK_LOCAL_PREFIX, 8) != 0) { return NULL; //Mot Link Local Address } if (memcmp((ipv6Address + 8), ADDR_SHORT_ADR_SUFFIC, 6) == 0) { return NULL; } // map uint8_t temporary_mac64[8]; memcpy(temporary_mac64, (ipv6Address + 8), 8); temporary_mac64[0] ^= 2; return mac_neighbor_entry_get_by_mac64(table_class, temporary_mac64, allocateNew, new_entry_allocated); } mac_neighbor_table_entry_t *mac_neighbor_entry_get_by_mac64(mac_neighbor_table_t *table_class, const uint8_t *mac64, bool allocateNew, bool *new_entry_allocated) { mac_neighbor_table_entry_t *entry = mac_neighbor_table_address_discover(table_class, mac64, ADDR_802_15_4_LONG); if (entry || !allocateNew) { if (new_entry_allocated) { *new_entry_allocated = false; } return entry; } if (new_entry_allocated) { *new_entry_allocated = true; } return mac_neighbor_table_entry_allocate(table_class, mac64); } mac_neighbor_table_entry_t *mac_neighbor_entry_get_priority(mac_neighbor_table_t *table_class) { ns_list_foreach(mac_neighbor_table_entry_t, entry, &table_class->neighbour_list) { if (entry->link_role == PRIORITY_PARENT_NEIGHBOUR) { return entry; } } return NULL; }