added nbr policy for Contiki RPL that avoids thrashing the nbr table with new entries all the time

This commit is contained in:
Joakim Eriksson 2015-08-20 17:09:32 +02:00
parent d181bd9e6f
commit 3fd8c4db2d
6 changed files with 383 additions and 68 deletions

View File

@ -245,6 +245,18 @@
#define RPL_WITH_DAO_ACK 1
#endif /* RPL_CONF_WITH_DAO_ACK */
/*
* Setting the DIO_REFRESH_DAO_ROUTES will make RPL always increase
* the DTSN (Destination Advertisement Trigger Sequence Number) when
* sending broadcast DIO. This is to get all children to re-register
* their DAO route.
* */
#ifdef RPL_CONF_DIO_REFRESH_DAO_ROUTES
#define RPL_DIO_REFRESH_DAO_ROUTES RPL_CONF_DIO_REFRESH_DAO_ROUTES
#else
#define RPL_DIO_REFRESH_DAO_ROUTES 0
#endif /* RPL_CONF_DIO_REFRESH_DAO_ROUTES */
/*
* RPL probing. When enabled, probes will be sent periodically to keep
* parent link estimates up to date.

View File

@ -1174,6 +1174,8 @@ rpl_local_repair(rpl_instance_t *instance)
}
rpl_reset_dio_timer(instance);
/* Request refresh of DAO registrations next DIO */
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
RPL_STAT(rpl_stats.local_repairs++);
}
@ -1250,6 +1252,23 @@ rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p)
return return_value;
}
/*---------------------------------------------------------------------------*/
static int
add_nbr_from_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
{
/* check if it is ok to add this nbr based on this DIO */
if(RPL_NBR_POLICY.check_add_from_dio(from, dio)) {
/* add this to the neighbor cache if not already there */
if(rpl_icmp6_update_nbr_table(from) == NULL) {
PRINTF("RPL: Out of memory, dropping DIO from ");
PRINT6ADDR(from);
PRINTF("\n");
return 0;
}
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
void
rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
{
@ -1303,7 +1322,11 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
if(instance == NULL) {
PRINTF("RPL: New instance detected (ID=%u): Joining...\n", dio->instance_id);
if(add_nbr_from_dio(from, dio)) {
rpl_join_instance(from, dio);
} else {
PRINTF("RPL: Not joining since could not add parent\n");
}
return;
}
@ -1315,6 +1338,10 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
if(dag == NULL) {
#if RPL_MAX_DAG_PER_INSTANCE > 1
PRINTF("RPL: Adding new DAG to known instance.\n");
if(!add_nbr_from_dio(from, dio)) {
PRINTF("RPL: Could not add new DAG, could not add parent\n");
return;
}
dag = rpl_add_dag(from, dio);
if(dag == NULL) {
PRINTF("RPL: Failed to add DAG.\n");
@ -1363,6 +1390,11 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
* whether to keep it in the set.
*/
if(!add_nbr_from_dio(from, dio)) {
PRINTF("RPL: Could not add parent based on DIO\n");
return;
}
p = rpl_find_parent(dag, from);
if(p == NULL) {
previous_dag = find_parent_dag(instance, from);

View File

@ -88,7 +88,6 @@ void RPL_DEBUG_DIO_INPUT(uip_ipaddr_t *, rpl_dio_t *);
void RPL_DEBUG_DAO_OUTPUT(rpl_parent_t *);
#endif
/* TODO: should these variables be a part of the instance? */
static uint8_t dao_sequence = RPL_LOLLIPOP_INIT;
extern rpl_of_t RPL_OF;
@ -182,6 +181,32 @@ set16(uint8_t *buffer, int pos, uint16_t value)
buffer[pos++] = value & 0xff;
}
/*---------------------------------------------------------------------------*/
uip_ds6_nbr_t *
rpl_icmp6_update_nbr_table(uip_ipaddr_t *from)
{
uip_ds6_nbr_t *nbr;
if((nbr = uip_ds6_nbr_lookup(from)) == NULL) {
if((nbr = uip_ds6_nbr_add(from, (uip_lladdr_t *)
packetbuf_addr(PACKETBUF_ADDR_SENDER),
0, NBR_REACHABLE)) != NULL) {
PRINTF("RPL: Neighbor added to neighbor cache ");
PRINT6ADDR(from);
PRINTF(", ");
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
PRINTF("\n");
}
}
if(nbr != NULL) {
#if UIP_ND6_SEND_NA
/* set reachable timer if we added or found the nbr entry */
stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
#endif /* UIP_ND6_SEND_NA */
}
return nbr;
}
/*---------------------------------------------------------------------------*/
static void
dis_input(void)
{
@ -205,11 +230,23 @@ dis_input(void)
rpl_reset_dio_timer(instance);
} else {
#endif /* !RPL_LEAF_ONLY */
/* Check if this neighbor should be added according to the policy. */
if(RPL_NBR_POLICY.check_add_from_dis(&UIP_IP_BUF->srcipaddr)) {
/* Add this to the neighbor cache if not already there */
if(rpl_icmp6_update_nbr_table(&UIP_IP_BUF->srcipaddr) == NULL) {
PRINTF("RPL: Out of Memory, not sending unicast DIO, DIS from ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINTF(", ");
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
PRINTF("\n");
return;
}
PRINTF("RPL: Unicast DIS, reply to sender\n");
dio_output(instance, &UIP_IP_BUF->srcipaddr);
}
}
}
}
uip_clear_buf();
}
/*---------------------------------------------------------------------------*/
@ -253,7 +290,6 @@ dio_input(void)
int i;
int len;
uip_ipaddr_t from;
uip_ds6_nbr_t *nbr;
memset(&dio, 0, sizeof(dio));
@ -274,32 +310,6 @@ dio_input(void)
PRINT6ADDR(&from);
PRINTF("\n");
if((nbr = uip_ds6_nbr_lookup(&from)) == NULL) {
if((nbr = uip_ds6_nbr_add(&from, (uip_lladdr_t *)
packetbuf_addr(PACKETBUF_ADDR_SENDER),
0, NBR_REACHABLE)) != NULL) {
#if UIP_ND6_SEND_NA
/* set reachable timer */
stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
#endif /* UIP_ND6_SEND_NA */
PRINTF("RPL: Neighbor added to neighbor cache ");
PRINT6ADDR(&from);
PRINTF(", ");
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
PRINTF("\n");
} else {
PRINTF("RPL: Out of memory, dropping DIO from ");
PRINT6ADDR(&from);
PRINTF(", ");
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
PRINTF("\n");
goto discard;
}
} else {
PRINTF("RPL: Neighbor already in neighbor cache\n");
}
buffer_length = uip_len - uip_l3_icmp_hdr_len;
/* Process the DIO base option. */
@ -796,20 +806,13 @@ dao_input(void)
PRINTF("RPL: adding DAO route\n");
if((nbr = uip_ds6_nbr_lookup(&dao_sender_addr)) == NULL) {
if((nbr = uip_ds6_nbr_add(&dao_sender_addr,
(uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER),
0, NBR_REACHABLE)) != NULL) {
#if UIP_ND6_SEND_NA
/* set reachable timer */
stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
#endif /* UIP_ND6_SEND_NA */
PRINTF("RPL: Neighbor added to neighbor cache ");
PRINT6ADDR(&dao_sender_addr);
PRINTF(", ");
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
PRINTF("\n");
} else {
/* Check if we should add another neighbor based on DAO. */
if(!RPL_NBR_POLICY.check_add_from_dao(&dao_sender_addr)) {
/* Do not add the neighbor. */
return;
}
/* Update and add neighbor - if no room - fail. */
if((nbr = rpl_icmp6_update_nbr_table(&dao_sender_addr)) == NULL) {
PRINTF("RPL: Out of Memory, dropping DAO from ");
PRINT6ADDR(&dao_sender_addr);
PRINTF(", ");
@ -817,9 +820,6 @@ dao_input(void)
PRINTF("\n");
goto discard;
}
} else {
PRINTF("RPL: Neighbor already in neighbor cache\n");
}
rep = rpl_add_route(dag, &prefix, prefixlen, &dao_sender_addr);
if(rep == NULL) {
@ -834,9 +834,8 @@ dao_input(void)
goto discard;
}
/* State is all zeroes, set lifetime but no need for other initialization. */
rep->state.lifetime = RPL_LIFETIME(instance, lifetime);
/* rep->state.learned_from = learned_from; */
/* rep->state.nopath_received = 0; */
#if RPL_CONF_MULTICAST
fwd_dao:
@ -877,8 +876,6 @@ fwd_dao:
ICMP6_RPL, RPL_CODE_DAO, buffer_length);
}
/* Ack sent immediately after forwarding (otherwise the packets is
corrupted. */
if(should_ack) {
PRINTF("RPL: sending DAO ACK\n");
dao_ack_output(instance, &dao_sender_addr, sequence,
@ -897,30 +894,33 @@ handle_dao_retransmission(void *ptr)
rpl_parent_t *parent;
uip_ipaddr_t prefix;
rpl_instance_t *instance;
parent = ptr;
parent = ptr;
if(parent == NULL || parent->dag == NULL || parent->dag->instance == NULL) {
return;
}
instance = parent->dag->instance;
if(instance->my_dao_transmissions >= RPL_DAO_MAX_RETRANSMISSIONS) {
/* no more retransmissions - what now ??? */
/* No more retransmissions - give up. */
if(instance->lifetime_unit == 0xffff && instance->default_lifetime == 0xff) {
/* RPL is using infinite lifetime for routes. This probably
means that the root is running an old version that does not
support DAO ack. Assume that everything is ok for now and
let the normal repair mechanisms detect any problems. */
/*
* ContikiRPL was previously using infinite lifetime for routes
* and no DAO_ACK configured. This probably means that the root
* and possibly other nodes might be running an old version that
* does not support DAO ack. Assume that everything is ok for
* now and let the normal repair mechanisms detect any problems.
*/
return;
}
if(instance->of->dao_ack_callback) {
/* Inform about the timeout for taking decision on punishment */
/* Inform the objective function about the timeout. */
instance->of->dao_ack_callback(parent, RPL_DAO_ACK_TIMEOUT);
}
/* Perform local repair and hope to find another parent. */
rpl_local_repair(instance);
/* give up this and hope to find another parent */
return;
}
@ -1102,7 +1102,7 @@ dao_ack_input(void)
#endif /* DEBUG */
if(sequence == instance->my_dao_seqno) {
PRINTF("RPL: DAO ACK for me!\n");
PRINTF("RPL: DAO %s for me!\n", status < 128 ? "ACK" : "NACK");
/* always stop the retransmit timer when the ACK arrived */
ctimer_stop(&instance->dao_retransmit_timer);

View File

@ -0,0 +1,244 @@
/*
* Copyright (c) 2014-2015, Yanzi Networks AB.
* 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 copyright holders 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 COPYRIGHT HOLDERS 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
* COPYRIGHT HOLDERS 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.
*
*/
/**
* \addtogroup uip6
* @{
*/
/**
* \file
*
* Default RPL NBR policy
* decides when to add a new discovered node to the nbr table from RPL.
*
* \author Joakim Eriksson <joakime@sics.se>
* Contributors: Niclas Finne <nfi@sics.se>, Oriol Piñol <oriol@yanzi.se>,
*
*/
#include "net/rpl/rpl-private.h"
#include "net/nbr-table.h"
#include "net/ipv6/uip-ds6-nbr.h"
#include "net/ipv6/uip-ds6-route.h"
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
/*
* Policy for neighbor adds
* - one node is locked (default route)
* - max X children (nexthops)
* - max Y "best parents"
* => at least MAX_NBRS - (Y + X + 1) free slots for other.
*/
#define MAX_CHILDREN (NBR_TABLE_MAX_NEIGHBORS - 3)
static int num_parents; /* any node that are possible parents */
static int num_children; /* all children that we have as nexthop */
static int num_free;
static uip_ds6_nbr_t *worst_rank_nbr; /* the parent that has the worst rank */
static rpl_rank_t worst_rank;
/*---------------------------------------------------------------------------*/
static void
update_nbr(void)
{
uip_ds6_nbr_t *nbr;
rpl_parent_t *parent;
int num_used;
int is_used;
rpl_rank_t rank;
worst_rank = 0;
worst_rank_nbr = NULL;
num_used = 0;
num_parents = 0;
num_children = 0;
nbr = nbr_table_head(ds6_neighbors);
while(nbr != NULL) {
linkaddr_t *lladdr = nbr_table_get_lladdr(ds6_neighbors, nbr);
is_used = 0;
parent = rpl_get_parent((uip_lladdr_t *)lladdr);
if(parent != NULL) {
num_parents++;
is_used++;
if(parent->dag != NULL && parent->dag->preferred_parent == parent) {
/* This is the preferred parent for the DAG and must not be removed */
/* Note: this assumes that only RPL adds default routes. */
} else if(worst_rank < INFINITE_RANK &&
parent->rank > 0 &&
parent->dag != NULL &&
parent->dag->instance != NULL &&
(rank = parent->dag->instance->of->calculate_rank(parent, 0)) > worst_rank) {
/* This is the worst-rank neighbor - this is a good candidate for removal */
worst_rank = rank;
worst_rank_nbr = nbr;
}
}
/* Check if this neighbor is used as nexthop and therefor being a
RPL child. */
if(uip_ds6_route_is_nexthop((uip_lladdr_t *)lladdr) != 0) {
is_used++;
num_children++;
}
if(is_used == 0) {
/* This neighbor is neither parent or child and can be safely removed */
worst_rank_nbr = nbr;
worst_rank = INFINITE_RANK;
} else if(is_used > 1) {
/* Both parent and child - this should never happen! */
PRINTF("NBR-POLICY: *** Neighbor is both child and candidate parent: ");
PRINTLLADDR((uip_lladdr_t *)lladdr);
PRINTF("\n");
}
nbr = nbr_table_next(ds6_neighbors, nbr);
num_used++;
}
/* how many more IP neighbors can be have? */
num_free = NBR_TABLE_MAX_NEIGHBORS - num_used;
PRINTF("NBR-POLICY: Free: %d, Children: %d, Parents: %d Routes: %d\n",
num_free, num_children, num_parents, uip_ds6_route_num_routes());
}
/*---------------------------------------------------------------------------*/
static int
remove_worst_nbr(void)
{
/* we assume that it is possible to remove the worst parent at the moment */
if(worst_rank_nbr != NULL) {
PRINTF("Removing worst ranked nbr ");
PRINTLLADDR((uip_lladdr_t*)nbr_table_get_lladdr(ds6_neighbors, worst_rank_nbr));
PRINTF(" with rank %d\n", worst_rank);
if(uip_ds6_nbr_rm(worst_rank_nbr)) {
worst_rank_nbr = NULL;
return 1;
}
PRINTF("FAILED to remove worst ranked nbr!\n");
return 0;
}
PRINTF("FAILED to remove worst rank nbr - no found\n");
return 0;
}
/*---------------------------------------------------------------------------*/
/* Called whenever we get a unicast DIS - e.g. someone that already
have this node in its table - since it is a unicast */
static int
check_add_from_dis(uip_ipaddr_t *from)
{
/* do a lookup to see if it is alread there - then allow add/update */
if(uip_ds6_nbr_lookup(from)) {
return 1;
}
update_nbr();
if(num_free > 0) {
return 1;
}
if(num_children < MAX_CHILDREN) {
return remove_worst_nbr();
}
return 0;
}
/*---------------------------------------------------------------------------*/
static int
check_add_from_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
{
rpl_instance_t *instance;
rpl_rank_t rank;
/* Do a lookup to see if it is already there - then allow add/update. */
if(uip_ds6_nbr_lookup(from)) {
return 1;
}
update_nbr();
/* If there is room for this neighbor just add it. */
if(num_free > 0) {
return 1;
}
instance = rpl_get_instance(dio->instance_id);
if(instance == NULL || instance->current_dag == NULL) {
PRINTF("Did not find instance id: %d\n", dio->instance_id);
return 0;
}
/* Add the new neighbor only if it is better than the preferred parent. */
rank = instance->of->calculate_rank(NULL, dio->rank);
if(rank < worst_rank - instance->min_hoprankinc / 2) {
/* Found *great* neighbor - add! */
PRINTF("Found better neighbor %d < %d - add to cache...\n",
rank, worst_rank);
return remove_worst_nbr();
}
PRINTF("Found worse neighbor with new %d and old %d - NOT add to cache.\n",
rank, worst_rank);
return 0;
}
/*---------------------------------------------------------------------------*/
static int
check_add_from_dao(uip_ipaddr_t *from)
{
/* Do a lookup to see if it is alread there - then allow add/update. */
if(uip_ds6_nbr_lookup(from)) {
return 1;
}
update_nbr();
/* Check if this DAO sender is not yet neighbor and there is already too
many children. */
if(num_children >= MAX_CHILDREN) {
PRINTF("Can not add another child - already at max.\n");
return 0;
}
return 1;
}
/*---------------------------------------------------------------------------*/
const struct nbr_policy rpl_nbr_policy = {
check_add_from_dis,
check_add_from_dio,
check_add_from_dao
};
/*---------------------------------------------------------------------------*/
/** @}*/

View File

@ -124,8 +124,17 @@
#define RPL_NOPATH_REMOVAL_DELAY 60
#endif /* RPL_CONF_NOPATH_REMOVAL_DELAY */
#ifdef RPL_CONF_DAO_MAX_RETRANSMISSIONS
#define RPL_DAO_MAX_RETRANSMISSIONS RPL_CONF_DAO_MAX_RETRANSMISSIONS
#else
#define RPL_DAO_MAX_RETRANSMISSIONS 5
#endif /* RPL_CONF_DAO_MAX_RETRANSMISSIONS */
#ifdef RPL_CONF_DAO_RETRANSMISSION_TIMEOUT
#define RPL_DAO_RETRANSMISSION_TIMEOUT RPL_CONF_DAO_RETRANSMISSION_TIMEOUT
#else
#define RPL_DAO_RETRANSMISSION_TIMEOUT (5 * CLOCK_SECOND)
#endif /* RPL_CONF_DAO_RETRANSMISSION_TIMEOUT */
/* Special value indicating immediate removal. */
#define RPL_ZERO_LIFETIME 0
@ -260,6 +269,24 @@ typedef struct rpl_stats rpl_stats_t;
extern rpl_stats_t rpl_stats;
#endif
struct nbr_policy {
/** check if it is ok to add a nbr via UC DIS - positive => ok */
int (* check_add_from_dis)(uip_ipaddr_t *from);
int (* check_add_from_dio)(uip_ipaddr_t *from, rpl_dio_t *dio);
int (* check_add_from_dao)(uip_ipaddr_t *from);
};
#ifdef RPL_CONF_NBR_POLICY
#define RPL_NBR_POLICY RPL_CONF_NBR_POLICY
#else /* RPL_CONF_NBR_POLICY */
#define RPL_NBR_POLICY rpl_nbr_policy
#endif /* RPL_CONF_NBR_POLICY */
extern const struct nbr_policy RPL_NBR_POLICY;
/*---------------------------------------------------------------------------*/
/* RPL macros. */
@ -280,6 +307,7 @@ void dao_output(rpl_parent_t *, uint8_t lifetime);
void dao_output_target(rpl_parent_t *, uip_ipaddr_t *, uint8_t lifetime);
void dao_ack_output(rpl_instance_t *, uip_ipaddr_t *, uint8_t, uint8_t);
void rpl_icmp6_register_handlers(void);
uip_ds6_nbr_t *rpl_icmp6_update_nbr_table(uip_ipaddr_t *from);
/* RPL logic functions. */
void rpl_join_dag(uip_ipaddr_t *from, rpl_dio_t *dio);

View File

@ -238,7 +238,6 @@ rpl_add_route(rpl_dag_t *dag, uip_ipaddr_t *prefix, int prefix_len,
rep->state.dag = dag;
rep->state.lifetime = RPL_LIFETIME(dag->instance, dag->instance->default_lifetime);
/* rep->state.learned_from = RPL_ROUTE_FROM_INTERNAL; */
PRINTF("RPL: Added a route to ");
PRINT6ADDR(prefix);