mirror of https://github.com/ARMmbed/mbed-os.git
505 lines
14 KiB
C
505 lines
14 KiB
C
/*
|
|
* Copyright (c) 2018-2019, Arm Limited 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_list.h"
|
|
#include "ns_trace.h"
|
|
#include "nsdynmemLIB.h"
|
|
#include "fhss_config.h"
|
|
#include "ws_management_api.h"
|
|
#include "NWK_INTERFACE/Include/protocol.h"
|
|
#include "6LoWPAN/ws/ws_cfg_settings.h"
|
|
#include "6LoWPAN/ws/ws_config.h"
|
|
#include "Security/protocols/sec_prot_cfg.h"
|
|
#include "6LoWPAN/ws/ws_pae_timers.h"
|
|
#include "Security/kmp/kmp_addr.h"
|
|
#include "Security/kmp/kmp_api.h"
|
|
#include "Security/protocols/sec_prot_certs.h"
|
|
#include "Security/protocols/sec_prot_keys.h"
|
|
#include "6LoWPAN/ws/ws_pae_lib.h"
|
|
#include "6LoWPAN/ws/ws_pae_key_storage.h"
|
|
|
|
#ifdef HAVE_WS
|
|
|
|
#define TRACE_GROUP "wspl"
|
|
|
|
void ws_pae_lib_kmp_list_init(kmp_list_t *kmp_list)
|
|
{
|
|
ns_list_init(kmp_list);
|
|
}
|
|
|
|
kmp_entry_t *ws_pae_lib_kmp_list_add(kmp_list_t *kmp_list, kmp_api_t *kmp)
|
|
{
|
|
// Already in list
|
|
kmp_entry_t *entry = ws_pae_lib_kmp_list_entry_get(kmp_list, kmp);
|
|
if (entry) {
|
|
return entry;
|
|
}
|
|
|
|
entry = ns_dyn_mem_alloc(sizeof(kmp_entry_t));
|
|
if (!entry) {
|
|
return NULL;
|
|
}
|
|
entry->kmp = kmp;
|
|
entry->timer_running = false;
|
|
|
|
ns_list_add_to_end(kmp_list, entry);
|
|
|
|
return entry;
|
|
}
|
|
|
|
int8_t ws_pae_lib_kmp_list_delete(kmp_list_t *kmp_list, kmp_api_t *kmp)
|
|
{
|
|
kmp_entry_t *entry = ws_pae_lib_kmp_list_entry_get(kmp_list, kmp);
|
|
|
|
if (entry) {
|
|
ns_list_remove(kmp_list, entry);
|
|
kmp_api_delete(entry->kmp);
|
|
ns_dyn_mem_free(entry);
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
kmp_api_t *ws_pae_lib_kmp_list_type_get(kmp_list_t *kmp_list, kmp_type_e type)
|
|
{
|
|
kmp_api_t *kmp = NULL;
|
|
|
|
ns_list_foreach(kmp_entry_t, cur, kmp_list) {
|
|
// If kmp type matches
|
|
if (kmp_api_type_get(cur->kmp) == type) {
|
|
/* If receiving of messages has not been disabled for the kmp (kmp is not
|
|
in terminating phase) prioritizes that kmp */
|
|
if (!kmp_api_receive_disable(cur->kmp)) {
|
|
return cur->kmp;
|
|
}
|
|
// Otherwise returns any kmp that matches
|
|
kmp = cur->kmp;
|
|
}
|
|
}
|
|
return kmp;
|
|
}
|
|
|
|
kmp_api_t *ws_pae_lib_kmp_list_instance_id_get(kmp_list_t *kmp_list, uint8_t instance_id)
|
|
{
|
|
ns_list_foreach(kmp_entry_t, cur, kmp_list) {
|
|
if (kmp_api_instance_id_get(cur->kmp) == instance_id) {
|
|
return cur->kmp;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void ws_pae_lib_kmp_list_free(kmp_list_t *kmp_list)
|
|
{
|
|
ns_list_foreach_safe(kmp_entry_t, cur, kmp_list) {
|
|
kmp_api_delete(cur->kmp);
|
|
ns_dyn_mem_free(cur);
|
|
}
|
|
}
|
|
|
|
kmp_entry_t *ws_pae_lib_kmp_list_entry_get(kmp_list_t *kmp_list, kmp_api_t *kmp)
|
|
{
|
|
ns_list_foreach(kmp_entry_t, cur, kmp_list) {
|
|
if (cur->kmp == kmp) {
|
|
return cur;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool ws_pae_lib_kmp_list_empty(kmp_list_t *kmp_list)
|
|
{
|
|
return ns_list_is_empty(kmp_list);
|
|
}
|
|
|
|
uint8_t ws_pae_lib_kmp_list_count(kmp_list_t *kmp_list)
|
|
{
|
|
return ns_list_count(kmp_list);
|
|
}
|
|
|
|
void ws_pae_lib_kmp_timer_start(kmp_list_t *kmp_list, kmp_entry_t *entry)
|
|
{
|
|
if (ns_list_get_first(kmp_list) != entry) {
|
|
ns_list_remove(kmp_list, entry);
|
|
ns_list_add_to_start(kmp_list, entry);
|
|
}
|
|
entry->timer_running = true;
|
|
}
|
|
|
|
void ws_pae_lib_kmp_timer_stop(kmp_list_t *kmp_list, kmp_entry_t *entry)
|
|
{
|
|
if (ns_list_get_last(kmp_list) != entry) {
|
|
ns_list_remove(kmp_list, entry);
|
|
ns_list_add_to_end(kmp_list, entry);
|
|
}
|
|
entry->timer_running = false;
|
|
}
|
|
|
|
bool ws_pae_lib_kmp_timer_update(kmp_list_t *kmp_list, uint16_t ticks, ws_pae_lib_kmp_timer_timeout timeout)
|
|
{
|
|
if (ns_list_is_empty(kmp_list)) {
|
|
return false;
|
|
}
|
|
|
|
bool timer_running = false;
|
|
|
|
ns_list_foreach_safe(kmp_entry_t, entry, kmp_list) {
|
|
if (entry->timer_running) {
|
|
timeout(entry->kmp, ticks);
|
|
timer_running = true;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return timer_running;
|
|
}
|
|
|
|
void ws_pae_lib_supp_list_init(supp_list_t *supp_list)
|
|
{
|
|
ns_list_init(supp_list);
|
|
}
|
|
|
|
supp_entry_t *ws_pae_lib_supp_list_add(supp_list_t *supp_list, const kmp_addr_t *addr)
|
|
{
|
|
supp_entry_t *entry = ns_dyn_mem_temporary_alloc(sizeof(supp_entry_t));
|
|
|
|
if (!entry) {
|
|
return NULL;
|
|
}
|
|
|
|
ws_pae_lib_supp_init(entry);
|
|
memset(&entry->addr, 0, sizeof(kmp_addr_t));
|
|
entry->addr.type = KMP_ADDR_EUI_64_AND_IP;
|
|
kmp_address_copy(&entry->addr, addr);
|
|
|
|
ns_list_add_to_start(supp_list, entry);
|
|
|
|
return entry;
|
|
}
|
|
|
|
int8_t ws_pae_lib_supp_list_remove(void *instance, supp_list_t *supp_list, supp_entry_t *supp, ws_pae_lib_supp_deleted supp_deleted)
|
|
{
|
|
ns_list_remove(supp_list, supp);
|
|
|
|
ws_pae_lib_supp_delete(supp);
|
|
|
|
ns_dyn_mem_free(supp);
|
|
|
|
if (supp_deleted != NULL) {
|
|
supp_deleted(instance);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
supp_entry_t *ws_pae_lib_supp_list_entry_eui_64_get(const supp_list_t *supp_list, const uint8_t *eui_64)
|
|
{
|
|
ns_list_foreach(supp_entry_t, cur, supp_list) {
|
|
if (memcmp(cur->addr.eui_64, eui_64, 8) == 0) {
|
|
return cur;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ws_pae_lib_supp_list_delete(supp_list_t *supp_list)
|
|
{
|
|
ns_list_foreach_safe(supp_entry_t, entry, supp_list) {
|
|
ws_pae_lib_supp_list_remove(NULL, supp_list, entry, NULL);
|
|
}
|
|
}
|
|
|
|
bool ws_pae_lib_supp_list_timer_update(void *instance, supp_list_t *active_supp_list, uint16_t ticks, ws_pae_lib_kmp_timer_timeout timeout, ws_pae_lib_supp_deleted supp_deleted)
|
|
{
|
|
bool timer_running = false;
|
|
|
|
ns_list_foreach_safe(supp_entry_t, entry, active_supp_list) {
|
|
bool running = ws_pae_lib_supp_timer_update(instance, entry, ticks, timeout);
|
|
if (running) {
|
|
timer_running = true;
|
|
} else {
|
|
ws_pae_lib_supp_list_to_inactive(instance, active_supp_list, entry, supp_deleted);
|
|
}
|
|
}
|
|
|
|
return timer_running;
|
|
}
|
|
|
|
void ws_pae_lib_supp_list_slow_timer_update(supp_list_t *supp_list, uint16_t seconds)
|
|
{
|
|
ns_list_foreach(supp_entry_t, entry, supp_list) {
|
|
if (sec_prot_keys_pmk_lifetime_decrement(&entry->sec_keys, seconds)) {
|
|
tr_info("PMK and PTK expired, eui-64: %s, system time: %"PRIu32"", trace_array(entry->addr.eui_64, 8), protocol_core_monotonic_time / 10);
|
|
}
|
|
if (sec_prot_keys_ptk_lifetime_decrement(&entry->sec_keys, seconds)) {
|
|
tr_info("PTK expired, eui-64: %s, system time: %"PRIu32"", trace_array(entry->addr.eui_64, 8), protocol_core_monotonic_time / 10);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ws_pae_lib_supp_init(supp_entry_t *entry)
|
|
{
|
|
ws_pae_lib_kmp_list_init(&entry->kmp_list);
|
|
memset(&entry->addr, 0, sizeof(kmp_addr_t));
|
|
memset(&entry->sec_keys, 0, sizeof(sec_prot_keys_t));
|
|
entry->ticks = 0;
|
|
entry->waiting_ticks = 0;
|
|
entry->store_ticks = ws_pae_key_storage_storing_interval_get() * 1000;
|
|
entry->active = true;
|
|
entry->access_revoked = false;
|
|
}
|
|
|
|
void ws_pae_lib_supp_delete(supp_entry_t *entry)
|
|
{
|
|
ws_pae_lib_kmp_list_free(&entry->kmp_list);
|
|
}
|
|
|
|
bool ws_pae_lib_supp_timer_update(void *instance, supp_entry_t *entry, uint16_t ticks, ws_pae_lib_kmp_timer_timeout timeout)
|
|
{
|
|
// Updates KMP timers and calls timeout callback
|
|
bool keep_timer_running = ws_pae_lib_kmp_timer_update(&entry->kmp_list, ticks, timeout);
|
|
|
|
// If KMPs are not active updates supplicant timer
|
|
if (!keep_timer_running) {
|
|
if (entry->ticks > ticks) {
|
|
keep_timer_running = true;
|
|
entry->ticks -= ticks;
|
|
} else {
|
|
entry->ticks = 0;
|
|
}
|
|
}
|
|
|
|
// Updates retry timer
|
|
if (entry->waiting_ticks > ticks) {
|
|
keep_timer_running = true;
|
|
entry->waiting_ticks -= ticks;
|
|
} else {
|
|
if (entry->waiting_ticks > 0) {
|
|
tr_info("Waiting supplicant timeout eui-64: %s", trace_array(entry->addr.eui_64, 8));
|
|
}
|
|
entry->waiting_ticks = 0;
|
|
}
|
|
|
|
if (!instance) {
|
|
return keep_timer_running;
|
|
}
|
|
|
|
// Updates retry timer
|
|
if (entry->store_ticks > ticks) {
|
|
entry->store_ticks -= ticks;
|
|
} else {
|
|
tr_info("PAE active entry key storage update timeout");
|
|
ws_pae_key_storage_supp_write(instance, entry);
|
|
entry->store_ticks = ws_pae_key_storage_storing_interval_get() * 1000;
|
|
}
|
|
|
|
return keep_timer_running;
|
|
}
|
|
|
|
void ws_pae_lib_supp_timer_ticks_set(supp_entry_t *entry, uint32_t ticks)
|
|
{
|
|
entry->ticks = ticks;
|
|
}
|
|
|
|
void ws_pae_lib_supp_timer_ticks_add(supp_entry_t *entry, uint32_t ticks)
|
|
{
|
|
entry->ticks += ticks;
|
|
}
|
|
|
|
bool ws_pae_lib_supp_timer_is_running(supp_entry_t *entry)
|
|
{
|
|
if (entry->ticks == 0) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ws_pae_lib_supp_list_to_active(supp_list_t *active_supp_list, supp_list_t *inactive_supp_list, supp_entry_t *entry)
|
|
{
|
|
if (entry->active) {
|
|
return;
|
|
}
|
|
|
|
tr_debug("PAE: to active, eui-64: %s", trace_array(entry->addr.eui_64, 8));
|
|
|
|
ns_list_remove(inactive_supp_list, entry);
|
|
ns_list_add_to_start(active_supp_list, entry);
|
|
|
|
entry->active = true;
|
|
entry->ticks = 0;
|
|
|
|
// Adds relay address data
|
|
entry->addr.type = KMP_ADDR_EUI_64_AND_IP;
|
|
}
|
|
|
|
void ws_pae_lib_supp_list_to_inactive(void *instance, supp_list_t *active_supp_list, supp_entry_t *entry, ws_pae_lib_supp_deleted supp_deleted)
|
|
{
|
|
if (!entry->active) {
|
|
return;
|
|
}
|
|
|
|
tr_debug("PAE: to inactive, eui-64: %s", trace_array(entry->addr.eui_64, 8));
|
|
|
|
if (entry->access_revoked) {
|
|
tr_info("Access revoked; deleted, eui-64: %s", trace_array(entry->addr.eui_64, 8));
|
|
ws_pae_lib_supp_list_remove(instance, active_supp_list, entry, supp_deleted);
|
|
return;
|
|
}
|
|
|
|
// Store to key storage
|
|
ws_pae_key_storage_supp_write(instance, entry);
|
|
|
|
// Remove supplicant entry
|
|
ws_pae_lib_supp_list_remove(instance, active_supp_list, entry, supp_deleted);
|
|
if (supp_deleted) {
|
|
supp_deleted(instance);
|
|
}
|
|
}
|
|
|
|
void ws_pae_lib_supp_list_purge(void *instance, supp_list_t *active_supp_list, uint16_t max_number, uint8_t max_purge, ws_pae_lib_supp_deleted supp_deleted)
|
|
{
|
|
uint16_t active_supp = ns_list_count(active_supp_list);
|
|
|
|
if (active_supp > max_number) {
|
|
uint16_t remove_count = active_supp - max_number;
|
|
if (max_purge > 0 && remove_count > max_purge) {
|
|
remove_count = max_purge;
|
|
}
|
|
|
|
// Remove entries from active list if there are no active KMPs ongoing for the entry
|
|
ns_list_foreach_safe(supp_entry_t, entry, active_supp_list) {
|
|
if (remove_count > 0 && ws_pae_lib_kmp_list_empty(&entry->kmp_list)) {
|
|
tr_info("Active supplicant removed, eui-64: %s", trace_array(kmp_address_eui_64_get(&entry->addr), 8));
|
|
ws_pae_lib_supp_list_remove(instance, active_supp_list, entry, supp_deleted);
|
|
remove_count--;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
uint16_t ws_pae_lib_supp_list_kmp_count(supp_list_t *supp_list, kmp_type_e type)
|
|
{
|
|
uint16_t kmp_count = 0;
|
|
|
|
ns_list_foreach(supp_entry_t, entry, supp_list) {
|
|
ns_list_foreach(kmp_entry_t, kmp_entry, &entry->kmp_list) {
|
|
if (kmp_api_type_get(kmp_entry->kmp) == type) {
|
|
kmp_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return kmp_count;
|
|
}
|
|
|
|
bool ws_pae_lib_supp_list_entry_is_in_list(supp_list_t *supp_list, supp_entry_t *searched_entry)
|
|
{
|
|
ns_list_foreach(supp_entry_t, entry, supp_list) {
|
|
if (entry == searched_entry) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
kmp_api_t *ws_pae_lib_supp_list_kmp_receive_check(supp_list_t *supp_list, const void *pdu, uint16_t size)
|
|
{
|
|
ns_list_foreach(supp_entry_t, entry, supp_list) {
|
|
ns_list_foreach(kmp_entry_t, kmp_entry, &entry->kmp_list) {
|
|
if (kmp_api_receive_check(kmp_entry->kmp, pdu, size)) {
|
|
return kmp_entry->kmp;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int8_t ws_pae_lib_shared_comp_list_init(shared_comp_list_t *comp_list)
|
|
{
|
|
ns_list_init(comp_list);
|
|
return 0;
|
|
}
|
|
|
|
int8_t ws_pae_lib_shared_comp_list_free(shared_comp_list_t *comp_list)
|
|
{
|
|
ns_list_foreach_safe(shared_comp_entry_t, entry, comp_list) {
|
|
if (entry->data->delete) {
|
|
entry->data->delete ();
|
|
}
|
|
ns_list_remove(comp_list, entry);
|
|
ns_dyn_mem_free(entry);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int8_t ws_pae_lib_shared_comp_list_add(shared_comp_list_t *comp_list, kmp_shared_comp_t *data)
|
|
{
|
|
ns_list_foreach(shared_comp_entry_t, entry, comp_list) {
|
|
if (entry->data == data) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
shared_comp_entry_t *entry = ns_dyn_mem_alloc(sizeof(shared_comp_entry_t));
|
|
if (!entry) {
|
|
return -1;
|
|
}
|
|
entry->data = data;
|
|
ns_list_add_to_end(comp_list, entry);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int8_t ws_pae_lib_shared_comp_list_remove(shared_comp_list_t *comp_list, kmp_shared_comp_t *data)
|
|
{
|
|
ns_list_foreach(shared_comp_entry_t, entry, comp_list) {
|
|
if (entry->data == data) {
|
|
ns_list_remove(comp_list, entry);
|
|
ns_dyn_mem_free(entry);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int8_t ws_pae_lib_shared_comp_list_timeout(shared_comp_list_t *comp_list, uint16_t ticks)
|
|
{
|
|
ns_list_foreach(shared_comp_entry_t, entry, comp_list) {
|
|
if (entry->data->timeout) {
|
|
entry->data->timeout(ticks);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* HAVE_WS */
|