diff --git a/core/net/rpl/rpl-ext-header.c b/core/net/rpl/rpl-ext-header.c index 1e3250e20..67cf2c5a7 100644 --- a/core/net/rpl/rpl-ext-header.c +++ b/core/net/rpl/rpl-ext-header.c @@ -73,6 +73,7 @@ rpl_verify_header(int uip_ext_opt_offset) uint16_t sender_rank; uint8_t sender_closer; uip_ds6_route_t *route; + rpl_parent_t *sender = NULL; if(UIP_HBHO_BUF->len != RPL_HOP_BY_HOP_LEN - 8) { PRINTF("RPL: Hop-by-hop extension header has wrong size\n"); @@ -132,10 +133,25 @@ rpl_verify_header(int uip_ext_opt_offset) instance->current_dag->rank ); + sender = nbr_table_get_from_lladdr(rpl_parents, packetbuf_addr(PACKETBUF_ADDR_SENDER)); + + if(UIP_EXT_HDR_OPT_RPL_BUF->flags & RPL_HDR_OPT_RANK_ERR) { + /* A rank error was signalled, attempt to repair it by updating + * the sender's rank from ext header */ + sender->rank = sender_rank; + rpl_select_dag(instance, sender); + } + if((down && !sender_closer) || (!down && sender_closer)) { PRINTF("RPL: Loop detected - senderrank: %d my-rank: %d sender_closer: %d\n", sender_rank, instance->current_dag->rank, sender_closer); + /* Attempt to repair the loop by sending a unicast DIO back to the sender + * so that it gets a fresh update of our rank. */ + if(sender != NULL) { + instance->unicast_dio_target = sender; + rpl_schedule_unicast_dio_immediately(instance); + } if(UIP_EXT_HDR_OPT_RPL_BUF->flags & RPL_HDR_OPT_RANK_ERR) { RPL_STAT(rpl_stats.loop_errors++); PRINTF("RPL: Rank error signalled in RPL option!\n"); diff --git a/core/net/rpl/rpl-private.h b/core/net/rpl/rpl-private.h index b49d71089..92f141fbf 100644 --- a/core/net/rpl/rpl-private.h +++ b/core/net/rpl/rpl-private.h @@ -312,6 +312,7 @@ rpl_of_t *rpl_find_of(rpl_ocp_t); /* Timer functions. */ void rpl_schedule_dao(rpl_instance_t *); void rpl_schedule_dao_immediately(rpl_instance_t *); +void rpl_schedule_unicast_dio_immediately(rpl_instance_t *instance); void rpl_cancel_dao(rpl_instance_t *instance); void rpl_schedule_probing(rpl_instance_t *instance); diff --git a/core/net/rpl/rpl-timers.c b/core/net/rpl/rpl-timers.c index 27fff0ddf..f9d8f84e4 100644 --- a/core/net/rpl/rpl-timers.c +++ b/core/net/rpl/rpl-timers.c @@ -327,6 +327,24 @@ rpl_cancel_dao(rpl_instance_t *instance) ctimer_stop(&instance->dao_lifetime_timer); } /*---------------------------------------------------------------------------*/ +static void +handle_unicast_dio_timer(void *ptr) +{ + rpl_instance_t *instance = (rpl_instance_t *)ptr; + uip_ipaddr_t *target_ipaddr = rpl_get_parent_ipaddr(instance->unicast_dio_target); + + if(target_ipaddr != NULL) { + dio_output(instance, target_ipaddr); + } +} +/*---------------------------------------------------------------------------*/ +void +rpl_schedule_unicast_dio_immediately(rpl_instance_t *instance) +{ + ctimer_set(&instance->unicast_dio_timer, 0, + handle_unicast_dio_timer, instance); +} +/*---------------------------------------------------------------------------*/ #if RPL_WITH_PROBING static rpl_parent_t * get_probing_target(rpl_dag_t *dag) diff --git a/core/net/rpl/rpl.h b/core/net/rpl/rpl.h index 70c877aeb..535304986 100644 --- a/core/net/rpl/rpl.h +++ b/core/net/rpl/rpl.h @@ -230,6 +230,8 @@ struct rpl_instance { struct ctimer dio_timer; struct ctimer dao_timer; struct ctimer dao_lifetime_timer; + struct ctimer unicast_dio_timer; + rpl_parent_t *unicast_dio_target; }; /*---------------------------------------------------------------------------*/