/* * Copyright (c) 2013-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" #ifdef HAVE_6LOWPAN_ND #include "ns_types.h" #include "string.h" #include "nsdynmemLIB.h" #include "ns_trace.h" #include "NWK_INTERFACE/Include/protocol.h" #include "Common_Protocols/icmpv6.h" #include "Common_Protocols/icmpv6_prefix.h" #include "Common_Protocols/icmpv6_radv.h" #include "randLIB.h" #ifdef HAVE_RPL #include "RPL/rpl_control.h" #include "RPL/rpl_data.h" #endif #include "MLE/mle.h" #include "6LoWPAN/IPHC_Decode/cipv6.h" #include "6LoWPAN/ND/nd_router_object.h" #include "6LoWPAN/Bootstraps/network_lib.h" #include "6LoWPAN/Bootstraps/protocol_6lowpan.h" #include "6LoWPAN/Bootstraps/protocol_6lowpan_bootstrap.h" #include "6LoWPAN/MAC/mac_helper.h" #include "Service_Libs/whiteboard/whiteboard.h" #include "common_functions.h" #include "BorderRouter/border_router.h" #include "Service_Libs/pan_blacklist/pan_blacklist_api.h" #include "6LoWPAN/MAC/mac_data_poll.h" #include "6LoWPAN/ws/ws_common.h" #include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h" #define TRACE_GROUP "loND" void icmp_nd_router_object_release(nd_router_t *router_object); uint8_t icmp_nd_router_prefix_ttl_update(nd_router_t *nd_router_object, protocol_interface_info_entry_t *cur_interface, uint16_t seconds); static uint8_t nd_router_bootstrap_timer(nd_router_t *cur, protocol_interface_info_entry_t *cur_interface, uint16_t ticks); #ifdef HAVE_RPL static void nd_ra_build(nd_router_t *cur, const uint8_t *address, protocol_interface_info_entry_t *cur_interface); static void nd_ns_forward_timer_reset(uint8_t *root_adr); static void nd_router_forward_timer(nd_router_t *cur, uint16_t ticks_update); static nd_router_t *nd_router_object_scan_by_prefix(const uint8_t *prefix, nwk_interface_id nwk_id); #else #define nd_ra_build(cur, address, cur_interface) ((void)0) #define nd_ns_forward_timer_reset(root_adr) ((void)0) #define nd_router_forward_timer(root_adr, ticks_update) ((void)0) #define nd_router_object_scan_by_prefix(prefix, nwk_id) NULL #endif static void lowpan_nd_address_cb(protocol_interface_info_entry_t *interface, if_address_entry_t *addr, if_address_callback_t reason); uint8_t nd_rs_build(nd_router_t *cur, protocol_interface_info_entry_t *cur_interface); bool icmp_nd_compare_to_def_next_hop(nd_router_next_hop *hop, sockaddr_t *adr); void icmp_nd_router_context_ttl_update(nd_router_t *nd_router_object, uint16_t seconds); //ND Router List static NS_LARGE NS_LIST_DEFINE(nd_router_list, nd_router_t, link); /* * Default values are documented in net_6lowpan_parameter_api.h - keep in sync. */ uint8_t nd_base_tick = 1; nd_parameters_s nd_params = { .rs_retry_max = 3, .rs_retry_interval_min = 15, .ns_retry_interval_min = 100, .ns_retry_linear_backoff = 100, .timer_random_max = 31, .ns_retry_max = 5, .multihop_dad = true, .iids_map_to_mac = false, .send_nud_probes = true, .ra_interval_min = 160, .ra_transmits = 3, .ra_cur_hop_limit = ADV_CUR_HOP_LIMIT, .ra_link_mtu = 0, .ra_reachable_time = 0, .ra_retrans_timer = 0, .ns_forward_timeout = 300, }; #ifdef HAVE_6LOWPAN_BORDER_ROUTER int8_t nd_set_br(nd_router_t *br) { if (ns_list_is_empty(&nd_router_list)) { ns_list_add_to_start(&nd_router_list, br); return 0; } return -1; } #endif bool nd_object_active(void) { if (!ns_list_is_empty(&nd_router_list)) { return true; } return false; } void icmp_nd_routers_init(void) { ns_list_foreach_safe(nd_router_t, cur, &nd_router_list) { ns_list_remove(&nd_router_list, cur); icmp_nd_router_object_release(cur); } } void icmp_nd_set_nd_def_router_address(uint8_t *ptr, nd_router_t *cur) { memcpy(ptr, ADDR_LINK_LOCAL_PREFIX, 8); ptr += 8; if (cur->default_hop.addrtype == ADDR_802_15_4_SHORT) { memcpy(ptr, ADDR_SHORT_ADR_SUFFIC, 6); ptr += 6; *ptr++ = cur->default_hop.address[0]; *ptr = cur->default_hop.address[1]; } else { memcpy(ptr, cur->default_hop.address, 8); } } void nd_ns_trig(nd_router_t *router_object, protocol_interface_info_entry_t *cur) { // ns_list_foreach(prefix_entry_t, prefix, &router_object->prefix_list) { if (prefix->options & PIO_A) { ns_list_foreach(if_address_entry_t, e, &cur->ip_addresses) { if (e->source == ADDR_SOURCE_SLAAC && (memcmp(e->address, prefix->prefix, 8) == 0)) { if (cur->if_6lowpan_dad_process.active) { e->state_timer = 5; } else { e->state_timer = 25; cur->if_6lowpan_dad_process.active = true; memcpy(cur->if_6lowpan_dad_process.address, e->address, 16); cur->if_6lowpan_dad_process.count = nd_params.ns_retry_max; } } } } } } void nd_router_base_init(nd_router_t *new_entry) { ns_list_link_init(new_entry, link); new_entry->nd_timer = 0; new_entry->nd_bootstrap_tick = 0; new_entry->nd_re_validate = 0; new_entry->mle_advert_timer = 0; new_entry->mle_purge_timer = 0; new_entry->default_hop.addrtype = ADDR_NONE; ns_list_init(&new_entry->prefix_list); ns_list_init(&new_entry->context_list); new_entry->secondaty_hop = 0; new_entry->ns_forward_timer = 0; new_entry->flags = 0; new_entry->ra_timing.initial_rtr_adv_count = 0; new_entry->ra_timing.rtr_adv_last_send_time = protocol_core_monotonic_time - 0x10000; new_entry->abro_version_num = 0; new_entry->trig_address_reg = false; } static void nd_router_remove(nd_router_t *router, protocol_interface_info_entry_t *interface) { tr_debug("route remove"); icmpv6_stop_router_advertisements(interface, router->border_router); ns_list_remove(&nd_router_list, router); icmp_nd_router_object_release(router); if (ns_list_is_empty(&nd_router_list)) { arm_6lowpan_bootstrap_init(interface); } } nd_router_t *icmp_nd_router_object_get(const uint8_t *border_router, nwk_interface_id nwk_id) { nd_router_t *new_entry = 0; uint_fast8_t count = 0; ns_list_foreach(nd_router_t, cur, &nd_router_list) { if (cur->nd_state == ND_BR_READY) { return NULL; } if (cur->nwk_id == nwk_id) { if (memcmp(cur->border_router, border_router, 16) == 0) { return cur; } } ++count; } if (count >= ND_OBJECT_MAX) { return NULL; } new_entry = ns_dyn_mem_alloc(sizeof(nd_router_t)); if (!new_entry) { tr_error("No heap for New Border Router"); return NULL; } new_entry->nd_state = ND_READY; new_entry->nwk_id = nwk_id; nd_router_base_init(new_entry); memcpy(new_entry->border_router, border_router, 16); new_entry->trig_address_reg = false; ns_list_add_to_end(&nd_router_list, new_entry); return new_entry; } void icmp_nd_router_object_reset(nd_router_t *router_object) { icmpv6_prefix_list_free(&router_object->prefix_list); lowpan_context_list_free(&router_object->context_list); if (router_object->secondaty_hop) { ns_dyn_mem_free(router_object->secondaty_hop); router_object->secondaty_hop = 0; } } #ifdef HAVE_6LOWPAN_BORDER_ROUTER int8_t icmp_nd_router_prefix_proxy_update(uint8_t *dptr, nd_router_setup_t *nd_router_object) { prefix_entry_t *new_entry = 0; uint8_t pre_setups; uint32_t lifeTime, prefTime; uint8_t prefix_len = *dptr++; pre_setups = *dptr++; lifeTime = common_read_32_bit(dptr); dptr += 4; prefTime = common_read_32_bit(dptr); dptr += 4; pre_setups |= PIO_R; new_entry = icmpv6_prefix_add(&nd_router_object->prefix_list, dptr, prefix_len, lifeTime, prefTime, pre_setups); if (new_entry) { new_entry->options = pre_setups; return 0; } return -2; } #endif uint8_t icmp_nd_router_prefix_valid(nd_router_t *nd_router_object) { ns_list_foreach(prefix_entry_t, cur, &nd_router_object->prefix_list) { if ((cur->options & PIO_A) && cur->lifetime) { return 1; } } return 0; } /* Returns 1 if the router object has been removed */ uint8_t icmp_nd_router_prefix_ttl_update(nd_router_t *nd_router_object, protocol_interface_info_entry_t *cur_interface, uint16_t seconds) { ns_list_foreach(prefix_entry_t, cur, &nd_router_object->prefix_list) { if (cur->preftime != 0xffffffff && cur->preftime) { if (cur->preftime <= seconds) { tr_warn("PREFTIME zero"); cur->preftime = 0; } else { cur->preftime -= seconds; } } if (cur->lifetime != 0xffffffff && cur->lifetime) { if (cur->lifetime > seconds) { cur->lifetime -= seconds; } else { tr_debug("Prefix Expiry"); if (nd_router_object->nd_state != ND_BR_READY) { if (cur->options & PIO_A) { tr_debug("Delete GP Address by Prefix, start RS"); nd_router_remove(nd_router_object, cur_interface); return 1; } } } } } return 0; } static int icmp_nd_slaac_prefix_address_gen(protocol_interface_info_entry_t *cur_interface, uint8_t *prefix, uint8_t prefix_len, uint32_t lifetime, uint32_t preftime, bool borRouterDevice, slaac_src_e slaac_src) { if_address_entry_t *address_entry = NULL; address_entry = icmpv6_slaac_address_add(cur_interface, prefix, prefix_len, lifetime, preftime, true, slaac_src); if (address_entry) { //Set Callback address_entry->cb = lowpan_nd_address_cb; if (borRouterDevice) { address_entry->state_timer = 0; } else { address_entry->state_timer = 45 + randLIB_get_random_in_range(1, nd_params.timer_random_max); //Allocate Addres registration state if (cur_interface->if_6lowpan_dad_process.active == false) { cur_interface->if_6lowpan_dad_process.count = nd_params.ns_retry_max; cur_interface->if_6lowpan_dad_process.active = true; memcpy(cur_interface->if_6lowpan_dad_process.address, address_entry->address, 16); } if ((cur_interface->lowpan_info & INTERFACE_NWK_BOOTSRAP_ACTIVE) && cur_interface->nwk_bootstrap_state == ER_SCAN) { cur_interface->nwk_bootstrap_state = ER_ADDRESS_REQ; cur_interface->bootsrap_state_machine_cnt = 0; } } return 0; } return -1; } static void lowpan_nd_address_cb(protocol_interface_info_entry_t *interface, if_address_entry_t *addr, if_address_callback_t reason) { nd_router_t *cur = NULL; bool g16_address; tr_debug("Interface ID: %i, ipv6: %s", interface->id, trace_ipv6(addr->address)); if (memcmp(&addr->address[8], ADDR_SHORT_ADR_SUFFIC, 6) == 0) { g16_address = true; } else { g16_address = false; } switch (reason) { case ADDR_CALLBACK_DAD_COMPLETE: if (addr_ipv6_equal(interface->if_6lowpan_dad_process.address, addr->address)) { tr_info("Address REG OK: %s", trace_ipv6(interface->if_6lowpan_dad_process.address)); interface->if_6lowpan_dad_process.active = false; interface->global_address_available = true; //Check If GP16 Address Add LL16 Address if (interface->lowpan_info & INTERFACE_NWK_BOOTSRAP_ACTIVE) { if (g16_address) { //Register also GP64 if NET_6LOWPAN_MULTI_GP_ADDRESS mode is enabled if (interface->lowpan_address_mode == NET_6LOWPAN_MULTI_GP_ADDRESS) { if (icmp_nd_slaac_prefix_address_gen(interface, addr->address, addr->prefix_len, addr->valid_lifetime, addr->preferred_lifetime, false, SLAAC_IID_DEFAULT) == 0) { return; } else { tr_warning("Secondary Address allocate fail"); } } } protocol_6lowpan_bootstrap_nd_ready(interface); } else { //Disable protocol poll mac_data_poll_protocol_poll_mode_decrement(interface); } } break; case ADDR_CALLBACK_TIMER: tr_debug("State Timer CB"); if (interface->if_6lowpan_dad_process.active) { if (memcmp(addr->address, interface->if_6lowpan_dad_process.address, 16) == 0) { cur = nd_get_object_by_nwk_id(interface->nwk_id); } else { addr->state_timer = 5; } } else { cur = nd_get_object_by_nwk_id(interface->nwk_id); if (cur) { interface->if_6lowpan_dad_process.count = nd_params.ns_retry_max; interface->if_6lowpan_dad_process.active = true; memcpy(interface->if_6lowpan_dad_process.address, addr->address, 16); } else { tr_debug("No ND Object for Address"); } } if (cur) { if (interface->if_6lowpan_dad_process.count) { nd_ns_build(cur, interface, addr->address); addr->state_timer = nd_params.ns_retry_interval_min; addr->state_timer += nd_params.ns_retry_linear_backoff * (nd_params.ns_retry_max - interface->if_6lowpan_dad_process.count); addr->state_timer += (randLIB_get_16bit() & nd_params.timer_random_max); tr_debug("NS Configured"); interface->if_6lowpan_dad_process.count--; //Enable Protocol Poll mode if (interface->lowpan_info & INTERFACE_NWK_CONF_MAC_RX_OFF_IDLE) { mac_data_poll_init_protocol_poll(interface); } } else { //ND FAIL tr_error("NS Fail"); protocol_6lowpan_neighbor_remove(interface, cur->default_hop.address, cur->default_hop.addrtype); if (cur->secondaty_hop) { tr_warn("Try Secondary Route"); memcpy(&cur->default_hop, cur->secondaty_hop, sizeof(nd_router_next_hop)); ns_dyn_mem_free(cur->secondaty_hop); cur->secondaty_hop = 0; interface->if_6lowpan_dad_process.count = nd_params.ns_retry_max; addr->state_timer = 1; } else { interface->if_6lowpan_dad_process.active = false; protocol_6lowpan_nd_borderrouter_connection_down(interface); } } } break; case ADDR_CALLBACK_PARENT_FULL: interface->if_6lowpan_dad_process.count = 0; tr_error("ND cache full--> Black list by given lifetime"); cur = nd_get_object_by_nwk_id(interface->nwk_id); if (cur) { pan_blacklist_pan_set(&interface->pan_blaclist_cache, mac_helper_panid_get(interface), cur->life_time); } break; case ADDR_CALLBACK_DAD_FAILED: if (g16_address) { tr_warn("ARO Failure:Duplicate address"); uint16_t shortAddress = common_read_16_bit(&addr->address[14]); tr_warn("Del old ll16"); protocol_6lowpan_del_ll16(interface, shortAddress); //Check if Application not freeze new address allocartion if (interface->reallocate_short_address_if_duplicate) { protocol_6lowpan_allocate_mac16(interface); interface->if_6lowpan_dad_process.active = false; if (icmp_nd_slaac_prefix_address_gen(interface, addr->address, addr->prefix_len, addr->valid_lifetime, addr->preferred_lifetime, false, SLAAC_IID_6LOWPAN_SHORT) == 0) { addr->state_timer = 1; return; } } } bootsrap_next_state_kick(ER_BOOTSTRAP_DAD_FAIL, interface); break; default: break; } } int8_t icmp_nd_router_prefix_update(uint8_t *dptr, nd_router_t *nd_router_object, protocol_interface_info_entry_t *cur_interface) { slaac_src_e slaac_src; bool borRouterDevice; prefix_entry_t *new_entry = 0; uint8_t prefix_len = *dptr++; uint8_t pre_setups = *dptr++; uint32_t lifetime = common_read_32_bit(dptr); uint32_t preftime = common_read_32_bit(dptr + 4); if (cur_interface->lowpan_address_mode == NET_6LOWPAN_GP16_ADDRESS || cur_interface->lowpan_address_mode == NET_6LOWPAN_MULTI_GP_ADDRESS) { slaac_src = SLAAC_IID_6LOWPAN_SHORT; } else { slaac_src = SLAAC_IID_DEFAULT; } if (cur_interface->border_router_setup) { borRouterDevice = true; } else { borRouterDevice = false; } //Read Lifetimes + skip resertved 4 bytes dptr += 12; new_entry = icmpv6_prefix_add(&nd_router_object->prefix_list, dptr, prefix_len, lifetime, preftime, pre_setups); if (new_entry) { if (new_entry->options == 0xff) { new_entry->options = pre_setups; if (new_entry->options & PIO_A) { if (icmpv6_slaac_prefix_update(cur_interface, new_entry->prefix, new_entry->prefix_len, new_entry->lifetime, new_entry->preftime) != 0) { icmp_nd_slaac_prefix_address_gen(cur_interface, new_entry->prefix, new_entry->prefix_len, new_entry->lifetime, new_entry->preftime, borRouterDevice, slaac_src); } } } else { icmpv6_slaac_prefix_update(cur_interface, dptr, prefix_len, lifetime, preftime); } if (nd_router_object->trig_address_reg) { nd_router_object->trig_address_reg = false; icmpv6_slaac_prefix_register_trig(cur_interface, dptr, prefix_len); } return 0; } return -2; } /* Update lifetime and expire contexts in ABRO storage */ void icmp_nd_router_context_ttl_update(nd_router_t *nd_router_object, uint16_t seconds) { ns_list_foreach_safe(lowpan_context_t, cur, &nd_router_object->context_list) { /* We're using seconds in call, but lifetime is in 100ms ticks */ if (cur->lifetime <= (uint32_t)seconds * 10) { /* When lifetime in the ABRO storage runs out, just drop it, * so we stop advertising it. This is different from the * interface context handling. */ ns_list_remove(&nd_router_object->context_list, cur); ns_dyn_mem_free(cur); } else { cur->lifetime -= (uint32_t)seconds * 10; } } } void icmp_nd_router_object_release(nd_router_t *router_object) { if (router_object) { icmp_nd_router_object_reset(router_object); ns_dyn_mem_free(router_object); } } void icmp_nd_border_router_release(nd_router_t *router_object) { if (router_object) { if (!ns_list_is_empty(&nd_router_list)) { ns_list_remove(&nd_router_list, router_object); } icmp_nd_router_object_reset(router_object); } } void gp_address_list_free(gp_ipv6_address_list_t *list) { ns_list_foreach_safe(gp_ipv6_address_entry_t, cur, list) { ns_list_remove(list, cur); ns_dyn_mem_free(cur); } } /* * Parse 6LoWPAN Context Options (RFC 6775 4.2) for ABRO storage. * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type | Length |Context Length | Res |C| CID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Reserved | Valid Lifetime | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * . . * . Context Prefix . * . . * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * */ static void icmp_nd_context_parse(buffer_t *buf, nd_router_t *nd_router_object) { const uint8_t *dptr = buffer_data_pointer(buf); uint16_t data_len = buffer_data_length(buf); uint16_t read = 0; while (read < data_len) { uint8_t type = dptr[0]; uint16_t len = dptr[1] * 8; read += len; if (len == 0 || read > data_len) { return; } else if (type == ICMPV6_OPT_6LOWPAN_CONTEXT) { uint8_t ctx_len = dptr[2]; if ((len == 16 && ctx_len <= 64) || (len == 24 && ctx_len <= 128)) { uint8_t c_id = dptr[3] & 0x1f; // ignore reserved fields uint16_t lifetime_mins = common_read_16_bit(dptr + 6); lowpan_context_update(&nd_router_object->context_list, c_id, lifetime_mins, dptr + 8, ctx_len, true); } else { tr_warn("Context len fail"); } } dptr += len; } } void icmp_nd_prefixs_parse(buffer_t *buf, nd_router_t *nd_router_object, protocol_interface_info_entry_t *cur_interface) { uint8_t *dptr; uint16_t data_len = buffer_data_length(buf); uint16_t readed = 0; uint8_t type, len; dptr = buffer_data_pointer(buf); while (readed < data_len) { type = *dptr++; len = *dptr++; if (len == 0) { return; } len <<= 3; readed += len; if (readed > data_len) { return; } else if (type == ICMPV6_OPT_PREFIX_INFO && len == 32) { icmp_nd_router_prefix_update(dptr, nd_router_object, cur_interface); dptr += 30; } else { dptr += (len - 2); } } } void icmp_nd_set_next_hop(nd_router_next_hop *hop, sockaddr_t *adr) { uint8_t *ptr = 0; ptr = hop->address; if (adr->addr_type == ADDR_IPV6) { if (memcmp(&adr->address[8], ADDR_SHORT_ADR_SUFFIC, 6) == 0) { hop->addrtype = ADDR_802_15_4_SHORT; memcpy(ptr, &adr->address[14], 2); } else { hop->addrtype = ADDR_802_15_4_LONG; memcpy(ptr, &adr->address[8], 8); } } else { hop->addrtype = adr->addr_type; memcpy(ptr, &adr->address[2], 8); } } bool icmp_nd_compare_to_def_next_hop(nd_router_next_hop *hop, sockaddr_t *adr) { if (adr->addr_type == ADDR_IPV6) { if (memcmp(&adr->address[8], ADDR_SHORT_ADR_SUFFIC, 6) == 0) { if (hop->addrtype == ADDR_802_15_4_SHORT) { if (memcmp(hop->address, &adr->address[14], 2) == 0) { return false; } } } else { if (hop->addrtype == ADDR_802_15_4_LONG) { if (memcmp(hop->address, &adr->address[8], 8) == 0) { return false; } } } } else { hop->addrtype = adr->addr_type; memcpy(hop->address, &adr->address[2], 8); } return true; } uint8_t nd_rs_build(nd_router_t *cur, protocol_interface_info_entry_t *cur_interface) { buffer_t *buf; if (cur) { uint8_t dest_addr[16]; icmp_nd_set_nd_def_router_address(dest_addr, cur); buf = icmpv6_build_rs(cur_interface, dest_addr); } else { buf = icmpv6_build_rs(cur_interface, NULL); } if (buf) { arm_net_protocol_packet_handler(buf, cur_interface); return 1; } return 0; } #ifdef HAVE_RPL static bool rpl_parents_only(const ipv6_route_info_t *route, bool valid) { return valid && rpl_data_is_rpl_parent_route(route->source); } #endif /* Neighbor Solicitation (RFC4861) with Address Registration Option (RFC6775) * and Source Link-Layer Address Option (RFC4861) */ void nd_ns_build(nd_router_t *cur, protocol_interface_info_entry_t *cur_interface, uint8_t *address_ptr) { uint8_t router[16]; aro_t aro; buffer_t *buf; #ifdef HAVE_RPL /* If we're a host, we will just send to our ND parent. But as a router, * we don't really maintain our ND parent - send NA instead to the RPL * parent we would use to talk to the border router. */ if ((cur_interface->lowpan_info & INTERFACE_NWK_ROUTER_DEVICE) && cur_interface->rpl_domain) { ipv6_route_t *route = ipv6_route_choose_next_hop(cur->border_router, cur_interface->id, rpl_parents_only); if (!route) { /* Important to return 1 so this counts as a "success" - caller then backs off due to lack of response and time out */ return; } memcpy(router, route->info.next_hop_addr, 16); } else #endif { icmp_nd_set_nd_def_router_address(router, cur); } aro.status = ARO_SUCCESS; aro.present = true; aro.lifetime = (cur->life_time / 60) + 1; memcpy(aro.eui64, cur_interface->mac, 8); buf = icmpv6_build_ns(cur_interface, router, address_ptr, true, false, &aro); protocol_push(buf); } /* RFC 6775 Duplicate Address Request/Confirmation packets * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type | Code | Checksum | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Status | Reserved | Registration Lifetime | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * + EUI-64 + * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * + + * | | * + Registered Address + * | | * + + * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * (and 8.2.1 implies this is can be followed by options, although * none are defined). */ #ifdef HAVE_6LOWPAN_ROUTER static bool nd_dar_dac_valid(buffer_t *buf) { const uint8_t *dptr = buffer_data_pointer(buf); if (buf->options.code != 0) { return false; } if (!icmpv6_options_well_formed_in_buffer(buf, 28)) { return false; } if (addr_is_ipv6_multicast(dptr + 12)) { return false; } if (addr_is_ipv6_unspecified(buf->src_sa.address) || addr_is_ipv6_multicast(buf->src_sa.address)) { return false; } return true; } #endif buffer_t *nd_dar_parse(buffer_t *buf, protocol_interface_info_entry_t *cur_interface) { #if defined WHITEBOARD && defined HAVE_6LOWPAN_BORDER_ROUTER uint8_t *dptr = buffer_data_pointer(buf); buffer_t *retbuf; uint8_t status; uint16_t lifetime; const uint8_t *eui64; if (!nd_dar_dac_valid(buf)) { goto drop; } status = *dptr; dptr += 2; lifetime = common_read_16_bit(dptr); dptr += 2; if (status != ARO_SUCCESS) { goto drop; } whiteboard_entry_t *wb; /* EUI-64 */ eui64 = dptr; dptr += 8; tr_debug("DAR adr: %s, from %s", trace_ipv6(dptr), trace_ipv6(buf->src_sa.address)); //SET White board wb = whiteboard_table_update(dptr, eui64, &status); if (wb && status == ARO_SUCCESS) { memcpy(wb->address, dptr, 16); memcpy(wb->eui64, eui64, 8); wb->interface_index = cur_interface->id; wb->ttl = UINT24_C(60) * lifetime; } retbuf = icmpv6_build_dad(cur_interface, NULL, ICMPV6_TYPE_INFO_DAC, buf->src_sa.address, eui64, dptr, status, lifetime); if (retbuf) { buffer_free(buf); return retbuf; } drop: #else (void)cur_interface; #endif return buffer_free(buf); } #ifdef HAVE_6LOWPAN_ROUTER static void nd_update_registration(protocol_interface_info_entry_t *cur_interface, ipv6_neighbour_t *neigh, const aro_t *aro) { /* We are about to send an ARO response - update our Neighbour Cache accordingly */ if (aro->status == ARO_SUCCESS && aro->lifetime != 0) { neigh->type = IP_NEIGHBOUR_REGISTERED; neigh->lifetime = aro->lifetime * UINT32_C(60); ipv6_neighbour_set_state(&cur_interface->ipv6_neighbour_cache, neigh, IP_NEIGHBOUR_STALE); /* Register with 2 seconds off the lifetime - don't want the NCE to expire before the route */ ipv6_route_add_metric(neigh->ip_address, 128, cur_interface->id, neigh->ip_address, ROUTE_ARO, NULL, 0, neigh->lifetime - 2, 32); /* We need to know peer is a host before publishing - this needs MLE. Not yet established * what to do without MLE - might need special external/non-external prioritisation at root. * This "publish for RFD" rule comes from ZigBee IP. */ mac_neighbor_table_entry_t *entry = mac_neighbor_table_address_discover(mac_neighbor_info(cur_interface), ipv6_neighbour_eui64(&cur_interface->ipv6_neighbour_cache, neigh), ADDR_802_15_4_LONG); if (entry) { if (!entry->ffd_device) { rpl_control_publish_host_address(protocol_6lowpan_rpl_domain, neigh->ip_address, neigh->lifetime); } } protocol_6lowpan_neighbor_address_state_synch(cur_interface, aro->eui64, neigh->ip_address + 8); } else { /* Um, no - can't transmit response if we remove NCE now! */ //ipv6_neighbour_entry_remove(&cur_interface->ipv6_neighbour_cache, neigh); neigh->type = IP_NEIGHBOUR_TENTATIVE; neigh->lifetime = 2; ipv6_neighbour_set_state(&cur_interface->ipv6_neighbour_cache, neigh, IP_NEIGHBOUR_STALE); ipv6_route_add_metric(neigh->ip_address, 128, cur_interface->id, neigh->ip_address, ROUTE_ARO, NULL, 0, 4, 32); rpl_control_unpublish_address(protocol_6lowpan_rpl_domain, neigh->ip_address); } } void nd_remove_registration(protocol_interface_info_entry_t *cur_interface, addrtype_t ll_type, const uint8_t *ll_address) { ns_list_foreach_safe(ipv6_neighbour_t, cur, &cur_interface->ipv6_neighbour_cache.list) { if ((cur->type == IP_NEIGHBOUR_REGISTERED || cur->type == IP_NEIGHBOUR_TENTATIVE) && ipv6_neighbour_ll_addr_match(cur, ll_type, ll_address)) { ipv6_route_delete(cur->ip_address, 128, cur_interface->id, cur->ip_address, ROUTE_ARO); ipv6_neighbour_entry_remove(&cur_interface->ipv6_neighbour_cache, cur); } } } /* Process ICMP Neighbor Solicitation (RFC 4861 + RFC 6775) ARO. */ bool nd_ns_aro_handler(protocol_interface_info_entry_t *cur_interface, const uint8_t *aro_opt, const uint8_t *slla_opt, const uint8_t *src_addr, aro_t *aro_out) { /* Ignore any ARO if source is link-local */ if (addr_is_ipv6_link_local(src_addr)) { return true; /* Transmit NA, without ARO */ } /* If we can't parse the SLLAO, then act as if no SLLAO: ignore ARO */ sockaddr_t ll_addr; if (!cur_interface->if_llao_parse(cur_interface, slla_opt, &ll_addr)) { return true; /* Transmit NA, without ARO */ } /* * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type = 33 | Length = 2 | Status | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Reserved | Registration Lifetime | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * + EUI-64 + * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ /* icmpv6_ns_handler has already checked incoming status == 0 */ aro_out->lifetime = common_read_16_bit(aro_opt + 6); memcpy(aro_out->eui64, aro_opt + 8, 8); /* Check if we are already using this address ourself */ if (addr_interface_address_compare(cur_interface, src_addr) == 0) { aro_out->present = true; aro_out->status = ARO_DUPLICATE; return true; } /* TODO - check hard upper limit on registrations? */ if (ws_info(cur_interface)) { aro_out->status = ws_common_allow_child_registration(cur_interface, aro_out->eui64, aro_out->lifetime); if (aro_out->status != ARO_SUCCESS) { aro_out->present = true; return true; } } /* We need to have entry in the Neighbour Cache */ ipv6_neighbour_t *neigh = ipv6_neighbour_lookup_or_create(&cur_interface->ipv6_neighbour_cache, src_addr); if (!neigh) { aro_out->present = true; aro_out->status = ARO_FULL; return true; } uint8_t *nce_eui64 = ipv6_neighbour_eui64(&cur_interface->ipv6_neighbour_cache, neigh); if (neigh->state != IP_NEIGHBOUR_NEW) { switch (neigh->type) { case IP_NEIGHBOUR_TENTATIVE: /* Is zero EUI-64 still possible? */ if (memcmp(nce_eui64, aro_out->eui64, 8) && memcmp(nce_eui64, ADDR_EUI64_ZERO, 8)) { /* Have a Tentative NCE with different EUI-64 - ignore NS; two * people trying to register at once. One should retry. */ return false; } break; case IP_NEIGHBOUR_REGISTERED: if (memcmp(nce_eui64, aro_out->eui64, 8)) { /* Already registered with different EUI-64 - duplicate */ aro_out->present = true; aro_out->status = ARO_DUPLICATE; return true; } break; case IP_NEIGHBOUR_GARBAGE_COLLECTIBLE: break; } } if (neigh->type != IP_NEIGHBOUR_REGISTERED) { neigh->type = IP_NEIGHBOUR_TENTATIVE; neigh->lifetime = TENTATIVE_NCE_LIFETIME; memcpy(nce_eui64, aro_out->eui64, 8); } /* Set the LL address, ensure it's marked STALE */ ipv6_neighbour_entry_update_unsolicited(&cur_interface->ipv6_neighbour_cache, neigh, ll_addr.addr_type, ll_addr.address); ipv6_neighbour_set_state(&cur_interface->ipv6_neighbour_cache, neigh, IP_NEIGHBOUR_STALE); if (ws_info(cur_interface)) { aro_out->status = ARO_SUCCESS; aro_out->present = true; // Todo: this might not be needed... nd_update_registration(cur_interface, neigh, aro_out); return true; } if (cur_interface->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER || nd_params.multihop_dad == false) { if (cur_interface->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) { whiteboard_entry_t *wb; wb = whiteboard_table_update(src_addr, aro_out->eui64, &aro_out->status); if (wb) { if (aro_out->status == ARO_SUCCESS) { memcpy(wb->address, src_addr, 16); memcpy(wb->eui64, aro_out->eui64, 8); wb->interface_index = cur_interface->id; wb->ttl = 50000;//life_time; } } else { tr_warn("white Board Registry fail"); aro_out->status = ARO_FULL; goto RESPONSE; } } RESPONSE: aro_out->present = true; nd_update_registration(cur_interface, neigh, aro_out); return true; /* Transmit NA */ } else { /* Non-border router and multihop DAD: relay as DAR to Border Router */ nd_router_t *nd_router_obj = 0; nd_router_obj = nd_router_object_scan_by_prefix(src_addr, cur_interface->nwk_id); if (!nd_router_obj) { /* Don't know where to send this. Do we say "yay" or "nay"? */ /* For now, ignore ARO, as with old code; don't set aro_out.present */ return true; } buffer_t *buf = icmpv6_build_dad(cur_interface, NULL, ICMPV6_TYPE_INFO_DAR, nd_router_obj->border_router, aro_out->eui64, src_addr, 0, aro_out->lifetime); if (!buf) { return false; /* Failed to build DAR - drop NS */ } tr_debug("RX:NS --> TX DAR to Root"); protocol_push(buf); if (nd_router_obj->ns_forward_timer == 0) { nd_router_obj->ns_forward_timer = nd_params.ns_forward_timeout; } return false; /* Tell ns_handler to not transmit now */ } } buffer_t *nd_dac_handler(buffer_t *buf, protocol_interface_info_entry_t *cur) { uint8_t *dptr, target_address[16], *reg_address; aro_t aro; dptr = buffer_data_pointer(buf); if (!nd_dar_dac_valid(buf)) { return buffer_free(buf); } nd_ns_forward_timer_reset(buf->src_sa.address); aro.status = *dptr; dptr += 2; aro.lifetime = common_read_16_bit(dptr); dptr += 2; /* EUI-64 */ memcpy(aro.eui64, dptr, 8); dptr += 8; reg_address = dptr; dptr += 16; ipv6_neighbour_t *neigh = ipv6_neighbour_lookup(&cur->ipv6_neighbour_cache, reg_address); if (!neigh || neigh->type == IP_NEIGHBOUR_GARBAGE_COLLECTIBLE || memcmp(ipv6_neighbour_eui64(&cur->ipv6_neighbour_cache, neigh), aro.eui64, 8)) { return buffer_free(buf); } nd_update_registration(cur, neigh, &aro); /* RFC 6775 has a bit of a hole here - what's the Target Address? */ /* It's not in the DAC. We didn't record locally when we sent the DAR */ /* I guess it's logical that we use a link-local address. We break */ /* RFC 4861 by responding "solicited", but not to the NS Target... */ /* However, my reading of RFC 4861 says that the receiver should do */ /* the right thing. Only problem is that what if they really did want */ /* to do a NUD probe for our GP addr, but included the ARO by mistake? */ if (addr_interface_get_ll_address(cur, target_address, 0)) { return buffer_free(buf); } /* NA builder will send it to the address in the buffer's source */ memcpy(buf->src_sa.address, reg_address, 16); buffer_t *na_buf = icmpv6_build_na(cur, true, true, false, target_address, &aro, buf->src_sa.address); buffer_free(buf); return na_buf; } #endif // HAVE_6LOWPAN_ROUTER /* Original ABRO-based all-in-one parser. This needs some rework to separate ABRO-related and unrelated bits */ /* Returns "false" if ABRO suggested it was a stale message, so not worth handling in the normal code */ bool nd_ra_process_abro(protocol_interface_info_entry_t *cur, buffer_t *buf, const uint8_t *dptr, uint8_t ra_flags, uint16_t router_lifetime) { nd_router_t *router; uint32_t abro_ver_num; uint16_t temp16; bool uptodate = false; /* XXX this is really a 32-bit number these days */ temp16 = common_read_16_bit(dptr); abro_ver_num = temp16; dptr += 2; temp16 = common_read_16_bit(dptr); //SET MSB bytes to 32-bit abro_ver_num |= ((uint32_t)temp16 << 16); dptr += 4; //If Border Router boot is state if (cur->border_router_setup) { if (memcmp(dptr, cur->border_router_setup->border_router_gp_adr, 16) == 0) { if (cur->border_router_setup->initActive) { //save New Context if (common_serial_number_greater_32(abro_ver_num, cur->border_router_setup->nd_border_router_configure->abro_version_num)) { cur->border_router_setup->initActive = false; cur->border_router_setup->nd_border_router_configure->abro_version_num = (abro_ver_num + 0x00010000); cur->border_router_setup->nd_nwk->abro_version_num = (abro_ver_num + 0x00010000); } else if (abro_ver_num == cur->border_router_setup->nd_border_router_configure->abro_version_num) { cur->border_router_setup->initActive = false; cur->border_router_setup->nd_border_router_configure->abro_version_num = (abro_ver_num + 0x00010000); cur->border_router_setup->nd_nwk->abro_version_num = (abro_ver_num + 0x00010000); } } return true; } return false; } router = icmp_nd_router_object_get(dptr, buf->interface->nwk_id); if (!router) { return true; } if (router->default_hop.addrtype == ADDR_NONE) { tr_debug("Create ND version"); router->flags = ra_flags; uptodate = true; icmp_nd_set_next_hop(&router->default_hop, &buf->src_sa); router->default_hop.LQI = buf->options.lqi; icmp_nd_prefixs_parse(buf, router, cur); icmp_nd_context_parse(buf, router); if ((cur->lowpan_info & INTERFACE_NWK_ROUTER_DEVICE) == 0) { //SET OnlY primary uint8_t address[8]; if (router->default_hop.addrtype == ADDR_802_15_4_SHORT) { address[6] = router->default_hop.address[0]; address[7] = router->default_hop.address[1]; memcpy(address, ADDR_SHORT_ADR_SUFFIC, 6); } else { memcpy(address, router->default_hop.address, 8); } protocol_6lowpan_neighbor_priority_update(cur, NULL, address); } router->nd_state = ND_READY; router->nd_re_validate = (router_lifetime / 5) * 4; router->nd_timer = 10; } else { bool new_router = icmp_nd_compare_to_def_next_hop(&router->default_hop, &buf->src_sa); if (abro_ver_num == router->abro_version_num) { /* Up-to-date for host processing, but don't necessarily process for multi-hop relaying */ uptodate = true; } /* XXX this is really a 32-bit number these days */ if (common_serial_number_greater_32(abro_ver_num, router->abro_version_num)) { uint32_t diff_update; diff_update = (abro_ver_num - router->abro_version_num); uptodate = true; //uprouter_info=1; if (diff_update >= 0x00010000) { tr_debug("Border Router Boot Trig NS"); router->trig_address_reg = true; } else { tr_debug("Next num"); } icmpv6_prefix_list_free(&router->prefix_list); lowpan_context_list_free(&router->context_list); icmp_nd_prefixs_parse(buf, router, cur); router->trig_address_reg = false; icmp_nd_context_parse(buf, router); icmpv6_restart_router_advertisements(cur, router->border_router); } else if (router->default_hop.LQI < buf->options.lqi) { /* XXX another zero-hysteresis parent swap */ if (new_router) { uint8_t *sec_ptr = NULL; if (router->secondaty_hop == 0) { router->secondaty_hop = ns_dyn_mem_alloc(sizeof(nd_router_next_hop)); } else { nd_router_next_hop *hop = router->secondaty_hop; sec_ptr = hop->address; if ((cur->lowpan_info & INTERFACE_NWK_ROUTER_DEVICE) == 0) { if (hop->addrtype == ADDR_802_15_4_SHORT) { hop->address[6] = hop->address[0]; hop->address[7] = hop->address[1]; memcpy(hop->address, ADDR_SHORT_ADR_SUFFIC, 6); } } } if ((cur->lowpan_info & INTERFACE_NWK_ROUTER_DEVICE) == 0) { //Remove old secondary priority and set new primary protocol_6lowpan_neighbor_priority_update(cur, sec_ptr, &buf->src_sa.address[8]); } // if (router->secondaty_hop) { memcpy(router->secondaty_hop, &router->default_hop, sizeof(nd_router_next_hop)); } icmp_nd_set_next_hop(&router->default_hop, &buf->src_sa); } router->default_hop.LQI = buf->options.lqi; } else if (new_router) { if (!router->secondaty_hop) { router->secondaty_hop = ns_dyn_mem_alloc(sizeof(nd_router_next_hop)); if (router->secondaty_hop) { router->secondaty_hop->LQI = 0; } } if (router->secondaty_hop) { if (router->secondaty_hop->LQI < buf->options.lqi) { icmp_nd_set_next_hop(router->secondaty_hop, &buf->src_sa); router->secondaty_hop->LQI = buf->options.lqi; if ((cur->lowpan_info & INTERFACE_NWK_ROUTER_DEVICE) == 0) { //SET only new primary protocol_6lowpan_neighbor_priority_update(cur, 0, &buf->src_sa.address[8]); } } } } /* If waiting for an RS response */ /* XXX this accepts it even if stale - no abro version check. Problem? */ /* XXX this section below smells like stuff that should be outside this function */ if (router->nd_state == ND_RS_UNCAST || router->nd_state == ND_RS_MULTICAST) { /* Set uptodate to true so we accept data into our interface tables. * Consistent with previous behaviour, but is this what we want? */ uptodate = true; router->flags = ra_flags; icmp_nd_set_next_hop(&router->default_hop, &buf->src_sa); router->default_hop.LQI = buf->options.lqi; icmp_nd_prefixs_parse(buf, router, cur); icmp_nd_context_parse(buf, router); if (router->nd_state == ND_RS_MULTICAST) { if ((cur->lowpan_info & INTERFACE_NWK_ROUTER_DEVICE) == 0) { router->nd_timer = 1; } else { router->nd_timer = nd_params.rs_retry_interval_min; } tr_debug("RS MC Done"); } else { router->nd_timer = 1; tr_debug("RS Unicast Done"); mac_data_poll_protocol_poll_mode_decrement(cur); } router->ns_retry = nd_params.ns_retry_max; router->nd_state = ND_READY; router->nd_re_validate = (router_lifetime / 5) * 4; } } if (uptodate) { router->abro_version_num = abro_ver_num; router->life_time = router_lifetime; } return uptodate; } /* This processes the 6CO for the interface itself - separate from the ABRO * multihop relay storage. * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type | Length |Context Length | Res |C| CID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Reserved | Valid Lifetime | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * . . * . Context Prefix . * . . * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * */ void nd_ra_process_lowpan_context_option(protocol_interface_info_entry_t *cur, const uint8_t *opt) { uint8_t opt_len = opt[1]; uint8_t ctx_len = opt[2]; uint8_t cid_flags = opt[3]; uint16_t lifetime = common_read_16_bit(opt + 6); if (ctx_len > 128 || ctx_len > (opt_len - 1) * 64) { return; } lowpan_context_update(&cur->lowpan_contexts, cid_flags, lifetime, opt + 8, ctx_len, true); } #ifdef HAVE_6LOWPAN_ROUTER static void nd_ra_build(nd_router_t *cur, const uint8_t *address, protocol_interface_info_entry_t *cur_interface) { if (!(cur_interface->lowpan_info & INTERFACE_NWK_BOOTSRAP_ADDRESS_REGISTER_READY) || !icmp_nd_router_prefix_valid(cur)) { return; } /* Base buffer size: RA Hdr = 12, ABRO = 24, MTU = up to 8, SLLAO = up to 16 */ uint16_t length = 12 + 24 + 8 + 16; length += 32 * ns_list_count(&cur->prefix_list); ns_list_foreach(lowpan_context_t, context_ptr, &cur->context_list) { length += (context_ptr->length <= 64) ? 16 : 24; } buffer_t *db = buffer_get(length); if (!db) { return; } else if (addr_interface_get_ll_address(cur_interface, db->src_sa.address, 0) != 0) { buffer_free(db); return; } db->src_sa.addr_type = ADDR_IPV6; uint8_t *dptr = buffer_data_pointer(db); *dptr++ = cur_interface->adv_cur_hop_limit; *dptr++ = cur->flags; dptr = common_write_16_bit(cur->life_time, dptr); dptr = common_write_32_bit(cur_interface->adv_reachable_time, dptr); dptr = common_write_32_bit(cur_interface->adv_retrans_timer, dptr); //SET ABRO *dptr++ = ICMPV6_OPT_AUTHORITATIVE_BORDER_RTR; *dptr++ = 3; //Len * 8 byte dptr = common_write_16_bit((uint16_t)cur->abro_version_num, dptr); dptr = common_write_16_bit((uint16_t)(cur->abro_version_num >> 16), dptr); dptr = common_write_16_bit(0, dptr); memcpy(dptr, cur->border_router, 16); dptr += 16; //SET Prefixs dptr = icmpv6_write_prefix_option(&cur->prefix_list, dptr, 0, cur_interface); ns_list_foreach(lowpan_context_t, context_ptr, &cur->context_list) { *dptr++ = ICMPV6_OPT_6LOWPAN_CONTEXT; *dptr++ = (context_ptr->length <= 64) ? 2 : 3; *dptr++ = context_ptr->length; *dptr++ = context_ptr->cid | (context_ptr->compression ? LOWPAN_CONTEXT_C : 0); dptr = common_write_16_bit(0, dptr); dptr = common_write_16_bit((context_ptr->lifetime + 599) / 600, dptr); length = (context_ptr->length <= 64) ? 8 : 16; memcpy(dptr, context_ptr->prefix, length); dptr += length; } //MTU if (cur_interface->adv_link_mtu != 0) { dptr = icmpv6_write_mtu_option(cur_interface->adv_link_mtu, dptr); } /* RFC 6775 mandates SLLAO in RA */ dptr = icmpv6_write_icmp_lla(cur_interface, dptr, ICMPV6_OPT_SRC_LL_ADDR, true, db->src_sa.address); if (address) { memcpy(db->dst_sa.address, address, 16); } else { memcpy(db->dst_sa.address, ADDR_LINK_LOCAL_ALL_NODES, 16); } buffer_data_end_set(db, dptr); db->dst_sa.addr_type = ADDR_IPV6; db->interface = cur_interface; db->info = (buffer_info_t)(B_FROM_ICMP | B_TO_ICMP | B_DIR_DOWN); db->options.type = ICMPV6_TYPE_INFO_RA; db->options.code = 0; db->options.hop_limit = 255; arm_net_protocol_packet_handler(db, cur_interface); } void nd_ra_build_by_abro(const uint8_t *abro, const uint8_t *dest, protocol_interface_info_entry_t *cur_interface) { ns_list_foreach(nd_router_t, cur, &nd_router_list) { if (addr_ipv6_equal(cur->border_router, abro)) { nd_ra_build(cur, dest, cur_interface); } } } void nd_trigger_ras_from_rs(const uint8_t *unicast_adr, protocol_interface_info_entry_t *cur_interface) { ns_list_foreach(nd_router_t, cur, &nd_router_list) { if (cur->nwk_id == cur_interface->nwk_id) { if (icmp_nd_router_prefix_valid(cur)) { //Allocate icmpv6_trigger_ra_from_rs(cur_interface, unicast_adr, cur->border_router); } } else { tr_error("BIND_CONFIRM FAIL!!"); } } } void nd_ns_forward_timer_reset(uint8_t *root_adr) { ns_list_foreach(nd_router_t, cur, &nd_router_list) { if (memcmp(root_adr, cur->border_router, 16) == 0) { if (cur->ns_forward_timer) { cur->ns_forward_timer = 0; tr_warn("RX VALID DAC"); } break; } } } static void nd_router_forward_timer(nd_router_t *cur, uint16_t ticks_update) { protocol_interface_info_entry_t *cur_interface; if (!(cur->ns_forward_timer)) { return; } if (cur->ns_forward_timer > ticks_update) { cur->ns_forward_timer -= ticks_update; return; } cur->ns_forward_timer = 0; cur_interface = protocol_stack_interface_info_get(cur->nwk_id); if (cur_interface) { if (cur->nd_re_validate > 10) { tr_debug("TRIG NS/ND"); cur->nd_timer = 1; cur->nd_re_validate = 1; } if (cur_interface->if_6lowpan_dad_process.active == false) { nd_ns_trig(cur, cur_interface); } } } ipv6_ra_timing_t *nd_ra_timing(const uint8_t abro[16]) { ns_list_foreach(nd_router_t, cur, &nd_router_list) { if (addr_ipv6_equal(abro, cur->border_router)) { return &cur->ra_timing; } } return NULL; } static nd_router_t *nd_router_object_scan_by_prefix(const uint8_t *ptr, nwk_interface_id nwk_id) { ns_list_foreach(nd_router_t, cur, &nd_router_list) { if (cur->nwk_id == nwk_id) { if (icmpv6_prefix_compare(&cur->prefix_list, ptr, 64)) { return cur; } } } return NULL; } #endif void gp_address_add_to_end(gp_ipv6_address_list_t *list, const uint8_t address[static 16]) { gp_ipv6_address_entry_t *new_entry; ns_list_foreach(gp_ipv6_address_entry_t, cur, list) { if (memcmp(cur->address, address, 16) == 0) { return; } } new_entry = ns_dyn_mem_alloc(sizeof(gp_ipv6_address_entry_t)); if (!new_entry) { tr_warn("No heap for New Address"); return; } memcpy(new_entry->address, address, 16); ns_list_add_to_end(list, new_entry); } /* Returns 1 if the router object has been removed */ static uint8_t nd_router_ready_timer(nd_router_t *cur, protocol_interface_info_entry_t *cur_interface, uint16_t ticks_update) { if (!cur->nd_timer) { return 0; } if (cur->nd_timer > ticks_update) { cur->nd_timer -= ticks_update; return 0; } //Take out last remaing time from ticks ticks_update -= cur->nd_timer; uint16_t updated_seconds = 1; cur->nd_timer = 10; if (ticks_update) { updated_seconds += (ticks_update / 10); //Set Next second based on over based time cur->nd_timer -= (ticks_update % 10); } if (icmp_nd_router_prefix_ttl_update(cur, cur_interface, updated_seconds)) { return 1; } //Update seconds icmp_nd_router_context_ttl_update(cur, updated_seconds); if (!cur->nd_re_validate) { return 0; } if (cur->nd_re_validate > updated_seconds) { cur->nd_re_validate -= updated_seconds; //tr_debug("NDR:Tick Update %u", cur->nd_re_validate); return 0; } if (cur->nd_state == ND_READY) { tr_debug("RE ND Process: RS Unicast!"); cur->ns_retry = nd_params.rs_retry_max; cur->nd_state = ND_RS_UNCAST; set_power_state(ICMP_ACTIVE); cur->nd_timer = 1; cur->nd_bootstrap_tick = (nd_base_tick - 1); if (cur_interface->lowpan_info & INTERFACE_NWK_CONF_MAC_RX_OFF_IDLE) { mac_data_poll_init_protocol_poll(cur_interface); } nd_router_bootstrap_timer(cur, cur_interface, 1); } else { /* ND_BR_READY */ nd_border_router_setup_refresh(cur->nwk_id, true); tr_debug("ND BR refresh ABRO"); cur->nd_re_validate = (cur->life_time / 4) * 3; icmpv6_restart_router_advertisements(cur_interface, cur->border_router); } return 0; } /* Returns 1 if the router object has been removed, or we want no further processing on this tick */ static uint8_t nd_router_bootstrap_timer(nd_router_t *cur, protocol_interface_info_entry_t *cur_interface, uint16_t ticks) { uint16_t scaled_ticks; /* * nd_timer is scaled by nd_base_tick during the discovery states, * to allow API to slow down the ND process. Note we count up and test * inequality, just in case someone decides to change nd_base_tick on * the fly. */ if (cur->nd_bootstrap_tick + ticks < nd_base_tick) { cur->nd_bootstrap_tick += ticks; return 0; } //Take off scaled ticks ticks -= (nd_base_tick - cur->nd_bootstrap_tick); scaled_ticks = 1 + (ticks / nd_base_tick); cur->nd_bootstrap_tick = 0 + (ticks % nd_base_tick); if (!cur->nd_timer) { tr_debug("NDB:Tick Update fail %u", scaled_ticks); return 0; } if (cur->nd_timer > scaled_ticks) { cur->nd_timer -= scaled_ticks; return 0; } switch (cur->nd_state) { case ND_RS_UNCAST: case ND_RS_MULTICAST: if (cur->ns_retry) { if (nd_rs_build(cur->nd_state == ND_RS_UNCAST ? cur : NULL, cur_interface)) { cur->nd_timer = nd_params.rs_retry_interval_min; cur->nd_timer += randLIB_get_16bit() & nd_params.timer_random_max; cur->ns_retry--; tr_debug(cur->nd_state == ND_RS_UNCAST ? "RS" : "RS+"); } else { cur->nd_timer = 2; } } else { //ND FAIL if (cur->nd_state == ND_RS_UNCAST) { cur->ns_retry = nd_params.rs_retry_max; cur->nd_state = ND_RS_MULTICAST; cur->nd_timer = 1; } else { //RS UNICAST Fail /*if (rpl_object_poisons() == 0) ??? */ { protocol_6lowpan_bootstrap_re_start(cur_interface); } return 1; } } break; case ND_READY: case ND_BR_READY: /* Not called for these states - put in to suppress GCC warning */ break; } return 0; } void nd_object_timer(protocol_interface_info_entry_t *cur_interface, uint16_t ticks_update) { ns_list_foreach_safe(nd_router_t, cur, &nd_router_list) { /* This may nd_router_remove(cur), so need to use safe loop */ if (cur_interface->nwk_id == cur->nwk_id) { protocol_6lowpan_link_advertise_handle(cur, cur_interface, ticks_update); if (cur->nd_state != ND_BR_READY) { nd_router_forward_timer(cur, ticks_update); } if (nd_is_ready_state(cur->nd_state)) { nd_router_ready_timer(cur, cur_interface, ticks_update); } else { nd_router_bootstrap_timer(cur, cur_interface, ticks_update); } return; } } } uint32_t nd_object_time_to_next_nd_reg(void) { uint32_t ret_val = 0; ns_list_foreach(nd_router_t, cur, &nd_router_list) { if (cur->nd_state == ND_READY) { ret_val = cur->nd_re_validate; ret_val++; ret_val *= 1000; /* XXX surely this should find the shortest, not the first? */ break; } } return ret_val; } uint8_t nd_prefix_dst_check(uint8_t *ptr) { ns_list_foreach(nd_router_t, cur, &nd_router_list) { if (icmpv6_prefix_compare(&cur->prefix_list, ptr, 64)) { return 1; } } return 0; } int8_t nd_parent_loose_indcate(uint8_t *neighbor_address, protocol_interface_info_entry_t *cur_interface) { int8_t ret_val = -1; addrtype_t adr_type = ADDR_802_15_4_LONG; uint8_t *adr_ptr = neighbor_address; nd_router_next_hop *hop; uint8_t compare_len = 8; ns_list_foreach(nd_router_t, cur, &nd_router_list) { if (!(cur_interface->lowpan_info & INTERFACE_NWK_BOOTSRAP_ADDRESS_REGISTER_READY)) { continue; } hop = &cur->default_hop; if (memcmp(neighbor_address, ADDR_SHORT_ADR_SUFFIC, 6) == 0) { adr_ptr += 6; adr_type = ADDR_802_15_4_SHORT; compare_len = 2; } if (hop->addrtype == adr_type) { if (memcmp(hop->address, adr_ptr, compare_len) == 0) { tr_debug("ND Primary Parent Lost"); if (cur->secondaty_hop == 0) { ret_val = -1; } else { ret_val = 0; tr_debug("Swap Secondary to primary"); memcpy(hop, cur->secondaty_hop, sizeof(nd_router_next_hop)); tr_debug("Trig NS/NA with new parent"); if (cur->nd_state == ND_READY) { cur->nd_re_validate = 1; if (cur_interface->if_6lowpan_dad_process.active == false) { nd_ns_trig(cur, cur_interface); } } ns_dyn_mem_free(cur->secondaty_hop); cur->secondaty_hop = 0; } return ret_val; } } else if (cur->secondaty_hop) { hop = cur->secondaty_hop; if (hop->addrtype == adr_type) { if (memcmp(hop->address, adr_ptr, compare_len) == 0) { tr_debug("ND Secondary Parent Lost"); ns_dyn_mem_free(cur->secondaty_hop); cur->secondaty_hop = 0; return 0; } } } } return -1; } nd_router_t *nd_get_object_by_nwk_id(nwk_interface_id nwk_id) { ns_list_foreach(nd_router_t, cur, &nd_router_list) { if (cur->nwk_id == nwk_id) { return cur; } } return 0; } nd_router_t *nd_get_pana_address(void) { return ns_list_get_first(&nd_router_list); } void nd_6lowpan_set_radv_params(protocol_interface_info_entry_t *cur_interface) { #ifndef NO_RADV_TX cur_interface->max_ra_delay_time = 20; cur_interface->min_delay_between_ras = 100; cur_interface->rtr_adv_unicast_to_rs = true; cur_interface->adv_cur_hop_limit = nd_params.ra_cur_hop_limit; cur_interface->adv_link_mtu = nd_params.ra_link_mtu; cur_interface->adv_reachable_time = nd_params.ra_reachable_time; cur_interface->adv_retrans_timer = nd_params.ra_retrans_timer; cur_interface->max_initial_rtr_adv_interval = nd_params.ra_interval_min; cur_interface->max_initial_rtr_advertisements = nd_params.ra_transmits; #else (void) cur_interface; #endif } #endif // HAVE_6LOWPAN_ND