mbed-os/connectivity/nanostack/sal-stack-nanostack/source/6LoWPAN/ws/ws_bootstrap.c

4350 lines
177 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters!

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

/*
* Copyright (c) 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 <string.h>
#include "nsconfig.h"
#ifdef HAVE_WS
#include "ns_types.h"
#include "ns_trace.h"
#include "nsdynmemLIB.h"
#include "net_interface.h"
#include "eventOS_event.h"
#include "randLIB.h"
#include "common_functions.h"
#include "mac_common_defines.h"
#include "sw_mac.h"
#include "ccmLIB.h"
#include "Core/include/ns_monitor.h"
#include "NWK_INTERFACE/Include/protocol.h"
#include "6LoWPAN/Bootstraps/protocol_6lowpan.h"
#include "6LoWPAN/Bootstraps/protocol_6lowpan_interface.h"
#include "ipv6_stack/protocol_ipv6.h"
#include "ipv6_stack/ipv6_routing_table.h"
#include "6LoWPAN/MAC/mac_helper.h"
#include "6LoWPAN/MAC/mac_data_poll.h"
#include "6LoWPAN/MAC/mpx_api.h"
#include "6LoWPAN/MAC/mac_ie_lib.h"
#include "MPL/mpl.h"
#include "RPL/rpl_protocol.h"
#include "RPL/rpl_control.h"
#include "RPL/rpl_data.h"
#include "RPL/rpl_policy.h"
#include "Common_Protocols/icmpv6.h"
#include "Common_Protocols/icmpv6_radv.h"
#include "Common_Protocols/ipv6_constants.h"
#include "Common_Protocols/ip.h"
#include "Service_Libs/Trickle/trickle.h"
#include "Service_Libs/fhss/channel_list.h"
#include "6LoWPAN/ws/ws_common_defines.h"
#include "6LoWPAN/ws/ws_common_defines.h"
#include "6LoWPAN/ws/ws_config.h"
#include "6LoWPAN/ws/ws_common.h"
#include "6LoWPAN/ws/ws_bootstrap.h"
#include "6LoWPAN/ws/ws_bbr_api_internal.h"
#include "6LoWPAN/ws/ws_common_defines.h"
#include "6LoWPAN/ws/ws_llc.h"
#include "6LoWPAN/ws/ws_neighbor_class.h"
#include "6LoWPAN/ws/ws_ie_lib.h"
#include "6LoWPAN/ws/ws_stats.h"
#include "6LoWPAN/ws/ws_cfg_settings.h"
#include "6LoWPAN/lowpan_adaptation_interface.h"
#include "Service_Libs/etx/etx.h"
#include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h"
#include "Service_Libs/nd_proxy/nd_proxy.h"
#include "Service_Libs/blacklist/blacklist.h"
#include "platform/topo_trace.h"
#include "dhcp_service_api.h"
#include "libDHCPv6/libDHCPv6.h"
#include "libDHCPv6/libDHCPv6_vendordata.h"
#include "DHCPv6_client/dhcpv6_client_api.h"
#include "ws_management_api.h"
#include "net_rpl.h"
#include "mac_api.h"
#include "6LoWPAN/ws/ws_pae_controller.h"
#include "6LoWPAN/ws/ws_eapol_pdu.h"
#include "6LoWPAN/ws/ws_eapol_auth_relay.h"
#include "6LoWPAN/ws/ws_eapol_relay.h"
#include "libNET/src/net_dns_internal.h"
#include "Service_Libs/random_early_detection/random_early_detection_api.h"
#define TRACE_GROUP "wsbs"
static void ws_bootstrap_event_handler(arm_event_s *event);
static void ws_bootstrap_state_change(protocol_interface_info_entry_t *cur, icmp_state_t nwk_bootstrap_state);
static bool ws_bootstrap_state_discovery(struct protocol_interface_info_entry *cur);
static bool ws_bootstrap_state_active(struct protocol_interface_info_entry *cur);
static bool ws_bootstrap_state_wait_rpl(struct protocol_interface_info_entry *cur);
static int8_t ws_bootsrap_event_trig(ws_bootsrap_event_type_e event_type, int8_t interface_id, arm_library_event_priority_e priority, void *event_data);
static bool ws_bootstrap_neighbor_info_request(struct protocol_interface_info_entry *interface, const uint8_t *mac_64, llc_neighbour_req_t *neighbor_buffer, bool request_new);
static uint16_t ws_bootstrap_routing_cost_calculate(protocol_interface_info_entry_t *cur);
static uint16_t ws_bootstrap_rank_get(protocol_interface_info_entry_t *cur);
static uint16_t ws_bootstrap_min_rank_inc_get(protocol_interface_info_entry_t *cur);
static void ws_bootstrap_mac_security_enable(protocol_interface_info_entry_t *cur);
static void ws_bootstrap_nw_key_set(protocol_interface_info_entry_t *cur, uint8_t operation, uint8_t index, uint8_t *key);
static void ws_bootstrap_nw_key_clear(protocol_interface_info_entry_t *cur, uint8_t slot);
static void ws_bootstrap_nw_key_index_set(protocol_interface_info_entry_t *cur, uint8_t index);
static void ws_bootstrap_nw_frame_counter_set(protocol_interface_info_entry_t *cur, uint32_t counter, uint8_t slot);
static void ws_bootstrap_nw_frame_counter_read(protocol_interface_info_entry_t *cur, uint32_t *counter, uint8_t slot);
static void ws_bootstrap_nw_info_updated(protocol_interface_info_entry_t *interface_ptr, uint16_t pan_id, uint16_t pan_version, char *network_name);
static void ws_bootstrap_authentication_completed(protocol_interface_info_entry_t *cur, auth_result_e result, uint8_t *target_eui_64);
static const uint8_t *ws_bootstrap_authentication_next_target(protocol_interface_info_entry_t *cur, const uint8_t *previous_eui_64, uint16_t *pan_id);
static bool ws_bootstrap_eapol_congestion_get(protocol_interface_info_entry_t *interface_ptr, uint16_t active_supp);
static void ws_bootstrap_pan_version_increment(protocol_interface_info_entry_t *cur);
static ws_nud_table_entry_t *ws_nud_entry_discover(protocol_interface_info_entry_t *cur, void *neighbor);
static void ws_nud_entry_remove(protocol_interface_info_entry_t *cur, mac_neighbor_table_entry_t *entry_ptr);
static bool ws_neighbor_entry_nud_notify(mac_neighbor_table_entry_t *entry_ptr, void *user_data);
static void ws_address_registration_update(protocol_interface_info_entry_t *interface, const uint8_t addr[16]);
static int8_t ws_bootstrap_neighbor_set(protocol_interface_info_entry_t *cur, parent_info_t *parent_ptr, bool clear_list);
static void ws_bootstrap_parent_confirm(protocol_interface_info_entry_t *cur, struct rpl_instance *instance);
static void ws_bootstrap_candidate_table_reset(protocol_interface_info_entry_t *cur);
static parent_info_t *ws_bootstrap_candidate_parent_get(struct protocol_interface_info_entry *cur, const uint8_t *addr, bool create);
static void ws_bootstrap_candidate_parent_sort(struct protocol_interface_info_entry *cur, parent_info_t *new_entry);
static void ws_bootstrap_packet_congestion_init(protocol_interface_info_entry_t *cur);
static void ws_bootstrap_asynch_trickle_stop(protocol_interface_info_entry_t *cur);
static void ws_bootstrap_advertise_start(protocol_interface_info_entry_t *cur);
static void ws_bootstrap_rpl_scan_start(protocol_interface_info_entry_t *cur);
static uint16_t ws_randomize_fixed_channel(uint16_t configured_fixed_channel, uint8_t number_of_channels, uint32_t *channel_mask);
typedef enum {
WS_PARENT_SOFT_SYNCH = 0, /**< let FHSS make decision if synchronization is needed*/
WS_PARENT_HARD_SYNCH, /**< Synch FHSS with latest synch information*/
WS_EAPOL_PARENT_SYNCH, /**< Broadcast synch with EAPOL parent*/
} ws_parent_synch_e;
static void ws_bootsrap_create_ll_address(uint8_t *ll_address, const uint8_t *mac64)
{
memcpy(ll_address, ADDR_LINK_LOCAL_PREFIX, 8);
memcpy(ll_address + 8, mac64, 8);
ll_address[8] ^= 2;
}
static mac_neighbor_table_entry_t *ws_bootstrap_mac_neighbor_allocate(struct protocol_interface_info_entry *interface, const uint8_t *src64)
{
mac_neighbor_table_entry_t *neighbor = mac_neighbor_table_entry_allocate(mac_neighbor_info(interface), src64);
if (!neighbor) {
return NULL;
}
// TODO only call these for new neighbour
mlme_device_descriptor_t device_desc;
neighbor->lifetime = ws_cfg_neighbour_temporary_lifetime_get();
neighbor->link_lifetime = ws_cfg_neighbour_temporary_lifetime_get();
mac_helper_device_description_write(interface, &device_desc, neighbor->mac64, neighbor->mac16, 0, false);
mac_helper_devicetable_set(&device_desc, interface, neighbor->index, interface->mac_parameters->mac_default_key_index, true);
return neighbor;
}
mac_neighbor_table_entry_t *ws_bootstrap_mac_neighbor_add(struct protocol_interface_info_entry *interface, const uint8_t *src64)
{
mac_neighbor_table_entry_t *neighbor = mac_neighbor_table_address_discover(mac_neighbor_info(interface), src64, MAC_ADDR_MODE_64_BIT);
if (neighbor) {
return neighbor;
}
return ws_bootstrap_mac_neighbor_allocate(interface, src64);
}
void ws_bootstrap_neighbor_set_stable(struct protocol_interface_info_entry *interface, const uint8_t *src64)
{
mac_neighbor_table_entry_t *neighbor = mac_neighbor_table_address_discover(mac_neighbor_info(interface), src64, MAC_ADDR_MODE_64_BIT);
if (neighbor && neighbor->link_lifetime != WS_NEIGHBOR_LINK_TIMEOUT) {
neighbor->lifetime = WS_NEIGHBOR_LINK_TIMEOUT;
neighbor->link_lifetime = WS_NEIGHBOR_LINK_TIMEOUT;
tr_info("Added new neighbor %s : index:%u", trace_array(src64, 8), neighbor->index);
}
}
void ws_bootstrap_mac_neighbor_short_time_set(struct protocol_interface_info_entry *interface, const uint8_t *src64, uint32_t valid_time)
{
mac_neighbor_table_entry_t *neighbor = mac_neighbor_table_address_discover(mac_neighbor_info(interface), src64, MAC_ADDR_MODE_64_BIT);
if (neighbor && neighbor->link_lifetime <= valid_time) {
//mlme_device_descriptor_t device_desc;
neighbor->lifetime = valid_time;
neighbor->link_lifetime = valid_time;
tr_debug("Set short response neighbor %s : index:%u", trace_array(src64, 8), neighbor->index);
}
}
static void ws_bootstrap_neighbor_delete(struct protocol_interface_info_entry *interface, mac_neighbor_table_entry_t *entry_ptr)
{
mac_helper_devicetable_remove(interface->mac_api, entry_ptr->index, entry_ptr->mac64);
etx_neighbor_remove(interface->id, entry_ptr->index, entry_ptr->mac64);
ws_neighbor_class_entry_remove(&interface->ws_info->neighbor_storage, entry_ptr->index);
}
static void ws_bootstap_eapol_neigh_entry_allocate(struct protocol_interface_info_entry *interface)
{
uint8_t mac_64[8];
memset(mac_64, 0, sizeof(mac_64));
mac_neighbor_table_entry_t *mac_entry = ws_bootstrap_mac_neighbor_add(interface, mac_64);
if (!mac_entry) {
return;
}
mac_entry->lifetime = 0xffffffff;
mac_entry->link_lifetime = 0xffffffff;
ws_neighbor_class_entry_t *ws_neigh = ws_neighbor_class_entry_get(&interface->ws_info->neighbor_storage, mac_entry->index);
if (!ws_neigh) {
return;
}
interface->ws_info->eapol_tx_index = mac_entry->index;
}
ws_neighbor_class_entry_t *ws_bootstrap_eapol_tx_temporary_set(struct protocol_interface_info_entry *interface, const uint8_t *src64)
{
mlme_device_descriptor_t device_desc;
mac_neighbor_table_entry_t *mac_entry = mac_neighbor_table_attribute_discover(mac_neighbor_info(interface), interface->ws_info->eapol_tx_index);
if (!mac_entry) {
return NULL;
}
memcpy(mac_entry->mac64, src64, 8);
mac_helper_device_description_write(interface, &device_desc, src64, 0xffff, 0, false);
mac_helper_devicetable_direct_set(interface->mac_api, &device_desc, interface->ws_info->eapol_tx_index);
return ws_neighbor_class_entry_get(&interface->ws_info->neighbor_storage, mac_entry->index);
}
void ws_bootstrap_eapol_tx_temporary_clear(struct protocol_interface_info_entry *interface)
{
mac_neighbor_table_entry_t *mac_entry = mac_neighbor_table_attribute_discover(mac_neighbor_info(interface), interface->ws_info->eapol_tx_index);
if (!mac_entry) {
return;
}
memset(mac_entry->mac64, 0xff, 8);
mac_helper_devicetable_remove(interface->mac_api, interface->ws_info->eapol_tx_index, NULL);
}
static void ws_bootstrap_neighbor_list_clean(struct protocol_interface_info_entry *interface)
{
mac_neighbor_table_neighbor_list_clean(mac_neighbor_info(interface));
//Allocate EAPOL TX temporary neigh entry
ws_bootstap_eapol_neigh_entry_allocate(interface);
}
static void ws_address_reregister_trig(struct protocol_interface_info_entry *interface)
{
if (interface->ws_info->aro_registration_timer == 0) {
interface->ws_info->aro_registration_timer = WS_NEIGHBOR_NUD_TIMEOUT;
}
}
static void ws_bootstrap_address_notification_cb(struct protocol_interface_info_entry *interface, const struct if_address_entry *addr, if_address_callback_t reason)
{
/* No need for LL address registration */
if (addr->source == ADDR_SOURCE_UNKNOWN || !interface->ws_info) {
return;
}
if (reason == ADDR_CALLBACK_DAD_COMPLETE) {
//If address is generated manually we need to force registration
if (addr->source != ADDR_SOURCE_DHCP) {
//Trigger Address Registration only when Bootstrap is ready
if (interface->nwk_bootstrap_state == ER_BOOTSRAP_DONE) {
tr_debug("Address registration %s", trace_ipv6(addr->address));
ws_address_registration_update(interface, addr->address);
}
ws_address_reregister_trig(interface);
}
if (addr_ipv6_scope(addr->address, interface) > IPV6_SCOPE_LINK_LOCAL) {
// at least ula address available inside mesh.
interface->global_address_available = true;
}
} else if (reason == ADDR_CALLBACK_DELETED) {
// What to do?
// Go through address list and check if there is global address still available
if (addr->source == ADDR_SOURCE_DHCP) {
//Deprecate dhcpv address
uint8_t address[16];
memcpy(address, addr->address, 16);
dhcp_client_global_address_delete(interface->id, NULL, address);
}
//Discover prefix policy
addr_policy_remove_by_label(WS_NON_PREFFRED_LABEL);
interface->global_address_available = false;
ns_list_foreach(if_address_entry_t, addr_str, &interface->ip_addresses) {
if (addr_ipv6_scope(addr_str->address, interface) > IPV6_SCOPE_LINK_LOCAL) {
// at least ula address available inside mesh.
interface->global_address_available = true;
break;
}
}
}
// Addressing in Wi-SUN interface was changed for Border router send new event so Application can update the state
if (interface->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER &&
interface->nwk_bootstrap_state == ER_BOOTSRAP_DONE) {
if (interface->bootsrap_state_machine_cnt == 0) {
interface->bootsrap_state_machine_cnt = 10; //Re trigger state check
}
}
}
static void ws_bootstrap_configure_max_retries(protocol_interface_info_entry_t *cur, uint8_t max_mac_retries)
{
mac_helper_mac_mlme_max_retry_set(cur->id, max_mac_retries);
}
static void ws_bootstrap_configure_csma_ca_backoffs(protocol_interface_info_entry_t *cur, uint8_t max_backoffs, uint8_t min_be, uint8_t max_be)
{
mac_helper_mac_mlme_max_csma_backoffs_set(cur->id, max_backoffs);
mac_helper_mac_mlme_be_set(cur->id, min_be, max_be);
}
static void ws_bootstrap_configure_data_request_restart(protocol_interface_info_entry_t *cur, uint8_t cca_failure_restart_max, uint8_t tx_failure_restart_max, uint16_t blacklist_min_ms, uint16_t blacklist_max_ms)
{
mlme_request_restart_config_t request_restart_config;
request_restart_config.cca_failure_restart_max = cca_failure_restart_max;
request_restart_config.tx_failure_restart_max = tx_failure_restart_max;
request_restart_config.blacklist_min_ms = blacklist_min_ms;
request_restart_config.blacklist_max_ms = blacklist_max_ms;
mac_helper_mac_mlme_data_request_restart_set(cur->id, &request_restart_config);
}
static int ws_bootstrap_tasklet_init(protocol_interface_info_entry_t *cur)
{
if (cur->bootStrapId < 0) {
cur->bootStrapId = eventOS_event_handler_create(&ws_bootstrap_event_handler, WS_INIT_EVENT);
tr_info("WS tasklet init");
}
if (cur->bootStrapId < 0) {
tr_error("tasklet init failed");
return -1;
}
return 0;
}
static void ws_nwk_event_post(protocol_interface_info_entry_t *cur, arm_nwk_interface_status_type_e posted_event)
{
arm_event_s event = {
.receiver = cur->net_start_tasklet,
.sender = protocol_read_tasklet_id(), /**< Event sender Tasklet ID */
.event_type = ARM_LIB_NWK_INTERFACE_EVENT,
.event_data = posted_event,
.event_id = (int8_t) cur->id,
.data_ptr = NULL,
.priority = ARM_LIB_LOW_PRIORITY_EVENT,
};
if (eventOS_event_send(&event) != 0) {
tr_error("nwk_net_event_post(): event send failed");
}
}
static int8_t ws_bootsrap_event_trig(ws_bootsrap_event_type_e event_type, int8_t interface_id, arm_library_event_priority_e priority, void *event_data)
{
arm_event_s event = {
.receiver = interface_id,
.sender = 0,
.event_type = event_type,
.priority = priority,
.data_ptr = event_data,
};
return eventOS_event_send(&event);
}
static void ws_nud_table_reset(protocol_interface_info_entry_t *cur)
{
//Empty active list
ns_list_foreach_safe(ws_nud_table_entry_t, entry, &cur->ws_info->active_nud_process) {
ns_list_remove(&cur->ws_info->active_nud_process, entry);
}
//Empty free list
ns_list_foreach_safe(ws_nud_table_entry_t, entry, &cur->ws_info->free_nud_entries) {
ns_list_remove(&cur->ws_info->free_nud_entries, entry);
}
//Add to free list to full
for (int i = 0; i < ACTIVE_NUD_PROCESS_MAX; i++) {
ns_list_add_to_end(&cur->ws_info->free_nud_entries, &cur->ws_info->nud_table_entrys[i]);
}
}
static ws_nud_table_entry_t *ws_nud_entry_get_free(protocol_interface_info_entry_t *cur)
{
ws_nud_table_entry_t *entry = ns_list_get_first(&cur->ws_info->free_nud_entries);
if (entry) {
entry->wait_response = false;
entry->retry_count = 0;
entry->nud_process = false;
entry->timer = randLIB_get_random_in_range(1, 900);
entry->neighbor_info = NULL;
ns_list_remove(&cur->ws_info->free_nud_entries, entry);
ns_list_add_to_end(&cur->ws_info->active_nud_process, entry);
}
return entry;
}
void ws_nud_entry_remove_active(protocol_interface_info_entry_t *cur, void *neighbor)
{
ws_nud_table_entry_t *entry = ws_nud_entry_discover(cur, neighbor);
if (entry) {
mac_neighbor_table_entry_t *mac_neighbor = neighbor;
ns_list_remove(&cur->ws_info->active_nud_process, entry);
ns_list_add_to_end(&cur->ws_info->free_nud_entries, entry);
if (mac_neighbor->nud_active) {
mac_neighbor_table_neighbor_refresh(mac_neighbor_info(cur), mac_neighbor, mac_neighbor->link_lifetime);
}
mac_neighbor_table_neighbor_connected(mac_neighbor_info(cur), mac_neighbor);
}
}
static ws_nud_table_entry_t *ws_nud_entry_discover(protocol_interface_info_entry_t *cur, void *neighbor)
{
ns_list_foreach(ws_nud_table_entry_t, entry, &cur->ws_info->active_nud_process) {
if (entry->neighbor_info == neighbor) {
return entry;
}
}
return NULL;
}
static void ws_nud_state_clean(protocol_interface_info_entry_t *cur, ws_nud_table_entry_t *entry)
{
mac_neighbor_table_entry_t *neighbor = entry->neighbor_info;
ns_list_remove(&cur->ws_info->active_nud_process, entry);
ns_list_add_to_end(&cur->ws_info->free_nud_entries, entry);
if (neighbor->nud_active) {
neighbor->nud_active = false;
mac_neighbor_info(cur)->active_nud_process--;
}
}
static void ws_nud_entry_remove(protocol_interface_info_entry_t *cur, mac_neighbor_table_entry_t *entry_ptr)
{
ws_nud_table_entry_t *nud_entry = ws_nud_entry_discover(cur, entry_ptr);
if (nud_entry) {
ws_nud_state_clean(cur, nud_entry);
}
}
if_address_entry_t *ws_probe_aro_address(protocol_interface_info_entry_t *interface)
{
if (interface->global_address_available) {
ns_list_foreach(if_address_entry_t, address, &interface->ip_addresses) {
if (addr_ipv6_scope(address->address, interface) > IPV6_SCOPE_LINK_LOCAL) {
return address;
}
}
}
return NULL;
}
static bool ws_nud_message_build(protocol_interface_info_entry_t *cur, mac_neighbor_table_entry_t *neighbor, bool nud_process)
{
//Send NS
uint8_t ll_target[16];
aro_t aro_temp;
//SET ARO and src address pointer to NULL by default
aro_t *aro_ptr = NULL;
uint8_t *src_address_ptr = NULL;
ws_bootsrap_create_ll_address(ll_target, neighbor->mac64);
if (nud_process) {
tr_info("NUD generate NS %u", neighbor->index);
} else {
tr_info("Probe generate NS %u", neighbor->index);
if_address_entry_t *gp_address = ws_probe_aro_address(cur);
if (gp_address) {
src_address_ptr = gp_address->address;
aro_temp.status = ARO_SUCCESS;
aro_temp.present = true;
memcpy(aro_temp.eui64, cur->mac, 8);
//Just Short Test
aro_temp.lifetime = 1;
aro_ptr = &aro_temp;
}
}
buffer_t *buffer = icmpv6_build_ns(cur, ll_target, src_address_ptr, true, false, aro_ptr);
if (buffer) {
buffer->options.traffic_class = IP_DSCP_CS6 << IP_TCLASS_DSCP_SHIFT;
protocol_push(buffer);
return true;
}
return false;
}
void ws_nud_active_timer(protocol_interface_info_entry_t *cur, uint16_t ticks)
{
//Convert TICKS to real milliseconds
if (ticks > 0xffff / 100) {
ticks = 0xffff;
} else if (ticks == 0) {
ticks = 1;
} else {
ticks *= 100;
}
ns_list_foreach_safe(ws_nud_table_entry_t, entry, &cur->ws_info->active_nud_process) {
if (entry->timer <= ticks) {
//TX Process or timeout
if (entry->wait_response) {
//Timeout for NUD or Probe
if (entry->nud_process) {
tr_debug("NUD NA timeout");
if (entry->retry_count < 2) {
entry->timer = randLIB_get_random_in_range(1, 900);
entry->wait_response = false;
} else {
//Clear entry from active list
ws_nud_state_clean(cur, entry);
//Remove whole entry
mac_neighbor_table_neighbor_remove(mac_neighbor_info(cur), entry->neighbor_info);
}
} else {
ws_nud_state_clean(cur, entry);
}
} else {
//Random TX wait period is over
entry->wait_response = ws_nud_message_build(cur, entry->neighbor_info, entry->nud_process);
if (!entry->wait_response) {
if (entry->nud_process && entry->retry_count < 2) {
entry->timer = randLIB_get_random_in_range(1, 900);
} else {
//Clear entry from active list
//Remove and try again later on
ws_nud_state_clean(cur, entry);
}
} else {
entry->retry_count++;
entry->timer = 5001;
}
}
} else {
entry->timer -= ticks;
}
}
}
static fhss_ws_neighbor_timing_info_t *ws_get_neighbor_info(const fhss_api_t *api, uint8_t eui64[8])
{
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_fhss_api(api);
if (!cur || !cur->mac_parameters || !mac_neighbor_info(cur)) {
return NULL;
}
mac_neighbor_table_entry_t *mac_neighbor = mac_neighbor_table_address_discover(mac_neighbor_info(cur), eui64, MAC_ADDR_MODE_64_BIT);
if (!mac_neighbor) {
return NULL;
}
ws_neighbor_class_entry_t *ws_neighbor = ws_neighbor_class_entry_get(&cur->ws_info->neighbor_storage, mac_neighbor->index);
if (!ws_neighbor) {
return NULL;
}
return &ws_neighbor->fhss_data;
}
static void ws_bootstrap_llc_hopping_update(struct protocol_interface_info_entry *cur, const fhss_ws_configuration_t *fhss_configuration)
{
cur->ws_info->hopping_schdule.uc_fixed_channel = fhss_configuration->unicast_fixed_channel;
cur->ws_info->hopping_schdule.bc_fixed_channel = fhss_configuration->broadcast_fixed_channel;
// Read UC channel function from WS info because FHSS might be temporarily configured to fixed channel during discovery.
cur->ws_info->hopping_schdule.uc_channel_function = cur->ws_info->cfg->fhss.fhss_uc_channel_function;
cur->ws_info->hopping_schdule.bc_channel_function = fhss_configuration->ws_bc_channel_function;
cur->ws_info->hopping_schdule.fhss_bc_dwell_interval = fhss_configuration->fhss_bc_dwell_interval;
cur->ws_info->hopping_schdule.fhss_broadcast_interval = fhss_configuration->fhss_broadcast_interval;
cur->ws_info->hopping_schdule.fhss_uc_dwell_interval = fhss_configuration->fhss_uc_dwell_interval;
cur->ws_info->hopping_schdule.fhss_bsi = fhss_configuration->bsi;
}
static uint8_t ws_generate_exluded_channel_list_from_active_channels(ws_excluded_channel_data_t *excluded_data, const uint32_t *selected_channel_mask, const uint32_t *global_channel_mask, uint16_t number_of_channels)
{
bool active_range = false;
//Clear Old Data
memset(excluded_data, 0, sizeof(ws_excluded_channel_data_t));
for (uint8_t i = 0; i < number_of_channels; i++) {
if (!(global_channel_mask[0 + (i / 32)] & (1 << (i % 32)))) {
//Global exluded channel
if (active_range) {
//Mark range stop here
active_range = false;
}
continue;
}
if (selected_channel_mask[0 + (i / 32)] & (1 << (i % 32))) {
if (active_range) {
//Mark range stop here
active_range = false;
}
} else {
//Mark excluded channel
//Swap Order already here
excluded_data->channel_mask[0 + (i / 32)] |= 1 << (31 - (i % 32));
excluded_data->excluded_channel_count++;
if (excluded_data->excluded_range_length < WS_EXCLUDED_MAX_RANGE_TO_SEND) {
if (!active_range) {
excluded_data->excluded_range_length++;
active_range = true;
//Set start channel
excluded_data->exluded_range[excluded_data->excluded_range_length - 1].range_start = i;
} else {
excluded_data->exluded_range[excluded_data->excluded_range_length - 1].range_end = i;
}
}
}
}
excluded_data->channel_mask_bytes_inline = ((number_of_channels + 7) / 8);
uint8_t channel_plan = 0;
if (excluded_data->excluded_range_length == 0) {
excluded_data->excuded_channel_ctrl = WS_EXC_CHAN_CTRL_NONE;
} else if (excluded_data->excluded_range_length <= WS_EXCLUDED_MAX_RANGE_TO_SEND) {
uint8_t range_length = (excluded_data->excluded_range_length * 4) + 3;
if (range_length <= ((number_of_channels + 7) / 8) + 6) {
excluded_data->excuded_channel_ctrl = WS_EXC_CHAN_CTRL_RANGE;
} else {
excluded_data->excuded_channel_ctrl = WS_EXC_CHAN_CTRL_BITMASK;
channel_plan = 1;
}
} else {
excluded_data->excuded_channel_ctrl = WS_EXC_CHAN_CTRL_BITMASK;
channel_plan = 1;
}
tr_debug("Excluded ctrl %u, exluded channel count %u, total domain channels %u", excluded_data->excuded_channel_ctrl, excluded_data->excluded_channel_count, number_of_channels);
return channel_plan;
}
static void ws_fhss_configure_channel_masks(protocol_interface_info_entry_t *cur, fhss_ws_configuration_t *fhss_configuration)
{
fhss_configuration->channel_mask_size = cur->ws_info->hopping_schdule.number_of_channels;
ws_generate_channel_list(fhss_configuration->channel_mask, cur->ws_info->hopping_schdule.number_of_channels, cur->ws_info->hopping_schdule.regulatory_domain, cur->ws_info->hopping_schdule.operating_class, cur->ws_info->hopping_schdule.channel_plan_id);
ws_generate_channel_list(fhss_configuration->unicast_channel_mask, cur->ws_info->hopping_schdule.number_of_channels, cur->ws_info->hopping_schdule.regulatory_domain, cur->ws_info->hopping_schdule.operating_class, cur->ws_info->hopping_schdule.channel_plan_id);
// using bitwise AND operation for user set channel mask to remove channels not allowed in this device
for (uint8_t n = 0; n < 8; n++) {
fhss_configuration->unicast_channel_mask[n] &= cur->ws_info->cfg->fhss.fhss_channel_mask[n];
}
//Update Exluded channels
cur->ws_info->hopping_schdule.channel_plan = ws_generate_exluded_channel_list_from_active_channels(&cur->ws_info->hopping_schdule.excluded_channels, fhss_configuration->unicast_channel_mask, fhss_configuration->channel_mask, cur->ws_info->hopping_schdule.number_of_channels);
}
static int8_t ws_fhss_initialize(protocol_interface_info_entry_t *cur)
{
fhss_api_t *fhss_api = ns_sw_mac_get_fhss_api(cur->mac_api);
fhss_ws_configuration_t fhss_configuration;
memset(&fhss_configuration, 0, sizeof(fhss_ws_configuration_t));
if (!fhss_api) {
// When FHSS doesn't exist yet, create one
ws_fhss_configure_channel_masks(cur, &fhss_configuration);
fhss_configuration.fhss_uc_dwell_interval = cur->ws_info->cfg->fhss.fhss_uc_dwell_interval;
fhss_configuration.ws_uc_channel_function = (fhss_ws_channel_functions)cur->ws_info->cfg->fhss.fhss_uc_channel_function;
fhss_configuration.ws_bc_channel_function = (fhss_ws_channel_functions)cur->ws_info->cfg->fhss.fhss_bc_channel_function;
fhss_configuration.fhss_bc_dwell_interval = cur->ws_info->cfg->fhss.fhss_bc_dwell_interval;
fhss_configuration.fhss_broadcast_interval = cur->ws_info->cfg->fhss.fhss_bc_interval;
fhss_api = ns_fhss_ws_create(&fhss_configuration, cur->ws_info->fhss_timer_ptr);
if (!fhss_api) {
return -1;
}
ns_sw_mac_fhss_register(cur->mac_api, fhss_api);
// Allow transmitting unicast frames only on TX slots in normal and expedited forwarding mode
ns_fhss_ws_set_tx_allowance_level(fhss_api, WS_TX_SLOT, WS_TX_SLOT);
} else {
// Read defaults from the configuration to help FHSS testing
const fhss_ws_configuration_t *fhss_configuration_copy = ns_fhss_ws_configuration_get(fhss_api);
if (!fhss_configuration_copy) {
// no configuration set yet
return 0;
}
fhss_configuration = *fhss_configuration_copy;
//Overwrite domain channel setup this will over write a default 35 channel
int num_of_channels = channel_list_count_channels(fhss_configuration_copy->unicast_channel_mask);
cur->ws_info->hopping_schdule.number_of_channels = (uint8_t) num_of_channels;
memcpy(cur->ws_info->cfg->fhss.fhss_channel_mask, fhss_configuration_copy->unicast_channel_mask, sizeof(uint32_t) * 8);
cur->ws_info->cfg->fhss.fhss_uc_channel_function = fhss_configuration_copy->ws_uc_channel_function;
cur->ws_info->cfg->fhss.fhss_bc_channel_function = fhss_configuration_copy->ws_bc_channel_function;
cur->ws_info->cfg->fhss.fhss_bc_dwell_interval = fhss_configuration_copy->fhss_bc_dwell_interval;
cur->ws_info->cfg->fhss.fhss_bc_interval = fhss_configuration_copy->fhss_broadcast_interval;
cur->ws_info->cfg->fhss.fhss_uc_dwell_interval = fhss_configuration_copy->fhss_uc_dwell_interval;
cur->ws_info->cfg->fhss.fhss_bc_fixed_channel = fhss_configuration_copy->broadcast_fixed_channel;
cur->ws_info->cfg->fhss.fhss_uc_fixed_channel = fhss_configuration_copy->unicast_fixed_channel;
ws_fhss_configure_channel_masks(cur, &fhss_configuration);
ns_fhss_ws_configuration_set(fhss_api, &fhss_configuration);
}
return 0;
}
static int8_t ws_fhss_set_defaults(protocol_interface_info_entry_t *cur, fhss_ws_configuration_t *fhss_configuration)
{
fhss_configuration->fhss_uc_dwell_interval = cur->ws_info->cfg->fhss.fhss_uc_dwell_interval;
fhss_configuration->ws_uc_channel_function = (fhss_ws_channel_functions)cur->ws_info->cfg->fhss.fhss_uc_channel_function;
fhss_configuration->ws_bc_channel_function = (fhss_ws_channel_functions)cur->ws_info->cfg->fhss.fhss_bc_channel_function;
fhss_configuration->fhss_bc_dwell_interval = cur->ws_info->cfg->fhss.fhss_bc_dwell_interval;
fhss_configuration->fhss_broadcast_interval = cur->ws_info->cfg->fhss.fhss_bc_interval;
if (cur->ws_info->cfg->fhss.fhss_uc_fixed_channel != 0xffff) {
fhss_configuration->unicast_fixed_channel = cur->ws_info->cfg->fhss.fhss_uc_fixed_channel;
}
fhss_configuration->broadcast_fixed_channel = cur->ws_info->cfg->fhss.fhss_bc_fixed_channel;
return 0;
}
static int8_t ws_fhss_border_router_configure(protocol_interface_info_entry_t *cur)
{
// Read configuration of existing FHSS and start using the default values for any network
fhss_ws_configuration_t fhss_configuration;
memset(&fhss_configuration, 0, sizeof(fhss_ws_configuration_t));
if (ns_fhss_ws_configuration_get(cur->ws_info->fhss_api)) {
memcpy(&fhss_configuration, ns_fhss_ws_configuration_get(cur->ws_info->fhss_api), sizeof(fhss_ws_configuration_t));
}
//GET BSI from BBR module
fhss_configuration.bsi = ws_bbr_bsi_generate(cur);
ws_fhss_configure_channel_masks(cur, &fhss_configuration);
// Randomize fixed channels. Only used if channel plan is fixed.
cur->ws_info->cfg->fhss.fhss_uc_fixed_channel = ws_randomize_fixed_channel(cur->ws_info->cfg->fhss.fhss_uc_fixed_channel, cur->ws_info->hopping_schdule.number_of_channels, fhss_configuration.channel_mask);
cur->ws_info->cfg->fhss.fhss_bc_fixed_channel = ws_randomize_fixed_channel(cur->ws_info->cfg->fhss.fhss_bc_fixed_channel, cur->ws_info->hopping_schdule.number_of_channels, fhss_configuration.channel_mask);
ws_fhss_set_defaults(cur, &fhss_configuration);
ns_fhss_ws_configuration_set(cur->ws_info->fhss_api, &fhss_configuration);
ws_bootstrap_llc_hopping_update(cur, &fhss_configuration);
return 0;
}
static bool ws_channel_allowed(uint8_t channel, uint32_t *channel_mask)
{
if ((1 << (channel % 32)) & (channel_mask[channel / 32])) {
return true;
}
return false;
}
static uint16_t ws_randomize_fixed_channel(uint16_t configured_fixed_channel, uint8_t number_of_channels, uint32_t *channel_mask)
{
if (configured_fixed_channel == 0xFFFF) {
uint16_t random_channel = randLIB_get_random_in_range(0, number_of_channels - 1);
while (ws_channel_allowed(random_channel, channel_mask) == false) {
random_channel = randLIB_get_random_in_range(0, number_of_channels - 1);
}
return random_channel;
} else {
return configured_fixed_channel;
}
}
static int8_t ws_fhss_configure(protocol_interface_info_entry_t *cur, bool discovery)
{
// Read configuration of existing FHSS and start using the default values for any network
fhss_ws_configuration_t fhss_configuration;
memset(&fhss_configuration, 0, sizeof(fhss_ws_configuration_t));
if (ns_fhss_ws_configuration_get(cur->ws_info->fhss_api)) {
memcpy(&fhss_configuration, ns_fhss_ws_configuration_get(cur->ws_info->fhss_api), sizeof(fhss_ws_configuration_t));
ws_fhss_set_defaults(cur, &fhss_configuration);
ws_fhss_configure_channel_masks(cur, &fhss_configuration);
}
// Discovery is done using fixed channel
if (discovery) {
fhss_configuration.ws_uc_channel_function = WS_FIXED_CHANNEL;
} else {
fhss_configuration.ws_uc_channel_function = (fhss_ws_channel_functions)cur->ws_info->cfg->fhss.fhss_uc_channel_function;
}
fhss_configuration.ws_bc_channel_function = WS_FIXED_CHANNEL;
fhss_configuration.fhss_broadcast_interval = 0;
uint8_t tmp_uc_fixed_channel = ws_randomize_fixed_channel(cur->ws_info->cfg->fhss.fhss_uc_fixed_channel, cur->ws_info->hopping_schdule.number_of_channels, fhss_configuration.channel_mask);
uint8_t tmp_bc_fixed_channel = ws_randomize_fixed_channel(cur->ws_info->cfg->fhss.fhss_bc_fixed_channel, cur->ws_info->hopping_schdule.number_of_channels, fhss_configuration.channel_mask);
fhss_configuration.unicast_fixed_channel = tmp_uc_fixed_channel;
fhss_configuration.broadcast_fixed_channel = tmp_bc_fixed_channel;
ns_fhss_ws_configuration_set(cur->ws_info->fhss_api, &fhss_configuration);
ns_fhss_ws_set_hop_count(cur->ws_info->fhss_api, 0xff);
ws_bootstrap_llc_hopping_update(cur, &fhss_configuration);
return 0;
}
static int8_t ws_fhss_enable(protocol_interface_info_entry_t *cur)
{
const fhss_ws_configuration_t *fhss_configuration = ns_fhss_ws_configuration_get(cur->ws_info->fhss_api);
if (!cur->ws_info->fhss_api || !fhss_configuration) {
return -1;
}
// Set the LLC information to follow the actual fhss settings
ws_bootstrap_llc_hopping_update(cur, fhss_configuration);
// Set neighbor info callback
if (ns_fhss_set_neighbor_info_fp(cur->ws_info->fhss_api, &ws_get_neighbor_info)) {
return -1;
}
if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) {
ns_fhss_ws_set_hop_count(cur->ws_info->fhss_api, 0);
}
return 0;
}
/* Sets the parent and broadcast schedule we are following
*
*/
static void ws_bootstrap_primary_parent_set(struct protocol_interface_info_entry *cur, llc_neighbour_req_t *neighbor_info, ws_parent_synch_e synch_req)
{
fhss_ws_configuration_t fhss_configuration;
if (!neighbor_info->ws_neighbor->broadcast_timing_info_stored) {
tr_error("No BC timing info for set new parent");
return;
}
memcpy(&fhss_configuration, ns_fhss_ws_configuration_get(cur->ws_info->fhss_api), sizeof(fhss_ws_configuration_t));
// Learning broadcast network configuration
if (neighbor_info->ws_neighbor->broadcast_shedule_info_stored) {
if (synch_req != WS_EAPOL_PARENT_SYNCH) {
ws_fhss_set_defaults(cur, &fhss_configuration);
}
fhss_configuration.ws_bc_channel_function = (fhss_ws_channel_functions)neighbor_info->ws_neighbor->fhss_data.bc_timing_info.broadcast_channel_function;
if (fhss_configuration.ws_bc_channel_function == WS_FIXED_CHANNEL) {
cur->ws_info->hopping_schdule.bc_fixed_channel = neighbor_info->ws_neighbor->fhss_data.bc_timing_info.fixed_channel;
cur->ws_info->cfg->fhss.fhss_bc_fixed_channel = neighbor_info->ws_neighbor->fhss_data.bc_timing_info.fixed_channel;
}
fhss_configuration.bsi = neighbor_info->ws_neighbor->fhss_data.bc_timing_info.broadcast_schedule_id;
fhss_configuration.fhss_bc_dwell_interval = neighbor_info->ws_neighbor->fhss_data.bc_timing_info.broadcast_dwell_interval;
fhss_configuration.fhss_broadcast_interval = neighbor_info->ws_neighbor->fhss_data.bc_timing_info.broadcast_interval;
fhss_configuration.broadcast_fixed_channel = cur->ws_info->cfg->fhss.fhss_bc_fixed_channel;
neighbor_info->ws_neighbor->synch_done = true;
}
ns_fhss_ws_configuration_set(cur->ws_info->fhss_api, &fhss_configuration);
// We have broadcast schedule set up set the broadcast parent schedule
ns_fhss_ws_set_parent(cur->ws_info->fhss_api, neighbor_info->neighbor->mac64, &neighbor_info->ws_neighbor->fhss_data.bc_timing_info, synch_req != WS_PARENT_SOFT_SYNCH);
// Update LLC to follow updated fhss settings
ws_bootstrap_llc_hopping_update(cur, &fhss_configuration);
}
void ws_bootstrap_eapol_parent_synch(struct protocol_interface_info_entry *cur, llc_neighbour_req_t *neighbor_info)
{
if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER || cur->ws_info->configuration_learned || !neighbor_info->ws_neighbor->broadcast_shedule_info_stored || !neighbor_info->ws_neighbor->broadcast_timing_info_stored) {
return;
}
if (ws_bootstrap_candidate_parent_get(cur, neighbor_info->neighbor->mac64, false) == NULL) {
return;
}
//Store Brodacst Shedule
if (!neighbor_info->ws_neighbor->synch_done) {
ws_bootstrap_primary_parent_set(cur, neighbor_info, WS_EAPOL_PARENT_SYNCH);
} else {
ns_fhss_ws_set_parent(cur->ws_info->fhss_api, neighbor_info->neighbor->mac64, &neighbor_info->ws_neighbor->fhss_data.bc_timing_info, false);
}
}
static void ws_bootstrap_ll_address_validate(struct protocol_interface_info_entry *cur)
{
// Configure EUI64 for MAC if missing
uint8_t mac64[8];
if (!cur->mac_api) {
return;
}
cur->mac_api->mac64_get(cur->mac_api, MAC_EXTENDED_DYNAMIC, mac64);
if (memcmp(mac64, ADDR_UNSPECIFIED, 8) == 0) {
cur->mac_api->mac64_get(cur->mac_api, MAC_EXTENDED_READ_ONLY, mac64);
}
if (memcmp(mac64, ADDR_UNSPECIFIED, 8) == 0) {
// Generate random mac because it was not available
randLIB_get_n_bytes_random(mac64, 8);
mac64[0] |= 2; //Set Local Bit
mac64[0] &= ~1; //Clear multicast bit
tr_info("Generated random MAC address");
}
tr_info("MAC address: %s", trace_array(mac64, 8));
mac_helper_mac64_set(cur, mac64);
memcpy(cur->iid_eui64, mac64, 8);
/* Invert U/L Bit */
cur->iid_eui64[0] ^= 2;
memcpy(cur->iid_slaac, cur->iid_eui64, 8);
}
/* \return 0x0100 to 0xFFFF ETX value (8 bit fraction)
* \return 0xFFFF address not associated
* \return 0x0000 address unknown or other error
* \return 0x0001 no ETX statistics on this interface
*/
uint16_t ws_etx_read(protocol_interface_info_entry_t *interface, addrtype_t addr_type, const uint8_t *addr_ptr)
{
uint16_t etx;
if (!addr_ptr || !interface) {
return 0;
}
uint8_t attribute_index;
mac_neighbor_table_entry_t *mac_neighbor = mac_neighbor_table_address_discover(mac_neighbor_info(interface), addr_ptr + PAN_ID_LEN, addr_type);
if (!mac_neighbor) {
return 0xffff;
}
attribute_index = mac_neighbor->index;
ws_neighbor_class_entry_t *ws_neighbour = ws_neighbor_class_entry_get(&interface->ws_info->neighbor_storage, attribute_index);
etx_storage_t *etx_entry = etx_storage_entry_get(interface->id, attribute_index);
if (!ws_neighbour || !etx_entry) {
return 0xffff;
}
etx = etx_local_etx_read(interface->id, attribute_index);
// If we dont have valid ETX for children we assume good ETX.
// After enough packets is sent to children real calculated ETX is given.
// This might result in ICMP source route errors returned to Border router causing secondary route uses
if (etx == 0xffff && ipv6_neighbour_has_registered_by_eui64(&interface->ipv6_neighbour_cache, mac_neighbor->mac64)) {
return 0x100;
}
return etx;
}
bool ws_bootstrap_nd_ns_transmit(protocol_interface_info_entry_t *cur, ipv6_neighbour_t *entry, bool unicast, uint8_t seq)
{
(void)cur;
(void)seq;
if (unicast) {
// Unicast NS is OK
return false;
}
// Fail the resolution
tr_warn("Link address lost for %s", trace_ipv6(entry->ip_address));
ipv6_neighbour_entry_remove(&cur->ipv6_neighbour_cache, entry);
// True means we skip the message sending
return true;
}
static void ws_bootstrap_dhcp_neighbour_update_cb(int8_t interface_id, uint8_t ll_addr[static 16])
{
if (memcmp(ll_addr, ADDR_LINK_LOCAL_PREFIX, 8)) {
return;
}
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
if (!cur) {
return;
}
uint8_t mac64[8];
memcpy(mac64, ll_addr + 8, 8);
mac64[0] ^= 2;
ws_bootstrap_mac_neighbor_short_time_set(cur, mac64, WS_NEIGHBOUR_DHCP_ENTRY_LIFETIME);
}
static void ws_bootstrap_dhcp_info_notify_cb(int8_t interface, dhcp_option_notify_t *options, dhcp_server_notify_info_t *server_info)
{
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface);
if (!cur) {
return;
}
uint8_t server_ll64[16];
memcpy(server_ll64, ADDR_LINK_LOCAL_PREFIX, 8);
if (server_info->duid_length == 8) {
memcpy(server_ll64 + 8, server_info->duid, 8);
} else {
server_ll64[8] = server_info->duid[0];
server_ll64[9] = server_info->duid[1];
server_ll64[10] = server_info->duid[2];
server_ll64[11] = 0xff;
server_ll64[12] = 0xfe;
server_ll64[13] = server_info->duid[3];
server_ll64[14] = server_info->duid[4];
server_ll64[15] = server_info->duid[5];
}
server_ll64[8] ^= 2;
switch (options->option_type) {
case DHCPV6_OPTION_VENDOR_SPECIFIC_INFO:
if (options->option.vendor_spesific.enterprise_number != ARM_ENTERPRISE_NUMBER) {
break;
}
while (options->option.vendor_spesific.data_length) {
uint16_t option_type;
char *domain;
uint8_t *address;
uint16_t option_len;
option_len = net_dns_option_vendor_option_data_get_next(options->option.vendor_spesific.data, options->option.vendor_spesific.data_length, &option_type);
tr_debug("DHCP vendor specific data type:%u length %d", option_type, option_len);
//tr_debug("DHCP vendor specific data %s", trace_array(options->option.vendor_spesific.data, options->option.vendor_spesific.data_length));
if (option_len == 0) {
// Option fields were corrupted
break;
}
if (option_type == ARM_DHCP_VENDOR_DATA_DNS_QUERY_RESULT) {
// Process ARM DNS query result
domain = NULL;
address = NULL;
if (net_dns_option_vendor_option_data_dns_query_read(options->option.vendor_spesific.data, options->option.vendor_spesific.data_length, &address, &domain) > 0 ||
domain || address) {
// Valid ARM DNS query entry
net_dns_query_result_set(interface, address, domain, server_info->life_time);
}
}
options->option.vendor_spesific.data_length -= option_len;
options->option.vendor_spesific.data += option_len;
}
break;
case DHCPV6_OPTION_DNS_SERVERS:
while (options->option.generic.data_length && options->option.generic.data_length >= 16 && options->option.generic.data_length % 16 == 0) {
// Validate payload to have full 16 byte length addresses without any extra bytes
net_dns_server_address_set(interface, server_ll64, options->option.generic.data, server_info->life_time);
options->option.generic.data_length -= 16;
options->option.generic.data += 16;
}
break;
case DHCPV6_OPTION_DOMAIN_LIST:
net_dns_server_search_list_set(interface, server_ll64, options->option.generic.data, options->option.generic.data_length, server_info->life_time);
break;
default:
break;
}
}
static void ws_bootstrap_memory_configuration()
{
/* Configure memory limits for garbage collection based on total memory size
* Starting from these values
* 5% for High mark
* 2% for critical mark
* 1% for Routing limit
* Memory High Critical Drop routing
* 32K RAM 3200 bytes 1280 Bytes 1024 bytes
* 64K RAM 3200 bytes 1280 Bytes 1024 bytes
* 128K RAM 6400 bytes 2560 Bytes 1280 bytes
* 320K RAM 16000 byte 6400 Bytes 3200 bytes
* 640K RAM 32000 byte 12800 Bytes 6400 bytes
* 1000K RAM 50000 bytes 20000 Bytes 10000 bytes
* 4000K RAM 120000 bytes 40000 Bytes 10000 bytes
* */
// In small memory devices there needs to lower limit so that there some change to be usable
// and there is no use for having very large values on high memory devices
ns_monitor_packet_ingress_rate_limit_by_memory(1024, 10000, 1);
ns_monitor_heap_gc_threshold_set(3200, 120000, 95, 1280, 40000, 98);
return;
}
static int8_t ws_bootstrap_up(protocol_interface_info_entry_t *cur)
{
int8_t ret_val = -1;
if (!cur) {
return -1;
}
if ((cur->configure_flags & INTERFACE_SETUP_MASK) != INTERFACE_SETUP_READY) {
tr_error("Interface not yet fully configured");
return -2;
}
if (ws_fhss_initialize(cur) != 0) {
tr_error("fhss initialization failed");
return -3;
}
if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) {
//BBR init like NVM read
ws_bbr_init(cur);
}
// Save FHSS api
cur->ws_info->fhss_api = ns_sw_mac_get_fhss_api(cur->mac_api);
ws_bootstrap_ll_address_validate(cur);
addr_interface_set_ll64(cur, NULL);
cur->nwk_nd_re_scan_count = 0;
// Trigger discovery for bootstrap
ret_val = nwk_6lowpan_up(cur);
if (ret_val) {
goto cleanup;
}
/* Wi-sun will trig event for stamechine this timer must be zero on init */
cur->bootsrap_state_machine_cnt = 0;
/* Disable SLLAO send/mandatory receive with the ARO */
cur->ipv6_neighbour_cache.use_eui64_as_slla_in_aro = true;
/* Omit sending of NA if ARO SUCCESS */
cur->ipv6_neighbour_cache.omit_na_aro_success = true;
/* Omit sending of NA and consider ACK to be success */
cur->ipv6_neighbour_cache.omit_na = true;
// do not process AROs from NA. This is overriden by Wi-SUN specific failure handling
cur->ipv6_neighbour_cache.recv_na_aro = false;
/* Disable NUD Probes */
cur->ipv6_neighbour_cache.send_nud_probes = false;
cur->ipv6_neighbour_cache.probe_avoided_routers = true;
/*Replace NS handler to disable multicast address queries */
cur->if_ns_transmit = ws_bootstrap_nd_ns_transmit;
dhcp_client_init(cur->id, DHCPV6_DUID_HARDWARE_IEEE_802_NETWORKS_TYPE);
dhcp_service_link_local_rx_cb_set(cur->id, ws_bootstrap_dhcp_neighbour_update_cb);
dhcp_client_configure(cur->id, true, true, true); //RENEW uses SOLICIT, Interface will use 1 instance for address get, IAID address hint is not used.
dhcp_client_solicit_timeout_set(cur->id, WS_DHCP_SOLICIT_TIMEOUT, WS_DHCP_SOLICIT_MAX_RT, WS_DHCP_SOLICIT_MAX_RC);
dhcp_client_option_notification_cb_set(cur->id, ws_bootstrap_dhcp_info_notify_cb);
// Configure memory limits and garbage collection values;
ws_bootstrap_memory_configuration();
ws_nud_table_reset(cur);
ws_bootstrap_candidate_table_reset(cur);
// Zero uptime counters
cur->ws_info->uptime = 0;
cur->ws_info->authentication_time = 0;
cur->ws_info->connected_time = 0;
blacklist_params_set(
WS_BLACKLIST_ENTRY_LIFETIME,
WS_BLACKLIST_TIMER_MAX_TIMEOUT,
WS_BLACKLIST_TIMER_TIMEOUT,
WS_BLACKLIST_ENTRY_MAX_NBR,
WS_BLACKLIST_PURGE_NBR,
WS_BLACKLIST_PURGE_TIMER_TIMEOUT);
ws_bootstrap_event_discovery_start(cur);
return 0;
cleanup:
return ret_val;
}
void ws_bootstrap_disconnect(protocol_interface_info_entry_t *cur, ws_bootsrap_event_type_e event_type)
{
if (cur->nwk_bootstrap_state == ER_RPL_NETWORK_LEAVING) {
//Already moved to leaving state.
return;
}
// We are no longer connected
cur->ws_info->connected_time = 0;
if (cur->rpl_domain && cur->nwk_bootstrap_state == ER_BOOTSRAP_DONE) {
//Stop Asych Timer
ws_bootstrap_asynch_trickle_stop(cur);
tr_debug("Start Network soft leaving");
if (event_type == WS_FAST_DISCONNECT) {
rpl_control_instant_poison(cur, cur->rpl_domain);
cur->bootsrap_state_machine_cnt = 80; //Give 8 seconds time to send Poison
} else {
rpl_control_poison(cur->rpl_domain, 1);
cur->bootsrap_state_machine_cnt = 6000; //Give 10 minutes time for poison if RPL is not report
}
} else {
ws_bootstrap_event_discovery_start(cur);
}
cur->nwk_bootstrap_state = ER_RPL_NETWORK_LEAVING;
}
static void ws_bootstrap_asynch_trickle_stop(protocol_interface_info_entry_t *cur)
{
cur->ws_info->trickle_pas_running = false;
cur->ws_info->trickle_pa_running = false;
cur->ws_info->trickle_pcs_running = false;
cur->ws_info->trickle_pc_running = false;
cur->ws_info->trickle_pc_consistency_block_period = 0;
}
static int8_t ws_bootstrap_down(protocol_interface_info_entry_t *cur)
{
if (!cur || !(cur->lowpan_info & INTERFACE_NWK_ACTIVE)) {
return -1;
}
tr_info("Wi-SUN ifdown");
// Reset MAC for safe upper layer memory free
protocol_mac_reset(cur);
ns_sw_mac_fhss_unregister(cur->mac_api);
ns_fhss_delete(cur->ws_info->fhss_api);
cur->ws_info->fhss_api = NULL;
// Reset WS information
ws_bootstrap_asynch_trickle_stop(cur);
ws_llc_reset(cur);
if (nd_proxy_downstream_interface_unregister(cur->id) != 0) {
tr_warn("nd proxy unregister failed");
}
ws_nud_table_reset(cur);
dhcp_client_delete(cur->id);
ws_eapol_relay_delete(cur);
ws_eapol_auth_relay_delete(cur);
ws_pae_controller_stop(cur);
ws_bootstrap_candidate_table_reset(cur);
blacklist_clear();
cur->if_common_forwarding_out_cb = NULL;
return nwk_6lowpan_down(cur);
}
void ws_bootstrap_configuration_reset(protocol_interface_info_entry_t *cur)
{
// Configure IP stack to operate as Wi-SUN node
// Do not process beacons
cur->mac_parameters->beacon_ind = NULL;
cur->mac_parameters->mac_security_level = 0;
// Set default parameters to interface
cur->configure_flags = INTERFACE_BOOTSTRAP_DEFINED;
cur->configure_flags |= INTERFACE_SECURITY_DEFINED;
cur->lowpan_info = 0;
switch (cur->bootsrap_mode) {
// case NET_6LOWPAN_SLEEPY_HOST:
case ARM_NWK_BOOTSRAP_MODE_6LoWPAN_HOST:
break;
case ARM_NWK_BOOTSRAP_MODE_6LoWPAN_ROUTER:
case ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER:
cur->lowpan_info |= INTERFACE_NWK_ROUTER_DEVICE;
break;
default:
tr_err("Invalid bootstrap_mode");
}
cur->nwk_bootstrap_state = ER_ACTIVE_SCAN;
cur->ws_info->network_pan_id = 0xffff;
ws_bootstrap_asynch_trickle_stop(cur);
//cur->mac_security_key_usage_update_cb = ws_management_mac_security_key_update_cb;
return;
}
static bool ws_bootstrap_network_name_matches(const struct mcps_data_ie_list *ie_ext, const char *network_name_ptr)
{
ws_wp_network_name_t network_name;
if (!network_name_ptr || !ie_ext) {
return false;
}
if (!ws_wp_nested_network_name_read(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &network_name)) {
tr_warn("No network name IE");
return false;
}
if (network_name.network_name_length != strlen(network_name_ptr)) {
return false;
}
if (strncmp(network_name_ptr, (char *)network_name.network_name, network_name.network_name_length) != 0) {
return false;
}
// names have equal length and same characters
return true;
}
static void ws_bootstrap_pan_advertisement_analyse_active(struct protocol_interface_info_entry *cur, ws_pan_information_t *pan_information)
{
/* In Active state
*
* A consistent transmission is defined as a PAN Advertisement received by a node with PAN ID and
* NETNAME-IE / Network Name matching that of the receiving node, and with a PAN-IE / Routing Cost
* the same or worse than (bigger than or equal to) that of the receiving node.
*
* Inconsistent:
*
* Received Routing Cost is smaller than stored one
*
* A PAN Advertisement received by a node with PAN ID and NETNAME-IE / Network name matching
* that of the receiving node, and PAN-IE / Routing Cost better than (smaller than) that of the receiving node.
*
*/
if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) {
//Border router never set consistent that will guarantee that BR will send advertisment
return;
}
#ifdef WISUN_1_0_ERRATA_FIX
if (pan_information->pan_size == cur->ws_info->pan_information.pan_size) {
//If same pan size information then set consistent value
trickle_consistent_heard(&cur->ws_info->trickle_pan_advertisement);
}
#else
// Wi-SUN 1.0 specified functionality, causes extra inconsistencies when we hear higher rank advertisements
if (pan_information->routing_cost >= ws_bootstrap_routing_cost_calculate(cur)) {
trickle_consistent_heard(&cur->ws_info->trickle_pan_advertisement);
} else {
trickle_inconsistent_heard(&cur->ws_info->trickle_pan_advertisement, &cur->ws_info->trickle_params_pan_discovery);
}
#endif
}
static parent_info_t *ws_bootstrap_candidate_parent_get_best(protocol_interface_info_entry_t *cur)
{
ns_list_foreach_safe(parent_info_t, entry, &cur->ws_info->parent_list_reserved) {
tr_info("candidate list a:%s panid:%x cost:%d size:%d rssi:%d txFailure:%u age:%"PRIu32, trace_array(entry->addr, 8), entry->pan_id, entry->pan_information.routing_cost, entry->pan_information.pan_size, entry->signal_dbm, entry->tx_fail, protocol_core_monotonic_time - entry->age);
}
return ns_list_get_first(&cur->ws_info->parent_list_reserved);
}
static void ws_bootstrap_decode_exclude_range_to_mask_by_range(void *mask_buffer, ws_excluded_channel_range_t *range_info, uint16_t number_of_channels)
{
uint16_t range_start, range_stop;
uint8_t mask_index = 0;
//uint8_t channel_index = 0;
uint8_t *range_ptr = range_info->range_start;
uint32_t *mask_ptr = mask_buffer;
while (range_info->number_of_range) {
range_start = common_read_16_bit_inverse(range_ptr);
range_ptr += 2;
range_stop = common_read_16_bit_inverse(range_ptr);
range_ptr += 2;
range_info->number_of_range--;
for (uint16_t channel = 0; channel < number_of_channels; channel++) {
if (channel && (channel % 32 == 0)) {
mask_index++;
//channel_index = 0;
}
if (channel >= range_start && channel <= range_stop) {
//mask_ptr[mask_index] |= 1 << (31 - channel_index);
mask_ptr[0 + (channel / 32)] |= 1 << (31 - (channel % 32));
} else if (channel > range_stop) {
break;
}
}
}
}
static void ws_bootstrap_candidate_parent_store(parent_info_t *parent, const struct mcps_data_ind_s *data, ws_utt_ie_t *ws_utt, ws_us_ie_t *ws_us, ws_pan_information_t *pan_information)
{
parent->ws_utt = *ws_utt;
// Saved from unicast IE
parent->ws_us = *ws_us;
//copy excluded channel here if it is inline
if (ws_us->excluded_channel_ctrl == WS_EXC_CHAN_CTRL_RANGE) {
memset(parent->excluded_channel_data, 0, 32);
//Decode Range to mask here
ws_bootstrap_decode_exclude_range_to_mask_by_range(parent->excluded_channel_data, &parent->ws_us.excluded_channels.range, 256);
parent->ws_us.excluded_channels.mask.channel_mask = parent->excluded_channel_data;
parent->ws_us.excluded_channels.mask.mask_len_inline = 32;
parent->ws_us.excluded_channel_ctrl = WS_EXC_CHAN_CTRL_BITMASK;
} else if (ws_us->excluded_channel_ctrl == WS_EXC_CHAN_CTRL_BITMASK) {
parent->ws_us.excluded_channels.mask.channel_mask = parent->excluded_channel_data;
memcpy(parent->excluded_channel_data, ws_us->excluded_channels.mask.channel_mask, ws_us->excluded_channels.mask.mask_len_inline);
}
// Saved from Pan information, do not overwrite pan_version as it is not valid here
parent->pan_information.pan_size = pan_information->pan_size;
parent->pan_information.routing_cost = pan_information->routing_cost;
parent->pan_information.use_parent_bs = pan_information->use_parent_bs;
parent->pan_information.rpl_routing_method = pan_information->rpl_routing_method;
parent->pan_information.version = pan_information->version;
// Saved from message
parent->timestamp = data->timestamp;
parent->pan_id = data->SrcPANId;
parent->link_quality = data->mpduLinkQuality;
parent->signal_dbm = data->signal_dbm;
memcpy(parent->addr, data->SrcAddr, 8);
parent->age = protocol_core_monotonic_time;
}
static void ws_bootstrap_candidate_table_reset(protocol_interface_info_entry_t *cur)
{
//Empty active list
ns_list_foreach_safe(parent_info_t, entry, &cur->ws_info->parent_list_free) {
ns_list_remove(&cur->ws_info->parent_list_free, entry);
}
//Empty free list
ns_list_foreach_safe(parent_info_t, entry, &cur->ws_info->parent_list_reserved) {
ns_list_remove(&cur->ws_info->parent_list_reserved, entry);
}
//Add to free list to full
for (int i = 0; i < WS_PARENT_LIST_SIZE; i++) {
ns_list_add_to_end(&cur->ws_info->parent_list_free, &cur->ws_info->parent_info[i]);
}
}
static parent_info_t *ws_bootstrap_candidate_parent_allocate(protocol_interface_info_entry_t *cur, const uint8_t *addr)
{
parent_info_t *entry = ns_list_get_first(&cur->ws_info->parent_list_free);
if (entry) {
memcpy(entry->addr, addr, 8);
ns_list_remove(&cur->ws_info->parent_list_free, entry);
ns_list_add_to_end(&cur->ws_info->parent_list_reserved, entry);
} else {
// If there is no free entries always allocate the last one of reserved as it is the worst
entry = ns_list_get_last(&cur->ws_info->parent_list_reserved);
}
if (entry) {
entry->tx_fail = 0;
}
return entry;
}
static parent_info_t *ws_bootstrap_candidate_parent_get(struct protocol_interface_info_entry *cur, const uint8_t *addr, bool create)
{
ns_list_foreach_safe(parent_info_t, entry, &cur->ws_info->parent_list_reserved) {
if (memcmp(entry->addr, addr, 8) == 0) {
return entry;
}
}
if (create) {
return ws_bootstrap_candidate_parent_allocate(cur, addr);
}
return NULL;
}
static void ws_bootstrap_candidate_parent_mark_failure(protocol_interface_info_entry_t *cur, const uint8_t *addr)
{
parent_info_t *entry = ws_bootstrap_candidate_parent_get(cur, addr, false);
if (entry) {
ns_list_remove(&cur->ws_info->parent_list_reserved, entry);
if (entry->tx_fail >= 2) {
ns_list_add_to_end(&cur->ws_info->parent_list_free, entry);
} else {
entry->tx_fail++;
//New last
ns_list_add_to_end(&cur->ws_info->parent_list_reserved, entry);
ws_bootstrap_candidate_parent_sort(cur, entry);
}
}
}
static bool ws_bootstrap_candidate_parent_compare(parent_info_t *p1, parent_info_t *p2)
{
// Return true if P2 is better
// signal lower than threshold for both
// pan_cost
// signal quality
if (p2->tx_fail > p1->tx_fail) {
return false;
}
if (ws_neighbor_class_rsl_from_dbm_calculate(p1->signal_dbm) < (DEVICE_MIN_SENS + CAND_PARENT_THRESHOLD + CAND_PARENT_HYSTERISIS) &&
ws_neighbor_class_rsl_from_dbm_calculate(p2->signal_dbm) > (DEVICE_MIN_SENS + CAND_PARENT_THRESHOLD + CAND_PARENT_HYSTERISIS)) {
// above threshold is always better than not.
return true;
}
if (ws_neighbor_class_rsl_from_dbm_calculate(p2->signal_dbm) < (DEVICE_MIN_SENS + CAND_PARENT_THRESHOLD + CAND_PARENT_HYSTERISIS) &&
ws_neighbor_class_rsl_from_dbm_calculate(p1->signal_dbm) > (DEVICE_MIN_SENS + CAND_PARENT_THRESHOLD + CAND_PARENT_HYSTERISIS)) {
// P2 is less than threshold and P1 is larger so P1 is always better.
return false;
}
// Select the lowest PAN cost
uint16_t p1_pan_cost = (p1->pan_information.routing_cost / PRC_WEIGHT_FACTOR) + (p1->pan_information.pan_size / PS_WEIGHT_FACTOR);
uint16_t p2_pan_cost = (p2->pan_information.routing_cost / PRC_WEIGHT_FACTOR) + (p2->pan_information.pan_size / PS_WEIGHT_FACTOR);
if (p1_pan_cost > p2_pan_cost) {
return true;
} else if (p1_pan_cost < p2_pan_cost) {
return false;
}
// If pan cost is the same then we select the one we hear highest
if (p1->signal_dbm < p2->signal_dbm) {
return true;
}
return false;
}
static void ws_bootstrap_candidate_list_clean(struct protocol_interface_info_entry *cur, uint8_t pan_max, uint32_t current_time, uint16_t pan_id)
{
int pan_count = 0;
ns_list_foreach_safe(parent_info_t, entry, &cur->ws_info->parent_list_reserved) {
if ((current_time - entry->age) > WS_PARENT_LIST_MAX_AGE) {
ns_list_remove(&cur->ws_info->parent_list_reserved, entry);
ns_list_add_to_end(&cur->ws_info->parent_list_free, entry);
continue;
}
if (entry->pan_id == pan_id) {
// Same panid if there is more than limited amount free those
pan_count++;
if (pan_count > pan_max) {
ns_list_remove(&cur->ws_info->parent_list_reserved, entry);
ns_list_add_to_end(&cur->ws_info->parent_list_free, entry);
continue;
}
}
}
}
static void ws_bootstrap_candidate_parent_sort(struct protocol_interface_info_entry *cur, parent_info_t *new_entry)
{
//Remove from the list
ns_list_foreach_safe(parent_info_t, entry, &cur->ws_info->parent_list_reserved) {
if (entry == new_entry) {
// own entry skip it
continue;
}
if (ws_bootstrap_candidate_parent_compare(entry, new_entry)) {
// New entry is better
//tr_debug("candidate list new is better");
ns_list_remove(&cur->ws_info->parent_list_reserved, new_entry);
ns_list_add_before(&cur->ws_info->parent_list_reserved, entry, new_entry);
return;
}
}
}
static void ws_bootstrap_pan_information_store(struct protocol_interface_info_entry *cur, const struct mcps_data_ind_s *data, ws_utt_ie_t *ws_utt, ws_us_ie_t *ws_us, ws_pan_information_t *pan_information)
{
parent_info_t *new_entry;
/* Have List of 20 heard neighbours
* Order those as best based on pan cost
* In single pan order based on signal quality
* in single PAN limit the amount of devices to 5
* If there is no advertisement heard for last hour Clear the neigbour.
*/
// Discovery state processing
//tr_info("neighbour: addr:%s panid:%x signal:%d", trace_array(data->SrcAddr, 8), data->SrcPANId, data->signal_dbm);
// Clean old entries
ws_bootstrap_candidate_list_clean(cur, WS_PARENT_LIST_MAX_PAN_IN_DISCOVERY, protocol_core_monotonic_time, data->SrcPANId);
new_entry = ws_bootstrap_candidate_parent_get(cur, data->SrcAddr, true);
if (!new_entry) {
tr_warn("neighbour creation fail");
return;
}
// Safe the information
ws_bootstrap_candidate_parent_store(new_entry, data, ws_utt, ws_us, pan_information);
// set to the correct place in list
ws_bootstrap_candidate_parent_sort(cur, new_entry);
return;
}
static void ws_bootstrap_pan_advertisement_analyse(struct protocol_interface_info_entry *cur, const struct mcps_data_ind_s *data, const struct mcps_data_ie_list *ie_ext, ws_utt_ie_t *ws_utt, ws_us_ie_t *ws_us)
{
//Validate Pan Conrfirmation is at packet
ws_pan_information_t pan_information;
if (!ws_wp_nested_pan_read(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &pan_information)) {
// Corrupted
tr_error("No pan information");
return;
}
if (ws_us->excluded_channel_ctrl) {
//Validate that we can storage data
if (ws_us->excluded_channel_ctrl == WS_EXC_CHAN_CTRL_BITMASK && ws_us->excluded_channels.mask.mask_len_inline > 32) {
return;
}
}
// Check pan flags so that it is valid
if (!pan_information.rpl_routing_method) {
// NOT RPL routing
//tr_warn("Not supported routing");
return;
}
// Store heard pans and possible candidate parents
ws_bootstrap_pan_information_store(cur, data, ws_utt, ws_us, &pan_information);
if (!(ws_bootstrap_state_active(cur) ||
ws_bootstrap_state_wait_rpl(cur))) {
// During discovery/eapol/config learn we dont do further processing for advertisements
return;
}
// Active state processing
//tr_debug("Advertisement active");
// In active operation less neighbours per pan is allowed
ws_bootstrap_candidate_list_clean(cur, WS_PARENT_LIST_MAX_PAN_IN_ACTIVE, protocol_core_monotonic_time, data->SrcPANId);
// Check if valid PAN
if (data->SrcPANId != cur->ws_info->network_pan_id) {
return;
}
// Save route cost for all known neighbors
llc_neighbour_req_t neighbor_info;
neighbor_info.neighbor = NULL;
if (ws_bootstrap_neighbor_info_request(cur, data->SrcAddr, &neighbor_info, false)) {
neighbor_info.ws_neighbor->routing_cost = pan_information.routing_cost;
}
ws_bootstrap_pan_advertisement_analyse_active(cur, &pan_information);
// Learn latest network information
if (cur->bootsrap_mode != ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER && neighbor_info.neighbor) {
uint8_t ll_address[16];
ws_bootsrap_create_ll_address(ll_address, neighbor_info.neighbor->mac64);
if (rpl_control_is_dodag_parent(cur, ll_address)) {
cur->ws_info->pan_information.pan_size = pan_information.pan_size;
cur->ws_info->pan_information.routing_cost = pan_information.routing_cost;
cur->ws_info->pan_information.rpl_routing_method = pan_information.rpl_routing_method;
cur->ws_info->pan_information.use_parent_bs = pan_information.use_parent_bs;
cur->ws_info->pan_information.version = pan_information.version;
}
}
}
static void ws_bootstrap_pan_advertisement_solicit_analyse(struct protocol_interface_info_entry *cur, const struct mcps_data_ind_s *data, ws_utt_ie_t *ws_utt, ws_us_ie_t *ws_us)
{
(void)data;
(void)ws_utt;
(void)ws_us;
/*
* An inconsistent transmission is defined as:
* A PAN Advertisement Solicit with NETNAME-IE matching that of the receiving node.
*/
trickle_inconsistent_heard(&cur->ws_info->trickle_pan_advertisement, &cur->ws_info->trickle_params_pan_discovery);
/*
* A consistent transmission is defined as
* a PAN Advertisement Solicit with NETNAME-IE / Network Name matching that configured on the receiving node.
*/
trickle_consistent_heard(&cur->ws_info->trickle_pan_advertisement_solicit);
/*
* Optimized PAN discovery to select the parent faster if we hear solicit from someone else
*/
if (ws_bootstrap_state_discovery(cur) && ws_cfg_network_config_get(cur) <= CONFIG_MEDIUM &&
cur->bootsrap_state_machine_cnt > cur->ws_info->trickle_params_pan_discovery.Imin * 2) {
cur->bootsrap_state_machine_cnt = cur->ws_info->trickle_params_pan_discovery.Imin + randLIB_get_random_in_range(0, cur->ws_info->trickle_params_pan_discovery.Imin);
tr_info("Making parent selection in %u s", (cur->bootsrap_state_machine_cnt / 10));
}
if (ws_bootstrap_state_active(cur) && cur->bootsrap_mode != ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) {
mac_neighbor_table_entry_t *neighbor = mac_neighbor_table_address_discover(mac_neighbor_info(cur), data->SrcAddr, ADDR_802_15_4_LONG);
if (neighbor && neighbor->link_role == PRIORITY_PARENT_NEIGHBOUR) {
ws_bootstrap_parent_confirm(cur, NULL);
}
}
}
static void ws_bootstrap_pan_config_analyse(struct protocol_interface_info_entry *cur, const struct mcps_data_ind_s *data, const struct mcps_data_ie_list *ie_ext, ws_utt_ie_t *ws_utt, ws_us_ie_t *ws_us)
{
uint16_t pan_version;
ws_bs_ie_t ws_bs_ie;
uint8_t *gtkhash_ptr;
if (data->SrcPANId != cur->ws_info->network_pan_id) {
return;
}
ws_bt_ie_t ws_bt_ie;
if (!ws_wh_bt_read(ie_ext->headerIeList, ie_ext->headerIeListLength, &ws_bt_ie)) {
tr_warn("BT-IE");
return;
}
/*
* A consistent transmission is defined as a PAN Configuration with a PAN-ID matching that of the receiving node and
* a PANVER-IE / PAN Version greater than or equal to the receiving nodes current PAN version.
*
* A inconsistent transmission is defined as:
*
* A PAN Configuration with PAN-ID matching that of the receiving node and a
* PANVER-IE / PAN Version that is less than the receiving nodes current PAN version.
*/
// TODO Add this to neighbor table
// TODO save all information from config message if version number has changed
if (!ws_wp_nested_pan_version_read(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &pan_version)) {
// Corrupted
tr_warn("no version");
return;
}
gtkhash_ptr = ws_wp_nested_gtkhash_read(ie_ext->payloadIeList, ie_ext->payloadIeListLength);
if (!gtkhash_ptr) {
// Corrupted
tr_error("No gtk hash");
return;
}
if (!ws_wp_nested_bs_read(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &ws_bs_ie)) {
// Corrupted
tr_error("No broadcast schedule");
return;
}
llc_neighbour_req_t neighbor_info;
bool neighbour_pointer_valid;
//Validate BSI
if (cur->bootsrap_mode != ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) {
if (cur->ws_info->ws_bsi_block.block_time && cur->ws_info->ws_bsi_block.old_bsi == ws_bs_ie.broadcast_schedule_identifier) {
tr_debug("Do not accept a old BSI: %u in time %"PRIu32, cur->ws_info->ws_bsi_block.old_bsi, cur->ws_info->ws_bsi_block.block_time);
//Refresh Block time when hear a old BSI
cur->ws_info->ws_bsi_block.block_time = cur->ws_info->cfg->timing.pan_timeout;
return;
}
//When Config is learned and USE Parent BS is enabled compare is this new BSI
if (cur->ws_info->configuration_learned && cur->ws_info->pan_information.use_parent_bs && ws_bs_ie.broadcast_schedule_identifier != cur->ws_info->hopping_schdule.fhss_bsi) {
//Accept only next possible BSI number
if ((cur->ws_info->hopping_schdule.fhss_bsi + 1) != ws_bs_ie.broadcast_schedule_identifier) {
tr_debug("Do not accept a unknown BSI: %u", ws_bs_ie.broadcast_schedule_identifier);
} else {
tr_debug("NEW Brodcast Schedule %u...BR rebooted", ws_bs_ie.broadcast_schedule_identifier);
cur->ws_info->ws_bsi_block.block_time = cur->ws_info->cfg->timing.pan_timeout;
cur->ws_info->ws_bsi_block.old_bsi = cur->ws_info->hopping_schdule.fhss_bsi;
ws_bootstrap_event_disconnect(cur, WS_NORMAL_DISCONNECT);
}
return;
}
}
if (cur->ws_info->configuration_learned || cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) {
//If we are border router or learned configuration we only update already learned neighbours.
neighbour_pointer_valid = ws_bootstrap_neighbor_info_request(cur, data->SrcAddr, &neighbor_info, false);
} else {
neighbour_pointer_valid = ws_bootstrap_neighbor_info_request(cur, data->SrcAddr, &neighbor_info, true);
if (!neighbour_pointer_valid) {
return;
}
ws_bootstrap_neighbor_set_stable(cur, data->SrcAddr);
}
if (neighbour_pointer_valid) {
//Update Neighbor Broadcast and Unicast Parameters
ws_neighbor_class_neighbor_unicast_time_info_update(neighbor_info.ws_neighbor, ws_utt, data->timestamp, (uint8_t *) data->SrcAddr);
ws_neighbor_class_neighbor_unicast_schedule_set(neighbor_info.ws_neighbor, ws_us, &cur->ws_info->hopping_schdule);
ws_neighbor_class_neighbor_broadcast_time_info_update(neighbor_info.ws_neighbor, &ws_bt_ie, data->timestamp);
ws_neighbor_class_neighbor_broadcast_schedule_set(neighbor_info.ws_neighbor, &ws_bs_ie);
}
if (cur->ws_info->configuration_learned) {
tr_info("PAN Config analyse own:%d, heard:%d", cur->ws_info->pan_information.pan_version, pan_version);
if (cur->ws_info->pan_information.pan_version == pan_version) {
//Check if Trgigle have been resetted in short time skip this then
if (cur->ws_info->trickle_pc_consistency_block_period == 0) {
// Same version heard so it is consistent
trickle_consistent_heard(&cur->ws_info->trickle_pan_config);
}
if (neighbour_pointer_valid && neighbor_info.neighbor->link_role == PRIORITY_PARENT_NEIGHBOUR) {
ws_bootstrap_primary_parent_set(cur, &neighbor_info, WS_PARENT_SOFT_SYNCH);
}
// no need to process more
return;
} else {
// received version is different so we need to reset the trickle
trickle_inconsistent_heard(&cur->ws_info->trickle_pan_config, &cur->ws_info->trickle_params_pan_discovery);
if (neighbour_pointer_valid && neighbor_info.neighbor->link_role == PRIORITY_PARENT_NEIGHBOUR) {
ws_bootstrap_primary_parent_set(cur, &neighbor_info, WS_PARENT_HARD_SYNCH);
}
if (common_serial_number_greater_16(cur->ws_info->pan_information.pan_version, pan_version)) {
// older version heard ignoring the message
return;
}
cur->ws_info->trickle_pc_consistency_block_period = WS_CONFIG_CONSISTENT_FILTER_PERIOD;
}
}
if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) {
//Border router does not learn network information
return;
}
/*
* Learn new information from neighbor
*/
tr_info("Updated PAN configuration own:%d, heard:%d", cur->ws_info->pan_information.pan_version, pan_version);
// restart PAN version timer
//Check Here Do we have a selected Primary parent
if (!cur->ws_info->configuration_learned || cur->ws_info->rpl_state == RPL_EVENT_DAO_DONE) {
ws_common_border_router_alive_update(cur);
}
cur->ws_info->pan_information.pan_version = pan_version;
ws_pae_controller_gtk_hash_update(cur, gtkhash_ptr);
ws_pae_controller_nw_key_index_update(cur, data->Key.KeyIndex - 1);
if (!cur->ws_info->configuration_learned) {
// Generate own hopping schedules Follow first parent broadcast and plans and also use same unicast dwell
tr_info("learn network configuration");
cur->ws_info->configuration_learned = true;
// return to state machine after 1-2 s
cur->bootsrap_state_machine_cnt = randLIB_get_random_in_range(10, 20);
// enable frequency hopping for unicast channel and start listening first neighbour
ws_bootstrap_primary_parent_set(cur, &neighbor_info, WS_PARENT_HARD_SYNCH);
// set neighbor as priority parent clear if there is others
protocol_6lowpan_neighbor_priority_clear_all(cur->id, PRIORITY_1ST);
neighbor_info.neighbor->link_role = PRIORITY_PARENT_NEIGHBOUR;
}
}
static void ws_bootstrap_pan_config_solicit_analyse(struct protocol_interface_info_entry *cur, const struct mcps_data_ind_s *data, ws_utt_ie_t *ws_utt, ws_us_ie_t *ws_us)
{
if (data->SrcPANId != cur->ws_info->network_pan_id) {
return;
}
/* TODO smart neighbour process
*
* Unsecure packet we cant trust the device?
*
* Question mark in specification also present, now we create neighbour.
* this is moved in future to NS/ND processing triggered by RPL
*
*/
llc_neighbour_req_t neighbor_info;
if (ws_bootstrap_neighbor_info_request(cur, data->SrcAddr, &neighbor_info, false)) {
ws_neighbor_class_neighbor_unicast_time_info_update(neighbor_info.ws_neighbor, ws_utt, data->timestamp, (uint8_t *) data->SrcAddr);
ws_neighbor_class_neighbor_unicast_schedule_set(neighbor_info.ws_neighbor, ws_us, &cur->ws_info->hopping_schdule);
}
if (ws_bootstrap_state_active(cur) && cur->bootsrap_mode != ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) {
mac_neighbor_table_entry_t *neighbor = mac_neighbor_table_address_discover(mac_neighbor_info(cur), data->SrcAddr, ADDR_802_15_4_LONG);
if (neighbor && neighbor->link_role == PRIORITY_PARENT_NEIGHBOUR) {
ws_bootstrap_parent_confirm(cur, NULL);
}
}
/*
* A consistent transmission is defined as a PAN Configuration Solicit with
* a PAN-ID matching that of the receiving node and a NETNAME-IE / Network Name
* matching that configured on the receiving node.
*/
trickle_consistent_heard(&cur->ws_info->trickle_pan_config_solicit);
/*
* inconsistent transmission is defined as either:
* A PAN Configuration Solicit with a PAN-ID matching that of the receiving node and
* a NETNAME-IE / Network Name matching the network name configured on the receiving
*/
trickle_inconsistent_heard(&cur->ws_info->trickle_pan_config, &cur->ws_info->trickle_params_pan_discovery);
}
static bool ws_channel_plan_zero_compare(ws_channel_plan_zero_t *rx_plan, ws_hopping_schedule_t *hopping_schdule)
{
if (rx_plan->operation_class != hopping_schdule->operating_class) {
return false;
} else if (rx_plan->regulator_domain != hopping_schdule->regulatory_domain) {
return false;
}
return true;
}
static bool ws_channel_plan_one_compare(ws_channel_plan_one_t *rx_plan, ws_hopping_schedule_t *hopping_schdule)
{
uint16_t num_of_channel = hopping_schdule->number_of_channels;
if (rx_plan->ch0 != hopping_schdule->ch0_freq) {
return false;
} else if (rx_plan->channel_spacing != hopping_schdule->channel_spacing) {
return false;
} else if (rx_plan->number_of_channel != num_of_channel) {
return false;
}
return true;
}
bool ws_bootstrap_validate_channel_plan(ws_us_ie_t *ws_us, struct protocol_interface_info_entry *cur)
{
if (ws_us->channel_plan == 0) {
if (!ws_channel_plan_zero_compare(&ws_us->plan.zero, &cur->ws_info->hopping_schdule)) {
return false;
}
} else if (ws_us->channel_plan == 1) {
if (!ws_channel_plan_one_compare(&ws_us->plan.one, &cur->ws_info->hopping_schdule)) {
return false;
}
}
return true;
}
bool ws_bootstrap_validate_channel_function(ws_us_ie_t *ws_us, ws_bs_ie_t *ws_bs)
{
if (ws_us) {
if (ws_us->channel_function != WS_FIXED_CHANNEL &&
ws_us->channel_function != WS_TR51CF &&
ws_us->channel_function != WS_DH1CF) {
return false;
}
}
if (ws_bs) {
if (ws_bs->channel_function != WS_FIXED_CHANNEL &&
ws_bs->channel_function != WS_TR51CF &&
ws_bs->channel_function != WS_DH1CF) {
return false;
}
}
return true;
}
static void ws_bootstrap_asynch_ind(struct protocol_interface_info_entry *cur, const struct mcps_data_ind_s *data, const struct mcps_data_ie_list *ie_ext, uint8_t message_type)
{
// Store weakest heard packet RSSI
if (cur->ws_info->weakest_received_rssi > data->signal_dbm) {
cur->ws_info->weakest_received_rssi = data->signal_dbm;
}
if (data->SrcAddrMode != MAC_ADDR_MODE_64_BIT) {
// Not from long address
return;
}
ws_stats_update(cur, STATS_WS_ASYNCH_RX, 1);
//Validate network name
switch (message_type) {
case WS_FT_PAN_ADVERT:
case WS_FT_PAN_ADVERT_SOL:
case WS_FT_PAN_CONF_SOL:
//Check Network Name
if (!ws_bootstrap_network_name_matches(ie_ext, cur->ws_info->cfg->gen.network_name)) {
// Not in our network
return;
}
break;
case WS_FT_PAN_CONF:
break;
default:
return;
}
//UTT-IE and US-IE are mandatory for all Asynch Messages
ws_utt_ie_t ws_utt;
if (!ws_wh_utt_read(ie_ext->headerIeList, ie_ext->headerIeListLength, &ws_utt)) {
// Corrupted
return;
}
ws_us_ie_t ws_us;
if (!ws_wp_nested_us_read(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &ws_us)) {
// Corrupted
return;
}
if (!ws_bootstrap_validate_channel_plan(&ws_us, cur) ||
!ws_bootstrap_validate_channel_function(&ws_us, NULL)) {
return;
}
//Handle Message's
switch (message_type) {
case WS_FT_PAN_ADVERT:
// Analyse Advertisement
ws_stats_update(cur, STATS_WS_ASYNCH_RX_PA, 1);
tr_info("received ADVERT Src:%s panid:%x rssi:%d", trace_array(data->SrcAddr, 8), data->SrcPANId, data->signal_dbm);
ws_bootstrap_pan_advertisement_analyse(cur, data, ie_ext, &ws_utt, &ws_us);
break;
case WS_FT_PAN_ADVERT_SOL:
ws_stats_update(cur, STATS_WS_ASYNCH_RX_PAS, 1);
tr_info("received ADVERT SOL Src:%s rssi:%d", trace_array(data->SrcAddr, 8), data->signal_dbm);
ws_bootstrap_pan_advertisement_solicit_analyse(cur, data, &ws_utt, &ws_us);
break;
case WS_FT_PAN_CONF:
ws_stats_update(cur, STATS_WS_ASYNCH_RX_PC, 1);
tr_info("received CONFIG Src:%s rssi:%d", trace_array(data->SrcAddr, 8), data->signal_dbm);
ws_bootstrap_pan_config_analyse(cur, data, ie_ext, &ws_utt, &ws_us);
break;
case WS_FT_PAN_CONF_SOL:
ws_stats_update(cur, STATS_WS_ASYNCH_RX_PCS, 1);
tr_info("received CONFIG SOL Src:%s rssi:%d", trace_array(data->SrcAddr, 8), data->signal_dbm);
ws_bootstrap_pan_config_solicit_analyse(cur, data, &ws_utt, &ws_us);
default:
// Unknown message do not process
break;
}
}
static void ws_bootstrap_asynch_confirm(struct protocol_interface_info_entry *interface, uint8_t asynch_message)
{
ws_stats_update(interface, STATS_WS_ASYNCH_TX, 1);
if (interface->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) {
if (asynch_message == WS_FT_PAN_CONF && interface->ws_info->pending_key_index_info.state == PENDING_KEY_INDEX_ACTIVATE) {
interface->ws_info->pending_key_index_info.state = NO_PENDING_PROCESS;
tr_info("Activate new default key %u", interface->ws_info->pending_key_index_info.index + 1);
mac_helper_security_auto_request_key_index_set(interface, interface->ws_info->pending_key_index_info.index, interface->ws_info->pending_key_index_info.index + 1);
}
}
}
uint32_t ws_time_from_last_unicast_traffic(uint32_t current_time_stamp, ws_neighbor_class_entry_t *ws_neighbor)
{
uint32_t time_from_last_unicast_shedule = current_time_stamp;
//Time from last RX unicast in us
time_from_last_unicast_shedule -= ws_neighbor->fhss_data.uc_timing_info.utt_rx_timestamp;
time_from_last_unicast_shedule /= 1000000; //Convert to seconds
return time_from_last_unicast_shedule;
}
static void ws_bootstrap_neighbor_table_clean(struct protocol_interface_info_entry *interface)
{
uint8_t ll_target[16];
if (mac_neighbor_info(interface)->neighbour_list_size <= mac_neighbor_info(interface)->list_total_size - ws_common_temporary_entry_size(mac_neighbor_info(interface)->list_total_size)) {
// Enough neighbor entries
return;
}
uint32_t temp_link_min_timeout;
if (mac_neighbor_info(interface)->neighbour_list_size == mac_neighbor_info(interface)->list_total_size) {
temp_link_min_timeout = 1; //Accept 1 second time from last
} else {
temp_link_min_timeout = interface->ws_info->cfg->timing.temp_link_min_timeout;
}
memcpy(ll_target, ADDR_LINK_LOCAL_PREFIX, 8);
uint32_t current_time_stamp = ns_sw_mac_read_current_timestamp(interface->mac_api);
mac_neighbor_table_entry_t *neighbor_entry_ptr = NULL;
ns_list_foreach_safe(mac_neighbor_table_entry_t, cur, &mac_neighbor_info(interface)->neighbour_list) {
ws_neighbor_class_entry_t *ws_neighbor = ws_neighbor_class_entry_get(&interface->ws_info->neighbor_storage, cur->index);
if (cur->index == interface->ws_info->eapol_tx_index) {
continue;
}
if (cur->link_role == PRIORITY_PARENT_NEIGHBOUR) {
//This is our primary parent we cannot delete
continue;
}
if (cur->nud_active) {
//If NUD process is active do not trig
// or Negative ARO is active
continue;
}
if (neighbor_entry_ptr && neighbor_entry_ptr->lifetime < cur->lifetime) {
// We have already shorter link entry found this cannot replace it
continue;
}
if (cur->link_lifetime > WS_NEIGHBOUR_TEMPORARY_ENTRY_LIFETIME && cur->link_lifetime <= WS_NEIGHBOUR_TEMPORARY_NEIGH_MAX_LIFETIME) {
//Do not permit to remove configured temp life time
continue;
}
if (cur->trusted_device) {
if (ipv6_neighbour_has_registered_by_eui64(&interface->ipv6_neighbour_cache, cur->mac64)) {
// We have registered entry so we have been selected as parent
continue;
}
memcpy(ll_target + 8, cur->mac64, 8);
ll_target[8] ^= 2;
if (rpl_control_is_dodag_parent(interface, ll_target)) {
// Possible parent is limited to 3 by default?
continue;
}
}
//Read current timestamp
uint32_t time_from_last_unicast_shedule = ws_time_from_last_unicast_traffic(current_time_stamp, ws_neighbor);
if (time_from_last_unicast_shedule >= temp_link_min_timeout) {
//Accept only Enough Old Device
if (!neighbor_entry_ptr) {
//Accept first compare
neighbor_entry_ptr = cur;
} else {
uint32_t compare_neigh_time = ws_time_from_last_unicast_traffic(current_time_stamp, ws_neighbor_class_entry_get(&interface->ws_info->neighbor_storage, neighbor_entry_ptr->index));
if (compare_neigh_time < time_from_last_unicast_shedule) {
//Accept older RX timeout allways
neighbor_entry_ptr = cur;
}
}
}
}
if (neighbor_entry_ptr) {
tr_info("dropped oldest neighbour %s", trace_array(neighbor_entry_ptr->mac64, 8));
mac_neighbor_table_neighbor_remove(mac_neighbor_info(interface), neighbor_entry_ptr);
}
}
static bool ws_bootstrap_neighbor_info_request(struct protocol_interface_info_entry *interface, const uint8_t *mac_64, llc_neighbour_req_t *neighbor_buffer, bool request_new)
{
neighbor_buffer->ws_neighbor = NULL;
neighbor_buffer->neighbor = mac_neighbor_table_address_discover(mac_neighbor_info(interface), mac_64, ADDR_802_15_4_LONG);
if (neighbor_buffer->neighbor) {
neighbor_buffer->ws_neighbor = ws_neighbor_class_entry_get(&interface->ws_info->neighbor_storage, neighbor_buffer->neighbor->index);
if (!neighbor_buffer->ws_neighbor) {
return false;
}
return true;
}
if (!request_new) {
return false;
}
ws_bootstrap_neighbor_table_clean(interface);
neighbor_buffer->neighbor = ws_bootstrap_mac_neighbor_add(interface, mac_64);
if (!neighbor_buffer->neighbor) {
return false;
}
neighbor_buffer->ws_neighbor = ws_neighbor_class_entry_get(&interface->ws_info->neighbor_storage, neighbor_buffer->neighbor->index);
if (!neighbor_buffer->ws_neighbor) {
mac_neighbor_table_neighbor_remove(mac_neighbor_info(interface), neighbor_buffer->neighbor);
return false;
}
ws_stats_update(interface, STATS_WS_NEIGHBOUR_ADD, 1);
return true;
}
static void ws_neighbor_entry_remove_notify(mac_neighbor_table_entry_t *entry_ptr, void *user_data)
{
protocol_interface_info_entry_t *cur = user_data;
lowpan_adaptation_neigh_remove_free_tx_tables(cur, entry_ptr);
// Sleepy host
if (cur->lowpan_info & INTERFACE_NWK_CONF_MAC_RX_OFF_IDLE) {
mac_data_poll_protocol_poll_mode_decrement(cur);
}
//TODO State machine check here
if (ipv6_neighbour_has_registered_by_eui64(&cur->ipv6_neighbour_cache, entry_ptr->mac64)) {
// Child entry deleted
ws_stats_update(cur, STATS_WS_CHILD_REMOVE, 1);
}
if (entry_ptr->ffd_device) {
protocol_6lowpan_release_short_link_address_from_neighcache(cur, entry_ptr->mac16);
protocol_6lowpan_release_long_link_address_from_neighcache(cur, entry_ptr->mac64);
}
//NUD Process Clear Here
ws_nud_entry_remove(cur, entry_ptr);
ws_bootstrap_neighbor_delete(cur, entry_ptr);
ws_stats_update(cur, STATS_WS_NEIGHBOUR_REMOVE, 1);
}
static uint32_t ws_probe_init_time_get(protocol_interface_info_entry_t *cur)
{
if (ws_cfg_network_config_get(cur) <= CONFIG_SMALL) {
return WS_SMALL_PROBE_INIT_BASE_SECONDS;
}
return WS_NORMAL_PROBE_INIT_BASE_SECONDS;
}
static bool ws_neighbor_entry_nud_notify(mac_neighbor_table_entry_t *entry_ptr, void *user_data)
{
uint32_t time_from_start = entry_ptr->link_lifetime - entry_ptr->lifetime;
uint8_t ll_address[16];
bool nud_proces = false;
bool activate_nud = false;
bool child;
bool candidate_parent;
protocol_interface_info_entry_t *cur = user_data;
ws_neighbor_class_entry_t *ws_neighbor = ws_neighbor_class_entry_get(&cur->ws_info->neighbor_storage, entry_ptr->index);
etx_storage_t *etx_entry = etx_storage_entry_get(cur->id, entry_ptr->index);
if (!entry_ptr->trusted_device || !ws_neighbor || !etx_entry || entry_ptr->link_lifetime <= WS_NEIGHBOUR_TEMPORARY_NEIGH_MAX_LIFETIME) {
return false;
}
ws_bootsrap_create_ll_address(ll_address, entry_ptr->mac64);
if (time_from_start > WS_NEIGHBOR_NUD_TIMEOUT) {
child = ipv6_neighbour_has_registered_by_eui64(&cur->ipv6_neighbour_cache, entry_ptr->mac64);
candidate_parent = rpl_control_is_dodag_parent_candidate(cur, ll_address, cur->ws_info->cfg->gen.rpl_parent_candidate_max);
/* For parents ARO registration is sent in link timeout times
* For candidate parents NUD is needed
* For children NUD is sent only at very close to end
*/
if (!child && !candidate_parent) {
// No need for keep alive
return false;
}
if (child && (time_from_start < WS_NEIGHBOR_NUD_TIMEOUT * 1.8)) {
/* This is our child with valid ARO registration send NUD if we are close to delete
*
* if ARO was received link is considered active so this is only in case of very long ARO registration times
*
* 1.8 means with link timeout of 30 minutes that NUD is sent 6 minutes before timeout
*
*/
return false;
}
if (time_from_start > WS_NEIGHBOR_NUD_TIMEOUT * 1.5) {
activate_nud = true;
} else {
uint16_t switch_prob = randLIB_get_random_in_range(0, WS_NUD_RANDOM_SAMPLE_LENGTH - 1);
//Take Random from time WS_NEIGHBOR_NUD_TIMEOUT - WS_NEIGHBOR_NUD_TIMEOUT*1.5
if (switch_prob < WS_NUD_RANDOM_COMPARE) {
activate_nud = true;
}
}
nud_proces = activate_nud;
} else if (etx_entry->etx_samples < WS_NEIGHBOR_ETX_SAMPLE_MAX) {
//Take Random number for trig a prope.
//Small network
//ETX Sample 0: random 1-4
//ETX Sample 1: random 2-8
//ETX Sample 2: random 4-16
//Medium and large
//ETX Sample 0: random 1-8
//ETX Sample 1: random 2-16
//ETX Sample 2: random 4-32
ws_bootsrap_create_ll_address(ll_address, entry_ptr->mac64);
if (!rpl_control_probe_parent_candidate(cur, ll_address)) {
return false;
}
uint32_t probe_period = ws_probe_init_time_get(cur) << etx_entry->etx_samples;
uint32_t time_block = 1 << etx_entry->etx_samples;
if (time_from_start >= probe_period) {
//tr_debug("Link Probe test %u Sample trig", etx_entry->etx_samples);
activate_nud = true;
} else if (time_from_start > time_block) {
uint16_t switch_prob = randLIB_get_random_in_range(0, probe_period - 1);
//Take Random from time WS_NEIGHBOR_NUD_TIMEOUT - WS_NEIGHBOR_NUD_TIMEOUT*1.5
if (switch_prob < 2) {
//tr_debug("Link Probe test with jitter %"PRIu32", sample %u", time_from_start, etx_entry->etx_samples);
activate_nud = true;
}
}
}
if (!activate_nud) {
return false;
}
ws_nud_table_entry_t *entry = ws_nud_entry_get_free(cur);
if (!entry) {
return false;
}
entry->neighbor_info = entry_ptr;
entry->nud_process = nud_proces;
return true;
}
int ws_bootstrap_init(int8_t interface_id, net_6lowpan_mode_e bootstrap_mode)
{
int ret_val = 0;
ws_neighbor_class_t neigh_info;
neigh_info.neigh_info_list = NULL;
neigh_info.list_size = 0;
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
if (!cur) {
return -1;
}
mac_description_storage_size_t buffer;
if (!cur->mac_api || !cur->mac_api->mac_storage_sizes_get || cur->mac_api->mac_storage_sizes_get(cur->mac_api, &buffer) != 0) {
return -2;
}
if (buffer.key_description_table_size < 4) {
tr_err("MAC key_description_table_size too short %d<4", buffer.key_description_table_size);
return -2;
}
if (ns_sw_mac_enable_frame_counter_per_key(cur->mac_api, true)) {
return -1;
}
if (!etx_storage_list_allocate(cur->id, buffer.device_decription_table_size)) {
return -1;
}
if (!etx_cached_etx_parameter_set(WS_ETX_MIN_WAIT_TIME, WS_ETX_MIN_SAMPLE_COUNT, WS_NEIGHBOR_FIRST_ETX_SAMPLE_MIN_COUNT)) {
etx_storage_list_allocate(cur->id, 0);
return -1;
}
if (!etx_allow_drop_for_poor_measurements(WS_ETX_BAD_INIT_LINK_LEVEL, WS_ETX_MAX_BAD_LINK_DROP)) {
etx_storage_list_allocate(cur->id, 0);
return -1;
}
etx_max_update_set(WS_ETX_MAX_UPDATE);
etx_max_set(WS_ETX_MAX);
if (blacklist_init() != 0) {
tr_err("MLE blacklist init failed.");
return -1;
}
switch (bootstrap_mode) {
// case NET_6LOWPAN_SLEEPY_HOST:
case NET_6LOWPAN_HOST:
cur->bootsrap_mode = ARM_NWK_BOOTSRAP_MODE_6LoWPAN_HOST;
break;
case NET_6LOWPAN_ROUTER:
cur->bootsrap_mode = ARM_NWK_BOOTSRAP_MODE_6LoWPAN_ROUTER;
break;
case NET_6LOWPAN_BORDER_ROUTER:
cur->bootsrap_mode = ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER;
break;
default:
return -3;
}
if (!ws_neighbor_class_alloc(&neigh_info, buffer.device_decription_table_size)) {
ret_val = -1;
goto init_fail;
}
//Disable allways by default
lowpan_adaptation_interface_mpx_register(interface_id, NULL, 0);
mac_neighbor_table_delete(mac_neighbor_info(cur));
mac_neighbor_info(cur) = mac_neighbor_table_create(buffer.device_decription_table_size, ws_neighbor_entry_remove_notify
, ws_neighbor_entry_nud_notify, cur);
if (!mac_neighbor_info(cur)) {
ret_val = -1;
goto init_fail;
}
ws_llc_create(cur, &ws_bootstrap_asynch_ind, &ws_bootstrap_asynch_confirm, &ws_bootstrap_neighbor_info_request);
mpx_api_t *mpx_api = ws_llc_mpx_api_get(cur);
if (!mpx_api) {
ret_val = -4;
goto init_fail;
}
if (ws_common_allocate_and_init(cur) < 0) {
ret_val = -4;
goto init_fail;
}
if (ws_cfg_settings_interface_set(cur) < 0) {
ret_val = -4;
goto init_fail;
}
if (ws_bootstrap_tasklet_init(cur) != 0) {
ret_val = -4;
goto init_fail;
}
//Register MPXUser to adapatation layer
if (lowpan_adaptation_interface_mpx_register(interface_id, mpx_api, MPX_LOWPAN_ENC_USER_ID) != 0) {
ret_val = -4;
goto init_fail;
}
//Init PAE controller and set callback
if (ws_pae_controller_init(cur) < 0) {
ret_val = -4;
goto init_fail;
}
if (ws_pae_controller_cb_register(cur, &ws_bootstrap_authentication_completed, &ws_bootstrap_authentication_next_target, &ws_bootstrap_nw_key_set, &ws_bootstrap_nw_key_clear, &ws_bootstrap_nw_key_index_set, &ws_bootstrap_nw_frame_counter_set, &ws_bootstrap_nw_frame_counter_read, &ws_bootstrap_pan_version_increment, &ws_bootstrap_nw_info_updated, &ws_bootstrap_eapol_congestion_get) < 0) {
ret_val = -4;
goto init_fail;
}
if (ws_pae_controller_configure(cur, &cur->ws_info->cfg->sec_timer, &cur->ws_info->cfg->sec_prot, &cur->ws_info->cfg->timing) < 0) {
ret_val = -4;
goto init_fail;
}
//Init EAPOL PDU handler and register it to MPX
if (ws_eapol_pdu_init(cur) < 0) {
ret_val = -4;
goto init_fail;
}
if (ws_eapol_pdu_mpx_register(cur, mpx_api, MPX_KEY_MANAGEMENT_ENC_USER_ID != 0)) {
ret_val = -4;
// add deallocs
goto init_fail;
}
cur->ipv6_neighbour_cache.link_mtu = cur->max_link_mtu = WS_MPX_MAX_MTU;
cur->if_up = ws_bootstrap_up;
cur->if_down = ws_bootstrap_down;
cur->ws_info->neighbor_storage = neigh_info;
cur->etx_read_override = ws_etx_read;
ws_bootstrap_configuration_reset(cur);
addr_notification_register(ws_bootstrap_address_notification_cb);
//Enable MAC Security by pass
mlme_set_t set_req;
bool state = true;
set_req.attr = macAcceptByPassUnknowDevice;
set_req.attr_index = 0;
set_req.value_pointer = &state;
set_req.value_size = sizeof(bool);
cur->mac_api->mlme_req(cur->mac_api, MLME_SET, &set_req);
// Set the default parameters for MPL
cur->mpl_proactive_forwarding = true;
// Specification is ruling out the compression mode, but we are now doing it.
cur->mpl_seed = true;
cur->mpl_seed_id_mode = MULTICAST_MPL_SEED_ID_IPV6_SRC_FOR_DOMAIN;
cur->mpl_control_trickle_params.TimerExpirations = 0;
cur->mpl_domain = mpl_domain_create(cur, ADDR_ALL_MPL_FORWARDERS, NULL, MULTICAST_MPL_SEED_ID_DEFAULT, -1, 0, NULL, NULL);
addr_add_group(cur, ADDR_REALM_LOCAL_ALL_NODES);
addr_add_group(cur, ADDR_REALM_LOCAL_ALL_ROUTERS);
return 0;
//Error handling and free memory
init_fail:
lowpan_adaptation_interface_mpx_register(interface_id, NULL, 0);
ws_eapol_pdu_mpx_register(cur, NULL, 0);
mac_neighbor_table_delete(mac_neighbor_info(cur));
etx_storage_list_allocate(cur->id, 0);
ws_neighbor_class_dealloc(&neigh_info);
ws_llc_delete(cur);
ws_eapol_pdu_delete(cur);
ws_pae_controller_delete(cur);
return ret_val;
}
int ws_bootstrap_restart(int8_t interface_id)
{
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
if (!cur || !cur->ws_info) {
return -1;
}
ws_bootstrap_event_discovery_start(cur);
return 0;
}
int ws_bootstrap_restart_delayed(int8_t interface_id)
{
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
if (!cur || !cur->ws_info) {
return -1;
}
ws_bootstrap_state_change(cur, ER_WAIT_RESTART);
cur->bootsrap_state_machine_cnt = 3;
return 0;
}
int ws_bootstrap_set_rf_config(protocol_interface_info_entry_t *cur, phy_rf_channel_configuration_s rf_configs)
{
mlme_set_t set_request;
// Set MAC mode
phy_802_15_4_mode_t mac_mode = IEEE_802_15_4G_2012;
set_request.attr = mac802_15_4Mode;
set_request.value_pointer = &mac_mode;
set_request.value_size = sizeof(phy_802_15_4_mode_t);
cur->mac_api->mlme_req(cur->mac_api, MLME_SET, &set_request);
// Set RF configuration
set_request.attr = macRfConfiguration;
set_request.value_pointer = &rf_configs;
set_request.value_size = sizeof(phy_rf_channel_configuration_s);
cur->mac_api->mlme_req(cur->mac_api, MLME_SET, &set_request);
// Set Ack wait duration
uint8_t bits_per_symbol = 1;
if (rf_configs.modulation == M_OFDM) {
bits_per_symbol = 4;
}
uint16_t ack_wait_symbols = WS_ACK_WAIT_SYMBOLS + (WS_TACK_MAX_MS * (rf_configs.datarate / 1000) / bits_per_symbol);
set_request.attr = macAckWaitDuration;
set_request.value_pointer = &ack_wait_symbols;
set_request.value_size = sizeof(ack_wait_symbols);
cur->mac_api->mlme_req(cur->mac_api, MLME_SET, &set_request);
// Set multi CSMA-CA configuration
mlme_multi_csma_ca_param_t multi_csma_params = {WS_NUMBER_OF_CSMA_PERIODS, WS_CSMA_MULTI_CCA_INTERVAL};
set_request.attr = macMultiCSMAParameters;
set_request.value_pointer = &multi_csma_params;
set_request.value_size = sizeof(mlme_multi_csma_ca_param_t);
cur->mac_api->mlme_req(cur->mac_api, MLME_SET, &set_request);
// Start automatic CCA threshold
mac_helper_start_auto_cca_threshold(cur->id, cur->ws_info->hopping_schdule.number_of_channels, CCA_DEFAULT_DBM, CCA_HIGH_LIMIT, CCA_LOW_LIMIT);
return 0;
}
int ws_bootstrap_neighbor_remove(protocol_interface_info_entry_t *cur, const uint8_t *ll_address)
{
mac_neighbor_table_entry_t *mac_neighbor = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur), ll_address, false, NULL);
if (mac_neighbor) {
mac_neighbor_table_neighbor_remove(mac_neighbor_info(cur), mac_neighbor);
}
return 0;
}
int ws_bootstrap_aro_failure(protocol_interface_info_entry_t *cur, const uint8_t *ll_address)
{
rpl_control_neighbor_delete(cur, ll_address);
ws_bootstrap_neighbor_remove(cur, ll_address);
return 0;
}
static int ws_bootstrap_set_domain_rf_config(protocol_interface_info_entry_t *cur)
{
phy_rf_channel_configuration_s rf_configs;
memset(&rf_configs, 0, sizeof(phy_rf_channel_configuration_s));
uint8_t phy_mode_id = cur->ws_info->hopping_schdule.phy_mode_id;
if (((phy_mode_id >= 34) && (phy_mode_id <= 38)) ||
((phy_mode_id >= 51) && (phy_mode_id <= 54)) ||
((phy_mode_id >= 68) && (phy_mode_id <= 70)) ||
((phy_mode_id >= 84) && (phy_mode_id <= 86))) {
rf_configs.modulation = M_OFDM;
rf_configs.datarate = ws_get_datarate_using_phy_mode_id(cur->ws_info->hopping_schdule.phy_mode_id);
rf_configs.ofdm_option = ws_get_ofdm_option_using_phy_mode_id(cur->ws_info->hopping_schdule.phy_mode_id);
rf_configs.ofdm_mcs = ws_get_ofdm_mcs_using_phy_mode_id(cur->ws_info->hopping_schdule.phy_mode_id);
} else {
if ((phy_mode_id >= 17) && (phy_mode_id <= 24)) {
rf_configs.fec = true;
} else {
rf_configs.fec = false;
}
rf_configs.modulation = M_2FSK;
rf_configs.datarate = ws_get_datarate_using_operating_mode(cur->ws_info->hopping_schdule.operating_mode);
rf_configs.modulation_index = ws_get_modulation_index_using_operating_mode(cur->ws_info->hopping_schdule.operating_mode);
}
rf_configs.channel_0_center_frequency = (uint32_t)cur->ws_info->hopping_schdule.ch0_freq * 100000;
rf_configs.channel_spacing = ws_decode_channel_spacing(cur->ws_info->hopping_schdule.channel_spacing);
rf_configs.number_of_channels = cur->ws_info->hopping_schdule.number_of_channels;
ws_bootstrap_set_rf_config(cur, rf_configs);
return 0;
}
static void ws_bootstrap_mac_activate(protocol_interface_info_entry_t *cur, uint16_t channel, uint16_t panid, bool coordinator)
{
mlme_start_t start_req;
memset(&start_req, 0, sizeof(mlme_start_t));
cur->mac_parameters->pan_id = panid;
cur->mac_parameters->mac_channel = channel;
start_req.PANId = panid;
start_req.LogicalChannel = channel;
start_req.BeaconOrder = 0x0f;
start_req.SuperframeOrder = 0x0f;
start_req.PANCoordinator = coordinator;
if (cur->mac_api) {
cur->mac_api->mlme_req(cur->mac_api, MLME_START, (void *)&start_req);
}
}
static void ws_bootstrap_fhss_activate(protocol_interface_info_entry_t *cur)
{
tr_debug("FHSS activate");
ws_fhss_enable(cur);
ws_llc_hopping_schedule_config(cur, &cur->ws_info->hopping_schdule);
// Only supporting fixed channel
tr_debug("MAC init");
mac_helper_pib_boolean_set(cur, macRxOnWhenIdle, true);
cur->lowpan_info &= ~INTERFACE_NWK_CONF_MAC_RX_OFF_IDLE;
ws_bootstrap_mac_security_enable(cur);
ws_bootstrap_mac_activate(cur, cur->ws_info->cfg->fhss.fhss_uc_fixed_channel, cur->ws_info->network_pan_id, true);
return;
}
static void ws_bootstrap_network_configuration_learn(protocol_interface_info_entry_t *cur)
{
tr_debug("Start using PAN configuration");
// Timing information can be modified here
ws_llc_set_pan_information_pointer(cur, &cur->ws_info->pan_information);
uint8_t *gtkhash = ws_pae_controller_gtk_hash_ptr_get(cur);
ws_llc_set_gtkhash(cur, gtkhash);
// TODO update own fhss schedules we are starting to follow first parent
return;
}
static void ws_bootstrap_ip_stack_addr_clear(protocol_interface_info_entry_t *cur)
{
tr_debug("ip stack address clear");
ns_list_foreach_safe(if_address_entry_t, addr, &cur->ip_addresses) {
if (addr->source != ADDR_SOURCE_STATIC &&
addr_ipv6_scope(addr->address, cur) > IPV6_SCOPE_LINK_LOCAL) {
// Remove all exept User set address
addr_delete_entry(cur, addr);
}
}
}
static void ws_bootstrap_ip_stack_reset(protocol_interface_info_entry_t *cur)
{
tr_debug("ip stack reset");
// Delete all temporary cached information
ipv6_neighbour_cache_flush(&cur->ipv6_neighbour_cache);
lowpan_context_list_free(&cur->lowpan_contexts);
}
static void ws_bootstrap_ip_stack_activate(protocol_interface_info_entry_t *cur)
{
tr_debug("ip stack init");
clear_power_state(ICMP_ACTIVE);
cur->lowpan_info |= INTERFACE_NWK_BOOTSRAP_ACTIVE;
ws_bootstrap_ip_stack_reset(cur);
}
static void ws_set_fhss_hop(protocol_interface_info_entry_t *cur)
{
uint16_t own_rank = ws_bootstrap_rank_get(cur);
uint16_t rank_inc = ws_bootstrap_min_rank_inc_get(cur);
if (own_rank == 0xffff || rank_inc == 0xffff) {
return;
}
// Calculate own hop count. This method gets inaccurate when hop count increases.
uint8_t own_hop = (own_rank - rank_inc) / rank_inc;
ns_fhss_ws_set_hop_count(cur->ws_info->fhss_api, own_hop);
if (own_hop == 1) {
// Allow transmitting unicast frames only on TX slots in normal mode and always in expedited forwarding mode for first hop
ns_fhss_ws_set_tx_allowance_level(cur->ws_info->fhss_api, WS_TX_SLOT, WS_TX_ALWAYS);
} else {
// Allow transmitting unicast frames only on TX slots in normal and expedited forwarding mode for other hops
ns_fhss_ws_set_tx_allowance_level(cur->ws_info->fhss_api, WS_TX_SLOT, WS_TX_SLOT);
}
tr_debug("own hop: %u, own rank: %u, rank inc: %u", own_hop, own_rank, rank_inc);
}
static void ws_address_registration_update(protocol_interface_info_entry_t *interface, const uint8_t addr[16])
{
rpl_control_register_address(interface, addr);
// Timer is used only to track full registrations
if (addr != NULL && interface->ws_info->aro_registration_timer) {
// Single address update and timer is running
return;
}
if (interface->ws_info->aro_registration_timer == 0) {
// Timer expired and check if we have valid address to register
ns_list_foreach(if_address_entry_t, address, &interface->ip_addresses) {
if (!addr_is_ipv6_link_local(address->address)) {
// We have still valid addresses let the timer run for next period
tr_info("ARO registration timer start");
interface->ws_info->aro_registration_timer = WS_NEIGHBOR_NUD_TIMEOUT;
return;
}
}
}
}
static void ws_address_parent_update(protocol_interface_info_entry_t *interface)
{
tr_info("RPL parent update ... register ARO");
ws_address_registration_update(interface, NULL);
}
static void ws_bootstrap_parent_confirm(protocol_interface_info_entry_t *cur, struct rpl_instance *instance)
{
/* Possible problem with the parent connection
* Give some time for parent to rejoin and confirm the connection with ARO and DAO
*/
const rpl_dodag_conf_t *config = NULL;
uint32_t Imin_secs = 0;
if (!ws_bootstrap_state_active(cur)) {
// If we are not in Active state no need to confirm parent
return;
}
tr_info("RPL parent confirm");
if (!instance) {
// If we dont have instance we take any available to get reference
instance = rpl_control_enumerate_instances(cur->rpl_domain, NULL);
}
if (instance) {
config = rpl_control_get_dodag_config(instance);
}
if (config) {
//dio imin Period caluclate in seconds
uint32_t Imin_ms = config->dio_interval_min < 32 ? (1ul << config->dio_interval_min) : 0xfffffffful;
//Covert to seconds and multiple by 2 so we give time to recovery so divide by 500 do that operation
Imin_secs = (Imin_ms + 499) / 500;
if (Imin_secs > 0xffff) {
Imin_secs = 0xffff;
}
}
if (Imin_secs == 0) {
// If we dont have RPL configuration we assume conservative value
Imin_secs = 60;
}
/*Speed up the ARO registration*/
if (cur->ws_info->aro_registration_timer > Imin_secs) {
cur->ws_info->aro_registration_timer = Imin_secs;
}
}
static void ws_rpl_parent_dis_callback(const uint8_t *ll_parent_address, void *handle, struct rpl_instance *instance)
{
(void) ll_parent_address;
protocol_interface_info_entry_t *cur = handle;
if (!cur->rpl_domain || cur->interface_mode != INTERFACE_UP) {
return;
}
//Multicast DIS from parent indicate that Parent is not valid in short time window possible
ws_bootstrap_parent_confirm(cur, instance);
}
static void ws_bootstrap_rpl_callback(rpl_event_t event, void *handle)
{
protocol_interface_info_entry_t *cur = handle;
if (!cur->rpl_domain || cur->interface_mode != INTERFACE_UP) {
return;
}
if (event == RPL_EVENT_POISON_FINISHED) {
//If we are waiting poison we will trig Discovery after couple seconds
if (cur->nwk_bootstrap_state == ER_RPL_NETWORK_LEAVING) {
cur->bootsrap_state_machine_cnt = 80; //Give 8 seconds time to send Poison
}
return;
}
// if waiting for RPL and
if (event == RPL_EVENT_DAO_DONE) {
// Trigger statemachine check
cur->bootsrap_state_machine_cnt = 1;
rpl_dodag_info_t dodag_info;
struct rpl_instance *instance = rpl_control_enumerate_instances(cur->rpl_domain, NULL);
if (instance && rpl_control_read_dodag_info(instance, &dodag_info)) {
tr_debug("Enable DHCPv6 relay");
dhcp_relay_agent_enable(cur->id, dodag_info.dodag_id);
tr_debug("Start EAPOL relay");
// Set both own port and border router port to 10253
ws_eapol_relay_start(cur, EAPOL_RELAY_SOCKET_PORT, dodag_info.dodag_id, EAPOL_RELAY_SOCKET_PORT);
// Set network information to PAE
ws_pae_controller_nw_info_set(cur, cur->ws_info->network_pan_id, cur->ws_info->pan_information.pan_version, cur->ws_info->cfg->gen.network_name);
// Network key is valid, indicate border router IID to controller
ws_pae_controller_nw_key_valid(cur, &dodag_info.dodag_id[8]);
//Update here Suplikant target by validated Primary Parent
if (cur->bootsrap_mode != ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) {
mac_neighbor_table_entry_t *mac_neighbor = mac_neighbor_entry_get_priority(mac_neighbor_info(cur));
if (mac_neighbor) {
ws_pae_controller_set_target(cur, cur->ws_info->network_pan_id, mac_neighbor->mac64);
}
}
// After successful DAO ACK connection to border router is verified
ws_common_border_router_alive_update(cur);
}
if (!cur->ws_info->trickle_pa_running || !cur->ws_info->trickle_pc_running) {
//Enable wi-sun asynch adverisment
ws_bootstrap_advertise_start(cur);
}
ws_set_fhss_hop(cur);
// Set retry configuration for bootstrap ready state
ws_bootstrap_configure_max_retries(cur, WS_MAX_FRAME_RETRIES);
// Set TX failure request restart configuration
ws_bootstrap_configure_data_request_restart(cur, WS_CCA_REQUEST_RESTART_MAX, WS_TX_REQUEST_RESTART_MAX, WS_REQUEST_RESTART_BLACKLIST_MIN, WS_REQUEST_RESTART_BLACKLIST_MAX);
} else if (event == RPL_EVENT_LOCAL_REPAIR_NO_MORE_DIS) {
/*
* RPL goes to passive mode, but does not require any extra changed
*
* We could remove our current addresses learned from RPL
* We could send solicit for configuration and then select new parent when those arrive
*
*/
} else if (event == RPL_EVENT_LOCAL_REPAIR_START) {
tr_debug("RPL local repair start");
//Disable Async and go to state 4 to confirm parent connection
ws_bootstrap_parent_confirm(cur, NULL);
// Move to state 4 if we see issues with primary parent
if (ws_bootstrap_state_active(cur)) {
tr_info("Move state 4 to wait parent connection confirmation");
ws_bootstrap_rpl_scan_start(cur);
ws_nwk_event_post(cur, ARM_NWK_NWK_CONNECTION_DOWN);
}
} else if (event == RPL_EVENT_DAO_PARENT_ADD) {
ws_address_parent_update(cur);
}
cur->ws_info->rpl_state = event;
tr_info("RPL event %d", event);
}
static void ws_dhcp_client_global_adress_cb(int8_t interface, uint8_t dhcp_addr[static 16], uint8_t prefix[static 16], bool register_status)
{
(void)prefix;
(void)interface;
//TODO add handler for negative status
tr_debug("DHCPv6 %s status %u with link %s", trace_ipv6(prefix), register_status, trace_ipv6(dhcp_addr));
if (register_status) {
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface);
if (cur) {
ws_address_reregister_trig(cur);
}
} else {
//Delete dhcpv6 client
dhcp_client_global_address_delete(interface, dhcp_addr, prefix);
}
}
void ws_dhcp_client_address_request(protocol_interface_info_entry_t *cur, uint8_t *prefix, uint8_t *parent_link_local)
{
if (dhcp_client_get_global_address(cur->id, parent_link_local, prefix, ws_dhcp_client_global_adress_cb) != 0) {
tr_error("DHCPp client request fail");
}
}
void ws_dhcp_client_address_delete(protocol_interface_info_entry_t *cur, uint8_t *prefix)
{
dhcp_client_global_address_delete(cur->id, NULL, prefix);
}
bool ws_eapol_relay_state_active(protocol_interface_info_entry_t *cur)
{
if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER || cur->nwk_bootstrap_state == ER_BOOTSRAP_DONE) {
return true;
}
return false;
}
static void ws_rpl_prefix_callback(prefix_entry_t *prefix, void *handle, uint8_t *parent_link_local)
{
protocol_interface_info_entry_t *cur = (protocol_interface_info_entry_t *) handle;
/* Check if A-Flag.
* A RPL node may use this option for the purpose of Stateless Address Autoconfiguration (SLAAC)
* from a prefix advertised by a parent.
*/
if (prefix->options & PIO_A) {
if (parent_link_local) {
if (icmpv6_slaac_prefix_update(cur, prefix->prefix, prefix->prefix_len, prefix->lifetime, prefix->preftime) != 0) {
ipv6_interface_slaac_handler(cur, prefix->prefix, prefix->prefix_len, prefix->lifetime, prefix->preftime);
/*
* Give SLAAC addresses a different label and low precedence to indicate that
* they probably shouldn't be used for external traffic. SLAAC use in Wi-SUN is non-standard,
* and we use it for mesh-local traffic we should prefer any DHCP-assigned addresses
* for talking to the outside world
*
*/
addr_policy_table_add_entry(prefix->prefix, prefix->prefix_len, 2, WS_NON_PREFFRED_LABEL);
}
} else {
icmpv6_slaac_prefix_update(cur, prefix->prefix, prefix->prefix_len, 0, 0);
}
} else if (prefix->prefix_len) {
// Create new address using DHCP
if (parent_link_local) {
ws_dhcp_client_address_request(cur, prefix->prefix, parent_link_local);
} else {
/* Deprecate address and remove client */
tr_debug("Prefix invalidation %s", trace_ipv6(prefix->prefix));
dhcp_client_global_address_delete(cur->id, NULL, prefix->prefix);
}
}
}
static bool ws_rpl_candidate_soft_filtering(protocol_interface_info_entry_t *cur, struct rpl_instance *instance)
{
//Already many candidates
uint16_t candidate_list_size = rpl_control_candidate_list_size(cur, instance);
if (candidate_list_size >= cur->ws_info->cfg->gen.rpl_parent_candidate_max) {
return false;
}
uint16_t selected_parents = rpl_control_selected_parent_count(cur, instance);
//Already enough selected candidates
if (selected_parents >= cur->ws_info->cfg->gen.rpl_selected_parent_max) {
candidate_list_size -= selected_parents;
if (candidate_list_size >= 2) {
//We have more candidates than selected
return false;
}
}
return true;
}
static bool ws_rpl_new_parent_callback(uint8_t *ll_parent_address, void *handle, struct rpl_instance *instance, uint16_t candidate_rank)
{
protocol_interface_info_entry_t *cur = handle;
if (!cur->rpl_domain || cur->interface_mode != INTERFACE_UP) {
return false;
}
if (blacklist_reject(ll_parent_address)) {
// Rejected by blacklist
return false;
}
uint8_t mac64[10];
//bool replace_ok = false;
//bool create_ok = false;
llc_neighbour_req_t neigh_buffer;
//Discover neigh ready here for possible ETX validate
memcpy(mac64, ll_parent_address + 8, 8);
mac64[0] ^= 2;
ws_bootstrap_neighbor_info_request(cur, mac64, &neigh_buffer, false);
//Discover Multicast temporary entry for create neighbour table entry for new candidate
ws_neighbor_temp_class_t *entry = ws_llc_get_multicast_temp_entry(cur, mac64);
if (!ws_rpl_candidate_soft_filtering(cur, instance)) {
//Acept only better than own rank here
if (candidate_rank >= rpl_control_current_rank(instance)) {
//Do not accept no more siblings
return false;
}
uint16_t candidate_list_size = rpl_control_candidate_list_size(cur, instance);
if (candidate_list_size > cur->ws_info->cfg->gen.rpl_parent_candidate_max + 1) {
//Accept only 1 better 1 time
return false;
}
if (!neigh_buffer.neighbor) {
//Do not accept any new in that Place
return false;
}
uint8_t replacing[16];
//Accept Know neighbour if it is enough good
if (!rpl_control_find_worst_neighbor(cur, instance, replacing)) {
return false;
}
// +2 Is for PAN ID space
memcpy(mac64 + 2, replacing + 8, 8);
mac64[2] ^= 2;
if (ws_etx_read(cur, ADDR_802_15_4_LONG, mac64) == 0xffff) {
//Not proped yet because ETX is 0xffff
return false;
}
uint16_t etx = 0;
if (neigh_buffer.neighbor) {
etx = etx_local_etx_read(cur->id, neigh_buffer.neighbor->index);
}
// Accept now only better one's when max candidates selected and max candidate list size is reached
return rpl_possible_better_candidate(cur, instance, replacing, candidate_rank, etx);
}
//Neighbour allready
if (neigh_buffer.neighbor) {
return true;
}
if (!entry) {
//No Multicast Entry Available
return false;
}
//Create entry
bool create_ok = ws_bootstrap_neighbor_info_request(cur, entry->mac64, &neigh_buffer, true);
if (create_ok) {
ws_neighbor_class_entry_t *ws_neigh = neigh_buffer.ws_neighbor;
ws_bootstrap_neighbor_set_stable(cur, entry->mac64);
//Copy fhss temporary data
*ws_neigh = entry->neigh_info_list;
mac_neighbor_table_trusted_neighbor(mac_neighbor_info(cur), neigh_buffer.neighbor, true);
}
ws_llc_free_multicast_temp_entry(cur, entry);
#if 0
neigh_create_ok:
if (create_ok && replace_ok) {
//Try remove here when accepted new better one possible
tr_debug("Remove %s by %s", trace_ipv6(replacing), trace_ipv6(ll_parent_address));
rpl_control_neighbor_delete_from_instance(cur, instance, replacing);
}
#endif
return create_ok;
}
static void ws_bootstrap_rpl_activate(protocol_interface_info_entry_t *cur)
{
tr_debug("RPL Activate");
bool downstream = true;
bool leaf = false;
addr_add_router_groups(cur);
rpl_control_set_domain_on_interface(cur, protocol_6lowpan_rpl_domain, downstream);
rpl_control_set_callback(protocol_6lowpan_rpl_domain, ws_bootstrap_rpl_callback, ws_rpl_prefix_callback, ws_rpl_new_parent_callback, ws_rpl_parent_dis_callback, cur);
// If i am router I Do this
rpl_control_force_leaf(protocol_6lowpan_rpl_domain, leaf);
rpl_control_process_routes(protocol_6lowpan_rpl_domain, false); // Wi-SUN assumes that no default route needed
rpl_control_request_parent_link_confirmation(true);
rpl_control_set_dio_multicast_min_config_advertisment_count(WS_MIN_DIO_MULTICAST_CONFIG_ADVERTISMENT_COUNT);
rpl_control_set_address_registration_timeout((WS_NEIGHBOR_LINK_TIMEOUT / 60) + 1);
rpl_control_set_dao_retry_count(WS_MAX_DAO_RETRIES);
rpl_control_set_initial_dao_ack_wait(WS_MAX_DAO_INITIAL_TIMEOUT);
rpl_control_set_mrhof_parent_set_size(WS_MAX_PARENT_SET_COUNT);
rpl_control_set_force_tunnel(true);
if (cur->bootsrap_mode != ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) {
rpl_control_set_memory_limits(WS_NODE_RPL_SOFT_MEM_LIMIT, WS_NODE_RPL_HARD_MEM_LIMIT);
}
// Set RPL Link ETX Validation Threshold to 2.5 - 33.0
// This setup will set ETX 0x800 to report ICMP error 18% probability
// When ETX start go over 0x280 forward dropping probability start increase linear to 100% at 0x2100
rpl_policy_forward_link_etx_threshold_set(0x280, 0x2100);
// Set the minimum target refresh to sen DAO registrations before pan timeout
rpl_control_set_minimum_dao_target_refresh(WS_RPL_DAO_MAX_TIMOUT);
cur->ws_info->rpl_state = 0xff; // Set invalid state and learn from event
}
static void ws_bootstrap_network_start(protocol_interface_info_entry_t *cur)
{
//Set Network names, Pan information configure, hopping schedule & GTKHash
ws_llc_set_network_name(cur, (uint8_t *)cur->ws_info->cfg->gen.network_name, strlen(cur->ws_info->cfg->gen.network_name));
ws_llc_set_pan_information_pointer(cur, &cur->ws_info->pan_information);
}
static void ws_bootstrap_network_discovery_configure(protocol_interface_info_entry_t *cur)
{
// Reset information to defaults
cur->ws_info->network_pan_id = 0xffff;
ws_common_regulatory_domain_config(cur, &cur->ws_info->hopping_schdule);
ws_bootstrap_set_domain_rf_config(cur);
ws_fhss_configure(cur, true);
//Set Network names, Pan information configure, hopping schedule & GTKHash
ws_llc_set_network_name(cur, (uint8_t *)cur->ws_info->cfg->gen.network_name, strlen(cur->ws_info->cfg->gen.network_name));
}
static void ws_bootstrap_advertise_start(protocol_interface_info_entry_t *cur)
{
cur->ws_info->trickle_pa_running = true;
trickle_start(&cur->ws_info->trickle_pan_advertisement, &cur->ws_info->trickle_params_pan_discovery);
cur->ws_info->trickle_pc_running = true;
cur->ws_info->trickle_pc_consistency_block_period = 0;
trickle_start(&cur->ws_info->trickle_pan_config, &cur->ws_info->trickle_params_pan_discovery);
}
static void ws_bootstrap_pan_version_increment(protocol_interface_info_entry_t *cur)
{
(void)cur;
ws_bbr_pan_version_increase(cur);
}
// Start network scan
static void ws_bootstrap_start_discovery(protocol_interface_info_entry_t *cur)
{
tr_debug("router discovery start");
// Remove network keys from MAC
ws_pae_controller_nw_keys_remove(cur);
ws_bootstrap_state_change(cur, ER_ACTIVE_SCAN);
cur->nwk_nd_re_scan_count = 0;
cur->ws_info->configuration_learned = false;
cur->ws_info->pan_timeout_timer = 0;
cur->ws_info->weakest_received_rssi = 0;
// Clear learned candidate parents
ws_bootstrap_candidate_table_reset(cur);
// Clear RPL information
rpl_control_free_domain_instances_from_interface(cur);
// Clear EAPOL relay address
ws_eapol_relay_delete(cur);
// Clear ip stack from old information
ws_bootstrap_ip_stack_reset(cur);
// New network scan started old addresses not assumed valid anymore
ws_bootstrap_ip_stack_addr_clear(cur);
if ((cur->lowpan_info & INTERFACE_NWK_BOOTSRAP_ACTIVE) != INTERFACE_NWK_BOOTSRAP_ACTIVE) {
// we have sent bootstrap ready event and now
// restarted discovery so bootstrap down event is sent
cur->lowpan_info |= INTERFACE_NWK_BOOTSRAP_ACTIVE;
ws_nwk_event_post(cur, ARM_NWK_NWK_CONNECTION_DOWN);
}
// Start advertisement solicit trickle and calculate when we are checking the status
cur->ws_info->trickle_pas_running = true;
if (cur->ws_info->trickle_pan_advertisement_solicit.I != cur->ws_info->trickle_params_pan_discovery.Imin) {
// Trickle not reseted so starting a new interval
trickle_start(&cur->ws_info->trickle_pan_advertisement_solicit, &cur->ws_info->trickle_params_pan_discovery);
}
// Discovery statemachine is checkked after we have sent the Solicit
uint32_t time_to_solicit = 0;
if (cur->ws_info->trickle_pan_advertisement_solicit.t > cur->ws_info->trickle_pan_advertisement_solicit.now) {
time_to_solicit = cur->ws_info->trickle_pan_advertisement_solicit.t - cur->ws_info->trickle_pan_advertisement_solicit.now;
}
tr_debug("Disc params imin %u, imax %u, expirations %u, k %u PAS Trickle I %u t %u, now %u, c %u",
cur->ws_info->trickle_params_pan_discovery.Imin, cur->ws_info->trickle_params_pan_discovery.Imax, cur->ws_info->trickle_params_pan_discovery.TimerExpirations, cur->ws_info->trickle_params_pan_discovery.k,
cur->ws_info->trickle_pan_advertisement_solicit.I, cur->ws_info->trickle_pan_advertisement_solicit.t, cur->ws_info->trickle_pan_advertisement_solicit.now, cur->ws_info->trickle_pan_advertisement_solicit.c);
time_to_solicit += cur->ws_info->trickle_params_pan_discovery.Imin + randLIB_get_random_in_range(0, cur->ws_info->trickle_params_pan_discovery.Imin);
if (time_to_solicit > 0xffff) {
time_to_solicit = 0xffff;
}
cur->bootsrap_state_machine_cnt = time_to_solicit;
tr_info("Making parent selection in %u s", (cur->bootsrap_state_machine_cnt / 10));
}
// Start authentication
static void ws_bootstrap_start_authentication(protocol_interface_info_entry_t *cur)
{
// Set PAN ID and network name to controller
ws_pae_controller_nw_info_set(cur, cur->ws_info->network_pan_id, cur->ws_info->pan_information.pan_version, cur->ws_info->cfg->gen.network_name);
ws_pae_controller_authenticate(cur);
}
static void ws_bootstrap_mac_security_enable(protocol_interface_info_entry_t *cur)
{
mac_helper_default_security_level_set(cur, AES_SECURITY_LEVEL_ENC_MIC64);
mac_helper_default_security_key_id_mode_set(cur, MAC_KEY_ID_MODE_IDX);
}
static void ws_bootstrap_nw_key_set(protocol_interface_info_entry_t *cur, uint8_t slot, uint8_t index, uint8_t *key)
{
mac_helper_security_key_to_descriptor_set(cur, key, index + 1, slot);
}
static void ws_bootstrap_nw_key_clear(protocol_interface_info_entry_t *cur, uint8_t slot)
{
mac_helper_security_key_descriptor_clear(cur, slot);
}
static void ws_bootstrap_nw_key_index_set(protocol_interface_info_entry_t *cur, uint8_t index)
{
if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) {
if (cur->mac_parameters->mac_default_key_index != 0 && cur->mac_parameters->mac_default_key_index != index + 1) {
tr_info("New Pending key Request %u", index + 1);
cur->ws_info->pending_key_index_info.state = PENDING_KEY_INDEX_ADVERTISMENT;
cur->ws_info->pending_key_index_info.index = index;
return;
}
}
mac_helper_security_auto_request_key_index_set(cur, index, index + 1);
}
static void ws_bootstrap_nw_frame_counter_set(protocol_interface_info_entry_t *cur, uint32_t counter, uint8_t slot)
{
// Set frame counter
mac_helper_key_link_frame_counter_set(cur->id, counter, slot);
}
static void ws_bootstrap_nw_frame_counter_read(protocol_interface_info_entry_t *cur, uint32_t *counter, uint8_t slot)
{
// Read frame counter
mac_helper_key_link_frame_counter_read(cur->id, counter, slot);
}
static void ws_bootstrap_nw_info_updated(protocol_interface_info_entry_t *cur, uint16_t pan_id, uint16_t pan_version, char *network_name)
{
/* For border router, the PAE controller reads PAN ID, PAN version and network name from storage.
* If they are set, takes them into use here.
*/
if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) {
// Get PAN ID and network name
ws_gen_cfg_t gen_cfg;
if (ws_cfg_gen_get(&gen_cfg, NULL) < 0) {
return;
}
// If PAN ID has not been set, set it
if (gen_cfg.network_pan_id == 0xffff) {
gen_cfg.network_pan_id = pan_id;
// Sets PAN version
cur->ws_info->pan_information.pan_version = pan_version;
cur->ws_info->pan_information.pan_version_set = true;
}
// If network name has not been set, set it
if (strlen(gen_cfg.network_name) == 0) {
strncpy(gen_cfg.network_name, network_name, 32);
}
// Stores the settings
ws_cfg_gen_set(cur, NULL, &gen_cfg, 0);
}
}
static void ws_bootstrap_authentication_completed(protocol_interface_info_entry_t *cur, auth_result_e result, uint8_t *target_eui_64)
{
if (result == AUTH_RESULT_OK) {
tr_debug("authentication success eui64:%s", trace_array(target_eui_64, 8));
if (target_eui_64) {
// Authentication was made contacting the authenticator
cur->ws_info->authentication_time = cur->ws_info->uptime;
}
ws_bootstrap_event_configuration_start(cur);
} else if (result == AUTH_RESULT_ERR_TX_ERR) {
// eapol parent selected is not working
tr_debug("authentication TX failed");
ws_bootstrap_candidate_parent_mark_failure(cur, target_eui_64);
// Go back for network scanning
ws_bootstrap_state_change(cur, ER_ACTIVE_SCAN);
// Start PAS interval between imin - imax.
cur->ws_info->trickle_pas_running = true;
trickle_start(&cur->ws_info->trickle_pan_advertisement_solicit, &cur->ws_info->trickle_params_pan_discovery);
// Parent selection is made before imin/2 so if there is parent candidates solicit is not sent
cur->bootsrap_state_machine_cnt = randLIB_get_random_in_range(10, cur->ws_info->trickle_params_pan_discovery.Imin >> 1);
tr_info("Making parent selection in %u s", (cur->bootsrap_state_machine_cnt / 10));
} else {
tr_debug("authentication failed");
// What else to do to start over again...
// Trickle is reseted when entering to discovery from state 2
trickle_inconsistent_heard(&cur->ws_info->trickle_pan_advertisement_solicit, &cur->ws_info->trickle_params_pan_discovery);
ws_bootstrap_event_discovery_start(cur);
}
}
static const uint8_t *ws_bootstrap_authentication_next_target(protocol_interface_info_entry_t *cur, const uint8_t *previous_eui_64, uint16_t *pan_id)
{
ws_bootstrap_candidate_parent_mark_failure(cur, previous_eui_64);
// Gets best target
parent_info_t *parent_info = ws_bootstrap_candidate_parent_get_best(cur);
if (parent_info) {
/* On failure still continues with the new parent, and on next call,
will try to set the neighbor again */
ws_bootstrap_neighbor_set(cur, parent_info, true);
*pan_id = parent_info->pan_id;
return parent_info->addr;
}
// If no targets found, retries the last one
return previous_eui_64;
}
static void ws_bootstrap_eapol_congestion_init(protocol_interface_info_entry_t *cur)
{
random_early_detection_free(cur->llc_random_early_detection);
cur->llc_random_early_detection = NULL;
if (cur->llc_random_early_detection == NULL) {
cur->llc_random_early_detection = random_early_detection_create(
cur->ws_info->cfg->sec_prot.max_simult_sec_neg_tx_queue_min,
cur->ws_info->cfg->sec_prot.max_simult_sec_neg_tx_queue_max,
100, RED_AVERAGE_WEIGHT_EIGHTH);
}
random_early_detection_free(cur->llc_eapol_random_early_detection);
cur->llc_eapol_random_early_detection = NULL;
if (cur->llc_eapol_random_early_detection == NULL) {
cur->llc_eapol_random_early_detection = random_early_detection_create(
cur->ws_info->cfg->sec_prot.max_simult_sec_neg_tx_queue_min,
cur->ws_info->cfg->sec_prot.max_simult_sec_neg_tx_queue_max,
100, RED_AVERAGE_WEIGHT_EIGHTH);
}
}
static bool ws_bootstrap_eapol_congestion_get(protocol_interface_info_entry_t *cur, uint16_t active_supp)
{
if (cur == NULL || cur->random_early_detection == NULL || cur->llc_random_early_detection == NULL || cur->llc_eapol_random_early_detection == NULL) {
return false;
}
bool return_value = false;
static struct red_info_s *red_info = NULL;
uint16_t adaptation_average = 0;
uint16_t llc_average = 0;
uint16_t llc_eapol_average = 0;
uint16_t average_sum = 0;
uint8_t active_max = 0;
//TODO implement API for HEAP info request
uint32_t heap_size;
const mem_stat_t *mem_stats = ns_dyn_mem_get_mem_stat();
if (mem_stats) {
heap_size = mem_stats->heap_sector_size;
} else {
heap_size = 0;
}
/*
* For different memory sizes the max simultaneous authentications will be
* 32k: (32k / 50k) * 2 + 1 = 1
* 65k: (65k / 50k) * 2 + 1 = 3
* 250k: (250k / 50k) * 2 + 1 = 11
* 1000k: (1000k / 50k) * 2 + 1 = 41
* 2000k: (2000k / 50k) * 2 + 1 = 50 (upper limit)
*/
active_max = (heap_size / 50000) * 2 + 1;
if (active_max > 50) {
active_max = 50;
}
// Read the values for adaptation and LLC queues
adaptation_average = random_early_detetction_aq_read(cur->random_early_detection);
llc_average = random_early_detetction_aq_read(cur->llc_random_early_detection);
llc_eapol_average = random_early_detetction_aq_read(cur->llc_eapol_random_early_detection);
// Calculate combined average
average_sum = adaptation_average + llc_average + llc_eapol_average;
// Maximum for active supplicants based on memory reached, fail
if (active_supp >= active_max) {
return_value = true;
goto congestion_get_end;
}
// Always allow at least five negotiations (if memory does not limit)
if (active_supp < 5) {
goto congestion_get_end;
}
if (red_info == NULL) {
red_info = random_early_detection_create(
cur->ws_info->cfg->sec_prot.max_simult_sec_neg_tx_queue_min,
cur->ws_info->cfg->sec_prot.max_simult_sec_neg_tx_queue_max,
100, RED_AVERAGE_WEIGHT_DISABLED);
}
if (red_info == NULL) {
goto congestion_get_end;
}
// Check drop probability
average_sum = random_early_detetction_aq_calc(red_info, average_sum);
return_value = random_early_detection_congestion_check(red_info);
congestion_get_end:
tr_info("Active supplicant limit, active: %i max: %i summed averageQ: %i adapt averageQ: %i LLC averageQ: %i LLC EAPOL averageQ: %i drop: %s", active_supp, active_max, average_sum, adaptation_average, llc_average, llc_eapol_average, return_value ? "T" : "F");
return return_value;
}
// Start configuration learning
static void ws_bootstrap_start_configuration_learn(protocol_interface_info_entry_t *cur)
{
tr_debug("router configuration learn start");
ws_bootstrap_state_change(cur, ER_SCAN);
cur->ws_info->configuration_learned = false;
// Clear all temporary information
ws_bootstrap_ip_stack_reset(cur);
cur->ws_info->pas_requests = 0;
//Calculate max time for config learn state
cur->ws_info->pan_config_sol_max_timeout = trickle_timer_max(&cur->ws_info->trickle_params_pan_discovery, PCS_MAX);
// Reset advertisement solicit trickle to start discovering network
cur->ws_info->trickle_pcs_running = true;
trickle_start(&cur->ws_info->trickle_pan_config_solicit, &cur->ws_info->trickle_params_pan_discovery);
trickle_inconsistent_heard(&cur->ws_info->trickle_pan_config_solicit, &cur->ws_info->trickle_params_pan_discovery);
}
static void ws_bootstrap_rpl_scan_start(protocol_interface_info_entry_t *cur)
{
tr_debug("Start RPL learn");
// Stop Trickle timers
ws_bootstrap_asynch_trickle_stop(cur);
// routers wait until RPL root is contacted
ws_bootstrap_state_change(cur, ER_RPL_SCAN);
// Change state as the state is checked in state machine
cur->ws_info->rpl_state = RPL_EVENT_LOCAL_REPAIR_START;
//For Large network and medium should do passive scan
if (ws_cfg_network_config_get(cur) > CONFIG_SMALL) {
// Set timeout for check to 30 - 60 seconds
cur->bootsrap_state_machine_cnt = randLIB_get_random_in_range(WS_RPL_DIS_INITIAL_TIMEOUT / 2, WS_RPL_DIS_INITIAL_TIMEOUT);
}
}
/*
* Event transitions
*
* */
void ws_bootstrap_event_discovery_start(protocol_interface_info_entry_t *cur)
{
ws_bootsrap_event_trig(WS_DISCOVERY_START, cur->bootStrapId, ARM_LIB_LOW_PRIORITY_EVENT, NULL);
}
void ws_bootstrap_event_configuration_start(protocol_interface_info_entry_t *cur)
{
ws_bootsrap_event_trig(WS_CONFIGURATION_START, cur->bootStrapId, ARM_LIB_LOW_PRIORITY_EVENT, NULL);
}
void ws_bootstrap_event_authentication_start(protocol_interface_info_entry_t *cur)
{
ws_bootstrap_state_change(cur, ER_PANA_AUTH);
}
void ws_bootstrap_event_operation_start(protocol_interface_info_entry_t *cur)
{
ws_bootsrap_event_trig(WS_OPERATION_START, cur->bootStrapId, ARM_LIB_LOW_PRIORITY_EVENT, NULL);
}
void ws_bootstrap_event_routing_ready(protocol_interface_info_entry_t *cur)
{
ws_bootsrap_event_trig(WS_ROUTING_READY, cur->bootStrapId, ARM_LIB_LOW_PRIORITY_EVENT, NULL);
}
void ws_bootstrap_event_disconnect(protocol_interface_info_entry_t *cur, ws_bootsrap_event_type_e event_type)
{
ws_bootsrap_event_trig(event_type, cur->bootStrapId, ARM_LIB_LOW_PRIORITY_EVENT, NULL);
}
void ws_bootstrap_configuration_trickle_reset(protocol_interface_info_entry_t *cur)
{
trickle_inconsistent_heard(&cur->ws_info->trickle_pan_config, &cur->ws_info->trickle_params_pan_discovery);
}
static void ws_set_asynch_channel_list(protocol_interface_info_entry_t *cur, asynch_request_t *async_req)
{
memset(&async_req->channel_list, 0, sizeof(channel_list_s));
if (cur->ws_info->cfg->fhss.fhss_uc_channel_function == WS_FIXED_CHANNEL) {
//SET 1 Channel only
uint16_t channel_number = cur->ws_info->cfg->fhss.fhss_uc_fixed_channel;
async_req->channel_list.channel_mask[0 + (channel_number / 32)] = (1 << (channel_number % 32));
} else {
ws_generate_channel_list(async_req->channel_list.channel_mask, cur->ws_info->hopping_schdule.number_of_channels, cur->ws_info->hopping_schdule.regulatory_domain, cur->ws_info->hopping_schdule.operating_class, cur->ws_info->hopping_schdule.channel_plan_id);
}
async_req->channel_list.channel_page = CHANNEL_PAGE_10;
}
static void ws_bootstrap_pan_advert_solicit(protocol_interface_info_entry_t *cur)
{
asynch_request_t async_req;
memset(&async_req, 0, sizeof(asynch_request_t));
async_req.message_type = WS_FT_PAN_ADVERT_SOL;
//Request UTT Header and US and Net name from payload
async_req.wh_requested_ie_list.utt_ie = true;
async_req.wp_requested_nested_ie_list.us_ie = true;
async_req.wp_requested_nested_ie_list.net_name_ie = true;
ws_set_asynch_channel_list(cur, &async_req);
async_req.security.SecurityLevel = 0;
ws_stats_update(cur, STATS_WS_ASYNCH_TX_PAS, 1);
ws_llc_asynch_request(cur, &async_req);
}
static void ws_bootstrap_pan_config_solicit(protocol_interface_info_entry_t *cur)
{
asynch_request_t async_req;
memset(&async_req, 0, sizeof(asynch_request_t));
async_req.message_type = WS_FT_PAN_CONF_SOL;
//Request UTT Header and US and Net name from payload
async_req.wh_requested_ie_list.utt_ie = true;
async_req.wp_requested_nested_ie_list.us_ie = true;
async_req.wp_requested_nested_ie_list.net_name_ie = true;
ws_set_asynch_channel_list(cur, &async_req);
async_req.security.SecurityLevel = 0;
ws_stats_update(cur, STATS_WS_ASYNCH_TX_PCS, 1);
ws_llc_asynch_request(cur, &async_req);
}
static struct rpl_instance *ws_get_rpl_instance(protocol_interface_info_entry_t *cur)
{
if (!cur || !cur->rpl_domain) {
return NULL;
}
struct rpl_instance *best_instance = NULL;
ns_list_foreach(struct rpl_instance, instance, &cur->rpl_domain->instances) {
best_instance = instance;
// Select best grounded and lowest rank? But there should be only one really
}
return best_instance;
}
static uint16_t ws_bootstrap_routing_cost_calculate(protocol_interface_info_entry_t *cur)
{
mac_neighbor_table_entry_t *mac_neighbor = mac_neighbor_entry_get_priority(mac_neighbor_info(cur));
if (!mac_neighbor) {
return 0xffff;
}
ws_neighbor_class_entry_t *ws_neighbor = ws_neighbor_class_entry_get(&cur->ws_info->neighbor_storage, mac_neighbor->index);
if (!ws_neighbor) {
return 0xffff;
}
uint16_t etx = etx_local_etx_read(cur->id, mac_neighbor->index);
if (etx == 0) {
etx = WS_ETX_MAX; //SET maximum value here if ETX is unknown
} else {
//Scale to 128 based ETX (local read return 0x100 - 0xffff
etx = etx >> 1;
}
// Make the 0xffff as maximum value
if (ws_neighbor->routing_cost + etx > 0xffff) {
return 0xffff;
}
return ws_neighbor->routing_cost + etx;
}
static uint16_t ws_bootstrap_rank_get(protocol_interface_info_entry_t *cur)
{
struct rpl_instance *rpl_instance = ws_get_rpl_instance(cur);
if (!rpl_instance) {
return 0xffff;
}
return rpl_control_current_rank(rpl_instance);
}
static uint16_t ws_bootstrap_min_rank_inc_get(protocol_interface_info_entry_t *cur)
{
struct rpl_instance *rpl_instance = ws_get_rpl_instance(cur);
if (!rpl_instance) {
return 0xffff;
}
struct rpl_dodag_info_t dodag_info;
if (!rpl_control_read_dodag_info(rpl_instance, &dodag_info)) {
return 0xffff;
}
return dodag_info.dag_min_hop_rank_inc;
}
static void ws_bootstrap_pan_advert(protocol_interface_info_entry_t *cur)
{
asynch_request_t async_req;
memset(&async_req, 0, sizeof(asynch_request_t));
async_req.message_type = WS_FT_PAN_ADVERT;
//Request UTT Header, Pan information and US and Net name from payload
async_req.wh_requested_ie_list.utt_ie = true;
async_req.wp_requested_nested_ie_list.us_ie = true;
async_req.wp_requested_nested_ie_list.pan_ie = true;
async_req.wp_requested_nested_ie_list.net_name_ie = true;
ws_set_asynch_channel_list(cur, &async_req);
async_req.security.SecurityLevel = 0;
if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) {
// Border routers write the NW size
cur->ws_info->pan_information.pan_size = ws_bbr_pan_size(cur);
cur->ws_info->pan_information.routing_cost = 0;
} else {
// Nodes need to calculate routing cost
// PAN size is saved from latest PAN advertisement
cur->ws_info->pan_information.routing_cost = ws_bootstrap_routing_cost_calculate(cur);
}
ws_stats_update(cur, STATS_WS_ASYNCH_TX_PA, 1);
ws_llc_asynch_request(cur, &async_req);
}
static void ws_bootstrap_pan_config(protocol_interface_info_entry_t *cur)
{
asynch_request_t async_req;
memset(&async_req, 0, sizeof(asynch_request_t));
async_req.message_type = WS_FT_PAN_CONF;
//Request UTT Header, Pan information and US and Net name from payload
async_req.wh_requested_ie_list.utt_ie = true;
async_req.wh_requested_ie_list.bt_ie = true;
async_req.wp_requested_nested_ie_list.us_ie = true;
async_req.wp_requested_nested_ie_list.bs_ie = true;
async_req.wp_requested_nested_ie_list.pan_version_ie = true;
async_req.wp_requested_nested_ie_list.gtkhash_ie = true;
async_req.wp_requested_nested_ie_list.vp_ie = true;
ws_set_asynch_channel_list(cur, &async_req);
async_req.security.SecurityLevel = mac_helper_default_security_level_get(cur);
async_req.security.KeyIdMode = mac_helper_default_security_key_id_mode_get(cur);
if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER && cur->ws_info->pending_key_index_info.state == PENDING_KEY_INDEX_ADVERTISMENT) {
async_req.security.KeyIndex = cur->ws_info->pending_key_index_info.index + 1;
cur->ws_info->pending_key_index_info.state = PENDING_KEY_INDEX_ACTIVATE;
} else {
async_req.security.KeyIndex = mac_helper_default_key_index_get(cur);
}
ws_stats_update(cur, STATS_WS_ASYNCH_TX_PC, 1);
ws_llc_asynch_request(cur, &async_req);
}
static int8_t ws_bootstrap_backbone_ip_addr_get(protocol_interface_info_entry_t *interface_ptr, uint8_t *address)
{
(void) interface_ptr;
(void) address;
if (ws_bbr_backbone_address_get(address)) {
return 0;
}
return -1;
}
static void ws_bootstrap_event_handler(arm_event_s *event)
{
ws_bootsrap_event_type_e event_type;
event_type = (ws_bootsrap_event_type_e)event->event_type;
protocol_interface_info_entry_t *cur;
cur = protocol_stack_interface_info_get_by_bootstrap_id(event->receiver);
if (!cur) {
return;
}
switch (event_type) {
case WS_INIT_EVENT:
tr_debug("tasklet init");
break;
case WS_DISCOVERY_START:
tr_info("Discovery start");
protocol_mac_reset(cur);
ws_llc_reset(cur);
lowpan_adaptation_interface_reset(cur->id);
//Clear Pending Key Index State
cur->ws_info->pending_key_index_info.state = NO_PENDING_PROCESS;
cur->mac_parameters->mac_default_key_index = 0;
ipv6_destination_cache_clean(cur->id);
// Clear parent blacklist
blacklist_clear();
// All trickle timers stopped to allow entry from any state
ws_bootstrap_asynch_trickle_stop(cur);
//Init Packet congestion
ws_bootstrap_packet_congestion_init(cur);
if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) {
tr_info("Border router start network");
if (!ws_bbr_ready_to_start(cur)) {
// Wi-SUN not started yet we wait for Border router permission
ws_bootstrap_state_change(cur, ER_WAIT_RESTART);
cur->nwk_nd_re_scan_count = randLIB_get_random_in_range(40, 100);
return;
}
// Clear Old information from stack
ws_nud_table_reset(cur);
ws_bootstrap_neighbor_list_clean(cur);
ws_bootstrap_ip_stack_reset(cur);
ws_pae_controller_auth_init(cur);
if (cur->ws_info->cfg->gen.network_pan_id == 0xffff) {
cur->ws_info->network_pan_id = randLIB_get_random_in_range(0, 0xfffd);
} else {
cur->ws_info->network_pan_id = cur->ws_info->cfg->gen.network_pan_id;
}
if (!cur->ws_info->pan_information.pan_version_set) {
cur->ws_info->pan_information.pan_version = randLIB_get_random_in_range(0, 0xffff);
cur->ws_info->pan_information.pan_version_set = true;
}
cur->ws_info->pan_information.pan_size = 0;
cur->ws_info->pan_information.routing_cost = 0;
cur->ws_info->pan_information.rpl_routing_method = true;
cur->ws_info->pan_information.use_parent_bs = true;
cur->ws_info->pan_information.version = WS_FAN_VERSION_1_0;
uint8_t *gtkhash = ws_pae_controller_gtk_hash_ptr_get(cur);
ws_llc_set_gtkhash(cur, gtkhash);
ws_bbr_pan_version_increase(cur);
// Set default parameters for FHSS when starting a discovery
ws_common_regulatory_domain_config(cur, &cur->ws_info->hopping_schdule);
ws_fhss_border_router_configure(cur);
ws_bootstrap_set_domain_rf_config(cur);
ws_bootstrap_fhss_activate(cur);
uint8_t ll_addr[16];
addr_interface_get_ll_address(cur, ll_addr, 1);
//SET EAPOL authenticator EUI64
ws_pae_controller_border_router_addr_write(cur, cur->mac);
// Set EAPOL relay to port 10255 and authenticator relay to 10253 (and to own ll address)
ws_eapol_relay_start(cur, BR_EAPOL_RELAY_SOCKET_PORT, ll_addr, EAPOL_RELAY_SOCKET_PORT);
// Set authenticator relay to port 10253 and PAE to 10254 (and to own ll address)
ws_eapol_auth_relay_start(cur, EAPOL_RELAY_SOCKET_PORT, ll_addr, PAE_AUTH_SOCKET_PORT);
// Set PAN ID and network name to controller
ws_pae_controller_nw_info_set(cur, cur->ws_info->network_pan_id, cur->ws_info->pan_information.pan_version, cur->ws_info->cfg->gen.network_name);
// Set backbone IP address get callback
ws_pae_controller_auth_cb_register(cur, ws_bootstrap_backbone_ip_addr_get);
// Set PAE port to 10254 and authenticator relay to 10253 (and to own ll address)
ws_pae_controller_authenticator_start(cur, PAE_AUTH_SOCKET_PORT, ll_addr, EAPOL_RELAY_SOCKET_PORT);
// Initialize eapol congestion tracking
ws_bootstrap_eapol_congestion_init(cur);
// Set retry configuration for bootstrap ready state
ws_bootstrap_configure_max_retries(cur, WS_MAX_FRAME_RETRIES);
// Set TX failure request restart configuration
ws_bootstrap_configure_data_request_restart(cur, WS_CCA_REQUEST_RESTART_MAX, WS_TX_REQUEST_RESTART_MAX, WS_REQUEST_RESTART_BLACKLIST_MIN, WS_REQUEST_RESTART_BLACKLIST_MAX);
// Set CSMA-CA backoff configuration
ws_bootstrap_configure_csma_ca_backoffs(cur, WS_MAX_CSMA_BACKOFFS, WS_MAC_MIN_BE, WS_MAC_MAX_BE);
ws_bootstrap_event_operation_start(cur);
break;
}
ws_pae_controller_supp_init(cur);
// Clear learned neighbours
ws_bootstrap_neighbor_list_clean(cur);
// Configure LLC for network discovery
ws_bootstrap_network_discovery_configure(cur);
ws_bootstrap_fhss_activate(cur);
// Set retry configuration for discovery state
ws_bootstrap_configure_max_retries(cur, WS_MAX_FRAME_RETRIES_BOOTSTRAP);
// Set TX failure request restart configuration for discovery state
ws_bootstrap_configure_data_request_restart(cur, WS_CCA_REQUEST_RESTART_MAX, WS_TX_REQUEST_RESTART_MAX_BOOTSTRAP, WS_REQUEST_RESTART_BLACKLIST_MIN, WS_REQUEST_RESTART_BLACKLIST_MAX);
// Set CSMA-CA backoff configuration
ws_bootstrap_configure_csma_ca_backoffs(cur, WS_MAX_CSMA_BACKOFFS, WS_MAC_MIN_BE, WS_MAC_MAX_BE);
// Start network scan
ws_bootstrap_start_discovery(cur);
break;
case WS_CONFIGURATION_START:
tr_info("Configuration start");
// Old configuration is considered invalid stopping all
ws_bootstrap_asynch_trickle_stop(cur);
// Build list of possible neighbours and learn first broadcast schedule
ws_bootstrap_start_configuration_learn(cur);
break;
case WS_OPERATION_START:
tr_info("operation start");
// Advertisements stopped during the RPL scan
ws_bootstrap_asynch_trickle_stop(cur);
// Activate RPL
// Activate IPv6 stack
ws_bootstrap_ip_stack_activate(cur);
ws_bootstrap_rpl_activate(cur);
ws_bootstrap_network_start(cur);
// Wait for RPL start
if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) {
ws_bootstrap_event_routing_ready(cur);
} else {
ws_bootstrap_rpl_scan_start(cur);
/* While in Join State 4, if a non Border Router determines it has been unable to communicate with the PAN Border
* Router for an interval of PAN_TIMEOUT, a node MUST assume failure of the PAN Border Router and MUST
* Transition to Join State 1
*/
ws_common_border_router_alive_update(cur);
}
break;
case WS_ROUTING_READY:
tr_info("Routing ready");
// stopped all to make sure we can enter here from any state
ws_bootstrap_asynch_trickle_stop(cur);
// Indicate PAE controller that bootstrap is ready
ws_pae_controller_bootstrap_done(cur);
ws_bootstrap_advertise_start(cur);
ws_bootstrap_state_change(cur, ER_BOOTSRAP_DONE);
break;
case WS_FAST_DISCONNECT:
ws_bootstrap_disconnect(cur, WS_FAST_DISCONNECT);
break;
case WS_NORMAL_DISCONNECT:
ws_bootstrap_disconnect(cur, WS_NORMAL_DISCONNECT);
break;
default:
tr_err("Invalid event received");
break;
}
}
static int8_t ws_bootstrap_neighbor_set(protocol_interface_info_entry_t *cur, parent_info_t *parent_ptr, bool clear_list)
{
uint16_t pan_id = cur->ws_info->network_pan_id;
// Add EAPOL neighbor
cur->ws_info->network_pan_id = parent_ptr->pan_id;
cur->ws_info->pan_information = parent_ptr->pan_information;
cur->ws_info->pan_information.pan_version = 0; // This is learned from actual configuration
// If PAN ID changes, clear learned neighbors and activate FHSS
if (pan_id != cur->ws_info->network_pan_id) {
if (clear_list) {
ws_bootstrap_neighbor_list_clean(cur);
}
ws_bootstrap_fhss_activate(cur);
}
llc_neighbour_req_t neighbor_info;
if (!ws_bootstrap_neighbor_info_request(cur, parent_ptr->addr, &neighbor_info, true)) {
//Remove Neighbour and set Link setup back
ns_list_remove(&cur->ws_info->parent_list_reserved, parent_ptr);
ns_list_add_to_end(&cur->ws_info->parent_list_free, parent_ptr);
return -1;
}
ws_bootstrap_neighbor_set_stable(cur, parent_ptr->addr);
ws_neighbor_class_neighbor_unicast_time_info_update(neighbor_info.ws_neighbor, &parent_ptr->ws_utt, parent_ptr->timestamp, parent_ptr->addr);
ws_neighbor_class_neighbor_unicast_schedule_set(neighbor_info.ws_neighbor, &parent_ptr->ws_us, &cur->ws_info->hopping_schdule);
return 0;
}
/*
* State machine
*
* */
void ws_bootstrap_network_scan_process(protocol_interface_info_entry_t *cur)
{
parent_info_t *selected_parent_ptr;
tr_debug("analyze network discovery result");
select_best_candidate:
selected_parent_ptr = ws_bootstrap_candidate_parent_get_best(cur);
if (!selected_parent_ptr) {
// Configure LLC for network discovery
ws_bootstrap_network_discovery_configure(cur);
// randomize new channel and start MAC
ws_bootstrap_fhss_activate(cur);
// Next check will be after one trickle
uint32_t random_start = cur->ws_info->trickle_params_pan_discovery.Imin + randLIB_get_random_in_range(0, cur->ws_info->trickle_params_pan_discovery.Imin);
if (random_start > 0xffff) {
random_start = 0xffff;
}
cur->bootsrap_state_machine_cnt = random_start;
tr_info("Making parent selection in %u s", (cur->bootsrap_state_machine_cnt / 10));
return;
}
tr_info("selected parent:%s panid %u", trace_array(selected_parent_ptr->addr, 8), selected_parent_ptr->pan_id);
if (ws_bootstrap_neighbor_set(cur, selected_parent_ptr, false) < 0) {
goto select_best_candidate;
}
ws_pae_controller_set_target(cur, selected_parent_ptr->pan_id, selected_parent_ptr->addr); // temporary!!! store since auth
ws_bootstrap_event_authentication_start(cur);
return;
}
void ws_bootstrap_configure_process(protocol_interface_info_entry_t *cur)
{
if (cur->ws_info->configuration_learned) {
ws_bootstrap_network_configuration_learn(cur);
ws_bootstrap_event_operation_start(cur);
return;
}
return;
}
void ws_bootstrap_rpl_wait_process(protocol_interface_info_entry_t *cur)
{
if (cur->ws_info->rpl_state == RPL_EVENT_DAO_DONE) {
// RPL routing is ready
cur->ws_info->connected_time = cur->ws_info->uptime;
ws_bootstrap_event_routing_ready(cur);
} else if (!rpl_control_have_dodag(cur->rpl_domain)) {
// RPL not ready send DIS message if possible
if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_ROUTER) {
// TODO Multicast DIS should be sent only if no DIO heard for some time
rpl_control_transmit_dis(cur->rpl_domain, cur, 0, 0, NULL, 0, ADDR_LINK_LOCAL_ALL_RPL_NODES);
}
// set timer for next DIS
cur->bootsrap_state_machine_cnt = randLIB_get_random_in_range(WS_RPL_DIS_TIMEOUT / 2, WS_RPL_DIS_TIMEOUT);
}
return;
}
static bool ws_bootstrap_state_discovery(struct protocol_interface_info_entry *cur)
{
if (cur->nwk_bootstrap_state == ER_ACTIVE_SCAN) {
return true;
}
return false;
}
static bool ws_bootstrap_state_authenticate(struct protocol_interface_info_entry *cur)
{
// Think about the state value
if (cur->nwk_bootstrap_state == ER_PANA_AUTH) {
return true;
}
return false;
}
static bool ws_bootstrap_state_configure(struct protocol_interface_info_entry *cur)
{
// Think about the state value
if (cur->nwk_bootstrap_state == ER_SCAN) {
return true;
}
return false;
}
static bool ws_bootstrap_state_wait_rpl(struct protocol_interface_info_entry *cur)
{
// Think about the state value
if (cur->nwk_bootstrap_state == ER_RPL_SCAN) {
return true;
}
return false;
}
static bool ws_bootstrap_state_active(struct protocol_interface_info_entry *cur)
{
if (cur->nwk_bootstrap_state == ER_BOOTSRAP_DONE) {
return true;
}
return false;
}
static void ws_bootstrap_state_change(protocol_interface_info_entry_t *cur, icmp_state_t nwk_bootstrap_state)
{
cur->bootsrap_state_machine_cnt = 1;
cur->nwk_bootstrap_state = nwk_bootstrap_state;
}
void ws_bootstrap_state_machine(protocol_interface_info_entry_t *cur)
{
switch (cur->nwk_bootstrap_state) {
case ER_WAIT_RESTART:
tr_debug("WS SM:Wait for startup");
ws_bootstrap_event_discovery_start(cur);
break;
case ER_ACTIVE_SCAN:
tr_debug("WS SM:Active Scan");
ws_bootstrap_network_scan_process(cur);
break;
case ER_SCAN:
tr_debug("WS SM:configuration Scan");
ws_bootstrap_configure_process(cur);
break;
case ER_PANA_AUTH:
tr_info("authentication start");
// Advertisements stopped during the EAPOL
ws_bootstrap_asynch_trickle_stop(cur);
ws_fhss_configure(cur, false);
int8_t new_default = cur->ws_info->weakest_received_rssi - 1;
if ((new_default < CCA_DEFAULT_DBM) && (new_default >= CCA_LOW_LIMIT) && (new_default <= CCA_HIGH_LIMIT)) {
// Restart automatic CCA threshold using weakest received RSSI as new default
mac_helper_start_auto_cca_threshold(cur->id, cur->ws_info->hopping_schdule.number_of_channels, cur->ws_info->weakest_received_rssi - 1, CCA_HIGH_LIMIT, CCA_LOW_LIMIT);
}
ws_bootstrap_start_authentication(cur);
break;
case ER_RPL_SCAN:
tr_debug("WS SM:Wait RPL to contact DODAG root");
ws_bootstrap_rpl_wait_process(cur);
break;
case ER_BOOTSRAP_DONE:
tr_debug("WS SM:Bootstrap Done");
// Bootstrap_done event to application
nwk_bootsrap_state_update(ARM_NWK_BOOTSTRAP_READY, cur);
break;
case ER_RPL_NETWORK_LEAVING:
tr_debug("WS SM:RPL Leaving ready trigger discovery");
ws_bootstrap_event_discovery_start(cur);
break;
default:
tr_warn("WS SM:Invalid state %d", cur->nwk_bootstrap_state);
}
}
void ws_bootstrap_trickle_timer(protocol_interface_info_entry_t *cur, uint16_t ticks)
{
if (cur->ws_info->trickle_pas_running &&
trickle_timer(&cur->ws_info->trickle_pan_advertisement_solicit, &cur->ws_info->trickle_params_pan_discovery, ticks)) {
// send PAN advertisement solicit
tr_info("Send PAN advertisement Solicit");
ws_bootstrap_pan_advert_solicit(cur);
}
if (cur->ws_info->trickle_pcs_running) {
//Update MAX config sol timeout timer
if (cur->ws_info->pan_config_sol_max_timeout > ticks) {
cur->ws_info->pan_config_sol_max_timeout -= ticks;
} else {
//Config sol state timeout
cur->ws_info->pan_config_sol_max_timeout = 0;
}
if (trickle_timer(&cur->ws_info->trickle_pan_config_solicit, &cur->ws_info->trickle_params_pan_discovery, ticks)) {
if (cur->ws_info->pas_requests < PCS_MAX) {
// send PAN Configuration solicit
tr_info("Send PAN configuration Solicit");
ws_bootstrap_pan_config_solicit(cur);
}
//Update counter every time reason that we detect PCS_MAX higher state
cur->ws_info->pas_requests++;
}
if (cur->ws_info->pas_requests > PCS_MAX || cur->ws_info->pan_config_sol_max_timeout == 0) {
// if MAX PCS sent or max waited timeout restart discovery
// Trickle is reseted when entering to discovery from state 3
tr_info("PAN configuration Solicit timeout");
trickle_inconsistent_heard(&cur->ws_info->trickle_pan_advertisement_solicit, &cur->ws_info->trickle_params_pan_discovery);
ws_bootstrap_event_discovery_start(cur);
return;
}
}
if (cur->ws_info->trickle_pa_running &&
trickle_timer(&cur->ws_info->trickle_pan_advertisement, &cur->ws_info->trickle_params_pan_discovery, ticks)) {
// send PAN advertisement
tr_info("Send PAN advertisement");
ws_bootstrap_pan_advert(cur);
}
if (cur->ws_info->trickle_pc_running) {
if (cur->ws_info->trickle_pc_consistency_block_period) {
if (ticks >= cur->ws_info->trickle_pc_consistency_block_period) {
cur->ws_info->trickle_pc_consistency_block_period = 0;
} else {
cur->ws_info->trickle_pc_consistency_block_period -= ticks;
}
}
if (trickle_timer(&cur->ws_info->trickle_pan_config, &cur->ws_info->trickle_params_pan_discovery, ticks)) {
// send PAN Configuration
tr_info("Send PAN configuration");
ws_bootstrap_pan_config(cur);
}
}
}
void ws_bootstrap_seconds_timer(protocol_interface_info_entry_t *cur, uint32_t seconds)
{
/* Border router keep alive check
*/
if (cur->ws_info->pan_timeout_timer) {
// PAN version timer running
if (cur->ws_info->pan_timeout_timer > seconds) {
cur->ws_info->pan_timeout_timer -= seconds;
if (cur->ws_info->pan_timeout_timer < cur->ws_info->cfg->timing.pan_timeout / 10) {
/* pan timeout is closing need to verify that DAO is tested before the pan times out.
This will give some extra time for RPL to find better parents.
Border router liveliness can be checked from version number change or from successful DAO registrations
in this case there has not been any version number changes during this PAN lifetime.
*/
rpl_control_dao_timeout(cur->rpl_domain, 20);
}
} else {
// Border router has timed out
//Clear Timeout timer
cur->ws_info->pan_timeout_timer = 0;
tr_warn("Border router has timed out");
ws_bootstrap_event_disconnect(cur, WS_FAST_DISCONNECT);
}
}
if (cur->ws_info->aro_registration_timer) {
if (cur->ws_info->aro_registration_timer > seconds) {
cur->ws_info->aro_registration_timer -= seconds;
} else {
// Update all addressess. This function will update the timer value if needed
cur->ws_info->aro_registration_timer = 0;
ws_address_registration_update(cur, NULL);
}
}
if (cur->ws_info->ws_bsi_block.block_time) {
if (cur->ws_info->ws_bsi_block.block_time > seconds) {
cur->ws_info->ws_bsi_block.block_time -= seconds;
} else {
//Clear A BSI blokker
cur->ws_info->ws_bsi_block.block_time = 0;
cur->ws_info->ws_bsi_block.old_bsi = 0;
}
}
/*Update join state statistics*/
if (ws_bootstrap_state_discovery(cur)) {
ws_stats_update(cur, STATS_WS_STATE_1, 1);
} else if (ws_bootstrap_state_authenticate(cur)) {
ws_stats_update(cur, STATS_WS_STATE_2, 1);
} else if (ws_bootstrap_state_configure(cur)) {
ws_stats_update(cur, STATS_WS_STATE_3, 1);
} else if (ws_bootstrap_state_wait_rpl(cur)) {
ws_stats_update(cur, STATS_WS_STATE_4, 1);
} else if (ws_bootstrap_state_active(cur)) {
ws_stats_update(cur, STATS_WS_STATE_5, 1);
}
cur->ws_info->uptime++;
ws_llc_timer_seconds(cur, seconds);
}
void ws_bootstrap_primary_parent_update(protocol_interface_info_entry_t *interface, mac_neighbor_table_entry_t *neighbor)
{
if (interface->ws_info) {
llc_neighbour_req_t neighbor_info;
neighbor_info.neighbor = neighbor;
neighbor_info.ws_neighbor = ws_neighbor_class_entry_get(&interface->ws_info->neighbor_storage, neighbor->index);
ws_bootstrap_primary_parent_set(interface, &neighbor_info, WS_PARENT_HARD_SYNCH);
uint8_t link_local_address[16];
ws_bootsrap_create_ll_address(link_local_address, neighbor->mac64);
dhcp_client_server_address_update(interface->id, NULL, link_local_address);
ws_bootstrap_secondary_parent_update(interface);
}
}
void ws_bootstrap_secondary_parent_update(protocol_interface_info_entry_t *interface)
{
if (interface->ws_info) {
ns_list_foreach(if_address_entry_t, address, &interface->ip_addresses) {
if (!addr_is_ipv6_link_local(address->address)) {
ws_address_parent_update(interface);
}
}
}
}
int ws_bootstrap_stack_info_get(protocol_interface_info_entry_t *cur, struct ws_stack_info *info_ptr)
{
ws_neighbor_class_entry_t *ws_neighbour = NULL;
memset(info_ptr, 0, sizeof(struct ws_stack_info));
mac_neighbor_table_entry_t *mac_parent = mac_neighbor_entry_get_priority(mac_neighbor_info(cur));
if (mac_parent) {
ws_neighbour = ws_neighbor_class_entry_get(&cur->ws_info->neighbor_storage, mac_parent->index);
ws_bootsrap_create_ll_address(info_ptr->parent, mac_parent->mac64);
}
if (ws_neighbour) {
info_ptr->rsl_in = ws_neighbor_class_rsl_in_get(ws_neighbour);
info_ptr->rsl_out = ws_neighbor_class_rsl_out_get(ws_neighbour);
info_ptr->routing_cost = ws_neighbour->routing_cost;
}
info_ptr->device_min_sens = DEVICE_MIN_SENS;
if (ws_bootstrap_state_discovery(cur)) {
info_ptr->join_state = 1;
} else if (ws_bootstrap_state_authenticate(cur)) {
info_ptr->join_state = 2;
} else if (ws_bootstrap_state_configure(cur)) {
info_ptr->join_state = 3;
} else if (ws_bootstrap_state_wait_rpl(cur)) {
info_ptr->join_state = 4;
} else if (ws_bootstrap_state_active(cur)) {
info_ptr->join_state = 5;
}
info_ptr->pan_id = cur->ws_info->network_pan_id;
return 0;
}
int ws_bootstrap_neighbor_info_get(protocol_interface_info_entry_t *cur, ws_neighbour_info_t *neighbor_ptr, uint16_t table_max)
{
uint8_t count = 0;
if (!neighbor_ptr) {
// Return the aount of neighbors.
for (int n = 0; n < mac_neighbor_info(cur)->list_total_size; n++) {
mac_neighbor_table_entry_t *mac_entry = mac_neighbor_table_attribute_discover(mac_neighbor_info(cur), n);
if (mac_entry && mac_entry->lifetime && mac_entry->lifetime != 0xffffffff) {
count++;
}
}
return count;
}
if (table_max > mac_neighbor_info(cur)->list_total_size) {
table_max = mac_neighbor_info(cur)->list_total_size;
}
for (int n = 0; n < mac_neighbor_info(cur)->list_total_size; n++) {
if (count > table_max) {
break;
}
mac_neighbor_table_entry_t *mac_entry = mac_neighbor_table_attribute_discover(mac_neighbor_info(cur), n);
ws_neighbor_class_entry_t *ws_neighbor = ws_neighbor_class_entry_get(&cur->ws_info->neighbor_storage, n);
if (mac_entry && ws_neighbor && mac_entry->lifetime && mac_entry->lifetime != 0xffffffff) {
// Active neighbor entry
uint8_t ll_address[16];
memset(neighbor_ptr + count, 0, sizeof(ws_neighbour_info_t));
neighbor_ptr[count].lifetime = mac_entry->lifetime;
neighbor_ptr[count].rsl_in = ws_neighbor_class_rsl_in_get(ws_neighbor);
neighbor_ptr[count].rsl_out = ws_neighbor_class_rsl_out_get(ws_neighbor);
// ETX is shown calculated as 8 bit integer, but more common way is to use 7 bit such that 128 means ETX:1.0
neighbor_ptr[count].etx = etx_local_etx_read(cur->id, mac_entry->index);
if (neighbor_ptr[count].etx != 0xffff) {
neighbor_ptr[count].etx = neighbor_ptr[count].etx >> 1;
}
ws_bootsrap_create_ll_address(ll_address, mac_entry->mac64);
memcpy(neighbor_ptr[count].link_local_address, ll_address, 16);
if (rpl_control_is_dodag_parent_candidate(cur, ll_address, cur->ws_info->cfg->gen.rpl_parent_candidate_max)) {
neighbor_ptr[count].type = WS_CANDIDATE_PARENT;
}
neighbor_ptr[count].rpl_rank = rpl_control_neighbor_info_get(cur, ll_address, neighbor_ptr[count].global_address);
if (mac_entry->link_role == PRIORITY_PARENT_NEIGHBOUR) {
neighbor_ptr[count].type = WS_PRIMARY_PARENT;
}
if (mac_entry->link_role == SECONDARY_PARENT_NEIGHBOUR) {
neighbor_ptr[count].type = WS_SECONDARY_PARENT;
}
if (mac_entry->link_role == CHILD_NEIGHBOUR) {
neighbor_ptr[count].type = WS_CHILD;
}
ipv6_neighbour_t *IPv6_neighbor = ipv6_neighbour_get_registered_by_eui64(&cur->ipv6_neighbour_cache, mac_entry->mac64);
if (IPv6_neighbor) {
//This is a child
neighbor_ptr[count].type = WS_CHILD;
memcpy(neighbor_ptr[count].global_address, IPv6_neighbor->ip_address, 16);
// Child lifetimes are based on Registration times not a link time
neighbor_ptr[count].lifetime = IPv6_neighbor->lifetime;
}
count++;
}
}
// Go through list
return count;
}
//Calculate max_packet queue size
static uint16_t ws_bootstrap_define_congestin_max_threshold(uint32_t heap_total_size, uint16_t packet_size, uint16_t packet_per_seconds, uint32_t max_delay, uint16_t min_packet_queue_size, uint16_t max_packet_queue_size)
{
uint32_t max_packet_count = 0;
if (heap_total_size) {
//Claculate how many packet can be max queue to half of heap
max_packet_count = (heap_total_size / 2) / packet_size;
}
//Calculate how many packet is possible to queue for guarantee given max delay
uint32_t max_delayded_queue_size = max_delay * packet_per_seconds;
if (max_packet_count > max_delayded_queue_size) {
//Limit queue size by MAX delay
max_packet_count = max_delayded_queue_size;
}
if (max_packet_count > max_packet_queue_size) {
//Limit queue size by Max
max_packet_count = max_packet_queue_size;
} else if (max_packet_count < min_packet_queue_size) {
//Limit queue size by Min
max_packet_count = min_packet_queue_size;
}
return (uint16_t)max_packet_count;
}
static uint16_t ws_bootstrap_packet_per_seconds(protocol_interface_info_entry_t *cur, uint16_t packet_size)
{
uint32_t data_rate = ws_common_datarate_get(cur);
//calculate how many packet is possible send in paper
data_rate /= 8 * packet_size;
//Divide optimal by / 5 because we split TX / RX slots and BC schedule
//With Packet size 500 it should return
//Return 15 for 300kBits
//Return 7 for 150kBits
//Return 2 for 50kBits
return data_rate / 5;
}
static void ws_bootstrap_packet_congestion_init(protocol_interface_info_entry_t *cur)
{
random_early_detection_free(cur->random_early_detection);
cur->random_early_detection = NULL;
//TODO implement API for HEAP info request
uint32_t heap_size;
const mem_stat_t *mem_stats = ns_dyn_mem_get_mem_stat();
if (mem_stats) {
heap_size = mem_stats->heap_sector_size;
} else {
heap_size = 0;
}
uint16_t packet_per_seconds = ws_bootstrap_packet_per_seconds(cur, WS_CONGESTION_PACKET_SIZE);
uint16_t min_th, max_th;
if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) {
max_th = ws_bootstrap_define_congestin_max_threshold(heap_size, WS_CONGESTION_PACKET_SIZE, packet_per_seconds, WS_CONGESTION_QUEUE_DELAY, WS_CONGESTION_BR_MIN_QUEUE_SIZE, WS_CONGESTION_BR_MAX_QUEUE_SIZE);
} else {
max_th = ws_bootstrap_define_congestin_max_threshold(heap_size, WS_CONGESTION_PACKET_SIZE, packet_per_seconds, WS_CONGESTION_QUEUE_DELAY, WS_CONGESTION_NODE_MIN_QUEUE_SIZE, WS_CONGESTION_NODE_MAX_QUEUE_SIZE);
}
min_th = max_th / 2;
tr_info("Wi-SUN packet congestion minTh %u, maxTh %u, drop probability %u weight %u, Packet/Seconds %u", min_th, max_th, WS_CONGESTION_RED_DROP_PROBABILITY, RED_AVERAGE_WEIGHT_EIGHTH, packet_per_seconds);
cur->random_early_detection = random_early_detection_create(min_th, max_th, WS_CONGESTION_RED_DROP_PROBABILITY, RED_AVERAGE_WEIGHT_EIGHTH);
}
#endif //HAVE_WS