/* * 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 "net_interface.h" #include "eventOS_event.h" #include "NWK_INTERFACE/Include/protocol.h" #include "6LoWPAN/Bootstraps/protocol_6lowpan.h" #include "6LoWPAN/Bootstraps/protocol_6lowpan_interface.h" #include "6LoWPAN/ws/ws_config.h" #include "6LoWPAN/ws/ws_common.h" #include "6LoWPAN/ws/ws_bootstrap.h" #include "RPL/rpl_control.h" #include "RPL/rpl_data.h" #include "Common_Protocols/icmpv6.h" #include "Common_Protocols/icmpv6_radv.h" #include "ws_management_api.h" #include "net_rpl.h" #include "Service_Libs/nd_proxy/nd_proxy.h" #include "6LoWPAN/ws/ws_bbr_api_internal.h" #include "DHCPv6_Server/DHCPv6_server_service.h" #define TRACE_GROUP "wsbs" #define RPL_INSTANCE_ID 1 #ifdef HAVE_WS_BORDER_ROUTER /* when creating BBR make ULA dodag ID always and when network becomes available add prefix to DHCP * * */ static int8_t backbone_interface_id = -1; // BBR backbone information static uint8_t static_dodag_prefix[8] = {0xfd, 0x00, 0x61, 0x72, 0x6d}; static uint8_t static_ula_address[16] = {0}; static uint8_t static_dodag_id[16] = {0}; static uint8_t global_dodag_id[16] = {0}; static uint32_t bbr_delay_timer = 20; // initial delay. static rpl_dodag_conf_t rpl_conf = { // Lifetime values .default_lifetime = 120, .lifetime_unit = 60, .objective_code_point = 1, // MRHOF algorithm used .authentication = 0, .path_control_size = 7, .dag_max_rank_increase = 2048, .min_hop_rank_increase = 256, // DIO configuration .dio_interval_min = WS_RPL_DIO_IMIN, .dio_interval_doublings = WS_RPL_DIO_DOUBLING, .dio_redundancy_constant = WS_RPL_DIO_REDUNDANCY }; void ws_bbr_rpl_config(uint8_t imin, uint8_t doubling, uint8_t redundancy) { if (imin == 0 || doubling == 0) { // use default values imin = WS_RPL_DIO_IMIN; doubling = WS_RPL_DIO_DOUBLING; redundancy = WS_RPL_DIO_REDUNDANCY; } if (rpl_conf.dio_interval_min == imin && rpl_conf.dio_interval_doublings == doubling && rpl_conf.dio_redundancy_constant == redundancy) { // Same values no update needed return; } rpl_conf.dio_interval_min = imin; rpl_conf.dio_interval_doublings = doubling; rpl_conf.dio_redundancy_constant = redundancy; if (protocol_6lowpan_rpl_root_dodag) { rpl_control_update_dodag_config(protocol_6lowpan_rpl_root_dodag, &rpl_conf); rpl_control_increment_dodag_version(protocol_6lowpan_rpl_root_dodag); } } static void ws_bbr_rpl_root_start(uint8_t *dodag_id) { tr_info("RPL root start"); rpl_data_init_root(); if (protocol_6lowpan_rpl_root_dodag) { rpl_control_delete_dodag_root(protocol_6lowpan_rpl_domain, protocol_6lowpan_rpl_root_dodag); protocol_6lowpan_rpl_root_dodag = NULL; } protocol_6lowpan_rpl_root_dodag = rpl_control_create_dodag_root(protocol_6lowpan_rpl_domain, RPL_INSTANCE_ID, dodag_id, &rpl_conf, rpl_conf.min_hop_rank_increase, RPL_GROUNDED | RPL_MODE_NON_STORING | RPL_DODAG_PREF(0)); if (!protocol_6lowpan_rpl_root_dodag) { tr_err("RPL dodag init failed"); return; } memcpy(static_dodag_id, dodag_id, 16); // RPL memory limits set larger for Border router rpl_control_set_memory_limits(64 * 1024, 0); uint8_t t_flags = PIO_A; rpl_control_update_dodag_prefix(protocol_6lowpan_rpl_root_dodag, dodag_id, 64, t_flags, 7200, 7200, false); rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, dodag_id, 64, 0x18, 7200, false); } static void ws_bbr_rpl_root_stop(void) { tr_info("RPL root stop"); rpl_control_delete_dodag_root(protocol_6lowpan_rpl_domain, protocol_6lowpan_rpl_root_dodag); protocol_6lowpan_rpl_root_dodag = NULL; memset(static_ula_address, 0, 16); memset(static_dodag_id, 0, 16); memset(global_dodag_id, 0, 16); } static int ws_border_router_proxy_validate(int8_t interface_id, uint8_t *address) { /* Could also check route type, but I don't think it really matters */ ipv6_route_t *route; route = ipv6_route_choose_next_hop(address, interface_id, NULL); if (!route || route->prefix_len < 128) { return -1; } return 0; } int ws_border_router_proxy_state_update(int8_t caller_interface_id, int8_t handler_interface_id, bool status) { (void)caller_interface_id; protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(handler_interface_id); if (!cur) { tr_error("No Interface"); return -1; } if (status) { tr_debug("Border router Backhaul link ready"); } else { tr_debug("Border router Backhaul link down"); } return 0; } static int ws_bbr_static_ula_create(protocol_interface_info_entry_t *cur) { if (memcmp(static_ula_address, ADDR_UNSPECIFIED, 16) != 0) { // address generated return 0; } tr_info("BBR generate ula prefix"); // This address is only used if no other address available. if_address_entry_t *add_entry = icmpv6_slaac_address_add(cur, static_dodag_prefix, 64, 0xffffffff, 0, true, SLAAC_IID_FIXED); if (!add_entry) { return -1; } memcpy(static_ula_address, add_entry->address, 16); tr_info("BBR generate ula prefix addr %s", trace_ipv6(static_ula_address)); return 0; } /* * 0 static non rooted self generated own address * 1 static address with backbone connectivity */ static int ws_bbr_static_dodag_get(protocol_interface_info_entry_t *cur, uint8_t *dodag_id_ptr) { protocol_interface_info_entry_t *bb_interface = protocol_stack_interface_info_get_by_id(backbone_interface_id); if (bb_interface && bb_interface->ipv6_configure->ipv6_stack_mode == NET_IPV6_BOOTSTRAP_STATIC) { // static configuration for ethernet available ns_list_foreach(if_address_entry_t, add_entry, &cur->ip_addresses) { if (memcmp(add_entry->address, bb_interface->ipv6_configure->static_prefix64, 8) == 0) { //tr_info("BBR static config available"); if (dodag_id_ptr) { memcpy(dodag_id_ptr, add_entry->address, 16); } return 1; } } } ws_bbr_static_ula_create(cur); // only own generated prefix available if (dodag_id_ptr) { memcpy(dodag_id_ptr, static_ula_address, 16); } return 0; } static int ws_bbr_dodag_get(protocol_interface_info_entry_t *cur, uint8_t *static_dodag_id_ptr, uint8_t *dodag_id_ptr) { uint8_t global_address[16]; if (static_dodag_id_ptr) { memset(static_dodag_id_ptr, 0, 16); } if (dodag_id_ptr) { memset(dodag_id_ptr, 0, 16); } if (ws_bbr_static_dodag_get(cur, static_dodag_id_ptr) < 0) { // no static configuration available return -1; } if (arm_net_address_get(backbone_interface_id, ADDR_IPV6_GP, global_address) != 0) { // No global prefix available return 0; } if (memcmp(global_address, dodag_id_ptr, 8) == 0) { // static address is same return 0; } memcpy(dodag_id_ptr, global_address, 16); return 0; } static void wisun_bbr_na_send(int8_t interface_id, const uint8_t target[static 16]) { protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return; } buffer_t *buffer = icmpv6_build_na(cur, false, true, true, target, NULL, ADDR_UNSPECIFIED); protocol_push(buffer); return; } static bool wisun_dhcp_address_add_cb(int8_t interfaceId, dhcp_address_cache_update_t *address_info, void *route_src) { (void)route_src; protocol_interface_info_entry_t *curPtr = protocol_stack_interface_info_get_by_id(interfaceId); if (!curPtr) { return false; } // When address is allocated we send NA to backbone to notify the new address and flush from other BBRs wisun_bbr_na_send(backbone_interface_id, address_info->allocatedAddress); return true; } static void ws_bbr_rpl_status_check(protocol_interface_info_entry_t *cur) { uint8_t static_id[16] = {0}; uint8_t global_id[16] = {0}; //tr_info("BBR status check"); ws_bbr_dodag_get(cur, static_id, global_id); if (memcmp(static_dodag_id, static_id, 16) != 0) { // Static id updated or first setup ws_bbr_rpl_root_start(static_id); } if (memcmp(global_dodag_id, global_id, 16) != 0) { // Global prefix changed if (memcmp(global_dodag_id, ADDR_UNSPECIFIED, 16) != 0) { // TODO remove old global prefix tr_info("RPL GUA deactivate %s", trace_ipv6(global_dodag_id)); rpl_control_update_dodag_prefix(protocol_6lowpan_rpl_root_dodag, static_dodag_id, 64, PIO_A, 7200, 7200, false); rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, static_dodag_id, 64, 0x18, 7200, false); // Old backbone information is deleted after 120 seconds rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, NULL, 0, 0, 120, true); rpl_control_update_dodag_prefix(protocol_6lowpan_rpl_root_dodag, global_dodag_id, 64, 0, 120, 0, true); rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, global_dodag_id, 64, 0, 120, true); ipv6_route_add_with_info(global_dodag_id, 64, backbone_interface_id, NULL, ROUTE_THREAD_BBR, NULL, 0, 120, 0); DHCPv6_server_service_delete(cur->id, global_dodag_id, false); // Set old addresses to deferred and timeout ws_dhcp_client_address_delete(cur, global_dodag_id); } // TODO add global prefix if (memcmp(global_id, ADDR_UNSPECIFIED, 16) != 0) { //DHCPv6 Server set here //Interface LL64 address uint8_t ll[16]; memcpy(ll, ADDR_LINK_LOCAL_PREFIX, 8); memcpy(&ll[8], cur->mac, 8); ll[8] ^= 2; if (DHCPv6_server_service_init(cur->id, global_id, cur->mac, DHCPV6_DUID_HARDWARE_IEEE_802_NETWORKS_TYPE) != 0) { tr_error("DHCPv6 Server create fail"); return; } DHCPv6_server_service_callback_set(cur->id, global_id, NULL, wisun_dhcp_address_add_cb); DHCPv6_server_service_set_address_autonous_flag(cur->id, global_id, true); DHCPv6_server_service_set_address_validlifetime(cur->id, global_id, 7200); tr_info("RPL GUA activate %s", trace_ipv6(global_id)); ws_dhcp_client_address_request(cur, global_id, ll); rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, NULL, 0, 0, 7200, false); rpl_control_update_dodag_prefix(protocol_6lowpan_rpl_root_dodag, static_dodag_id, 64, PIO_A, 7200, 0, false); rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, static_dodag_id, 64, 0x18, 7200, false); rpl_control_update_dodag_prefix(protocol_6lowpan_rpl_root_dodag, global_id, 64, 0, 7200, 7200, false); rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, global_id, 64, 0, 7200, false); ipv6_route_add_with_info(global_id, 64, backbone_interface_id, NULL, ROUTE_THREAD_BBR, NULL, 0, 0xffffffff, 0); } memcpy(global_dodag_id, global_id, 16); rpl_control_increment_dodag_version(protocol_6lowpan_rpl_root_dodag); nd_proxy_downstream_interface_register(cur->id, ws_border_router_proxy_validate, ws_border_router_proxy_state_update); } } void ws_bbr_seconds_timer(protocol_interface_info_entry_t *cur, uint32_t seconds) { (void)seconds; if (!ws_info(cur)) { return; } if (cur->bootsrap_mode != ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) { // Not a border router return; } if (!cur->rpl_domain) { // RPL not started return; } if (bbr_delay_timer > seconds) { bbr_delay_timer -= seconds; } else { bbr_delay_timer = 20; // 20 second interval between status checks // prequisists // Wi-SUN network configuration started without RPL // RPL configured simple // 1. Wait for backend connection // 2. When address becomes available in backend start RPL dodag // 3. if address removed remove dodag // RPL configured Advanced // 1. Add ULA DODAG and and start ROOT even without backend // a. If static prefix configured use it. // b. generate random ULA and publish it to backend // 2. if GUA prefix becomes available in backend add new prefix to DODAG // 3. if GUA prefix is removed remove the prefix. if (protocol_6lowpan_rpl_root_dodag) { // Border router is active if (0 != protocol_interface_address_compare(static_dodag_id)) { // Dodag has become invalid need to delete tr_info("RPL static dodag not valid anymore %s", trace_ipv6(static_dodag_id)); ws_bbr_rpl_root_stop(); } else { } } ws_bbr_rpl_status_check(cur); } // Normal BBR operation if (protocol_6lowpan_rpl_root_dodag) { if (cur->ws_info->pan_version_timer > seconds) { cur->ws_info->pan_version_timer -= seconds; } else { // Border router has timed out tr_debug("Border router version number update"); cur->ws_info->pan_version_timer = PAN_VERSION_LIFETIME; cur->ws_info->pan_information.pan_version++; // Inconsistent for border router to make information distribute faster ws_bootstrap_configuration_trickle_reset(cur); if (cur->ws_info->network_size_config == NETWORK_SIZE_AUTOMATIC) { ws_common_network_size_configure(cur, cur->ws_info->pan_information.pan_size); } // We update the RPL version in same time to allow nodes to reselect parent // As configuration is made so that devices cant move downward in dodag this allows it // TODO think the correct rate for this if (cur->ws_info->pan_information.pan_version && cur->ws_info->pan_information.pan_version % RPL_VERSION_LIFETIME / PAN_VERSION_LIFETIME == 0) { // Third the rate of configuration version change at default 5 hours rpl_control_increment_dodag_version(protocol_6lowpan_rpl_root_dodag); } } } } uint16_t test_pan_size_override = 0xffff; uint16_t ws_bbr_pan_size(protocol_interface_info_entry_t *cur) { uint16_t result = 0; if (test_pan_size_override != 0xffff) { return test_pan_size_override; } rpl_control_get_instance_dao_target_count(cur->rpl_domain, RPL_INSTANCE_ID, NULL, &result); return result; } #endif //HAVE_WS_BORDER_ROUTER /* Public APIs * */ int ws_bbr_start(int8_t interface_id, int8_t bb_interface_id) { #ifdef HAVE_WS_BORDER_ROUTER (void)interface_id; protocol_interface_info_entry_t *bb_interface = protocol_stack_interface_info_get_by_id(bb_interface_id); if (!bb_interface) { return -1; } // TODO make bb configurations backbone_interface_id = bb_interface_id; return 0; #else (void)interface_id; (void)bb_interface_id; return -1; #endif } void ws_bbr_stop(int8_t interface_id) { #ifdef HAVE_WS_BORDER_ROUTER (void)interface_id; backbone_interface_id = -1; if (!protocol_6lowpan_rpl_domain) { return; } rpl_control_delete_dodag_root(protocol_6lowpan_rpl_domain, protocol_6lowpan_rpl_root_dodag); protocol_6lowpan_rpl_root_dodag = NULL; #else (void)interface_id; #endif } int ws_bbr_node_keys_remove(int8_t interface_id, uint8_t *eui64) { (void) interface_id; (void) eui64; return -1; } int ws_bbr_node_access_revoke_start(int8_t interface_id) { (void) interface_id; return -1; }