diff --git a/sys/include/net/gnrc/rpl.h b/sys/include/net/gnrc/rpl.h index dbdc2b1c52d6..34ac6b72dd09 100644 --- a/sys/include/net/gnrc/rpl.h +++ b/sys/include/net/gnrc/rpl.h @@ -151,6 +151,7 @@ #include "net/gnrc/rpl/dodag.h" #include "net/gnrc/rpl/of_manager.h" #include "net/fib.h" +#include "time_units.h" #include "trickle.h" #ifdef MODULE_NETSTATS_RPL @@ -627,6 +628,13 @@ extern netstats_rpl_t gnrc_rpl_netstats; #define CONFIG_GNRC_RPL_PARENT_TIMEOUT_DIS_RETRIES (3) #endif +/** + * @brief Timeout for floating DODAGs in milliseconds. + */ +#ifndef CONFIG_GNRC_RPL_DODAG_FLOAT_TIMEOUT +# define CONFIG_GNRC_RPL_DODAG_FLOAT_TIMEOUT (15 * MS_PER_SEC * SEC_PER_MIN) +#endif + /** * @brief Default network interface for GNRC RPL */ diff --git a/sys/include/net/gnrc/rpl/dodag.h b/sys/include/net/gnrc/rpl/dodag.h index e23c543748d3..e35fe385ddd0 100644 --- a/sys/include/net/gnrc/rpl/dodag.h +++ b/sys/include/net/gnrc/rpl/dodag.h @@ -100,6 +100,13 @@ void gnrc_rpl_instance_remove(gnrc_rpl_instance_t *inst); */ gnrc_rpl_instance_t *gnrc_rpl_instance_get(uint8_t instance_id); +/** + * @brief Setup self as root for the @p dodag. + * + * @param[in] dodag Pointer to the dodag. + */ +void gnrc_rpl_dodag_root_init(gnrc_rpl_dodag_t *dodag); + /** * @brief Initialize a new RPL DODAG with the id @p dodag_id for the instance @p instance. * @@ -159,6 +166,15 @@ void gnrc_rpl_parent_update(gnrc_rpl_dodag_t *dodag, gnrc_rpl_parent_t *parent); */ void gnrc_rpl_cleanup_start(gnrc_rpl_dodag_t *dodag); +/** + * @brief Poison all routes of @p dodag by setting an infinite rank, and schedule + * a cleanup for the @p dodag. + * + * @param[in] dodag Pointer to the DODAG + * + */ +void gnrc_rpl_poison_routes(gnrc_rpl_dodag_t *dodag); + /** * @brief Start a local repair. * diff --git a/sys/include/net/gnrc/rpl/structs.h b/sys/include/net/gnrc/rpl/structs.h index fc80056834eb..054d9155005d 100644 --- a/sys/include/net/gnrc/rpl/structs.h +++ b/sys/include/net/gnrc/rpl/structs.h @@ -330,6 +330,10 @@ struct gnrc_rpl_dodag { uint8_t dio_opts; /**< options in the next DIO (see @ref GNRC_RPL_REQ_DIO_OPTS "DIO Options") */ evtimer_msg_event_t dao_event; /**< DAO TX events (see @ref GNRC_RPL_MSG_TYPE_DODAG_DAO_TX) */ + /** + * floating dodag timeout events (see @ref GNRC_RPL_MSG_TYPE_DODAG_FLOAT_TIMEOUT) + */ + evtimer_msg_event_t float_timeout_event; trickle_t trickle; /**< trickle representation */ }; diff --git a/sys/net/gnrc/routing/rpl/gnrc_rpl.c b/sys/net/gnrc/routing/rpl/gnrc_rpl.c index 19e6b761c7d7..ec67f737551c 100644 --- a/sys/net/gnrc/routing/rpl/gnrc_rpl.c +++ b/sys/net/gnrc/routing/rpl/gnrc_rpl.c @@ -141,37 +141,14 @@ gnrc_rpl_instance_t *gnrc_rpl_root_init(uint8_t instance_id, const ipv6_addr_t * instance_id = gnrc_rpl_gen_instance_id(local_inst_id); } - gnrc_rpl_dodag_t *dodag = NULL; gnrc_rpl_instance_t *inst = gnrc_rpl_root_instance_init(instance_id, dodag_id, GNRC_RPL_DEFAULT_MOP); if (!inst) { return NULL; } - - dodag = &inst->dodag; - - dodag->dtsn = 1; - dodag->prf = 0; - dodag->dio_interval_doubl = CONFIG_GNRC_RPL_DEFAULT_DIO_INTERVAL_DOUBLINGS; - dodag->dio_min = CONFIG_GNRC_RPL_DEFAULT_DIO_INTERVAL_MIN; - dodag->dio_redun = CONFIG_GNRC_RPL_DEFAULT_DIO_REDUNDANCY_CONSTANT; - dodag->default_lifetime = CONFIG_GNRC_RPL_DEFAULT_LIFETIME; - dodag->lifetime_unit = CONFIG_GNRC_RPL_LIFETIME_UNIT; - dodag->version = GNRC_RPL_COUNTER_INIT; - dodag->grounded = GNRC_RPL_GROUNDED; - dodag->node_status = GNRC_RPL_ROOT_NODE; - dodag->my_rank = GNRC_RPL_ROOT_RANK; - dodag->dio_opts |= GNRC_RPL_REQ_DIO_OPT_DODAG_CONF; - - if (!IS_ACTIVE(CONFIG_GNRC_RPL_WITHOUT_PIO)) { - dodag->dio_opts |= GNRC_RPL_REQ_DIO_OPT_PREFIX_INFO; - } - - trickle_start(gnrc_rpl_pid, &dodag->trickle, GNRC_RPL_MSG_TYPE_TRICKLE_MSG, - (1 << dodag->dio_min), dodag->dio_interval_doubl, - dodag->dio_redun); - gnrc_rpl_rpble_update(dodag); + gnrc_rpl_dodag_root_init(&inst->dodag); + gnrc_rpl_rpble_update(&inst->dodag); return inst; } @@ -273,6 +250,15 @@ static void _parent_timeout(gnrc_rpl_parent_t *parent) evtimer_add_msg(&gnrc_rpl_evtimer, &parent->timeout_event, gnrc_rpl_pid); } +/* Handle timeout for floating DODAG by poisoning all routes. */ +static void _dodag_float_timeout(gnrc_rpl_dodag_t *dodag) +{ + if (dodag->grounded != GNRC_RPL_GROUNDED) { + gnrc_rpl_poison_routes(dodag); + } + evtimer_del(&gnrc_rpl_evtimer, (evtimer_event_t *)&dodag->float_timeout_event); +} + static void *_event_loop(void *args) { msg_t msg, reply; @@ -323,6 +309,11 @@ static void *_event_loop(void *args) gnrc_rpl_instance_remove(instance); } break; + case GNRC_RPL_MSG_TYPE_DODAG_FLOAT_TIMEOUT: + DEBUG("RPL: GNRC_RPL_MSG_TYPE_DODAG_FLOAT_TIMEOUT received\n"); + instance = msg.content.ptr; + _dodag_float_timeout(&instance->dodag); + break; case GNRC_RPL_MSG_TYPE_TRICKLE_MSG: DEBUG("RPL: GNRC_RPL_MSG_TYPE_TRICKLE_MSG received\n"); trickle = msg.content.ptr; diff --git a/sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c b/sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c index e0bf6ad39673..c6e30eff413e 100644 --- a/sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c +++ b/sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c @@ -77,6 +77,18 @@ static uint16_t _dflt_route_lifetime_sec(gnrc_rpl_dodag_t *dodag) (GNRC_RPL_PARENT_PROBE_INTERVAL / MS_PER_SEC)); } +void gnrc_rpl_poison_routes(gnrc_rpl_dodag_t *dodag) +{ + if (dodag->my_rank != GNRC_RPL_INFINITE_RANK) { + DEBUG("RPL: Poison all children routes in DODAG.\n"); + + /* Poison routes by advertising infinity rank */ + dodag->my_rank = GNRC_RPL_INFINITE_RANK; + trickle_reset_timer(&dodag->trickle); + gnrc_rpl_cleanup_start(dodag); + } +} + bool gnrc_rpl_instance_add(uint8_t instance_id, gnrc_rpl_instance_t **inst) { *inst = NULL; @@ -130,8 +142,9 @@ void gnrc_rpl_dodag_remove(gnrc_rpl_dodag_t *dodag) gnrc_rpl_dodag_remove_all_parents(dodag); trickle_stop(&dodag->trickle); evtimer_del(&gnrc_rpl_evtimer, (evtimer_event_t *)&dodag->dao_event); + evtimer_del(&gnrc_rpl_evtimer, (evtimer_event_t *)&dodag->float_timeout_event); evtimer_del(&gnrc_rpl_evtimer, (evtimer_event_t *)&dodag->instance->cleanup_event); - + memset(dodag, 0, sizeof(gnrc_rpl_dodag_t)); } void gnrc_rpl_instance_remove(gnrc_rpl_instance_t *inst) @@ -150,6 +163,30 @@ gnrc_rpl_instance_t *gnrc_rpl_instance_get(uint8_t instance_id) return NULL; } +void gnrc_rpl_dodag_root_init(gnrc_rpl_dodag_t *dodag) +{ + dodag->dtsn = 1; + dodag->prf = 0; + dodag->dio_interval_doubl = CONFIG_GNRC_RPL_DEFAULT_DIO_INTERVAL_DOUBLINGS; + dodag->dio_min = CONFIG_GNRC_RPL_DEFAULT_DIO_INTERVAL_MIN; + dodag->dio_redun = CONFIG_GNRC_RPL_DEFAULT_DIO_REDUNDANCY_CONSTANT; + dodag->default_lifetime = CONFIG_GNRC_RPL_DEFAULT_LIFETIME; + dodag->lifetime_unit = CONFIG_GNRC_RPL_LIFETIME_UNIT; + dodag->version = GNRC_RPL_COUNTER_INIT; + dodag->grounded = GNRC_RPL_GROUNDED; + dodag->node_status = GNRC_RPL_ROOT_NODE; + dodag->my_rank = GNRC_RPL_ROOT_RANK; + dodag->dio_opts |= GNRC_RPL_REQ_DIO_OPT_DODAG_CONF; + + if (!IS_ACTIVE(CONFIG_GNRC_RPL_WITHOUT_PIO)) { + dodag->dio_opts |= GNRC_RPL_REQ_DIO_OPT_PREFIX_INFO; + } + + trickle_start(gnrc_rpl_pid, &dodag->trickle, GNRC_RPL_MSG_TYPE_TRICKLE_MSG, + (1 << dodag->dio_min), dodag->dio_interval_doubl, + dodag->dio_redun); +} + bool gnrc_rpl_dodag_init(gnrc_rpl_instance_t *instance, const ipv6_addr_t *dodag_id, kernel_pid_t iface) { @@ -176,6 +213,8 @@ bool gnrc_rpl_dodag_init(gnrc_rpl_instance_t *instance, const ipv6_addr_t *dodag dodag->iface = iface; dodag->dao_event.msg.content.ptr = instance; dodag->dao_event.msg.type = GNRC_RPL_MSG_TYPE_DODAG_DAO_TX; + dodag->float_timeout_event.msg.content.ptr = instance; + dodag->float_timeout_event.msg.type = GNRC_RPL_MSG_TYPE_DODAG_FLOAT_TIMEOUT; if ((netif != NULL) && !(netif->flags & GNRC_NETIF_FLAGS_IPV6_FORWARDING)) { gnrc_rpl_leaf_operation(dodag); @@ -191,6 +230,32 @@ bool gnrc_rpl_dodag_init(gnrc_rpl_instance_t *instance, const ipv6_addr_t *dodag return true; } +bool _float_dodag(gnrc_rpl_dodag_t *dodag) +{ + evtimer_event_t *float_event = (evtimer_event_t *)&dodag->float_timeout_event; + + DEBUG("RPL: Set node as root for floating DODAG.\n"); + + evtimer_del(&gnrc_rpl_evtimer, float_event); + + /* find address on interface that matches the prefix of the old dodag */ + gnrc_netif_t *netif = gnrc_netif_get_by_pid(dodag->iface); + int idx = gnrc_netif_ipv6_addr_match(netif, &dodag->dodag_id); + if (idx < 0) { + DEBUG("RPL: could not find address to use as DODAGID."); + return false; + } + dodag->dodag_id = netif->ipv6.addrs[idx]; + gnrc_rpl_dodag_root_init(dodag); + dodag->grounded &= !GNRC_RPL_GROUNDED; + + /* floating dodag should timeout eventually if no new grounded dodag is found */ + float_event->offset = CONFIG_GNRC_RPL_DODAG_FLOAT_TIMEOUT; + evtimer_add_msg(&gnrc_rpl_evtimer, &dodag->float_timeout_event, gnrc_rpl_pid); + + return true; +} + void gnrc_rpl_dodag_remove_all_parents(gnrc_rpl_dodag_t *dodag) { gnrc_rpl_parent_t *elt = NULL; @@ -276,10 +341,8 @@ void gnrc_rpl_local_repair(gnrc_rpl_dodag_t *dodag) dodag->dtsn++; - if (dodag->my_rank != GNRC_RPL_INFINITE_RANK) { - dodag->my_rank = GNRC_RPL_INFINITE_RANK; - trickle_reset_timer(&dodag->trickle); - gnrc_rpl_cleanup_start(dodag); + if ((CONFIG_GNRC_RPL_DODAG_FLOAT_TIMEOUT <= 0) || !_float_dodag(dodag)) { + gnrc_rpl_poison_routes(dodag); } if (dodag->parents) { diff --git a/sys/net/gnrc/routing/rpl/gnrc_rpl_internal/globals.h b/sys/net/gnrc/routing/rpl/gnrc_rpl_internal/globals.h index a9c6c8188926..1e74d33db2a0 100644 --- a/sys/net/gnrc/routing/rpl/gnrc_rpl_internal/globals.h +++ b/sys/net/gnrc/routing/rpl/gnrc_rpl_internal/globals.h @@ -45,6 +45,10 @@ extern evtimer_msg_t gnrc_rpl_evtimer; * @brief Message type for DAO transmissions. */ #define GNRC_RPL_MSG_TYPE_DODAG_DAO_TX (0x0906) +/** + * @brief Message type for floating DODAG timeouts. + */ +#define GNRC_RPL_MSG_TYPE_DODAG_FLOAT_TIMEOUT (0x0907) /** @} */ /**