/* * Copyright (c) 2016-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "nsconfig.h" #include "ns_types.h" #include "ns_trace.h" #include #include "net_load_balance_api.h" #include "net_interface.h" #include "NWK_INTERFACE/Include/protocol.h" #ifdef HAVE_6LOWPAN_ND #include "net_rpl.h" #include "Service_Libs/load_balance/load_balance_api.h" #include "Service_Libs/mle_service/mle_service_api.h" #include "nsdynmemLIB.h" #include "ns_list.h" #include "6LoWPAN/Bootstraps/protocol_6lowpan.h" #include "mlme.h" #include "mac_api.h" #include "sw_mac.h" #include "6LoWPAN/MAC/mac_helper.h" #include "6LoWPAN/MAC/mac_data_poll.h" #include "libNET/src/net_load_balance_internal.h" #ifdef ECC #include "libX509_V3.h" #include "ecc.h" #endif #ifdef PANA #include "Security/PANA/pana.h" #include "Security/PANA/pana_internal_api.h" #endif #include "6LoWPAN/ND/nd_router_object.h" #ifdef HAVE_RPL #include "RPL/rpl_control.h" #endif #define TRACE_GROUP "6lb" static uint8_t net_load_balance_priority_get(const void *load_balance_user) { const protocol_interface_info_entry_t *cur = load_balance_user; if (!cur->rpl_domain) { return 0; } int16_t priority = protocol_6lowpan_rpl_global_priority_get(); if (priority < 0) { priority = 0; } else if (priority > 255) { priority = 255; } return priority; } static void net_load_balance_beacon_tx(const void *load_balance_user) { const protocol_interface_info_entry_t *cur = load_balance_user; mlme_set_t set_req; set_req.attr = macLoadBalancingBeaconTx; set_req.attr_index = 0; set_req.value_pointer = NULL; set_req.value_size = 0; cur->mac_api->mlme_req(cur->mac_api, MLME_SET, &set_req); } static bool net_load_balance_network_switch_req(void *load_balance_user, struct mlme_pan_descriptor_s *PANDescriptor, const uint8_t *beacon_payload, uint16_t beacon_payload_length) { protocol_interface_info_entry_t *interface = load_balance_user; //Run down current interface setup and do start if (interface->if_down(interface) != 0) { return false; } #ifdef PANA pana_reset_client_session(); #endif if (interface->if_up(interface) != 0) { return false; } if (!protocol_6lowpan_bootsrap_link_set(interface, PANDescriptor, beacon_payload, beacon_payload_length)) { return false; } //Start configured bootstrap return protocol_6lowpan_bootsrap_start(interface); } static void net_load_balance_link_reject(const protocol_interface_info_entry_t *cur) { uint8_t ll_all_nodes[16]; memcpy(ll_all_nodes, ADDR_LINK_LOCAL_ALL_NODES, 16); mle_service_interface_receiver_handler_update(cur->id, NULL); mle_service_reject_message_build(cur->id, ll_all_nodes, false); nd_router_t *nd_router = nd_get_object_by_nwk_id(cur->nwk_id); if (nd_router) { nd_router->nd_timer = 0; nd_router->ns_forward_timer = 0; nd_router->mle_advert_timer = 0; } } static bool net_load_balance_network_switch_notify_cb(const void *load_balance_user, load_balance_nwk_switch_operation operation, uint16_t *timeout) { const protocol_interface_info_entry_t *cur = load_balance_user; switch (operation) { case LB_ROUTER_LEAVE: if (arm_nwk_6lowpan_rpl_dodag_poison(cur->id) == 0) { *timeout = 8; } else { *timeout = 0; } break; case LB_NEIGHBOUR_LEAVE: net_load_balance_link_reject(cur); *timeout = 4; break; } return true; } #endif int8_t net_load_balance_network_switch_cb_set(int8_t interface_id, net_load_balance_network_switch_notify *network_switch_notify) { #ifdef HAVE_6LOWPAN_ND protocol_interface_info_entry_t *interface_ptr = protocol_stack_interface_info_get_by_id(interface_id); if (!interface_ptr) { return -1; } return load_balance_network_switch_cb_set(interface_ptr->lb_api, network_switch_notify); #else return -1; #endif } int8_t net_load_balance_create(int8_t interface_id, bool enable_periodic_beacon_interval) { #ifdef HAVE_6LOWPAN_ND protocol_interface_info_entry_t *interface_ptr = protocol_stack_interface_info_get_by_id(interface_id); if (!interface_ptr || !interface_ptr->mac_api) { return -1; } else if (interface_ptr->lb_api) { return -3; } //Allocate load balance user class load_balance_api_t *lb_api = load_balance_create(net_load_balance_network_switch_notify_cb, enable_periodic_beacon_interval); if (!lb_api) { return -2; } //Here initialize load balance based on current mac max beacon data size uint16_t beacon_payload_size; if (interface_ptr->mac_api->phyMTU > MAC_IEEE_802_15_4_MAX_PHY_PACKET_SIZE) { beacon_payload_size = MAC_IEEE_802_15_4_MAX_PHY_PACKET_SIZE - MAC_IEEE_802_15_4_MAX_BEACON_OVERHEAD; } else { beacon_payload_size = interface_ptr->mac_api->phyMTU - MAC_IEEE_802_15_4_MAX_BEACON_OVERHEAD; } if (lb_api->lb_initialize(lb_api, net_load_balance_beacon_tx, net_load_balance_priority_get, net_load_balance_network_switch_req, beacon_payload_size, interface_ptr) != 0) { load_balance_delete(lb_api); return -2; } //Store here load balance class pointer pointer interface_ptr->lb_api = lb_api; //Enable if if ((interface_ptr->lowpan_info & (INTERFACE_NWK_ACTIVE | INTERFACE_NWK_BOOTSRAP_ADDRESS_REGISTER_READY)) == (INTERFACE_NWK_ACTIVE | INTERFACE_NWK_BOOTSRAP_ADDRESS_REGISTER_READY)) { net_load_balance_internal_state_activate(interface_ptr, true); } return 0; #else return -1; #endif } int8_t net_load_balance_delete(int8_t interface_id) { #ifdef HAVE_6LOWPAN_ND protocol_interface_info_entry_t *interface_ptr = protocol_stack_interface_info_get_by_id(interface_id); if (!interface_ptr) { return -1; } load_balance_api_t *lb_api = interface_ptr->lb_api; interface_ptr->lb_api = NULL; return load_balance_delete(lb_api); #else return -1; #endif } int8_t net_load_balance_threshold_set(int8_t interface_id, uint8_t threshold_min, uint8_t threshold_max) { #ifdef HAVE_6LOWPAN_ND protocol_interface_info_entry_t *interface_ptr = protocol_stack_interface_info_get_by_id(interface_id); if (!interface_ptr) { return -1; } if (threshold_min > threshold_max) { return -1; } return load_balance_network_threshold_set(interface_ptr->lb_api, threshold_min, threshold_max); #else return -1; #endif } void net_load_balance_internal_state_activate(protocol_interface_info_entry_t *interface_ptr, bool state) { #ifdef HAVE_6LOWPAN_ND if (!interface_ptr || !interface_ptr->lb_api || !interface_ptr->mac_api) { return; } uint32_t trigle_period = 3600; uint32_t route_lifetime_period = 3600; //Enable Load Balance #ifdef HAVE_RPL if (state && interface_ptr->rpl_domain) { struct rpl_instance *instance = rpl_control_lookup_instance(interface_ptr->rpl_domain, 1, NULL); if (instance) { const rpl_dodag_conf_t *dodag_config = rpl_control_get_dodag_config(instance); if (dodag_config) { //dio max Period caluclate in seconds uint32_t Imax_ms = (dodag_config->dio_interval_min + dodag_config->dio_interval_doublings) < 32 ? (1ul << (dodag_config->dio_interval_min + dodag_config->dio_interval_doublings)) : 0xfffffffful; trigle_period = Imax_ms / 1000; route_lifetime_period = (uint32_t)dodag_config->default_lifetime * dodag_config->lifetime_unit; } } } #endif interface_ptr->lb_api->lb_enable(interface_ptr->lb_api, state, trigle_period, route_lifetime_period); mlme_set_t set_req; set_req.attr = macLoadBalancingAcceptAnyBeacon; set_req.attr_index = 0; set_req.value_pointer = &state; set_req.value_size = sizeof(bool); interface_ptr->mac_api->mlme_req(interface_ptr->mac_api, MLME_SET, &set_req); #endif } #ifdef HAVE_RPL #ifdef HAVE_6LOWPAN_BORDER_ROUTER static int8_t net_load_balance_api_get_node_count_cb(void *lb_user, uint16_t *node_count) { protocol_interface_info_entry_t *interface_ptr = lb_user; if (rpl_control_get_instance_dao_target_count(interface_ptr->rpl_domain, 1, NULL, node_count)) { return 0; } return -1; } #endif #endif #ifdef HAVE_RPL #ifdef HAVE_6LOWPAN_BORDER_ROUTER static int8_t net_load_balance_api_get_set_load_level_cb(void *lb_user, uint8_t load_level) { //Call DODAG preference protocol_interface_info_entry_t *interface_ptr = lb_user; if (!interface_ptr->rpl_domain || interface_ptr->rpl_domain != protocol_6lowpan_rpl_domain || !protocol_6lowpan_rpl_root_dodag) { return -1; } if (load_level > RPL_DODAG_PREF_MASK) { load_level = RPL_DODAG_PREF_MASK; } rpl_control_set_dodag_pref(protocol_6lowpan_rpl_root_dodag, RPL_DODAG_PREF_MASK - load_level); return 0; } #endif #endif int8_t net_load_balance_load_level_update_enable(int8_t interface_id, uint16_t expected_device_count) { #ifdef HAVE_RPL #ifdef HAVE_6LOWPAN_BORDER_ROUTER protocol_interface_info_entry_t *interface_ptr = protocol_stack_interface_info_get_by_id(interface_id); if (!interface_ptr) { return -1; } uint16_t temp16 = expected_device_count % 8; if (temp16) { expected_device_count += (8 - temp16); } return load_balance_network_load_monitor_enable(interface_ptr->lb_api, expected_device_count, RPL_DODAG_PREF_MASK + 1, net_load_balance_api_get_node_count_cb, net_load_balance_api_get_set_load_level_cb); #else (void)interface_id; (void)expected_device_count; return -1; #endif #else (void)interface_id; (void)expected_device_count; return -1; #endif } /** * \brief Disable automatic network load level update * * \param interface_id interface id * * \return 0 process ok -1 Unknown interface id */ int8_t net_load_balance_load_level_update_disable(int8_t interface_id) { #ifdef HAVE_RPL #ifdef HAVE_6LOWPAN_BORDER_ROUTER protocol_interface_info_entry_t *interface_ptr = protocol_stack_interface_info_get_by_id(interface_id); if (!interface_ptr) { return -1; } return load_balance_network_load_monitor_disable(interface_ptr->lb_api); #else (void)interface_id; return -1; #endif #else (void)interface_id; return -1; #endif } int8_t net_load_balance_set_max_probability(int8_t interface_id, uint8_t max_p) { #ifdef HAVE_6LOWPAN_ND protocol_interface_info_entry_t *interface_ptr = protocol_stack_interface_info_get_by_id(interface_id); if (!interface_ptr) { return -1; } return load_balance_set_max_probability(interface_ptr->lb_api, max_p); #else return -1; #endif }