mirror of https://github.com/ARMmbed/mbed-os.git
1750 lines
63 KiB
C
1750 lines
63 KiB
C
/*
|
|
* Copyright (c) 2013-2018, 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:
|
|
#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(neigh->ip_address, 128, cur_interface->id, NULL, ROUTE_ARO, neigh->lifetime - 2, 0);
|
|
|
|
/* 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 && !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(neigh->ip_address, 128, cur_interface->id, NULL, ROUTE_ARO, 4, 0);
|
|
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, NULL,
|
|
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? */
|
|
|
|
/* 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;
|
|
#endif
|
|
}
|
|
#endif // HAVE_6LOWPAN_ND
|
|
|