contiki/core/net/rpl/rpl-dag.c
Joakim Eriksson 946be77248 Added support for end-to-end DAO ACK for Contiki RPL.
This is a fix for Contiki RPL so that it fully supports DAO ACK in
an end-to-end fashion. When DAO is sent it will be forwarded upwards
as before. DAO ACK will be forwarded downwards until it reach the node
that initiated the DAO ACK and unlike before it is not a single-hop
DAO ACK but it is fully reaching the RPL ROOT before any DAO ACK is
sent back. DAO ACK also now fully support different status messages
(success / fail).
2016-03-26 09:28:08 +01:00

1431 lines
44 KiB
C

/*
* Copyright (c) 2010, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* Logic for Directed Acyclic Graphs in RPL.
*
* \author Joakim Eriksson <joakime@sics.se>, Nicolas Tsiftes <nvt@sics.se>
* Contributors: George Oikonomou <oikonomou@users.sourceforge.net> (multicast)
*/
/**
* \addtogroup uip6
* @{
*/
#include "contiki.h"
#include "net/rpl/rpl-private.h"
#include "net/ip/uip.h"
#include "net/ipv6/uip-nd6.h"
#include "net/ipv6/uip-ds6-nbr.h"
#include "net/nbr-table.h"
#include "net/ipv6/multicast/uip-mcast6.h"
#include "lib/list.h"
#include "lib/memb.h"
#include "sys/ctimer.h"
#include <limits.h>
#include <string.h>
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
/* A configurable function called after every RPL parent switch */
#ifdef RPL_CALLBACK_PARENT_SWITCH
void RPL_CALLBACK_PARENT_SWITCH(rpl_parent_t *old, rpl_parent_t *new);
#endif /* RPL_CALLBACK_PARENT_SWITCH */
/*---------------------------------------------------------------------------*/
extern rpl_of_t RPL_OF;
static rpl_of_t * const objective_functions[] = {&RPL_OF};
/*---------------------------------------------------------------------------*/
/* RPL definitions. */
#ifndef RPL_CONF_GROUNDED
#define RPL_GROUNDED 0
#else
#define RPL_GROUNDED RPL_CONF_GROUNDED
#endif /* !RPL_CONF_GROUNDED */
/*---------------------------------------------------------------------------*/
/* Per-parent RPL information */
NBR_TABLE_GLOBAL(rpl_parent_t, rpl_parents);
/*---------------------------------------------------------------------------*/
/* Allocate instance table. */
rpl_instance_t instance_table[RPL_MAX_INSTANCES];
rpl_instance_t *default_instance;
/*---------------------------------------------------------------------------*/
void
rpl_print_neighbor_list(void)
{
if(default_instance != NULL && default_instance->current_dag != NULL &&
default_instance->of != NULL && default_instance->of->calculate_rank != NULL) {
int curr_dio_interval = default_instance->dio_intcurrent;
int curr_rank = default_instance->current_dag->rank;
rpl_parent_t *p = nbr_table_head(rpl_parents);
clock_time_t now = clock_time();
printf("RPL: rank %u dioint %u, %u nbr(s)\n", curr_rank, curr_dio_interval, uip_ds6_nbr_num());
while(p != NULL) {
uip_ds6_nbr_t *nbr = rpl_get_nbr(p);
printf("RPL: nbr %3u %5u, %5u => %5u %c%c (last tx %u min ago)\n",
nbr_table_get_lladdr(rpl_parents, p)->u8[7],
p->rank, nbr ? nbr->link_metric : 0,
default_instance->of->calculate_rank(p, 0),
default_instance->current_dag == p->dag ? 'd' : ' ',
p == default_instance->current_dag->preferred_parent ? '*' : ' ',
(unsigned)((now - p->last_tx_time) / (60 * CLOCK_SECOND)));
p = nbr_table_next(rpl_parents, p);
}
printf("RPL: end of list\n");
}
}
/*---------------------------------------------------------------------------*/
uip_ds6_nbr_t *
rpl_get_nbr(rpl_parent_t *parent)
{
linkaddr_t *lladdr = NULL;
lladdr = nbr_table_get_lladdr(rpl_parents, parent);
if(lladdr != NULL) {
return nbr_table_get_from_lladdr(ds6_neighbors, lladdr);
} else {
return NULL;
}
}
/*---------------------------------------------------------------------------*/
static void
nbr_callback(void *ptr)
{
rpl_remove_parent(ptr);
}
void
rpl_dag_init(void)
{
nbr_table_register(rpl_parents, (nbr_table_callback *)nbr_callback);
}
/*---------------------------------------------------------------------------*/
rpl_parent_t *
rpl_get_parent(uip_lladdr_t *addr)
{
rpl_parent_t *p = nbr_table_get_from_lladdr(rpl_parents, (linkaddr_t *)addr);
return p;
}
/*---------------------------------------------------------------------------*/
rpl_rank_t
rpl_get_parent_rank(uip_lladdr_t *addr)
{
rpl_parent_t *p = nbr_table_get_from_lladdr(rpl_parents, (linkaddr_t *)addr);
if(p != NULL) {
return p->rank;
} else {
return 0;
}
}
/*---------------------------------------------------------------------------*/
uint16_t
rpl_get_parent_link_metric(const uip_lladdr_t *addr)
{
uip_ds6_nbr_t *nbr;
nbr = nbr_table_get_from_lladdr(ds6_neighbors, (const linkaddr_t *)addr);
if(nbr != NULL) {
return nbr->link_metric;
} else {
return 0;
}
}
/*---------------------------------------------------------------------------*/
uip_ipaddr_t *
rpl_get_parent_ipaddr(rpl_parent_t *p)
{
linkaddr_t *lladdr = nbr_table_get_lladdr(rpl_parents, p);
return uip_ds6_nbr_ipaddr_from_lladdr((uip_lladdr_t *)lladdr);
}
/*---------------------------------------------------------------------------*/
static void
rpl_set_preferred_parent(rpl_dag_t *dag, rpl_parent_t *p)
{
if(dag != NULL && dag->preferred_parent != p) {
PRINTF("RPL: rpl_set_preferred_parent ");
if(p != NULL) {
PRINT6ADDR(rpl_get_parent_ipaddr(p));
} else {
PRINTF("NULL");
}
PRINTF(" used to be ");
if(dag->preferred_parent != NULL) {
PRINT6ADDR(rpl_get_parent_ipaddr(dag->preferred_parent));
} else {
PRINTF("NULL");
}
PRINTF("\n");
#ifdef RPL_CALLBACK_PARENT_SWITCH
RPL_CALLBACK_PARENT_SWITCH(dag->preferred_parent, p);
#endif /* RPL_CALLBACK_PARENT_SWITCH */
/* Always keep the preferred parent locked, so it remains in the
* neighbor table. */
nbr_table_unlock(rpl_parents, dag->preferred_parent);
nbr_table_lock(rpl_parents, p);
dag->preferred_parent = p;
}
}
/*---------------------------------------------------------------------------*/
/* Greater-than function for the lollipop counter. */
/*---------------------------------------------------------------------------*/
static int
lollipop_greater_than(int a, int b)
{
/* Check if we are comparing an initial value with an old value */
if(a > RPL_LOLLIPOP_CIRCULAR_REGION && b <= RPL_LOLLIPOP_CIRCULAR_REGION) {
return (RPL_LOLLIPOP_MAX_VALUE + 1 + b - a) > RPL_LOLLIPOP_SEQUENCE_WINDOWS;
}
/* Otherwise check if a > b and comparable => ok, or
if they have wrapped and are still comparable */
return (a > b && (a - b) < RPL_LOLLIPOP_SEQUENCE_WINDOWS) ||
(a < b && (b - a) > (RPL_LOLLIPOP_CIRCULAR_REGION + 1-
RPL_LOLLIPOP_SEQUENCE_WINDOWS));
}
/*---------------------------------------------------------------------------*/
/* Remove DAG parents with a rank that is at least the same as minimum_rank. */
static void
remove_parents(rpl_dag_t *dag, rpl_rank_t minimum_rank)
{
rpl_parent_t *p;
PRINTF("RPL: Removing parents (minimum rank %u)\n",
minimum_rank);
p = nbr_table_head(rpl_parents);
while(p != NULL) {
if(dag == p->dag && p->rank >= minimum_rank) {
rpl_remove_parent(p);
}
p = nbr_table_next(rpl_parents, p);
}
}
/*---------------------------------------------------------------------------*/
static void
nullify_parents(rpl_dag_t *dag, rpl_rank_t minimum_rank)
{
rpl_parent_t *p;
PRINTF("RPL: Nullifying parents (minimum rank %u)\n",
minimum_rank);
p = nbr_table_head(rpl_parents);
while(p != NULL) {
if(dag == p->dag && p->rank >= minimum_rank) {
rpl_nullify_parent(p);
}
p = nbr_table_next(rpl_parents, p);
}
}
/*---------------------------------------------------------------------------*/
static int
should_send_dao(rpl_instance_t *instance, rpl_dio_t *dio, rpl_parent_t *p)
{
/* if MOP is set to no downward routes no DAO should be sent */
if(instance->mop == RPL_MOP_NO_DOWNWARD_ROUTES) {
return 0;
}
/* check if the new DTSN is more recent */
return p == instance->current_dag->preferred_parent &&
(lollipop_greater_than(dio->dtsn, p->dtsn));
}
/*---------------------------------------------------------------------------*/
static int
acceptable_rank(rpl_dag_t *dag, rpl_rank_t rank)
{
return rank != INFINITE_RANK &&
((dag->instance->max_rankinc == 0) ||
DAG_RANK(rank, dag->instance) <= DAG_RANK(dag->min_rank + dag->instance->max_rankinc, dag->instance));
}
/*---------------------------------------------------------------------------*/
static rpl_dag_t *
get_dag(uint8_t instance_id, uip_ipaddr_t *dag_id)
{
rpl_instance_t *instance;
rpl_dag_t *dag;
int i;
instance = rpl_get_instance(instance_id);
if(instance == NULL) {
return NULL;
}
for(i = 0; i < RPL_MAX_DAG_PER_INSTANCE; ++i) {
dag = &instance->dag_table[i];
if(dag->used && uip_ipaddr_cmp(&dag->dag_id, dag_id)) {
return dag;
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
rpl_dag_t *
rpl_set_root(uint8_t instance_id, uip_ipaddr_t *dag_id)
{
rpl_dag_t *dag;
rpl_instance_t *instance;
uint8_t version;
int i;
version = RPL_LOLLIPOP_INIT;
instance = rpl_get_instance(instance_id);
if(instance != NULL) {
for(i = 0; i < RPL_MAX_DAG_PER_INSTANCE; ++i) {
dag = &instance->dag_table[i];
if(dag->used) {
if(uip_ipaddr_cmp(&dag->dag_id, dag_id)) {
version = dag->version;
RPL_LOLLIPOP_INCREMENT(version);
}
if(dag == dag->instance->current_dag) {
PRINTF("RPL: Dropping a joined DAG when setting this node as root");
dag->instance->current_dag = NULL;
} else {
PRINTF("RPL: Dropping a DAG when setting this node as root");
}
rpl_free_dag(dag);
}
}
}
dag = rpl_alloc_dag(instance_id, dag_id);
if(dag == NULL) {
PRINTF("RPL: Failed to allocate a DAG\n");
return NULL;
}
instance = dag->instance;
dag->version = version;
dag->joined = 1;
dag->grounded = RPL_GROUNDED;
dag->preference = RPL_PREFERENCE;
instance->mop = RPL_MOP_DEFAULT;
instance->of = &RPL_OF;
rpl_set_preferred_parent(dag, NULL);
memcpy(&dag->dag_id, dag_id, sizeof(dag->dag_id));
instance->dio_intdoubl = RPL_DIO_INTERVAL_DOUBLINGS;
instance->dio_intmin = RPL_DIO_INTERVAL_MIN;
/* The current interval must differ from the minimum interval in order to
trigger a DIO timer reset. */
instance->dio_intcurrent = RPL_DIO_INTERVAL_MIN +
RPL_DIO_INTERVAL_DOUBLINGS;
instance->dio_redundancy = RPL_DIO_REDUNDANCY;
instance->max_rankinc = RPL_MAX_RANKINC;
instance->min_hoprankinc = RPL_MIN_HOPRANKINC;
instance->default_lifetime = RPL_DEFAULT_LIFETIME;
instance->lifetime_unit = RPL_DEFAULT_LIFETIME_UNIT;
dag->rank = ROOT_RANK(instance);
if(instance->current_dag != dag && instance->current_dag != NULL) {
/* Remove routes installed by DAOs. */
rpl_remove_routes(instance->current_dag);
instance->current_dag->joined = 0;
}
instance->current_dag = dag;
instance->dtsn_out = RPL_LOLLIPOP_INIT;
instance->of->update_metric_container(instance);
default_instance = instance;
PRINTF("RPL: Node set to be a DAG root with DAG ID ");
PRINT6ADDR(&dag->dag_id);
PRINTF("\n");
ANNOTATE("#A root=%u\n", dag->dag_id.u8[sizeof(dag->dag_id) - 1]);
rpl_reset_dio_timer(instance);
return dag;
}
/*---------------------------------------------------------------------------*/
int
rpl_repair_root(uint8_t instance_id)
{
rpl_instance_t *instance;
instance = rpl_get_instance(instance_id);
if(instance == NULL ||
instance->current_dag->rank != ROOT_RANK(instance)) {
PRINTF("RPL: rpl_repair_root triggered but not root\n");
return 0;
}
RPL_STAT(rpl_stats.root_repairs++);
RPL_LOLLIPOP_INCREMENT(instance->current_dag->version);
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
PRINTF("RPL: rpl_repair_root initiating global repair with version %d\n", instance->current_dag->version);
rpl_reset_dio_timer(instance);
return 1;
}
/*---------------------------------------------------------------------------*/
static void
set_ip_from_prefix(uip_ipaddr_t *ipaddr, rpl_prefix_t *prefix)
{
memset(ipaddr, 0, sizeof(uip_ipaddr_t));
memcpy(ipaddr, &prefix->prefix, (prefix->length + 7) / 8);
uip_ds6_set_addr_iid(ipaddr, &uip_lladdr);
}
/*---------------------------------------------------------------------------*/
static void
check_prefix(rpl_prefix_t *last_prefix, rpl_prefix_t *new_prefix)
{
uip_ipaddr_t ipaddr;
uip_ds6_addr_t *rep;
if(last_prefix != NULL && new_prefix != NULL &&
last_prefix->length == new_prefix->length &&
uip_ipaddr_prefixcmp(&last_prefix->prefix, &new_prefix->prefix, new_prefix->length) &&
last_prefix->flags == new_prefix->flags) {
/* Nothing has changed. */
return;
}
if(last_prefix != NULL) {
set_ip_from_prefix(&ipaddr, last_prefix);
rep = uip_ds6_addr_lookup(&ipaddr);
if(rep != NULL) {
PRINTF("RPL: removing global IP address ");
PRINT6ADDR(&ipaddr);
PRINTF("\n");
uip_ds6_addr_rm(rep);
}
}
if(new_prefix != NULL) {
set_ip_from_prefix(&ipaddr, new_prefix);
if(uip_ds6_addr_lookup(&ipaddr) == NULL) {
PRINTF("RPL: adding global IP address ");
PRINT6ADDR(&ipaddr);
PRINTF("\n");
uip_ds6_addr_add(&ipaddr, 0, ADDR_AUTOCONF);
}
}
}
/*---------------------------------------------------------------------------*/
int
rpl_set_prefix(rpl_dag_t *dag, uip_ipaddr_t *prefix, unsigned len)
{
rpl_prefix_t last_prefix;
uint8_t last_len = dag->prefix_info.length;
if(len > 128) {
return 0;
}
if(dag->prefix_info.length != 0) {
memcpy(&last_prefix, &dag->prefix_info, sizeof(rpl_prefix_t));
}
memset(&dag->prefix_info.prefix, 0, sizeof(dag->prefix_info.prefix));
memcpy(&dag->prefix_info.prefix, prefix, (len + 7) / 8);
dag->prefix_info.length = len;
dag->prefix_info.flags = UIP_ND6_RA_FLAG_AUTONOMOUS;
PRINTF("RPL: Prefix set - will announce this in DIOs\n");
/* Autoconfigure an address if this node does not already have an address
with this prefix. Otherwise, update the prefix */
if(last_len == 0) {
PRINTF("rpl_set_prefix - prefix NULL\n");
check_prefix(NULL, &dag->prefix_info);
} else {
PRINTF("rpl_set_prefix - prefix NON-NULL\n");
check_prefix(&last_prefix, &dag->prefix_info);
}
return 1;
}
/*---------------------------------------------------------------------------*/
int
rpl_set_default_route(rpl_instance_t *instance, uip_ipaddr_t *from)
{
if(instance->def_route != NULL) {
PRINTF("RPL: Removing default route through ");
PRINT6ADDR(&instance->def_route->ipaddr);
PRINTF("\n");
uip_ds6_defrt_rm(instance->def_route);
instance->def_route = NULL;
}
if(from != NULL) {
PRINTF("RPL: Adding default route through ");
PRINT6ADDR(from);
PRINTF("\n");
instance->def_route = uip_ds6_defrt_add(from,
RPL_DEFAULT_ROUTE_INFINITE_LIFETIME ? 0 : RPL_LIFETIME(instance, instance->default_lifetime));
if(instance->def_route == NULL) {
return 0;
}
} else {
PRINTF("RPL: Removing default route\n");
if(instance->def_route != NULL) {
uip_ds6_defrt_rm(instance->def_route);
} else {
PRINTF("RPL: Not actually removing default route, since instance had no default route\n");
}
}
return 1;
}
/*---------------------------------------------------------------------------*/
rpl_instance_t *
rpl_alloc_instance(uint8_t instance_id)
{
rpl_instance_t *instance, *end;
for(instance = &instance_table[0], end = instance + RPL_MAX_INSTANCES;
instance < end; ++instance) {
if(instance->used == 0) {
memset(instance, 0, sizeof(*instance));
instance->instance_id = instance_id;
instance->def_route = NULL;
instance->used = 1;
#if RPL_WITH_PROBING
rpl_schedule_probing(instance);
#endif /* RPL_WITH_PROBING */
return instance;
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
rpl_dag_t *
rpl_alloc_dag(uint8_t instance_id, uip_ipaddr_t *dag_id)
{
rpl_dag_t *dag, *end;
rpl_instance_t *instance;
instance = rpl_get_instance(instance_id);
if(instance == NULL) {
instance = rpl_alloc_instance(instance_id);
if(instance == NULL) {
RPL_STAT(rpl_stats.mem_overflows++);
return NULL;
}
}
for(dag = &instance->dag_table[0], end = dag + RPL_MAX_DAG_PER_INSTANCE; dag < end; ++dag) {
if(!dag->used) {
memset(dag, 0, sizeof(*dag));
dag->used = 1;
dag->rank = INFINITE_RANK;
dag->min_rank = INFINITE_RANK;
dag->instance = instance;
return dag;
}
}
RPL_STAT(rpl_stats.mem_overflows++);
return NULL;
}
/*---------------------------------------------------------------------------*/
void
rpl_set_default_instance(rpl_instance_t *instance)
{
default_instance = instance;
}
/*---------------------------------------------------------------------------*/
rpl_instance_t *
rpl_get_default_instance(void)
{
return default_instance;
}
/*---------------------------------------------------------------------------*/
void
rpl_free_instance(rpl_instance_t *instance)
{
rpl_dag_t *dag;
rpl_dag_t *end;
PRINTF("RPL: Leaving the instance %u\n", instance->instance_id);
/* Remove any DAG inside this instance */
for(dag = &instance->dag_table[0], end = dag + RPL_MAX_DAG_PER_INSTANCE; dag < end; ++dag) {
if(dag->used) {
rpl_free_dag(dag);
}
}
rpl_set_default_route(instance, NULL);
#if RPL_WITH_PROBING
ctimer_stop(&instance->probing_timer);
#endif /* RPL_WITH_PROBING */
ctimer_stop(&instance->dio_timer);
ctimer_stop(&instance->dao_timer);
ctimer_stop(&instance->dao_lifetime_timer);
if(default_instance == instance) {
default_instance = NULL;
}
instance->used = 0;
}
/*---------------------------------------------------------------------------*/
void
rpl_free_dag(rpl_dag_t *dag)
{
if(dag->joined) {
PRINTF("RPL: Leaving the DAG ");
PRINT6ADDR(&dag->dag_id);
PRINTF("\n");
dag->joined = 0;
/* Remove routes installed by DAOs. */
rpl_remove_routes(dag);
/* Remove autoconfigured address */
if((dag->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS)) {
check_prefix(&dag->prefix_info, NULL);
}
remove_parents(dag, 0);
}
dag->used = 0;
}
/*---------------------------------------------------------------------------*/
rpl_parent_t *
rpl_add_parent(rpl_dag_t *dag, rpl_dio_t *dio, uip_ipaddr_t *addr)
{
rpl_parent_t *p = NULL;
/* Is the parent known by ds6? Drop this request if not.
* Typically, the parent is added upon receiving a DIO. */
const uip_lladdr_t *lladdr = uip_ds6_nbr_lladdr_from_ipaddr(addr);
PRINTF("RPL: rpl_add_parent lladdr %p ", lladdr);
PRINT6ADDR(addr);
PRINTF("\n");
if(lladdr != NULL) {
/* Add parent in rpl_parents */
p = nbr_table_add_lladdr(rpl_parents, (linkaddr_t *)lladdr);
if(p == NULL) {
PRINTF("RPL: rpl_add_parent p NULL\n");
} else {
uip_ds6_nbr_t *nbr;
nbr = rpl_get_nbr(p);
p->dag = dag;
p->rank = dio->rank;
p->dtsn = dio->dtsn;
/* Check whether we have a neighbor that has not gotten a link metric yet */
if(nbr != NULL && nbr->link_metric == 0) {
nbr->link_metric = RPL_INIT_LINK_METRIC * RPL_DAG_MC_ETX_DIVISOR;
}
#if RPL_DAG_MC != RPL_DAG_MC_NONE
memcpy(&p->mc, &dio->mc, sizeof(p->mc));
#endif /* RPL_DAG_MC != RPL_DAG_MC_NONE */
}
}
return p;
}
/*---------------------------------------------------------------------------*/
static rpl_parent_t *
find_parent_any_dag_any_instance(uip_ipaddr_t *addr)
{
uip_ds6_nbr_t *ds6_nbr = uip_ds6_nbr_lookup(addr);
const uip_lladdr_t *lladdr = uip_ds6_nbr_get_ll(ds6_nbr);
return nbr_table_get_from_lladdr(rpl_parents, (linkaddr_t *)lladdr);
}
/*---------------------------------------------------------------------------*/
rpl_parent_t *
rpl_find_parent(rpl_dag_t *dag, uip_ipaddr_t *addr)
{
rpl_parent_t *p = find_parent_any_dag_any_instance(addr);
if(p != NULL && p->dag == dag) {
return p;
} else {
return NULL;
}
}
/*---------------------------------------------------------------------------*/
static rpl_dag_t *
find_parent_dag(rpl_instance_t *instance, uip_ipaddr_t *addr)
{
rpl_parent_t *p = find_parent_any_dag_any_instance(addr);
if(p != NULL) {
return p->dag;
} else {
return NULL;
}
}
/*---------------------------------------------------------------------------*/
rpl_parent_t *
rpl_find_parent_any_dag(rpl_instance_t *instance, uip_ipaddr_t *addr)
{
rpl_parent_t *p = find_parent_any_dag_any_instance(addr);
if(p && p->dag && p->dag->instance == instance) {
return p;
} else {
return NULL;
}
}
/*---------------------------------------------------------------------------*/
rpl_dag_t *
rpl_select_dag(rpl_instance_t *instance, rpl_parent_t *p)
{
rpl_parent_t *last_parent;
rpl_dag_t *dag, *end, *best_dag;
rpl_rank_t old_rank;
old_rank = instance->current_dag->rank;
last_parent = instance->current_dag->preferred_parent;
best_dag = instance->current_dag;
if(best_dag->rank != ROOT_RANK(instance)) {
if(rpl_select_parent(p->dag) != NULL) {
if(p->dag != best_dag) {
best_dag = instance->of->best_dag(best_dag, p->dag);
}
} else if(p->dag == best_dag) {
best_dag = NULL;
for(dag = &instance->dag_table[0], end = dag + RPL_MAX_DAG_PER_INSTANCE; dag < end; ++dag) {
if(dag->used && dag->preferred_parent != NULL && dag->preferred_parent->rank != INFINITE_RANK) {
if(best_dag == NULL) {
best_dag = dag;
} else {
best_dag = instance->of->best_dag(best_dag, dag);
}
}
}
}
}
if(best_dag == NULL) {
/* No parent found: the calling function handle this problem. */
return NULL;
}
if(instance->current_dag != best_dag) {
/* Remove routes installed by DAOs. */
rpl_remove_routes(instance->current_dag);
PRINTF("RPL: New preferred DAG: ");
PRINT6ADDR(&best_dag->dag_id);
PRINTF("\n");
if(best_dag->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS) {
check_prefix(&instance->current_dag->prefix_info, &best_dag->prefix_info);
} else if(instance->current_dag->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS) {
check_prefix(&instance->current_dag->prefix_info, NULL);
}
best_dag->joined = 1;
instance->current_dag->joined = 0;
instance->current_dag = best_dag;
}
instance->of->update_metric_container(instance);
/* Update the DAG rank. */
best_dag->rank = instance->of->calculate_rank(best_dag->preferred_parent, 0);
if(last_parent == NULL || best_dag->rank < best_dag->min_rank) {
best_dag->min_rank = best_dag->rank;
} else if(!acceptable_rank(best_dag, best_dag->rank)) {
PRINTF("RPL: New rank unacceptable!\n");
rpl_set_preferred_parent(instance->current_dag, NULL);
if(instance->mop != RPL_MOP_NO_DOWNWARD_ROUTES && last_parent != NULL) {
/* Send a No-Path DAO to the removed preferred parent. */
dao_output(last_parent, RPL_ZERO_LIFETIME);
}
return NULL;
}
if(best_dag->preferred_parent != last_parent) {
rpl_set_default_route(instance, rpl_get_parent_ipaddr(best_dag->preferred_parent));
PRINTF("RPL: Changed preferred parent, rank changed from %u to %u\n",
(unsigned)old_rank, best_dag->rank);
RPL_STAT(rpl_stats.parent_switch++);
if(instance->mop != RPL_MOP_NO_DOWNWARD_ROUTES) {
if(last_parent != NULL) {
/* Send a No-Path DAO to the removed preferred parent. */
dao_output(last_parent, RPL_ZERO_LIFETIME);
}
/* The DAO parent set changed - schedule a DAO transmission. */
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
rpl_schedule_dao(instance);
}
rpl_reset_dio_timer(instance);
#if DEBUG
rpl_print_neighbor_list();
#endif
} else if(best_dag->rank != old_rank) {
PRINTF("RPL: Preferred parent update, rank changed from %u to %u\n",
(unsigned)old_rank, best_dag->rank);
}
return best_dag;
}
/*---------------------------------------------------------------------------*/
static rpl_parent_t *
best_parent(rpl_dag_t *dag)
{
rpl_parent_t *p, *best;
best = NULL;
p = nbr_table_head(rpl_parents);
while(p != NULL) {
if(p->dag != dag || p->rank == INFINITE_RANK) {
/* ignore this neighbor */
} else if(best == NULL) {
best = p;
} else {
best = dag->instance->of->best_parent(best, p);
}
p = nbr_table_next(rpl_parents, p);
}
return best;
}
/*---------------------------------------------------------------------------*/
rpl_parent_t *
rpl_select_parent(rpl_dag_t *dag)
{
rpl_parent_t *best = best_parent(dag);
if(best != NULL) {
rpl_set_preferred_parent(dag, best);
dag->rank = dag->instance->of->calculate_rank(dag->preferred_parent, 0);
} else {
dag->rank = INFINITE_RANK;
}
return best;
}
/*---------------------------------------------------------------------------*/
void
rpl_remove_parent(rpl_parent_t *parent)
{
PRINTF("RPL: Removing parent ");
PRINT6ADDR(rpl_get_parent_ipaddr(parent));
PRINTF("\n");
rpl_nullify_parent(parent);
nbr_table_remove(rpl_parents, parent);
}
/*---------------------------------------------------------------------------*/
void
rpl_nullify_parent(rpl_parent_t *parent)
{
rpl_dag_t *dag = parent->dag;
/* This function can be called when the preferred parent is NULL, so we
need to handle this condition in order to trigger uip_ds6_defrt_rm. */
if(parent == dag->preferred_parent || dag->preferred_parent == NULL) {
dag->rank = INFINITE_RANK;
if(dag->joined) {
if(dag->instance->def_route != NULL) {
PRINTF("RPL: Removing default route ");
PRINT6ADDR(rpl_get_parent_ipaddr(parent));
PRINTF("\n");
uip_ds6_defrt_rm(dag->instance->def_route);
dag->instance->def_route = NULL;
}
/* Send No-Path DAO only to preferred parent, if any */
if(parent == dag->preferred_parent) {
dao_output(parent, RPL_ZERO_LIFETIME);
rpl_set_preferred_parent(dag, NULL);
}
}
}
PRINTF("RPL: Nullifying parent ");
PRINT6ADDR(rpl_get_parent_ipaddr(parent));
PRINTF("\n");
}
/*---------------------------------------------------------------------------*/
void
rpl_move_parent(rpl_dag_t *dag_src, rpl_dag_t *dag_dst, rpl_parent_t *parent)
{
if(parent == dag_src->preferred_parent) {
rpl_set_preferred_parent(dag_src, NULL);
dag_src->rank = INFINITE_RANK;
if(dag_src->joined && dag_src->instance->def_route != NULL) {
PRINTF("RPL: Removing default route ");
PRINT6ADDR(rpl_get_parent_ipaddr(parent));
PRINTF("\n");
PRINTF("rpl_move_parent\n");
uip_ds6_defrt_rm(dag_src->instance->def_route);
dag_src->instance->def_route = NULL;
}
} else if(dag_src->joined) {
/* Remove uIPv6 routes that have this parent as the next hop. */
rpl_remove_routes_by_nexthop(rpl_get_parent_ipaddr(parent), dag_src);
}
PRINTF("RPL: Moving parent ");
PRINT6ADDR(rpl_get_parent_ipaddr(parent));
PRINTF("\n");
parent->dag = dag_dst;
}
/*---------------------------------------------------------------------------*/
rpl_dag_t *
rpl_get_any_dag(void)
{
int i;
for(i = 0; i < RPL_MAX_INSTANCES; ++i) {
if(instance_table[i].used && instance_table[i].current_dag->joined) {
return instance_table[i].current_dag;
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
rpl_instance_t *
rpl_get_instance(uint8_t instance_id)
{
int i;
for(i = 0; i < RPL_MAX_INSTANCES; ++i) {
if(instance_table[i].used && instance_table[i].instance_id == instance_id) {
return &instance_table[i];
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
rpl_of_t *
rpl_find_of(rpl_ocp_t ocp)
{
unsigned int i;
for(i = 0;
i < sizeof(objective_functions) / sizeof(objective_functions[0]);
i++) {
if(objective_functions[i]->ocp == ocp) {
return objective_functions[i];
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
void
rpl_join_instance(uip_ipaddr_t *from, rpl_dio_t *dio)
{
rpl_instance_t *instance;
rpl_dag_t *dag;
rpl_parent_t *p;
rpl_of_t *of;
/* Determine the objective function by using the
objective code point of the DIO. */
of = rpl_find_of(dio->ocp);
if(of == NULL) {
PRINTF("RPL: DIO for DAG instance %u does not specify a supported OF: %u\n",
dio->instance_id, dio->ocp);
return;
}
dag = rpl_alloc_dag(dio->instance_id, &dio->dag_id);
if(dag == NULL) {
PRINTF("RPL: Failed to allocate a DAG object!\n");
return;
}
instance = dag->instance;
p = rpl_add_parent(dag, dio, from);
PRINTF("RPL: Adding ");
PRINT6ADDR(from);
PRINTF(" as a parent: ");
if(p == NULL) {
PRINTF("failed\n");
instance->used = 0;
return;
}
p->dtsn = dio->dtsn;
PRINTF("succeeded\n");
/* Autoconfigure an address if this node does not already have an address
with this prefix. */
if(dio->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS) {
check_prefix(NULL, &dio->prefix_info);
}
dag->joined = 1;
dag->preference = dio->preference;
dag->grounded = dio->grounded;
dag->version = dio->version;
instance->of = of;
instance->mop = dio->mop;
instance->current_dag = dag;
instance->dtsn_out = RPL_LOLLIPOP_INIT;
instance->max_rankinc = dio->dag_max_rankinc;
instance->min_hoprankinc = dio->dag_min_hoprankinc;
instance->dio_intdoubl = dio->dag_intdoubl;
instance->dio_intmin = dio->dag_intmin;
instance->dio_intcurrent = instance->dio_intmin + instance->dio_intdoubl;
instance->dio_redundancy = dio->dag_redund;
instance->default_lifetime = dio->default_lifetime;
instance->lifetime_unit = dio->lifetime_unit;
memcpy(&dag->dag_id, &dio->dag_id, sizeof(dio->dag_id));
/* Copy prefix information from the DIO into the DAG object. */
memcpy(&dag->prefix_info, &dio->prefix_info, sizeof(rpl_prefix_t));
rpl_set_preferred_parent(dag, p);
instance->of->update_metric_container(instance);
dag->rank = instance->of->calculate_rank(p, 0);
/* So far this is the lowest rank we are aware of. */
dag->min_rank = dag->rank;
if(default_instance == NULL) {
default_instance = instance;
}
PRINTF("RPL: Joined DAG with instance ID %u, rank %hu, DAG ID ",
dio->instance_id, dag->rank);
PRINT6ADDR(&dag->dag_id);
PRINTF("\n");
ANNOTATE("#A join=%u\n", dag->dag_id.u8[sizeof(dag->dag_id) - 1]);
rpl_reset_dio_timer(instance);
rpl_set_default_route(instance, from);
if(instance->mop != RPL_MOP_NO_DOWNWARD_ROUTES) {
rpl_schedule_dao_immediately(instance);
} else {
PRINTF("RPL: The DIO does not meet the prerequisites for sending a DAO\n");
}
}
#if RPL_MAX_DAG_PER_INSTANCE > 1
/*---------------------------------------------------------------------------*/
rpl_dag_t *
rpl_add_dag(uip_ipaddr_t *from, rpl_dio_t *dio)
{
rpl_instance_t *instance;
rpl_dag_t *dag, *previous_dag;
rpl_parent_t *p;
rpl_of_t *of;
dag = rpl_alloc_dag(dio->instance_id, &dio->dag_id);
if(dag == NULL) {
PRINTF("RPL: Failed to allocate a DAG object!\n");
return NULL;
}
instance = dag->instance;
previous_dag = find_parent_dag(instance, from);
if(previous_dag == NULL) {
PRINTF("RPL: Adding ");
PRINT6ADDR(from);
PRINTF(" as a parent: ");
p = rpl_add_parent(dag, dio, from);
if(p == NULL) {
PRINTF("failed\n");
dag->used = 0;
return NULL;
}
PRINTF("succeeded\n");
} else {
p = rpl_find_parent(previous_dag, from);
if(p != NULL) {
rpl_move_parent(previous_dag, dag, p);
}
}
p->rank = dio->rank;
/* Determine the objective function by using the
objective code point of the DIO. */
of = rpl_find_of(dio->ocp);
if(of != instance->of ||
instance->mop != dio->mop ||
instance->max_rankinc != dio->dag_max_rankinc ||
instance->min_hoprankinc != dio->dag_min_hoprankinc ||
instance->dio_intdoubl != dio->dag_intdoubl ||
instance->dio_intmin != dio->dag_intmin ||
instance->dio_redundancy != dio->dag_redund ||
instance->default_lifetime != dio->default_lifetime ||
instance->lifetime_unit != dio->lifetime_unit) {
PRINTF("RPL: DIO for DAG instance %u incompatible with previous DIO\n",
dio->instance_id);
rpl_remove_parent(p);
dag->used = 0;
return NULL;
}
dag->used = 1;
dag->grounded = dio->grounded;
dag->preference = dio->preference;
dag->version = dio->version;
memcpy(&dag->dag_id, &dio->dag_id, sizeof(dio->dag_id));
/* copy prefix information into the dag */
memcpy(&dag->prefix_info, &dio->prefix_info, sizeof(rpl_prefix_t));
rpl_set_preferred_parent(dag, p);
dag->rank = instance->of->calculate_rank(p, 0);
dag->min_rank = dag->rank; /* So far this is the lowest rank we know of. */
PRINTF("RPL: Joined DAG with instance ID %u, rank %hu, DAG ID ",
dio->instance_id, dag->rank);
PRINT6ADDR(&dag->dag_id);
PRINTF("\n");
ANNOTATE("#A join=%u\n", dag->dag_id.u8[sizeof(dag->dag_id) - 1]);
rpl_process_parent_event(instance, p);
p->dtsn = dio->dtsn;
return dag;
}
#endif /* RPL_MAX_DAG_PER_INSTANCE > 1 */
/*---------------------------------------------------------------------------*/
static void
global_repair(uip_ipaddr_t *from, rpl_dag_t *dag, rpl_dio_t *dio)
{
rpl_parent_t *p;
remove_parents(dag, 0);
dag->version = dio->version;
/* copy parts of the configuration so that it propagates in the network */
dag->instance->dio_intdoubl = dio->dag_intdoubl;
dag->instance->dio_intmin = dio->dag_intmin;
dag->instance->dio_redundancy = dio->dag_redund;
dag->instance->default_lifetime = dio->default_lifetime;
dag->instance->lifetime_unit = dio->lifetime_unit;
dag->instance->of->reset(dag);
dag->min_rank = INFINITE_RANK;
RPL_LOLLIPOP_INCREMENT(dag->instance->dtsn_out);
p = rpl_add_parent(dag, dio, from);
if(p == NULL) {
PRINTF("RPL: Failed to add a parent during the global repair\n");
dag->rank = INFINITE_RANK;
} else {
dag->rank = dag->instance->of->calculate_rank(p, 0);
dag->min_rank = dag->rank;
PRINTF("RPL: rpl_process_parent_event global repair\n");
rpl_process_parent_event(dag->instance, p);
}
PRINTF("RPL: Participating in a global repair (version=%u, rank=%hu)\n",
dag->version, dag->rank);
RPL_STAT(rpl_stats.global_repairs++);
}
/*---------------------------------------------------------------------------*/
void
rpl_local_repair(rpl_instance_t *instance)
{
int i;
if(instance == NULL) {
PRINTF("RPL: local repair requested for instance NULL\n");
return;
}
PRINTF("RPL: Starting a local instance repair\n");
for(i = 0; i < RPL_MAX_DAG_PER_INSTANCE; i++) {
if(instance->dag_table[i].used) {
instance->dag_table[i].rank = INFINITE_RANK;
nullify_parents(&instance->dag_table[i], 0);
}
}
rpl_reset_dio_timer(instance);
RPL_STAT(rpl_stats.local_repairs++);
}
/*---------------------------------------------------------------------------*/
void
rpl_recalculate_ranks(void)
{
rpl_parent_t *p;
/*
* We recalculate ranks when we receive feedback from the system rather
* than RPL protocol messages. This periodical recalculation is called
* from a timer in order to keep the stack depth reasonably low.
*/
p = nbr_table_head(rpl_parents);
while(p != NULL) {
if(p->dag != NULL && p->dag->instance && (p->flags & RPL_PARENT_FLAG_UPDATED)) {
p->flags &= ~RPL_PARENT_FLAG_UPDATED;
PRINTF("RPL: rpl_process_parent_event recalculate_ranks\n");
if(!rpl_process_parent_event(p->dag->instance, p)) {
PRINTF("RPL: A parent was dropped\n");
}
}
p = nbr_table_next(rpl_parents, p);
}
}
/*---------------------------------------------------------------------------*/
int
rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p)
{
int return_value;
#if DEBUG
rpl_rank_t old_rank;
old_rank = instance->current_dag->rank;
#endif /* DEBUG */
return_value = 1;
if(!acceptable_rank(p->dag, p->rank)) {
/* The candidate parent is no longer valid: the rank increase resulting
from the choice of it as a parent would be too high. */
PRINTF("RPL: Unacceptable rank %u\n", (unsigned)p->rank);
rpl_nullify_parent(p);
if(p != instance->current_dag->preferred_parent) {
return 0;
} else {
return_value = 0;
}
}
if(rpl_select_dag(instance, p) == NULL) {
/* No suitable parent; trigger a local repair. */
PRINTF("RPL: No parents found in any DAG\n");
rpl_local_repair(instance);
return 0;
}
#if DEBUG
if(DAG_RANK(old_rank, instance) != DAG_RANK(instance->current_dag->rank, instance)) {
PRINTF("RPL: Moving in the instance from rank %hu to %hu\n",
DAG_RANK(old_rank, instance), DAG_RANK(instance->current_dag->rank, instance));
if(instance->current_dag->rank != INFINITE_RANK) {
PRINTF("RPL: The preferred parent is ");
PRINT6ADDR(rpl_get_parent_ipaddr(instance->current_dag->preferred_parent));
PRINTF(" (rank %u)\n",
(unsigned)DAG_RANK(instance->current_dag->preferred_parent->rank, instance));
} else {
PRINTF("RPL: We don't have any parent");
}
}
#endif /* DEBUG */
return return_value;
}
/*---------------------------------------------------------------------------*/
void
rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
{
rpl_instance_t *instance;
rpl_dag_t *dag, *previous_dag;
rpl_parent_t *p;
#if RPL_CONF_MULTICAST
/* If the root is advertising MOP 2 but we support MOP 3 we can still join
* In that scenario, we suppress DAOs for multicast targets */
if(dio->mop < RPL_MOP_STORING_NO_MULTICAST) {
#else
if(dio->mop != RPL_MOP_DEFAULT) {
#endif
PRINTF("RPL: Ignoring a DIO with an unsupported MOP: %d\n", dio->mop);
return;
}
dag = get_dag(dio->instance_id, &dio->dag_id);
instance = rpl_get_instance(dio->instance_id);
if(dag != NULL && instance != NULL) {
if(lollipop_greater_than(dio->version, dag->version)) {
if(dag->rank == ROOT_RANK(instance)) {
PRINTF("RPL: Root received inconsistent DIO version number (current: %u, received: %u)\n", dag->version, dio->version);
dag->version = dio->version;
RPL_LOLLIPOP_INCREMENT(dag->version);
rpl_reset_dio_timer(instance);
} else {
PRINTF("RPL: Global repair\n");
if(dio->prefix_info.length != 0) {
if(dio->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS) {
PRINTF("RPL: Prefix announced in DIO\n");
rpl_set_prefix(dag, &dio->prefix_info.prefix, dio->prefix_info.length);
}
}
global_repair(from, dag, dio);
}
return;
}
if(lollipop_greater_than(dag->version, dio->version)) {
/* The DIO sender is on an older version of the DAG. */
PRINTF("RPL: old version received => inconsistency detected\n");
if(dag->joined) {
rpl_reset_dio_timer(instance);
return;
}
}
}
if(instance == NULL) {
PRINTF("RPL: New instance detected (ID=%u): Joining...\n", dio->instance_id);
rpl_join_instance(from, dio);
return;
}
if(instance->current_dag->rank == ROOT_RANK(instance) && instance->current_dag != dag) {
PRINTF("RPL: Root ignored DIO for different DAG\n");
return;
}
if(dag == NULL) {
#if RPL_MAX_DAG_PER_INSTANCE > 1
PRINTF("RPL: Adding new DAG to known instance.\n");
dag = rpl_add_dag(from, dio);
if(dag == NULL) {
PRINTF("RPL: Failed to add DAG.\n");
return;
}
#else /* RPL_MAX_DAG_PER_INSTANCE > 1 */
PRINTF("RPL: Only one instance supported.\n");
return;
#endif /* RPL_MAX_DAG_PER_INSTANCE > 1 */
}
if(dio->rank < ROOT_RANK(instance)) {
PRINTF("RPL: Ignoring DIO with too low rank: %u\n",
(unsigned)dio->rank);
return;
} else if(dio->rank == INFINITE_RANK && dag->joined) {
rpl_reset_dio_timer(instance);
}
/* Prefix Information Option treated to add new prefix */
if(dio->prefix_info.length != 0) {
if(dio->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS) {
PRINTF("RPL: Prefix announced in DIO\n");
rpl_set_prefix(dag, &dio->prefix_info.prefix, dio->prefix_info.length);
}
}
if(dag->rank == ROOT_RANK(instance)) {
if(dio->rank != INFINITE_RANK) {
instance->dio_counter++;
}
return;
}
/* The DIO comes from a valid DAG, we can refresh its lifetime */
dag->lifetime = (1UL << (instance->dio_intmin + instance->dio_intdoubl)) / 1000;
PRINTF("Set dag ");
PRINT6ADDR(&dag->dag_id);
PRINTF(" lifetime to %ld\n", dag->lifetime);
/*
* At this point, we know that this DIO pertains to a DAG that
* we are already part of. We consider the sender of the DIO to be
* a candidate parent, and let rpl_process_parent_event decide
* whether to keep it in the set.
*/
p = rpl_find_parent(dag, from);
if(p == NULL) {
previous_dag = find_parent_dag(instance, from);
if(previous_dag == NULL) {
/* Add the DIO sender as a candidate parent. */
p = rpl_add_parent(dag, dio, from);
if(p == NULL) {
PRINTF("RPL: Failed to add a new parent (");
PRINT6ADDR(from);
PRINTF(")\n");
return;
}
PRINTF("RPL: New candidate parent with rank %u: ", (unsigned)p->rank);
PRINT6ADDR(from);
PRINTF("\n");
} else {
p = rpl_find_parent(previous_dag, from);
if(p != NULL) {
rpl_move_parent(previous_dag, dag, p);
}
}
} else {
if(p->rank == dio->rank) {
PRINTF("RPL: Received consistent DIO\n");
if(dag->joined) {
instance->dio_counter++;
}
}
}
p->rank = dio->rank;
/* Parent info has been updated, trigger rank recalculation */
p->flags |= RPL_PARENT_FLAG_UPDATED;
PRINTF("RPL: preferred DAG ");
PRINT6ADDR(&instance->current_dag->dag_id);
PRINTF(", rank %u, min_rank %u, ",
instance->current_dag->rank, instance->current_dag->min_rank);
PRINTF("parent rank %u, parent etx %u, link metric %u, instance etx %u\n",
p->rank, -1/*p->mc.obj.etx*/, rpl_get_nbr(p)->link_metric, instance->mc.obj.etx);
/* We have allocated a candidate parent; process the DIO further. */
#if RPL_DAG_MC != RPL_DAG_MC_NONE
memcpy(&p->mc, &dio->mc, sizeof(p->mc));
#endif /* RPL_DAG_MC != RPL_DAG_MC_NONE */
if(rpl_process_parent_event(instance, p) == 0) {
PRINTF("RPL: The candidate parent is rejected\n");
return;
}
/* We don't use route control, so we can have only one official parent. */
if(dag->joined && p == dag->preferred_parent) {
if(should_send_dao(instance, dio, p)) {
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
rpl_schedule_dao(instance);
}
/* We received a new DIO from our preferred parent.
* Call uip_ds6_defrt_add to set a fresh value for the lifetime counter */
uip_ds6_defrt_add(from, RPL_DEFAULT_ROUTE_INFINITE_LIFETIME ? 0 : RPL_LIFETIME(instance, instance->default_lifetime));
}
p->dtsn = dio->dtsn;
}
/*---------------------------------------------------------------------------*/
/** @} */