From c6537820b1c37fdcf90399d39008757775154a58 Mon Sep 17 00:00:00 2001 From: Simon Duquennoy Date: Fri, 14 Jul 2017 14:29:17 +0200 Subject: [PATCH] Minor rpl-lite fixes for DAG leaving/joining --- apps/shell/shell-commands.c | 2 +- core/net/rpl-lite/rpl-dag.c | 180 ++++++++++++++++++---------------- core/net/rpl-lite/rpl-dag.h | 5 + core/net/rpl-lite/rpl-icmp6.c | 17 +++- 4 files changed, 114 insertions(+), 90 deletions(-) diff --git a/apps/shell/shell-commands.c b/apps/shell/shell-commands.c index c1cf3d9ee..9e8c27eb7 100644 --- a/apps/shell/shell-commands.c +++ b/apps/shell/shell-commands.c @@ -220,7 +220,7 @@ PT_THREAD(cmd_rpl_set_root(struct pt *pt, shell_output_func output, char *args)) } else { if(rpl_dag_root_is_root()) { SHELL_OUTPUT(output, "Setting as non-root node: leaving DAG\n"); - rpl_dag_leave(); + rpl_dag_poison_and_leave(); } else { SHELL_OUTPUT(output, "Node is not a DAG root\n"); } diff --git a/core/net/rpl-lite/rpl-dag.c b/core/net/rpl-lite/rpl-dag.c index 4ac21ed2e..7c4c3f299 100644 --- a/core/net/rpl-lite/rpl-dag.c +++ b/core/net/rpl-lite/rpl-dag.c @@ -88,8 +88,10 @@ rpl_dag_leave(void) LOG_INFO_(", instance %u\n", curr_instance.instance_id); /* Issue a no-path DAO */ - RPL_LOLLIPOP_INCREMENT(curr_instance.dag.dao_curr_seqno); - rpl_icmp6_dao_output(0); + if(!rpl_dag_root_is_root()) { + RPL_LOLLIPOP_INCREMENT(curr_instance.dag.dao_curr_seqno); + rpl_icmp6_dao_output(0); + } /* Forget past link statistics */ link_stats_reset(); @@ -111,6 +113,13 @@ rpl_dag_leave(void) } /*---------------------------------------------------------------------------*/ void +rpl_dag_poison_and_leave(void) +{ + curr_instance.dag.state = DAG_POISONING; + rpl_timers_schedule_state_update(); +} +/*---------------------------------------------------------------------------*/ +void rpl_dag_periodic(unsigned seconds) { if(curr_instance.used) { @@ -223,97 +232,98 @@ rpl_dag_ready_to_advertise(void) void rpl_dag_update_state(void) { - if(curr_instance.used) { - if(!rpl_dag_root_is_root()) { - rpl_nbr_t *old_parent = curr_instance.dag.preferred_parent; - rpl_rank_t old_rank = curr_instance.dag.rank; + rpl_rank_t old_rank; - /* Any scheduled state update is no longer needed */ - rpl_timers_unschedule_state_update(); + if(!curr_instance.used) { + return; + } - if(curr_instance.dag.state == DAG_POISONING) { - rpl_neighbor_set_preferred_parent(NULL); - curr_instance.dag.rank = RPL_INFINITE_RANK; - if(old_rank != RPL_INFINITE_RANK) { - /* Advertise that we are leaving, and leave after a delay */ - LOG_WARN("poisoning and leaving after a delay\n"); - rpl_timers_dio_reset("Poison routes"); - rpl_timers_schedule_leaving(); + old_rank = curr_instance.dag.rank; + /* Any scheduled state update is no longer needed */ + rpl_timers_unschedule_state_update(); + + if(curr_instance.dag.state == DAG_POISONING) { + rpl_neighbor_set_preferred_parent(NULL); + curr_instance.dag.rank = RPL_INFINITE_RANK; + if(old_rank != RPL_INFINITE_RANK) { + /* Advertise that we are leaving, and leave after a delay */ + LOG_WARN("poisoning and leaving after a delay\n"); + rpl_timers_dio_reset("Poison routes"); + rpl_timers_schedule_leaving(); + } + } else if(!rpl_dag_root_is_root()) { + rpl_nbr_t *old_parent = curr_instance.dag.preferred_parent; + rpl_nbr_t *nbr; + + /* Select and set preferred parent */ + rpl_neighbor_set_preferred_parent(rpl_neighbor_select_best()); + /* Update rank */ + curr_instance.dag.rank = rpl_neighbor_rank_via_nbr(curr_instance.dag.preferred_parent); + + /* Update better_parent_since flag for each neighbor */ + nbr = nbr_table_head(rpl_neighbors); + while(nbr != NULL) { + if(rpl_neighbor_rank_via_nbr(nbr) < curr_instance.dag.rank) { + /* This neighbor would be a better parent than our current. + Set 'better_parent_since' if not already set. */ + if(nbr->better_parent_since == 0) { + nbr->better_parent_since = clock_time(); /* Initialize */ } } else { - rpl_nbr_t *nbr; - - /* Select and set preferred parent */ - rpl_neighbor_set_preferred_parent(rpl_neighbor_select_best()); - /* Update rank */ - curr_instance.dag.rank = rpl_neighbor_rank_via_nbr(curr_instance.dag.preferred_parent); - - /* Update better_parent_since flag for each neighbor */ - nbr = nbr_table_head(rpl_neighbors); - while(nbr != NULL) { - if(rpl_neighbor_rank_via_nbr(nbr) < curr_instance.dag.rank) { - /* This neighbor would be a better parent than our current. - Set 'better_parent_since' if not already set. */ - if(nbr->better_parent_since == 0) { - nbr->better_parent_since = clock_time(); /* Initialize */ - } - } else { - nbr->better_parent_since = 0; /* Not a better parent */ - } - nbr = nbr_table_next(rpl_neighbors, nbr); - } - - if(old_parent == NULL || curr_instance.dag.rank < curr_instance.dag.lowest_rank) { - /* This is a slight departure from RFC6550: if we had no preferred parent before, - * reset lowest_rank. This helps recovering from temporary bad link conditions. */ - curr_instance.dag.lowest_rank = curr_instance.dag.rank; - } - - /* Reset DIO timer in case of significant rank update */ - if(curr_instance.dag.last_advertised_rank != RPL_INFINITE_RANK - && curr_instance.dag.rank != RPL_INFINITE_RANK - && ABS((int32_t)curr_instance.dag.rank - curr_instance.dag.last_advertised_rank) > RPL_SIGNIFICANT_CHANGE_THRESHOLD) { - LOG_WARN("significant rank update %u->%u\n", - curr_instance.dag.last_advertised_rank, curr_instance.dag.rank); - /* Update already here to avoid multiple resets in a row */ - curr_instance.dag.last_advertised_rank = curr_instance.dag.rank; - rpl_timers_dio_reset("Significant rank update"); - } - - /* Parent switch */ - if(curr_instance.dag.preferred_parent != old_parent) { - /* We just got a parent (was NULL), reset trickle timer to advertise this */ - if(old_parent == NULL) { - curr_instance.dag.state = DAG_JOINED; - rpl_timers_dio_reset("Got parent"); - LOG_WARN("found parent: "); - LOG_WARN_6ADDR(rpl_neighbor_get_ipaddr(curr_instance.dag.preferred_parent)); - LOG_WARN_(", staying in DAG\n"); - rpl_timers_unschedule_leaving(); - } - - /* Schedule a DAO */ - if(curr_instance.dag.preferred_parent != NULL) { - rpl_timers_schedule_dao(); - } else { - /* We have no more parent, schedule DIS to get a chance to hear updated state */ - curr_instance.dag.state = DAG_INITIALIZED; - LOG_WARN("no parent, scheduling periodic DIS, will leave if no parent is found\n"); - rpl_timers_dio_reset("Poison routes"); - rpl_timers_schedule_periodic_dis(); - rpl_timers_schedule_leaving(); - } - - #if LOG_INFO_ENABLED - rpl_neighbor_print_list("Parent switch"); - #endif /* LOG_INFO_ENABLED */ - } + nbr->better_parent_since = 0; /* Not a better parent */ } + nbr = nbr_table_next(rpl_neighbors, nbr); } - /* Finally, update metric container */ - curr_instance.of->update_metric_container(); + if(old_parent == NULL || curr_instance.dag.rank < curr_instance.dag.lowest_rank) { + /* This is a slight departure from RFC6550: if we had no preferred parent before, + * reset lowest_rank. This helps recovering from temporary bad link conditions. */ + curr_instance.dag.lowest_rank = curr_instance.dag.rank; + } + + /* Reset DIO timer in case of significant rank update */ + if(curr_instance.dag.last_advertised_rank != RPL_INFINITE_RANK + && curr_instance.dag.rank != RPL_INFINITE_RANK + && ABS((int32_t)curr_instance.dag.rank - curr_instance.dag.last_advertised_rank) > RPL_SIGNIFICANT_CHANGE_THRESHOLD) { + LOG_WARN("significant rank update %u->%u\n", + curr_instance.dag.last_advertised_rank, curr_instance.dag.rank); + /* Update already here to avoid multiple resets in a row */ + curr_instance.dag.last_advertised_rank = curr_instance.dag.rank; + rpl_timers_dio_reset("Significant rank update"); + } + + /* Parent switch */ + if(curr_instance.dag.preferred_parent != old_parent) { + /* We just got a parent (was NULL), reset trickle timer to advertise this */ + if(old_parent == NULL) { + curr_instance.dag.state = DAG_JOINED; + rpl_timers_dio_reset("Got parent"); + LOG_WARN("found parent: "); + LOG_WARN_6ADDR(rpl_neighbor_get_ipaddr(curr_instance.dag.preferred_parent)); + LOG_WARN_(", staying in DAG\n"); + rpl_timers_unschedule_leaving(); + } + + /* Schedule a DAO */ + if(curr_instance.dag.preferred_parent != NULL) { + rpl_timers_schedule_dao(); + } else { + /* We have no more parent, schedule DIS to get a chance to hear updated state */ + curr_instance.dag.state = DAG_INITIALIZED; + LOG_WARN("no parent, scheduling periodic DIS, will leave if no parent is found\n"); + rpl_timers_dio_reset("Poison routes"); + rpl_timers_schedule_periodic_dis(); + rpl_timers_schedule_leaving(); + } + +#if LOG_INFO_ENABLED + rpl_neighbor_print_list("Parent switch"); +#endif /* LOG_INFO_ENABLED */ + } } + + /* Finally, update metric container */ + curr_instance.of->update_metric_container(); } /*---------------------------------------------------------------------------*/ static rpl_nbr_t * diff --git a/core/net/rpl-lite/rpl-dag.h b/core/net/rpl-lite/rpl-dag.h index e7cd0332f..07ef3ca66 100644 --- a/core/net/rpl-lite/rpl-dag.h +++ b/core/net/rpl-lite/rpl-dag.h @@ -57,6 +57,11 @@ * \return The description string */ const char *rpl_dag_state_to_str(enum rpl_dag_state state); +/** + * Start poisoning and leave the DAG after a delay + * +*/ +void rpl_dag_poison_and_leave(void); /** * Leaves the current DAG * diff --git a/core/net/rpl-lite/rpl-icmp6.c b/core/net/rpl-lite/rpl-icmp6.c index 1e54c2237..756689bbd 100644 --- a/core/net/rpl-lite/rpl-icmp6.c +++ b/core/net/rpl-lite/rpl-icmp6.c @@ -557,10 +557,19 @@ rpl_icmp6_dao_output(uint8_t lifetime) /* Make sure we're up-to-date before sending data out */ rpl_dag_update_state(); - if(!curr_instance.used || curr_instance.dag.preferred_parent == NULL - || prefix == NULL || parent_ipaddr == NULL || curr_instance.mop == RPL_MOP_NO_DOWNWARD_ROUTES) { - LOG_WARN("rpl_icmp6_dao_output: node not ready to send a DAO (used %u, pref parent %u, prefix %u, mop %u)\n", - curr_instance.used, curr_instance.dag.preferred_parent != NULL && parent_ipaddr != NULL, prefix != NULL, curr_instance.mop); + if(!curr_instance.used) { + LOG_WARN("rpl_icmp6_dao_output: not in an instance, skip sending DAO\n"); + return; + } + + if(curr_instance.dag.preferred_parent == NULL) { + LOG_WARN("rpl_icmp6_dao_output: no preferred parent, skip sending DAO\n"); + return; + } + + if(prefix == NULL || parent_ipaddr == NULL || curr_instance.mop == RPL_MOP_NO_DOWNWARD_ROUTES) { + LOG_WARN("rpl_icmp6_dao_output: node not ready to send a DAO (prefix %p, parent addr %p, mop %u)\n", + prefix, parent_ipaddr, curr_instance.mop); return; }