/* * 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 #include "nsconfig.h" #include "ns_types.h" #include "ns_trace.h" #include "randLIB.h" #include #include #include "Common_Protocols/icmpv6.h" #include "6LoWPAN/ws/ws_config.h" #include "6LoWPAN/ws/ws_common_defines.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_pae_controller.h" #include "Service_Libs/etx/etx.h" #include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h" #include "Service_Libs/blacklist/blacklist.h" #include "RPL/rpl_protocol.h" #include "RPL/rpl_control.h" #include "ws_management_api.h" #include "mac_api.h" #ifdef HAVE_WS #define TRACE_GROUP "wscm" // estimated sensitivity -93 dbm converted to Wi-SUN RSL range // This provides a range of -174 (0) to +80 (254) dBm uint8_t DEVICE_MIN_SENS = 174 - 93; #define TRICKLE_IMIN_60_SECS (60 * 10) #define TRICKLE_IMIN_30_SECS (30 * 10) #define TRICKLE_IMIN_15_SECS (15 * 10) static const trickle_params_t trickle_params_pan_discovery_large = { .Imin = TRICKLE_IMIN_60_SECS, /* 60 second; ticks are 1s */ .Imax = TRICKLE_IMIN_60_SECS << 4, /* 960 seconds 16 min*/ .k = 1, /* 1 */ .TimerExpirations = TRICKLE_EXPIRATIONS_INFINITE }; static const trickle_params_t trickle_params_pan_discovery_medium = { .Imin = TRICKLE_IMIN_30_SECS, /* 30 second; ticks are 1s */ .Imax = TRICKLE_IMIN_30_SECS << 5, /* 960 seconds 16 min*/ .k = 1, /* 1 */ .TimerExpirations = TRICKLE_EXPIRATIONS_INFINITE }; static const trickle_params_t trickle_params_pan_discovery_small = { .Imin = TRICKLE_IMIN_15_SECS, /* 15 second; ticks are 1s */ .Imax = TRICKLE_IMIN_15_SECS << 2, /* 60 seconds 1 min*/ .k = 1, /* 1 */ .TimerExpirations = TRICKLE_EXPIRATIONS_INFINITE }; uint16_t test_max_child_count_override = 0xffff; int8_t ws_generate_channel_list(uint32_t *channel_mask, uint16_t number_of_channels, uint8_t regulatory_domain) { (void)regulatory_domain; for (uint8_t i = 0; i < number_of_channels; i++) { channel_mask[0 + (i / 32)] |= (1 << (i % 32)); } return 0; } static uint32_t ws_decode_channel_spacing(uint8_t channel_spacing) { if (CHANNEL_SPACING_100 == channel_spacing) { return 100000; } else if (CHANNEL_SPACING_200 == channel_spacing) { return 200000; } else if (CHANNEL_SPACING_250 == channel_spacing) { return 250000; } else if (CHANNEL_SPACING_400 == channel_spacing) { return 400000; } else if (CHANNEL_SPACING_600 == channel_spacing) { return 600000; } return 0; } static uint32_t ws_get_datarate_using_operating_mode(uint8_t operating_mode) { if ((OPERATING_MODE_1a == operating_mode) || (OPERATING_MODE_1b == operating_mode)) { return 50000; } else if ((OPERATING_MODE_2a == operating_mode) || (OPERATING_MODE_2b == operating_mode)) { return 100000; } else if (OPERATING_MODE_3 == operating_mode) { return 150000; } else if ((OPERATING_MODE_4a == operating_mode) || (OPERATING_MODE_4b == operating_mode)) { return 200000; } else if (OPERATING_MODE_5 == operating_mode) { return 300000; } return 0; } static phy_modulation_index_e ws_get_modulation_index_using_operating_mode(uint8_t operating_mode) { if ((OPERATING_MODE_1b == operating_mode) || (OPERATING_MODE_2b == operating_mode) || (OPERATING_MODE_4b == operating_mode)) { return MODULATION_INDEX_1_0; } else { return MODULATION_INDEX_0_5; } } static int ws_set_domain_rf_config(protocol_interface_info_entry_t *cur) { phy_rf_channel_configuration_s rf_configs; 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.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.modulation = M_2FSK; rf_configs.number_of_channels = cur->ws_info->hopping_schdule.number_of_channels; ws_bootstrap_set_rf_config(cur, rf_configs); return 0; } int8_t ws_common_regulatory_domain_config(protocol_interface_info_entry_t *cur) { cur->ws_info->hopping_schdule.channel_plan = 0; if (cur->ws_info->hopping_schdule.regulatory_domain == REG_DOMAIN_KR) { if (cur->ws_info->hopping_schdule.operating_class == 1) { cur->ws_info->hopping_schdule.ch0_freq = 9171; cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_200; } else if (cur->ws_info->hopping_schdule.operating_class == 2) { cur->ws_info->hopping_schdule.ch0_freq = 9173; cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_400; } else { return -1; } } else if (cur->ws_info->hopping_schdule.regulatory_domain == REG_DOMAIN_EU) { if (cur->ws_info->hopping_schdule.operating_class == 1) { cur->ws_info->hopping_schdule.ch0_freq = 8631; cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_100; } else if (cur->ws_info->hopping_schdule.operating_class == 2) { cur->ws_info->hopping_schdule.ch0_freq = 8631; cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_200; } else if (cur->ws_info->hopping_schdule.operating_class == 3) { cur->ws_info->hopping_schdule.ch0_freq = 8701; cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_100; } else if (cur->ws_info->hopping_schdule.operating_class == 4) { cur->ws_info->hopping_schdule.ch0_freq = 8702; cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_200; } else { return -1; } } else if (cur->ws_info->hopping_schdule.regulatory_domain == REG_DOMAIN_IN) { if (cur->ws_info->hopping_schdule.operating_class == 1) { cur->ws_info->hopping_schdule.ch0_freq = 8651; cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_100; } else if (cur->ws_info->hopping_schdule.operating_class == 2) { cur->ws_info->hopping_schdule.ch0_freq = 8651; cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_200; } else { return -1; } } else if (cur->ws_info->hopping_schdule.regulatory_domain == REG_DOMAIN_NA) { if (cur->ws_info->hopping_schdule.operating_class == 1) { cur->ws_info->hopping_schdule.ch0_freq = 9022; cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_200; } else if (cur->ws_info->hopping_schdule.operating_class == 2) { cur->ws_info->hopping_schdule.ch0_freq = 9024; cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_400; } else if (cur->ws_info->hopping_schdule.operating_class == 3) { cur->ws_info->hopping_schdule.ch0_freq = 9026; cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_600; } else { return -1; } } else if (cur->ws_info->hopping_schdule.regulatory_domain == REG_DOMAIN_JP) { if (cur->ws_info->hopping_schdule.operating_class == 1) { cur->ws_info->hopping_schdule.ch0_freq = 9206; cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_200; } else if (cur->ws_info->hopping_schdule.operating_class == 2) { cur->ws_info->hopping_schdule.ch0_freq = 9209; cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_400; } else if (cur->ws_info->hopping_schdule.operating_class == 3) { cur->ws_info->hopping_schdule.ch0_freq = 9208; cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_600; } else { return -1; } } else if (cur->ws_info->hopping_schdule.regulatory_domain == REG_DOMAIN_WW) { if (cur->ws_info->hopping_schdule.operating_class == 1) { cur->ws_info->hopping_schdule.ch0_freq = 24002; cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_200; } else if (cur->ws_info->hopping_schdule.operating_class == 2) { cur->ws_info->hopping_schdule.ch0_freq = 24004; cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_400; } else { return -1; } } else { return -1; } cur->ws_info->hopping_schdule.number_of_channels = (uint8_t)ws_common_channel_number_calc(cur->ws_info->hopping_schdule.regulatory_domain, cur->ws_info->hopping_schdule.operating_class); if (!cur->ws_info->hopping_schdule.number_of_channels) { return -1; } // Note: doesn't work for Brazil region ws_generate_channel_list(cur->ws_info->hopping_schdule.channel_mask, cur->ws_info->hopping_schdule.number_of_channels, cur->ws_info->hopping_schdule.regulatory_domain); ws_set_domain_rf_config(cur); return 0; } uint16_t ws_common_channel_number_calc(uint8_t regulatory_domain, uint8_t operating_class) { if (regulatory_domain == REG_DOMAIN_KR) { if (operating_class == 1) { return 32; } else if (operating_class == 2) { return 16; } } else if (regulatory_domain == REG_DOMAIN_EU) { if (operating_class == 1) { return 69; } else if (operating_class == 2) { return 35; } else if (operating_class == 3) { return 55; } else if (operating_class == 4) { return 27; } } else if (regulatory_domain == REG_DOMAIN_IN) { if (operating_class == 1) { return 19; } else if (operating_class == 2) { return 10; } } else if (regulatory_domain == REG_DOMAIN_NA) { if (operating_class == 1) { return 129; } else if (operating_class == 2) { return 64; } else if (operating_class == 3) { return 42; } } else if (regulatory_domain == REG_DOMAIN_JP) { if (operating_class == 1) { return 38; } else if (operating_class == 2) { return 18; } else if (operating_class == 3) { return 12; } } else if (regulatory_domain == REG_DOMAIN_WW) { if (operating_class == 1) { // TODO we dont support this yet, but it is used as test value return 416; } else if (operating_class == 2) { return 207; } } return 0; } int8_t ws_common_allocate_and_init(protocol_interface_info_entry_t *cur) { if (!cur->ws_info) { cur->ws_info = ns_dyn_mem_alloc(sizeof(ws_info_t)); } if (!cur->ws_info) { return -1; } memset(cur->ws_info, 0, sizeof(ws_info_t)); ns_list_init(&cur->ws_info->active_nud_process); ns_list_init(&cur->ws_info->free_nud_entries); ns_list_init(&cur->ws_info->parent_list_free); ns_list_init(&cur->ws_info->parent_list_reserved); cur->ws_info->pan_information.use_parent_bs = true; cur->ws_info->pan_information.rpl_routing_method = true; cur->ws_info->pan_information.version = WS_FAN_VERSION_1_0; cur->ws_info->hopping_schdule.regulatory_domain = REG_DOMAIN_EU; cur->ws_info->hopping_schdule.operating_mode = OPERATING_MODE_3; cur->ws_info->hopping_schdule.operating_class = 2; // Clock drift value 255 indicates that information is not provided cur->ws_info->hopping_schdule.clock_drift = 255; // Timing accuracy is given from 0 to 2.55msec with 10usec resolution cur->ws_info->hopping_schdule.timing_accurancy = 100; ws_common_regulatory_domain_config(cur); cur->ws_info->network_size_config = NETWORK_SIZE_MEDIUM; cur->ws_info->rpl_parent_candidate_max = WS_RPL_PARENT_CANDIDATE_MAX; cur->ws_info->rpl_selected_parent_max = WS_RPL_SELECTED_PARENT_MAX; ws_common_network_size_configure(cur, 200); // defaults to medium network size // Set defaults for the device. user can modify these. cur->ws_info->fhss_uc_fixed_channel = 0xffff; cur->ws_info->fhss_bc_fixed_channel = 0xffff; cur->ws_info->fhss_uc_dwell_interval = WS_FHSS_UC_DWELL_INTERVAL; cur->ws_info->fhss_bc_interval = WS_FHSS_BC_INTERVAL; cur->ws_info->fhss_bc_dwell_interval = WS_FHSS_BC_DWELL_INTERVAL; cur->ws_info->fhss_uc_channel_function = WS_DH1CF; cur->ws_info->fhss_bc_channel_function = WS_DH1CF; cur->ws_info->pending_key_index_info.state = NO_PENDING_PROCESS; for (uint8_t n = 0; n < 8; n++) { cur->ws_info->fhss_channel_mask[n] = 0xffffffff; } return 0; } void ws_common_network_size_configure(protocol_interface_info_entry_t *cur, uint16_t network_size) { // TODO Modify NUD timings based on network size // TODO Modify EAPOLL timings if (network_size < 100) { // Configure the Wi-SUN discovery trickle parameters cur->ws_info->trickle_params_pan_discovery = trickle_params_pan_discovery_small; // default values are for Wi-SUN small network parameters // imin: 14 (16s) // doublings:3 (128s) // redundancy; 0 Disabled if (cur->ws_info->network_size_config == NETWORK_SIZE_AUTOMATIC) { ws_bbr_rpl_config(cur, 14, 3, 0, WS_RPL_MAX_HOP_RANK_INCREASE, WS_RPL_MIN_HOP_RANK_INCREASE); } else if (cur->ws_info->network_size_config == NETWORK_SIZE_CERTIFICATE) { ws_bbr_rpl_config(cur, 0, 0, 0, WS_CERTIFICATE_RPL_MAX_HOP_RANK_INCREASE, WS_CERTIFICATE_RPL_MIN_HOP_RANK_INCREASE); } else { ws_bbr_rpl_config(cur, 0, 0, 0, WS_RPL_MAX_HOP_RANK_INCREASE, WS_RPL_MIN_HOP_RANK_INCREASE); } ws_pae_controller_timing_adjust(1); // Fast and reactive network } else if (network_size < 300) { // Configure the Wi-SUN discovery trickle parameters cur->ws_info->trickle_params_pan_discovery = trickle_params_pan_discovery_medium; // Something in between // imin: 15 (32s) // doublings:5 (960s) // redundancy; 10 ws_bbr_rpl_config(cur, 15, 5, 10, WS_RPL_MAX_HOP_RANK_INCREASE, WS_RPL_MIN_HOP_RANK_INCREASE); ws_pae_controller_timing_adjust(9); // medium limited network } else { // Configure the Wi-SUN discovery trickle parameters cur->ws_info->trickle_params_pan_discovery = trickle_params_pan_discovery_large; // Wi-SUN Large network parameters // imin: 19 (524s, 9 min) // doublings:1 (1048s, 17 min) // redundancy; 10 May need some tuning still ws_bbr_rpl_config(cur, 19, 1, 10, WS_RPL_MAX_HOP_RANK_INCREASE, WS_RPL_MIN_HOP_RANK_INCREASE); ws_pae_controller_timing_adjust(24); // Very slow and high latency network } return; } void ws_common_seconds_timer(protocol_interface_info_entry_t *cur, uint32_t seconds) { ws_bbr_seconds_timer(cur, seconds); ws_bootstrap_seconds_timer(cur, seconds); blacklist_ttl_update(seconds); } void ws_common_fast_timer(protocol_interface_info_entry_t *cur, uint16_t ticks) { ws_bootstrap_trickle_timer(cur, ticks); ws_nud_active_timer(cur, ticks); } void ws_common_neighbor_update(protocol_interface_info_entry_t *cur, const uint8_t *ll_address) { //Neighbor connectected update mac_neighbor_table_entry_t *mac_neighbor = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur), ll_address, false, NULL); if (mac_neighbor) { ws_nud_entry_remove_active(cur, mac_neighbor); } } void ws_common_aro_failure(protocol_interface_info_entry_t *cur, const uint8_t *ll_address) { tr_warn("ARO registration Failure %s", trace_ipv6(ll_address)); blacklist_update(ll_address, false); ws_bootstrap_aro_failure(cur, ll_address); } void ws_common_neighbor_remove(protocol_interface_info_entry_t *cur, const uint8_t *ll_address) { tr_debug("neighbor remove %s", trace_ipv6(ll_address)); ws_bootstrap_neighbor_remove(cur, ll_address); } uint8_t ws_common_allow_child_registration(protocol_interface_info_entry_t *interface, const uint8_t *eui64) { uint8_t child_count = 0; uint8_t max_child_count = mac_neighbor_info(interface)->list_total_size - WS_NON_CHILD_NEIGHBOUR_COUNT; // Test API to limit child count if (test_max_child_count_override != 0xffff) { max_child_count = test_max_child_count_override; } //Validate Is EUI64 already allocated for any address if (ipv6_neighbour_has_registered_by_eui64(&interface->ipv6_neighbour_cache, eui64)) { tr_info("Child registration from old child"); return ARO_SUCCESS; } //Verify that we have Selected Parent if (interface->bootsrap_mode != ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER && !rpl_control_parent_candidate_list_size(interface, true)) { tr_info("Do not accept new ARO child: no selected parent"); return ARO_TOPOLOGICALLY_INCORRECT; } ns_list_foreach_safe(mac_neighbor_table_entry_t, cur, &mac_neighbor_info(interface)->neighbour_list) { if (ipv6_neighbour_has_registered_by_eui64(&interface->ipv6_neighbour_cache, cur->mac64)) { child_count++; } } if (child_count >= max_child_count) { tr_warn("Child registration not allowed %d/%d, max:%d", child_count, max_child_count, mac_neighbor_info(interface)->list_total_size); return ARO_FULL; } tr_info("Child registration allowed %d/%d, max:%d", child_count, max_child_count, mac_neighbor_info(interface)->list_total_size); return ARO_SUCCESS; } bool ws_common_negative_aro_mark(protocol_interface_info_entry_t *interface, const uint8_t *eui64) { mac_neighbor_table_entry_t *neighbour = mac_neighbor_table_address_discover(mac_neighbor_info(interface), eui64, ADDR_802_15_4_LONG); if (!neighbour) { return false; } ws_neighbor_class_entry_t *ws_neighbor = ws_neighbor_class_entry_get(&interface->ws_info->neighbor_storage, neighbour->index); ws_neighbor->negative_aro_send = true; neighbour->lifetime = WS_NEIGHBOR_TEMPORARY_LINK_MIN_TIMEOUT_SMALL; //Remove anyway if Packet is freed before MAC push return true; } uint32_t ws_common_version_lifetime_get(uint8_t config) { uint32_t lifetime; if (config == NETWORK_SIZE_SMALL || config == NETWORK_SIZE_CERTIFICATE) { lifetime = PAN_VERSION_SMALL_NETWORK_LIFETIME; } else if (config == NETWORK_SIZE_MEDIUM) { lifetime = PAN_VERSION_MEDIUM_NETWORK_LIFETIME; } else { lifetime = PAN_VERSION_LARGE_NETWORK_LIFETIME; } return lifetime; } uint32_t ws_common_version_timeout_get(uint8_t config) { uint32_t lifetime; if (config == NETWORK_SIZE_SMALL || config == NETWORK_SIZE_CERTIFICATE) { lifetime = PAN_VERSION_SMALL_NETWORK_TIMEOUT; } else if (config == NETWORK_SIZE_MEDIUM) { lifetime = PAN_VERSION_MEDIUM_NETWORK_TIMEOUT; } else { lifetime = PAN_VERSION_LARGE_NETWORK_TIMEOUT; } return lifetime; } #endif // HAVE_WS