You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
439 lines
14 KiB
439 lines
14 KiB
/** |
|
* Copyright (C) 2013, 2014 INRIA. |
|
* Copyright (C) 2015 Cenk Gündoğan <cnkgndgn@gmail.com> |
|
* |
|
* This file is subject to the terms and conditions of the GNU Lesser |
|
* General Public License v2.1. See the file LICENSE in the top level |
|
* directory for more details. |
|
*/ |
|
|
|
/** |
|
* @{ |
|
* |
|
* @file |
|
* @author Eric Engel <eric.engel@fu-berlin.de> |
|
* @author Cenk Gündoğan <cnkgndgn@gmail.com> |
|
*/ |
|
|
|
#include <stdbool.h> |
|
#include "net/af.h" |
|
#include "net/gnrc/ipv6.h" |
|
#include "net/gnrc/ipv6/netif.h" |
|
#include "net/gnrc/rpl/dodag.h" |
|
#include "net/gnrc/rpl/structs.h" |
|
#include "utlist.h" |
|
|
|
#include "net/gnrc/rpl.h" |
|
#ifdef MODULE_GNRC_RPL_P2P |
|
#include "net/gnrc/rpl/p2p.h" |
|
#include "net/gnrc/rpl/p2p_dodag.h" |
|
#endif |
|
|
|
#define ENABLE_DEBUG (0) |
|
#include "debug.h" |
|
|
|
#if ENABLE_DEBUG |
|
static char addr_str[IPV6_ADDR_MAX_STR_LEN]; |
|
#endif |
|
|
|
static gnrc_rpl_parent_t *_gnrc_rpl_find_preferred_parent(gnrc_rpl_dodag_t *dodag); |
|
static void _rpl_trickle_send_dio(void *args); |
|
|
|
static void _rpl_trickle_send_dio(void *args) |
|
{ |
|
gnrc_rpl_instance_t *inst = (gnrc_rpl_instance_t *) args; |
|
gnrc_rpl_dodag_t *dodag = &inst->dodag; |
|
|
|
/* a leaf node does not send DIOs periodically */ |
|
if (dodag->node_status == GNRC_RPL_LEAF_NODE) { |
|
trickle_stop(&dodag->trickle); |
|
return; |
|
} |
|
|
|
#ifdef MODULE_GNRC_RPL_P2P |
|
if (dodag->instance->mop == GNRC_RPL_P2P_MOP) { |
|
gnrc_rpl_p2p_ext_t *p2p_ext = gnrc_rpl_p2p_ext_get(dodag); |
|
if (p2p_ext && (p2p_ext->for_me || ((p2p_ext->lifetime_sec <= 0) || p2p_ext->stop))) { |
|
trickle_stop(&dodag->trickle); |
|
return; |
|
} |
|
} |
|
#endif |
|
|
|
gnrc_rpl_send_DIO(inst, (ipv6_addr_t *) &ipv6_addr_all_rpl_nodes); |
|
DEBUG("trickle callback: Instance (%d) | DODAG: (%s)\n", inst->id, |
|
ipv6_addr_to_str(addr_str,&dodag->dodag_id, sizeof(addr_str))); |
|
} |
|
|
|
bool gnrc_rpl_instance_add(uint8_t instance_id, gnrc_rpl_instance_t **inst) |
|
{ |
|
*inst = NULL; |
|
bool first = true; |
|
for (uint8_t i = 0; i < GNRC_RPL_INSTANCES_NUMOF; ++i) { |
|
/* save position to the first unused instance */ |
|
if ((gnrc_rpl_instances[i].state == 0) && first) { |
|
*inst = &gnrc_rpl_instances[i]; |
|
first = false; |
|
continue; |
|
} |
|
else if ((gnrc_rpl_instances[i].state != 0) && (gnrc_rpl_instances[i].id == instance_id)) { |
|
DEBUG("Instance with id %d exists\n", instance_id); |
|
*inst = &gnrc_rpl_instances[i]; |
|
return false; |
|
} |
|
} |
|
|
|
if (*inst != NULL) { |
|
(*inst)->id = instance_id; |
|
(*inst)->state = 1; |
|
(*inst)->max_rank_inc = GNRC_RPL_DEFAULT_MAX_RANK_INCREASE; |
|
(*inst)->min_hop_rank_inc = GNRC_RPL_DEFAULT_MIN_HOP_RANK_INCREASE; |
|
(*inst)->dodag.parents = NULL; |
|
return true; |
|
} |
|
|
|
/* no space available to allocate a new instance */ |
|
DEBUG("Could not allocate a new RPL instance\n"); |
|
*inst = NULL; |
|
return false; |
|
} |
|
|
|
bool gnrc_rpl_instance_remove_by_id(uint8_t instance_id) |
|
{ |
|
for(uint8_t i = 0; i < GNRC_RPL_INSTANCES_NUMOF; ++i) { |
|
if (gnrc_rpl_instances[i].id == instance_id) { |
|
return gnrc_rpl_instance_remove(&gnrc_rpl_instances[i]); |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
bool gnrc_rpl_instance_remove(gnrc_rpl_instance_t *inst) |
|
{ |
|
gnrc_rpl_dodag_t *dodag = &inst->dodag; |
|
#ifdef MODULE_GNRC_RPL_P2P |
|
gnrc_rpl_p2p_ext_remove(dodag); |
|
#endif |
|
gnrc_rpl_dodag_remove_all_parents(dodag); |
|
trickle_stop(&dodag->trickle); |
|
memset(inst, 0, sizeof(gnrc_rpl_instance_t)); |
|
return true; |
|
} |
|
|
|
gnrc_rpl_instance_t *gnrc_rpl_instance_get(uint8_t instance_id) |
|
{ |
|
for (uint8_t i = 0; i < GNRC_RPL_INSTANCES_NUMOF; ++i) { |
|
if ((gnrc_rpl_instances[i].state != 0) && (gnrc_rpl_instances[i].id == instance_id)) { |
|
return &gnrc_rpl_instances[i]; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
bool gnrc_rpl_dodag_init(gnrc_rpl_instance_t *instance, ipv6_addr_t *dodag_id, kernel_pid_t iface, |
|
gnrc_ipv6_netif_addr_t *netif_addr) |
|
{ |
|
/* TODO: check if netif_addr belongs to iface */ |
|
|
|
assert(instance && (instance->state > 0)); |
|
|
|
gnrc_rpl_dodag_t *dodag = &instance->dodag; |
|
|
|
dodag->dodag_id = *dodag_id; |
|
dodag->my_rank = GNRC_RPL_INFINITE_RANK; |
|
dodag->trickle.callback.func = &_rpl_trickle_send_dio; |
|
dodag->trickle.callback.args = instance; |
|
dodag->dio_interval_doubl = GNRC_RPL_DEFAULT_DIO_INTERVAL_DOUBLINGS; |
|
dodag->dio_min = GNRC_RPL_DEFAULT_DIO_INTERVAL_MIN; |
|
dodag->dio_redun = GNRC_RPL_DEFAULT_DIO_REDUNDANCY_CONSTANT; |
|
dodag->default_lifetime = GNRC_RPL_DEFAULT_LIFETIME; |
|
dodag->lifetime_unit = GNRC_RPL_LIFETIME_UNIT; |
|
dodag->node_status = GNRC_RPL_NORMAL_NODE; |
|
dodag->dao_seq = GNRC_RPL_COUNTER_INIT; |
|
dodag->dtsn = 0; |
|
dodag->dao_ack_received = false; |
|
dodag->dao_counter = 0; |
|
dodag->instance = instance; |
|
dodag->iface = iface; |
|
dodag->netif_addr = netif_addr; |
|
|
|
#ifdef MODULE_GNRC_RPL_P2P |
|
if ((instance->mop == GNRC_RPL_P2P_MOP) && (gnrc_rpl_p2p_ext_new(dodag) == NULL)) { |
|
DEBUG("RPL: could not allocate new P2P-RPL DODAG extension. Remove DODAG\n"); |
|
gnrc_rpl_instance_remove(instance); |
|
return false; |
|
} |
|
#endif |
|
|
|
return true; |
|
} |
|
|
|
void gnrc_rpl_dodag_remove_all_parents(gnrc_rpl_dodag_t *dodag) |
|
{ |
|
gnrc_rpl_parent_t *elt, *tmp; |
|
LL_FOREACH_SAFE(dodag->parents, elt, tmp) { |
|
gnrc_rpl_parent_remove(elt); |
|
} |
|
dodag->my_rank = GNRC_RPL_INFINITE_RANK; |
|
} |
|
|
|
bool gnrc_rpl_parent_add_by_addr(gnrc_rpl_dodag_t *dodag, ipv6_addr_t *addr, |
|
gnrc_rpl_parent_t **parent) |
|
{ |
|
*parent = NULL; |
|
bool first = true; |
|
for (uint8_t i = 0; i < GNRC_RPL_PARENTS_NUMOF; ++i) { |
|
/* save position to the first unused parent */ |
|
if ((gnrc_rpl_parents[i].state == 0) && first) { |
|
*parent = &gnrc_rpl_parents[i]; |
|
first = false; |
|
continue; |
|
} |
|
/* return false if parent exists */ |
|
else if ((gnrc_rpl_parents[i].state != 0) && (gnrc_rpl_parents[i].dodag == dodag) && |
|
ipv6_addr_equal(&gnrc_rpl_parents[i].addr, addr)) { |
|
DEBUG("parent (%s) exists\n", ipv6_addr_to_str(addr_str, addr, sizeof(addr_str))); |
|
*parent = &gnrc_rpl_parents[i]; |
|
return false; |
|
} |
|
} |
|
|
|
if (*parent != NULL) { |
|
(*parent)->dodag = dodag; |
|
LL_APPEND(dodag->parents, *parent); |
|
(*parent)->state = 1; |
|
(*parent)->addr = *addr; |
|
return true; |
|
} |
|
|
|
/* no space available to allocate a new parent */ |
|
DEBUG("Could not allocate a new parent\n"); |
|
*parent = NULL; |
|
return false; |
|
} |
|
|
|
bool gnrc_rpl_parent_remove(gnrc_rpl_parent_t *parent) |
|
{ |
|
assert(parent != NULL); |
|
|
|
gnrc_rpl_dodag_t *dodag = parent->dodag; |
|
|
|
if (parent == dodag->parents) { |
|
fib_remove_entry(&gnrc_ipv6_fib_table, |
|
(uint8_t *) ipv6_addr_unspecified.u8, |
|
sizeof(ipv6_addr_t)); |
|
|
|
/* set the default route to the next parent for now */ |
|
if (parent->next) { |
|
uint32_t now = xtimer_now_usec() / US_PER_SEC; |
|
fib_add_entry(&gnrc_ipv6_fib_table, |
|
dodag->iface, |
|
(uint8_t *) ipv6_addr_unspecified.u8, |
|
sizeof(ipv6_addr_t), |
|
0x0, |
|
parent->next->addr.u8, |
|
sizeof(ipv6_addr_t), |
|
FIB_FLAG_RPL_ROUTE, |
|
(parent->next->lifetime - now) * MS_PER_SEC); |
|
} |
|
} |
|
LL_DELETE(dodag->parents, parent); |
|
memset(parent, 0, sizeof(gnrc_rpl_parent_t)); |
|
return true; |
|
} |
|
|
|
void gnrc_rpl_local_repair(gnrc_rpl_dodag_t *dodag) |
|
{ |
|
DEBUG("RPL: [INFO] Local Repair started\n"); |
|
|
|
dodag->dtsn++; |
|
|
|
if (dodag->parents) { |
|
gnrc_rpl_dodag_remove_all_parents(dodag); |
|
fib_remove_entry(&gnrc_ipv6_fib_table, |
|
(uint8_t *) ipv6_addr_unspecified.u8, |
|
sizeof(ipv6_addr_t)); |
|
} |
|
|
|
if (dodag->my_rank != GNRC_RPL_INFINITE_RANK) { |
|
dodag->my_rank = GNRC_RPL_INFINITE_RANK; |
|
trickle_reset_timer(&dodag->trickle); |
|
dodag->instance->cleanup = GNRC_RPL_CLEANUP_TIME; |
|
} |
|
} |
|
|
|
void gnrc_rpl_parent_update(gnrc_rpl_dodag_t *dodag, gnrc_rpl_parent_t *parent) |
|
{ |
|
/* update Parent lifetime */ |
|
if (parent != NULL) { |
|
uint32_t now = xtimer_now_usec(); |
|
parent->lifetime = (now / US_PER_SEC) + (dodag->default_lifetime * dodag->lifetime_unit); |
|
#ifdef MODULE_GNRC_RPL_P2P |
|
if (dodag->instance->mop != GNRC_RPL_P2P_MOP) { |
|
#endif |
|
if (parent == dodag->parents) { |
|
fib_add_entry(&gnrc_ipv6_fib_table, |
|
dodag->iface, |
|
(uint8_t *) ipv6_addr_unspecified.u8, |
|
sizeof(ipv6_addr_t), |
|
0x00, |
|
parent->addr.u8, |
|
sizeof(ipv6_addr_t), |
|
FIB_FLAG_RPL_ROUTE, |
|
(dodag->default_lifetime * dodag->lifetime_unit) * MS_PER_SEC); |
|
} |
|
#ifdef MODULE_GNRC_RPL_P2P |
|
} |
|
#endif |
|
} |
|
|
|
if (_gnrc_rpl_find_preferred_parent(dodag) == NULL) { |
|
gnrc_rpl_local_repair(dodag); |
|
} |
|
} |
|
|
|
/** |
|
* @brief Find the parent with the lowest rank and update the DODAG's preferred parent |
|
* |
|
* @param[in] dodag Pointer to the DODAG |
|
* |
|
* @return Pointer to the preferred parent, on success. |
|
* @return NULL, otherwise. |
|
*/ |
|
static gnrc_rpl_parent_t *_gnrc_rpl_find_preferred_parent(gnrc_rpl_dodag_t *dodag) |
|
{ |
|
gnrc_rpl_parent_t *old_best = dodag->parents; |
|
gnrc_rpl_parent_t *new_best = old_best; |
|
uint16_t old_rank = dodag->my_rank; |
|
gnrc_rpl_parent_t *elt, *tmp; |
|
|
|
if (dodag->parents == NULL) { |
|
return NULL; |
|
} |
|
|
|
LL_FOREACH(dodag->parents, elt) { |
|
new_best = dodag->instance->of->which_parent(new_best, elt); |
|
} |
|
|
|
if (new_best->rank == GNRC_RPL_INFINITE_RANK) { |
|
return NULL; |
|
} |
|
|
|
if (new_best != old_best) { |
|
LL_DELETE(dodag->parents, new_best); |
|
LL_PREPEND(dodag->parents, new_best); |
|
/* no-path DAOs only for the storing mode */ |
|
if ((dodag->instance->mop == GNRC_RPL_MOP_STORING_MODE_NO_MC) || |
|
(dodag->instance->mop == GNRC_RPL_MOP_STORING_MODE_MC)) { |
|
gnrc_rpl_send_DAO(dodag->instance, &old_best->addr, 0); |
|
gnrc_rpl_delay_dao(dodag); |
|
} |
|
|
|
#ifdef MODULE_GNRC_RPL_P2P |
|
if (dodag->instance->mop != GNRC_RPL_P2P_MOP) { |
|
#endif |
|
fib_add_entry(&gnrc_ipv6_fib_table, |
|
dodag->iface, |
|
(uint8_t *) ipv6_addr_unspecified.u8, |
|
sizeof(ipv6_addr_t), |
|
0x00, |
|
dodag->parents->addr.u8, |
|
sizeof(ipv6_addr_t), |
|
FIB_FLAG_RPL_ROUTE, |
|
(dodag->default_lifetime * dodag->lifetime_unit) * MS_PER_SEC); |
|
#ifdef MODULE_GNRC_RPL_P2P |
|
} |
|
#endif |
|
|
|
} |
|
|
|
dodag->my_rank = dodag->instance->of->calc_rank(dodag->parents, 0); |
|
if (dodag->my_rank != old_rank) { |
|
trickle_reset_timer(&dodag->trickle); |
|
} |
|
|
|
LL_FOREACH_SAFE(dodag->parents, elt, tmp) { |
|
if (DAGRANK(dodag->my_rank, dodag->instance->min_hop_rank_inc) |
|
<= DAGRANK(elt->rank, dodag->instance->min_hop_rank_inc)) { |
|
gnrc_rpl_parent_remove(elt); |
|
} |
|
} |
|
|
|
return dodag->parents; |
|
} |
|
|
|
gnrc_rpl_instance_t *gnrc_rpl_root_instance_init(uint8_t instance_id, ipv6_addr_t *dodag_id, |
|
uint8_t mop) |
|
{ |
|
if (gnrc_rpl_pid == KERNEL_PID_UNDEF) { |
|
DEBUG("RPL: RPL thread not started\n"); |
|
return NULL; |
|
} |
|
|
|
ipv6_addr_t *configured_addr; |
|
gnrc_ipv6_netif_addr_t *netif_addr = NULL; |
|
gnrc_rpl_instance_t *inst = NULL; |
|
gnrc_rpl_dodag_t *dodag = NULL; |
|
kernel_pid_t iface; |
|
|
|
if (!(ipv6_addr_is_global(dodag_id) || ipv6_addr_is_unique_local_unicast(dodag_id))) { |
|
DEBUG("RPL: dodag id (%s) must be a global or unique local IPv6 address\n", |
|
ipv6_addr_to_str(addr_str, dodag_id, sizeof(addr_str))); |
|
return NULL; |
|
} |
|
|
|
if ((iface = gnrc_ipv6_netif_find_by_addr(&configured_addr, dodag_id)) == KERNEL_PID_UNDEF) { |
|
DEBUG("RPL: no IPv6 address configured to match the given dodag id: %s\n", |
|
ipv6_addr_to_str(addr_str, dodag_id, sizeof(addr_str))); |
|
return NULL; |
|
} |
|
|
|
if ((netif_addr = gnrc_ipv6_netif_addr_get(configured_addr)) == NULL) { |
|
DEBUG("RPL: no netif address found for %s\n", |
|
ipv6_addr_to_str(addr_str, configured_addr, sizeof(addr_str))); |
|
return NULL; |
|
} |
|
|
|
if (gnrc_rpl_instance_add(instance_id, &inst)) { |
|
inst->of = (gnrc_rpl_of_t *) gnrc_rpl_get_of_for_ocp(GNRC_RPL_DEFAULT_OCP); |
|
inst->mop = mop; |
|
inst->min_hop_rank_inc = GNRC_RPL_DEFAULT_MIN_HOP_RANK_INCREASE; |
|
inst->max_rank_inc = GNRC_RPL_DEFAULT_MAX_RANK_INCREASE; |
|
} |
|
else if (inst == NULL) { |
|
DEBUG("RPL: could not allocate memory for a new instance with id %d\n", instance_id); |
|
return NULL; |
|
} |
|
else { |
|
DEBUG("RPL: instance (%d) exists\n", instance_id); |
|
return NULL; |
|
} |
|
|
|
if (!gnrc_rpl_dodag_init(inst, dodag_id, iface, netif_addr)) { |
|
DEBUG("RPL: could not initialize DODAG\n"); |
|
gnrc_rpl_instance_remove(inst); |
|
return NULL; |
|
} |
|
|
|
dodag = &inst->dodag; |
|
dodag->instance = inst; |
|
|
|
return inst; |
|
} |
|
|
|
void gnrc_rpl_leaf_operation(gnrc_rpl_dodag_t *dodag) |
|
{ |
|
dodag->node_status = GNRC_RPL_LEAF_NODE; |
|
/* send INFINITE_RANK DIO to current children */ |
|
gnrc_rpl_send_DIO(dodag->instance, NULL); |
|
} |
|
|
|
void gnrc_rpl_router_operation(gnrc_rpl_dodag_t *dodag) |
|
{ |
|
dodag->node_status = GNRC_RPL_NORMAL_NODE; |
|
/* announce presence to neighborhood */ |
|
trickle_reset_timer(&dodag->trickle); |
|
} |
|
/** |
|
* @} |
|
*/
|
|
|