diff --git a/core/net/rpl/rpl-conf.h b/core/net/rpl/rpl-conf.h index 95a05a499..a2c0e6dd2 100644 --- a/core/net/rpl/rpl-conf.h +++ b/core/net/rpl/rpl-conf.h @@ -207,4 +207,65 @@ #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 + +/* + * RPL probing expiration time. + * */ +#ifdef RPL_CONF_PROBING_EXPIRATION_TIME +#define RPL_PROBING_EXPIRATION_TIME RPL_CONF_PROBING_EXPIRATION_TIME +#else +#define RPL_PROBING_EXPIRATION_TIME (10 * 60 * 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 + +/* + * Function used to calculate next RPL probing interval + * */ +#ifdef RPL_CONF_PROBING_DELAY_FUNC +#define RPL_PROBING_DELAY_FUNC RPL_CONF_PROBING_DELAY_FUNC +#else +#define RPL_PROBING_DELAY_FUNC() ((RPL_PROBING_INTERVAL / 2) \ + + random_rand() % (RPL_PROBING_INTERVAL)) +#endif + #endif /* RPL_CONF_H */ diff --git a/core/net/rpl/rpl-dag.c b/core/net/rpl/rpl-dag.c index 14a368e34..8250a9e84 100644 --- a/core/net/rpl/rpl-dag.c +++ b/core/net/rpl/rpl-dag.c @@ -75,12 +75,37 @@ 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]; rpl_instance_t *default_instance; +/*---------------------------------------------------------------------------*/ +void +rpl_print_neighbor_list() +{ + 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 (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 ? '*' : ' ', + (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) @@ -484,6 +509,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; } } @@ -549,6 +577,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); @@ -744,6 +775,9 @@ rpl_select_dag(rpl_instance_t *instance, rpl_parent_t *p) 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); @@ -1344,7 +1378,7 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio) 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*/, p->link_metric, instance->mc.obj.etx); + 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. */ 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 3dd0ac178..b49d71089 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 ea7bfe7f2..27fff0ddf 100644 --- a/core/net/rpl/rpl-timers.c +++ b/core/net/rpl/rpl-timers.c @@ -166,6 +166,10 @@ handle_dio_timer(void *ptr) } new_dio_interval(instance); } + +#if DEBUG + rpl_print_neighbor_list(); +#endif } /*---------------------------------------------------------------------------*/ void @@ -323,5 +327,97 @@ 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 probes the current + * preferred parent if we have not updated its link for RPL_PROBING_EXPIRATION_TIME. + * Otherwise, it picks at random between: + * (1) selecting the best parent not updated for RPL_PROBING_EXPIRATION_TIME + * (2) selecting the least recently updated parent + */ + rpl_parent_t *p; + rpl_parent_t *probing_target = NULL; + rpl_rank_t probing_target_rank = INFINITE_RANK; + /* min_last_tx is the clock time RPL_PROBING_EXPIRATION_TIME in the past */ + clock_time_t min_last_tx = clock_time(); + min_last_tx = min_last_tx > 2 * RPL_PROBING_EXPIRATION_TIME + ? min_last_tx - RPL_PROBING_EXPIRATION_TIME : 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) { + probing_target = dag->preferred_parent; + } + + /* With 50% probability: probe best parent not updated for RPL_PROBING_EXPIRATION_TIME */ + if(probing_target == NULL && (random_rand() % 2) == 0) { + p = nbr_table_head(rpl_parents); + while(p != NULL) { + if(p->dag == dag && p->last_tx_time < min_last_tx) { + /* p is in our dag and needs probing */ + rpl_rank_t p_rank = dag->instance->of->calculate_rank(p, 0); + if(probing_target == NULL + || p_rank < probing_target_rank) { + probing_target = p; + probing_target_rank = p_rank; + } + } + p = nbr_table_next(rpl_parents, p); + } + } + + /* The default probing target is the least recently updated parent */ + if(probing_target == NULL) { + p = nbr_table_head(rpl_parents); + while(p != NULL) { + if(p->dag == dag) { + if(probing_target == NULL + || p->last_tx_time < probing_target->last_tx_time) { + probing_target = p; + } + } + 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) +{ + ctimer_set(&instance->probing_timer, RPL_PROBING_DELAY_FUNC(), + 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 512c1a8f0..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; @@ -251,6 +255,10 @@ rpl_rank_t rpl_get_parent_rank(uip_lladdr_t *addr); uint16_t rpl_get_parent_link_metric(const uip_lladdr_t *addr); 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 diff --git a/regression-tests/12-rpl/09-rpl-probing.csc b/regression-tests/12-rpl/09-rpl-probing.csc new file mode 100644 index 000000000..bc29029af --- /dev/null +++ b/regression-tests/12-rpl/09-rpl-probing.csc @@ -0,0 +1,258 @@ + + + [APPS_DIR]/mrm + [APPS_DIR]/mspsim + [APPS_DIR]/avrora + [APPS_DIR]/serial_socket + [APPS_DIR]/collect-view + [APPS_DIR]/powertracker + + My simulation + 123456 + 1000000 + + org.contikios.cooja.radiomediums.UDGM + 50.0 + 50.0 + 1.0 + 1.0 + + + 40000 + + + org.contikios.cooja.contikimote.ContikiMoteType + mtype190 + Sender + [CONTIKI_DIR]/regression-tests/12-rpl/code/sender-node.c + make clean TARGET=cooja +make sender-node.cooja TARGET=cooja + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.Battery + org.contikios.cooja.contikimote.interfaces.ContikiVib + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + org.contikios.cooja.contikimote.interfaces.ContikiRS232 + org.contikios.cooja.contikimote.interfaces.ContikiBeeper + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.contikimote.interfaces.ContikiIPAddress + org.contikios.cooja.contikimote.interfaces.ContikiRadio + org.contikios.cooja.contikimote.interfaces.ContikiButton + org.contikios.cooja.contikimote.interfaces.ContikiPIR + org.contikios.cooja.contikimote.interfaces.ContikiClock + org.contikios.cooja.contikimote.interfaces.ContikiLED + org.contikios.cooja.contikimote.interfaces.ContikiCFS + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + false + + + org.contikios.cooja.contikimote.ContikiMoteType + mtype481 + RPL root + [CONTIKI_DIR]/regression-tests/12-rpl/code/root-node.c + make clean TARGET=cooja +make root-node.cooja TARGET=cooja + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.Battery + org.contikios.cooja.contikimote.interfaces.ContikiVib + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + org.contikios.cooja.contikimote.interfaces.ContikiRS232 + org.contikios.cooja.contikimote.interfaces.ContikiBeeper + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.contikimote.interfaces.ContikiIPAddress + org.contikios.cooja.contikimote.interfaces.ContikiRadio + org.contikios.cooja.contikimote.interfaces.ContikiButton + org.contikios.cooja.contikimote.interfaces.ContikiPIR + org.contikios.cooja.contikimote.interfaces.ContikiClock + org.contikios.cooja.contikimote.interfaces.ContikiLED + org.contikios.cooja.contikimote.interfaces.ContikiCFS + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + false + + + org.contikios.cooja.contikimote.ContikiMoteType + mtype692 + Receiver + [CONTIKI_DIR]/regression-tests/12-rpl/code/receiver-node.c + make clean TARGET=cooja +make receiver-node.cooja TARGET=cooja + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.Battery + org.contikios.cooja.contikimote.interfaces.ContikiVib + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + org.contikios.cooja.contikimote.interfaces.ContikiRS232 + org.contikios.cooja.contikimote.interfaces.ContikiBeeper + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.contikimote.interfaces.ContikiIPAddress + org.contikios.cooja.contikimote.interfaces.ContikiRadio + org.contikios.cooja.contikimote.interfaces.ContikiButton + org.contikios.cooja.contikimote.interfaces.ContikiPIR + org.contikios.cooja.contikimote.interfaces.ContikiClock + org.contikios.cooja.contikimote.interfaces.ContikiLED + org.contikios.cooja.contikimote.interfaces.ContikiCFS + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + false + + + + org.contikios.cooja.interfaces.Position + 8.0 + 2.0 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 1 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + mtype481 + + + + org.contikios.cooja.interfaces.Position + -7.19071602882406 + 34.96668248624779 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 2 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + mtype190 + + + + org.contikios.cooja.interfaces.Position + -17.870288882812428 + 4.581754854333804 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 3 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + mtype692 + + + + org.contikios.cooja.plugins.SimControl + 280 + 2 + 160 + 400 + 0 + + + org.contikios.cooja.plugins.Visualizer + + true + org.contikios.cooja.plugins.skins.IDVisualizerSkin + org.contikios.cooja.plugins.skins.UDGMVisualizerSkin + org.contikios.cooja.plugins.skins.GridVisualizerSkin + org.contikios.cooja.plugins.skins.MoteTypeVisualizerSkin + 2.494541140753371 0.0 0.0 2.494541140753371 168.25302383129448 116.2254386098645 + + 400 + 3 + 400 + 1 + 1 + + + org.contikios.cooja.plugins.LogListener + + + + + + 597 + 0 + 428 + 402 + 162 + + + org.contikios.cooja.plugins.Notes + + Enter notes here + true + + 904 + 4 + 160 + 680 + 0 + + + org.contikios.cooja.plugins.ScriptRunner + + + true + + 605 + 1 + 684 + 604 + 14 + + +