diff --git a/core/net/rpl/rpl-conf.h b/core/net/rpl/rpl-conf.h index 95a05a499..d3749e033 100644 --- a/core/net/rpl/rpl-conf.h +++ b/core/net/rpl/rpl-conf.h @@ -207,4 +207,46 @@ #define RPL_INSERT_HBH_OPTION 1 #endif +/* + * RPL probing. When enabled, probes will be sent periodically to keep + * parent link estimates up to date. + * */ +#ifdef RPL_CONF_WITH_PROBING +#define RPL_WITH_PROBING RPL_CONF_WITH_PROBING +#else +#define RPL_WITH_PROBING 1 +#endif + +/* + * RPL probing interval. + * */ +#ifdef RPL_CONF_PROBING_INTERVAL +#define RPL_PROBING_INTERVAL RPL_CONF_PROBING_INTERVAL +#else +#define RPL_PROBING_INTERVAL (120 * CLOCK_SECOND) +#endif + +/* + * Function used to select the next parent to be probed. + * */ +#ifdef RPL_CONF_PROBING_SELECT_FUNC +#define RPL_PROBING_SELECT_FUNC RPL_CONF_PROBING_SELECT_FUNC +#else +#define RPL_PROBING_SELECT_FUNC(dag) get_probing_target((dag)) +#endif + +/* + * Function used to send RPL probes. + * To probe with DIO, use: + * #define RPL_CONF_PROBING_SEND_FUNC(instance, addr) dio_output((instance), (addr)) + * To probe with DIS, use: + * #define RPL_CONF_PROBING_SEND_FUNC(instance, addr) dis_output((addr)) + * Any other custom probing function is also acceptable. + * */ +#ifdef RPL_CONF_PROBING_SEND_FUNC +#define RPL_PROBING_SEND_FUNC RPL_CONF_PROBING_SEND_FUNC +#else +#define RPL_PROBING_SEND_FUNC(instance, addr) dio_output((instance), (addr)) +#endif + #endif /* RPL_CONF_H */ diff --git a/core/net/rpl/rpl-dag.c b/core/net/rpl/rpl-dag.c index e0ca1b11b..0bae981b3 100644 --- a/core/net/rpl/rpl-dag.c +++ b/core/net/rpl/rpl-dag.c @@ -75,7 +75,7 @@ static rpl_of_t * const objective_functions[] = {&RPL_OF}; /*---------------------------------------------------------------------------*/ /* Per-parent RPL information */ -NBR_TABLE(rpl_parent_t, rpl_parents); +NBR_TABLE_GLOBAL(rpl_parent_t, rpl_parents); /*---------------------------------------------------------------------------*/ /* Allocate instance table. */ rpl_instance_t instance_table[RPL_MAX_INSTANCES]; @@ -90,14 +90,17 @@ rpl_print_neighbor_list() 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\n", + printf("RPL: nbr %3u %5u, %5u => %5u %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), - p == default_instance->current_dag->preferred_parent ? '*' : ' '); + p->rank, nbr ? nbr->link_metric : 0, + default_instance->of->calculate_rank(p, 0), + p == default_instance->current_dag->preferred_parent ? '*' : ' ', + (now - p->last_tx_time) / (60 * CLOCK_SECOND)); p = nbr_table_next(rpl_parents, p); } printf("RPL: end of list\n"); @@ -496,6 +499,9 @@ rpl_alloc_instance(uint8_t instance_id) 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; } } @@ -556,6 +562,9 @@ rpl_free_instance(rpl_instance_t *instance) 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); diff --git a/core/net/rpl/rpl-icmp6.c b/core/net/rpl/rpl-icmp6.c index c9890b706..66e311d51 100644 --- a/core/net/rpl/rpl-icmp6.c +++ b/core/net/rpl/rpl-icmp6.c @@ -466,8 +466,12 @@ dio_output(rpl_instance_t *instance, uip_ipaddr_t *uc_addr) buffer[pos++] = instance->dtsn_out; - /* always request new DAO to refresh route */ - RPL_LOLLIPOP_INCREMENT(instance->dtsn_out); + if(uc_addr == NULL) { + /* Request new DAO to refresh route. We do not do this for unicast DIO + * in order to avoid DAO messages after a DIS-DIO update, + * or upon unicast DIO probing. */ + RPL_LOLLIPOP_INCREMENT(instance->dtsn_out); + } /* reserved 2 bytes */ buffer[pos++] = 0; /* flags */ diff --git a/core/net/rpl/rpl-private.h b/core/net/rpl/rpl-private.h index a9ca3f375..ea4e34f90 100644 --- a/core/net/rpl/rpl-private.h +++ b/core/net/rpl/rpl-private.h @@ -313,6 +313,7 @@ rpl_of_t *rpl_find_of(rpl_ocp_t); void rpl_schedule_dao(rpl_instance_t *); void rpl_schedule_dao_immediately(rpl_instance_t *); void rpl_cancel_dao(rpl_instance_t *instance); +void rpl_schedule_probing(rpl_instance_t *instance); void rpl_reset_dio_timer(rpl_instance_t *); void rpl_reset_periodic_timer(void); diff --git a/core/net/rpl/rpl-timers.c b/core/net/rpl/rpl-timers.c index a849144cf..a5b0e0fc1 100644 --- a/core/net/rpl/rpl-timers.c +++ b/core/net/rpl/rpl-timers.c @@ -327,5 +327,90 @@ rpl_cancel_dao(rpl_instance_t *instance) ctimer_stop(&instance->dao_lifetime_timer); } /*---------------------------------------------------------------------------*/ +#if RPL_WITH_PROBING +static rpl_parent_t * +get_probing_target(rpl_dag_t *dag) +{ + /* Returns the next probing target. The current implementation looks for the + * best parent to which we have not transmitted since 2 * RPL_PROBING_INTERVAL. + * This will mostly select the preferred and second best parents. Probing the + * second best parent is important: if the link is good, RPL might choose to + * switch to it. If the link is bad, the second best parent will decrease in + * ranking, and another parent will be probed next time. */ + rpl_parent_t *p; + rpl_parent_t *probing_target; + rpl_rank_t probing_target_rank; + /* Look for a parent we have not sent to since 2 * RPL_PROBING_INTERVAL, + * e.g. with last_tx_time earlier than min_last_tx */ + clock_time_t min_last_tx = clock_time(); + min_last_tx = min_last_tx > 2 * RPL_PROBING_INTERVAL ? min_last_tx - 2 * RPL_PROBING_INTERVAL : 1; + + if(dag == NULL || + dag->instance == NULL || + dag->preferred_parent == NULL) { + return NULL; + } + + /* Our preferred parent needs probing */ + if(dag->preferred_parent->last_tx_time < min_last_tx) { + return dag->preferred_parent; + } + + /* Look for the best parent that needs probing. */ + probing_target = NULL; + probing_target_rank = INFINITE_RANK; + p = nbr_table_head(rpl_parents); + while(p != NULL) { + if(p->dag == dag && p->last_tx_time < min_last_tx) { + rpl_rank_t p_rank = dag->instance->of->calculate_rank(p, 0); + /* p is in our dag and needs probing */ + if(probing_target == NULL) { + probing_target = p; + probing_target_rank = p_rank; + } else { + if(p_rank < probing_target_rank) { + /* Found better candidate */ + probing_target = p; + probing_target_rank = p_rank; + } + } + } + p = nbr_table_next(rpl_parents, p); + } + + return probing_target; +} +/*---------------------------------------------------------------------------*/ +static void +handle_probing_timer(void *ptr) +{ + rpl_instance_t *instance = (rpl_instance_t *)ptr; + rpl_parent_t *probing_target = RPL_PROBING_SELECT_FUNC(instance->current_dag); + + /* Perform probing */ + if(probing_target != NULL && rpl_get_parent_ipaddr(probing_target) != NULL) { + PRINTF("RPL: probing %3u\n", + nbr_table_get_lladdr(rpl_parents, probing_target)->u8[7]); + /* Send probe, e.g. unicast DIO or DIS */ + RPL_PROBING_SEND_FUNC(instance, rpl_get_parent_ipaddr(probing_target)); + } + + /* Schedule next probing */ + rpl_schedule_probing(instance); + +#if DEBUG + rpl_print_neighbor_list(); +#endif +} +/*---------------------------------------------------------------------------*/ +void +rpl_schedule_probing(rpl_instance_t *instance) +{ + clock_time_t delay = (RPL_PROBING_INTERVAL / 2) + + random_rand() % (RPL_PROBING_INTERVAL / 2); + ctimer_set(&instance->probing_timer, delay, + handle_probing_timer, instance); +} +#endif /* RPL_WITH_PROBING */ /** @}*/ diff --git a/core/net/rpl/rpl.c b/core/net/rpl/rpl.c index f92d5f5f8..db5230611 100644 --- a/core/net/rpl/rpl.c +++ b/core/net/rpl/rpl.c @@ -263,6 +263,7 @@ rpl_link_neighbor_callback(const linkaddr_t *addr, int status, int numtx) parent->flags |= RPL_PARENT_FLAG_UPDATED; if(instance->of->neighbor_link_callback != NULL) { instance->of->neighbor_link_callback(parent, status, numtx); + parent->last_tx_time = clock_time(); } } } diff --git a/core/net/rpl/rpl.h b/core/net/rpl/rpl.h index e370d5d52..e6e385375 100644 --- a/core/net/rpl/rpl.h +++ b/core/net/rpl/rpl.h @@ -114,6 +114,7 @@ struct rpl_parent { rpl_metric_container_t mc; #endif /* RPL_DAG_MC != RPL_DAG_MC_NONE */ rpl_rank_t rank; + clock_time_t last_tx_time; uint8_t dtsn; uint8_t flags; }; @@ -224,6 +225,9 @@ struct rpl_instance { uint16_t dio_totrecv; #endif /* RPL_CONF_STATS */ clock_time_t dio_next_delay; /* delay for completion of dio interval */ +#if RPL_WITH_PROBING + struct ctimer probing_timer; +#endif /* RPL_WITH_PROBING */ struct ctimer dio_timer; struct ctimer dao_timer; struct ctimer dao_lifetime_timer; @@ -253,6 +257,9 @@ void rpl_dag_init(void); uip_ds6_nbr_t *rpl_get_nbr(rpl_parent_t *parent); void rpl_print_neighbor_list(); +/* Per-parent RPL information */ +NBR_TABLE_DECLARE(rpl_parents); + /** * RPL modes *