takashi kadono / Mbed OS Nucleo_446

Dependencies:   ssd1331

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers rpl_upward.c Source File

rpl_upward.c

00001 /*
00002  * Copyright (c) 2015-2018, Arm Limited and affiliates.
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 /* rpl_upward.c deals with management of the DODAG and upward routes within an
00019  * instance.
00020  *
00021  * rpl_domain_t is accessible, but not normally manipulated - all routines in
00022  * this file works on a specific instance.
00023  *
00024  * rpl_instance_t, rpl_dodag_t, rpl_dodag_version_t, rpl_neighbour_t are all accessible.
00025  */
00026 
00027 #include "nsconfig.h"
00028 
00029 #ifdef HAVE_RPL
00030 #include "ns_types.h"
00031 #include "ns_list.h"
00032 #include "ns_trace.h"
00033 #include "common_functions.h"
00034 #include "randLIB.h"
00035 #include "nsdynmemLIB.h"
00036 #include <string.h>
00037 #include "ip6string.h"
00038 
00039 #include "net_interface.h"
00040 
00041 #include "Core/include/address.h"
00042 #include "Common_Protocols/icmpv6.h"
00043 #include "Common_Protocols/icmpv6_prefix.h"
00044 #include "NWK_INTERFACE/Include/protocol_abstract.h"
00045 #include "NWK_INTERFACE/Include/protocol_stats.h"
00046 #include "Service_Libs/Trickle/trickle.h"
00047 #include "ipv6_stack/ipv6_routing_table.h"
00048 #include "6LoWPAN/Bootstraps/protocol_6lowpan.h"
00049 #include "Common_Protocols/ipv6_resolution.h"
00050 
00051 #include "RPL/rpl_protocol.h"
00052 #include "RPL/rpl_policy.h"
00053 #include "RPL/rpl_control.h"
00054 #include "RPL/rpl_objective.h"
00055 #include "RPL/rpl_upward.h"
00056 #include "RPL/rpl_downward.h"
00057 #include "RPL/rpl_structures.h"
00058 
00059 #include "net_rpl.h"
00060 
00061 #define TRACE_GROUP "rplu"
00062 
00063 /* How many times to transmit/retransmit a zero-lifetime route */
00064 #define RPL_MAX_FINAL_RTR_ADVERTISEMENTS 3
00065 
00066 #define RPL_DEFAULT_DIO_INTERVAL_MIN 3
00067 #define RPL_DEFAULT_DIO_INTERVAL_DOUBLINGS 20
00068 
00069 #define RPL_DEFAULT_IMIN_TICKS (((1ull << RPL_DEFAULT_DIO_INTERVAL_MIN) + 99) / 100)
00070 #define RPL_DEFAULT_IMAX_TICKS (((1ull << (RPL_DEFAULT_DIO_INTERVAL_MIN+RPL_DEFAULT_DIO_INTERVAL_DOUBLINGS)) + 99) / 100)
00071 
00072 static NS_LIST_DEFINE(rpl_candidate_neighbour_set, rpl_neighbour_t, candidate_neighbour_link);
00073 
00074 static void rpl_instance_remove_parents(rpl_instance_t *instance);
00075 static void rpl_instance_remove_system_routes_through_parent(rpl_instance_t *instance, rpl_neighbour_t *parent);
00076 static void rpl_dodag_update_system_route(rpl_dodag_t *dodag, rpl_dio_route_t *route);
00077 
00078 /* Rank comparison, and DAGRank(rank) */
00079 uint16_t nrpl_dag_rank(const rpl_dodag_t *dodag, uint16_t rank)
00080 {
00081     return rank == RPL_RANK_INFINITE ? rank : rank / dodag->config.min_hop_rank_increase;
00082 }
00083 
00084 /* Silly function needed because RPL HbH option includes dagrank directly */
00085 rpl_cmp_t rpl_rank_compare_dagrank_rank(const rpl_dodag_t *dodag, uint16_t dag_rank_a, uint16_t b)
00086 {
00087     /* Special infinity handling makes sure we're absolutely solid, but callers
00088      * do need to then consider the (faint) possibility of unordered.
00089      */
00090     if (dag_rank_a == RPL_RANK_INFINITE) {
00091         return b == RPL_RANK_INFINITE ? RPL_CMP_UNORDERED : RPL_CMP_GREATER;
00092     } else if (b == RPL_RANK_INFINITE) {
00093         return RPL_CMP_LESS;
00094     }
00095 
00096     if (!dodag) {
00097         return RPL_CMP_UNORDERED;
00098     }
00099 
00100     uint16_t dag_rank_b = nrpl_dag_rank(dodag, b);
00101     if (dag_rank_a < dag_rank_b) {
00102         return RPL_CMP_LESS;
00103     } else if (dag_rank_a == dag_rank_b) {
00104         return RPL_CMP_EQUAL;
00105     } else {
00106         return RPL_CMP_GREATER;
00107     }
00108 }
00109 
00110 rpl_cmp_t rpl_rank_compare(const rpl_dodag_t *dodag, uint16_t a, uint16_t b)
00111 {
00112     uint16_t dag_rank_a = nrpl_dag_rank(dodag, a);
00113     return rpl_rank_compare_dagrank_rank(dodag, dag_rank_a, b);
00114 }
00115 
00116 /* Given a Rank, round up to the next higher integral rank */
00117 uint16_t rpl_rank_next_level(const rpl_dodag_t *dodag, uint16_t a)
00118 {
00119     uint16_t r = dodag->config.min_hop_rank_increase * (1 + nrpl_dag_rank(dodag, a));
00120     return r >= a ? r : RPL_RANK_INFINITE;
00121 }
00122 
00123 /* Given a Rank, return the maximum Rank with equal DAGRank */
00124 uint16_t rpl_rank_max_at_level(const rpl_dodag_t *dodag, uint16_t a)
00125 {
00126     uint16_t r = dodag->config.min_hop_rank_increase * (1 + nrpl_dag_rank(dodag, a)) - 1;
00127     return r >= a ? r : RPL_RANK_INFINITE;
00128 }
00129 
00130 /* Add two ranks, checking for overflow */
00131 uint16_t rpl_rank_add(uint16_t a, uint16_t b)
00132 {
00133     uint16_t r = a + b;
00134     return r >= a ? r : RPL_RANK_INFINITE;
00135 }
00136 
00137 /* Subtract two ranks, checking for overflow */
00138 uint16_t rpl_rank_sub(uint16_t a, uint16_t b)
00139 {
00140     uint16_t r = a - b;
00141     return r <= a ? r : 0;
00142 }
00143 
00144 /* Sequence counter operations (RFC 6550 S7.2) */
00145 #define RPL_SEQUENCE_WINDOW 16
00146 
00147 uint8_t rpl_seq_init(void)
00148 {
00149     return 256 - RPL_SEQUENCE_WINDOW;
00150 }
00151 
00152 uint8_t rpl_seq_inc(uint8_t seq)
00153 {
00154     return seq == 127 ? 0 : (uint8_t)(seq+1);
00155 }
00156 
00157 rpl_cmp_t rpl_seq_compare(uint8_t a, uint8_t b)
00158 {
00159     if (a == b) {
00160         return RPL_CMP_EQUAL;
00161     } else if (a >= 128 && b < 128) {
00162         if (256 + b - a <= RPL_SEQUENCE_WINDOW) {
00163              return RPL_CMP_LESS;
00164         } else {
00165              return RPL_CMP_GREATER;
00166         }
00167     } else if (b >= 128 && a < 128) {
00168         if (256 + a - b <= RPL_SEQUENCE_WINDOW) {
00169              return RPL_CMP_GREATER;
00170         } else {
00171              return RPL_CMP_LESS;
00172         }
00173     } else if (a < 128) /* so both <= 127 */ {
00174         /* RFC 6550 description is wrong/misleading ("If the absolute magnitude
00175          * of difference between the two sequence counters is less than or
00176          * equal to SEQUENCE_WINDOW...)". This doesn't cover the wrap
00177          * at the end of the circular region - the difference has to
00178          * be computed modulo-128..
00179          */
00180         uint8_t diff = (a - b) & 127;
00181         if (diff <= RPL_SEQUENCE_WINDOW) {
00182             return RPL_CMP_GREATER;
00183         } else if (diff >= 128 - RPL_SEQUENCE_WINDOW) {
00184             return RPL_CMP_LESS;
00185         } else {
00186             return RPL_CMP_UNORDERED;
00187         }
00188     } else /* both >= 128 */ {
00189         /* In this case, there's no wrap, so absolute difference being bigger
00190          * than SEQUENCE_WINDOW is unordered, as the RFC says (230 could be
00191          * newer than 250 due to reboot, or could be old).
00192          */
00193         uint8_t abs_diff = a > b ? a - b : b - a;
00194         if (abs_diff > RPL_SEQUENCE_WINDOW) {
00195             return RPL_CMP_UNORDERED;
00196         } else if (a > b) {
00197             return RPL_CMP_GREATER;
00198         } else {
00199             return RPL_CMP_LESS;
00200         }
00201      }
00202 }
00203 
00204 void rpl_instance_set_dodag_version(rpl_instance_t *instance, rpl_dodag_version_t *version, uint16_t rank)
00205 {
00206     if (!version || rpl_dodag_am_leaf(version->dodag)) {
00207         if (!version) {
00208             tr_debug("No version -> set RPL_RANK_INFINITE");
00209         } else {
00210             tr_debug("Leaf -> set RPL_RANK_INFINITE");
00211         }
00212         rank = RPL_RANK_INFINITE;
00213     }
00214 
00215     instance->current_rank = rank;
00216 
00217     rpl_dodag_version_t *old_version = instance->current_dodag_version;
00218     if (old_version == version) {
00219         return;
00220     }
00221 
00222     instance->current_dodag_version = version;
00223     if (version) {
00224         version->dodag->used = true;
00225         if (version->dodag->root) {
00226             rpl_instance_remove_parents(instance);
00227         }
00228 
00229         /* Need to call trickle_start somewhere (to avoid uninitialised variables) - this is it */
00230         if (!old_version || old_version->dodag != version->dodag) {
00231             trickle_start(&instance->dio_timer, &version->dodag->dio_timer_params);
00232         }
00233     }
00234 
00235     /* Then changing dodag version is an inconsistency. We may be changing from non-NULL to NULL, in which case we use old parameters to do poison */
00236     trickle_inconsistent_heard(&instance->dio_timer, version ? &version->dodag->dio_timer_params : &old_version->dodag->dio_timer_params);
00237 }
00238 
00239 rpl_dodag_version_t *rpl_instance_current_dodag_version(const rpl_instance_t *instance) {
00240     return instance->current_dodag_version;
00241 }
00242 
00243 rpl_dodag_t *rpl_instance_current_dodag(const rpl_instance_t *instance) {
00244     return instance->current_dodag_version ? instance->current_dodag_version->dodag : NULL;
00245 }
00246 
00247 #ifdef HAVE_RPL_ROOT
00248 bool rpl_instance_am_root(const rpl_instance_t *instance)
00249 {
00250     rpl_dodag_t *dodag = rpl_instance_current_dodag(instance);
00251     return dodag ? rpl_dodag_am_root(dodag) : false;
00252 }
00253 #endif
00254 
00255 uint8_t rpl_instance_mop(const rpl_instance_t *instance)
00256 {
00257     rpl_dodag_t *dodag = rpl_instance_current_dodag(instance);
00258     /* MOP is supposed to be the same for all DODAGs, so take any */
00259     if (!dodag) {
00260         dodag = ns_list_get_first(&instance->dodags);
00261     }
00262     return dodag ? rpl_dodag_mop(dodag) : RPL_MODE_NO_DOWNWARD;
00263 }
00264 
00265 rpl_neighbour_t *rpl_instance_preferred_parent(const rpl_instance_t *instance)
00266 {
00267     /* DODAG parents are first in the neighbour list, and preferred is first.
00268      * So if we have a preferred parent, it's the first entry, and its
00269      * dodag_parent (or was_dodag_parent) flag is set.
00270      */
00271     rpl_neighbour_t *neighbour = ns_list_get_first(&instance->candidate_neighbours);
00272     if (!neighbour || (!neighbour->dodag_parent && !neighbour->was_dodag_parent)) {
00273         return NULL;
00274     }
00275     return neighbour;
00276 }
00277 
00278 /* If we're a member of a DODAG Version matching the predicate in this instance,
00279  * return it. Mainly used for handling DODAG Information Solicitations.
00280  */
00281 rpl_dodag_version_t *rpl_instance_predicate_match(rpl_instance_t *instance, uint8_t pred, uint8_t instance_id, const uint8_t *dodagid, uint8_t version_num)
00282 {
00283     rpl_dodag_version_t *dodag_version = instance->current_dodag_version;
00284     if (!dodag_version) {
00285         return NULL;
00286     }
00287     if ((pred & RPL_SOLINFO_PRED_INSTANCEID) && (instance->id != instance_id)) {
00288         return NULL;
00289     }
00290     if ((pred & RPL_SOLINFO_PRED_DODAGID) && !addr_ipv6_equal(dodag_version->dodag->id, dodagid)) {
00291         return NULL;
00292     }
00293     if ((pred & RPL_SOLINFO_PRED_VERSION) && dodag_version->number != version_num) {
00294         return NULL;
00295     }
00296     return dodag_version;
00297 }
00298 
00299 void rpl_instance_inconsistency(rpl_instance_t *instance)
00300 {
00301     if (instance->current_dodag_version) {
00302         trickle_inconsistent_heard(&instance->dio_timer, &instance->current_dodag_version->dodag->dio_timer_params);
00303     }
00304 }
00305 
00306 void rpl_instance_consistent_rx(rpl_instance_t *instance)
00307 {
00308     if (!instance->dio_not_consistent) {
00309         trickle_consistent_heard(&instance->dio_timer);
00310     }
00311 }
00312 
00313 void rpl_instance_increment_dtsn(rpl_instance_t *instance)
00314 {
00315     instance->dtsn = rpl_seq_inc(instance->dtsn);
00316     instance->last_dao_trigger_time = protocol_core_monotonic_time;
00317     instance->srh_error_count = 0;
00318     /* Should a DTSN increment trigger DIOs, thus? */
00319     rpl_instance_inconsistency(instance);
00320 }
00321 
00322 void rpl_instance_poison(rpl_instance_t *instance, uint8_t count)
00323 {
00324     if (instance->poison_count < count) {
00325         instance->poison_count = count;
00326     }
00327     rpl_instance_inconsistency(instance);
00328 }
00329 
00330 void rpl_instance_force_leaf(rpl_instance_t *instance)
00331 {
00332     instance->current_rank = RPL_RANK_INFINITE;
00333 }
00334 
00335 void rpl_instance_trigger_parent_selection(rpl_instance_t *instance, uint16_t delay)
00336 {
00337     if (instance->parent_selection_timer == 0 || instance->parent_selection_timer > delay) {
00338         instance->parent_selection_timer = randLIB_randomise_base(delay, 0x7333, 0x8CCD) /* +/- 10% */;
00339     }
00340 }
00341 
00342 static void rpl_instance_parent_selection_timer(rpl_instance_t *instance, uint16_t seconds)
00343 {
00344     if (instance->parent_selection_timer > seconds) {
00345         instance->parent_selection_timer -= seconds;
00346     } else if (instance->parent_selection_timer != 0){
00347         tr_debug("Timed parent selection");
00348         rpl_instance_run_parent_selection(instance);
00349     }
00350 }
00351 
00352 rpl_dodag_t *rpl_lookup_dodag(const rpl_instance_t *instance, const uint8_t *dodagid)
00353 {
00354     ns_list_foreach(rpl_dodag_t, dodag, &instance->dodags) {
00355         if (addr_ipv6_equal(dodag->id, dodagid)) {
00356             dodag->timestamp = protocol_core_monotonic_time;
00357             return dodag;
00358         }
00359     }
00360     return NULL;
00361 }
00362 
00363 rpl_dodag_version_t *rpl_lookup_dodag_version(const rpl_dodag_t *dodag, uint8_t version_num)
00364 {
00365     ns_list_foreach(rpl_dodag_version_t, dodag_version, &dodag->versions) {
00366         if (dodag_version->number == version_num) {
00367             return dodag_version;
00368         }
00369     }
00370     return NULL;
00371 }
00372 
00373 rpl_neighbour_t *rpl_lookup_neighbour_by_ll_address(const rpl_instance_t *instance, const uint8_t *addr, int8_t if_id)
00374 {
00375     ns_list_foreach(rpl_neighbour_t, neighbour, &instance->candidate_neighbours) {
00376         if (neighbour->interface_id == if_id && addr_ipv6_equal(neighbour->ll_address, addr)) {
00377             return neighbour;
00378         }
00379     }
00380     return NULL;
00381 }
00382 
00383 rpl_neighbour_t *rpl_create_neighbour(rpl_dodag_version_t *version, const uint8_t *addr, int8_t if_id, uint8_t g_mop_prf, uint8_t dtsn)
00384 {
00385     /* Should gate higher-rank neighbours here - ignore higher-rank neighbours
00386      * unless in some sort of local repair.
00387      */
00388 
00389     rpl_neighbour_t *neighbour = rpl_alloc(sizeof(rpl_neighbour_t));
00390     if (!neighbour) {
00391         return NULL;
00392     }
00393 
00394     rpl_instance_t *instance = version->dodag->instance;
00395     neighbour->dodag_version = version;
00396     memcpy(neighbour->ll_address, addr, 16);
00397     memset(neighbour->global_address, 0, 16);
00398     neighbour->have_global_address = false;
00399     neighbour->dodag_parent = neighbour->was_dodag_parent = false;
00400     neighbour->considered = false;
00401     neighbour->interface_id = if_id;
00402     neighbour->rank = 0xffff;
00403     neighbour->g_mop_prf = g_mop_prf;
00404     neighbour->dtsn = dtsn;
00405     neighbour->dao_path_control = 0;
00406 
00407     /* Need to limit number of neighbours here - chucking worst neighbour */
00408 
00409     /* Neighbour list wants parents at the front of the list; slot new
00410      * people in after the parents. Then old neighbours get pushed to the end.
00411      */
00412     ns_list_foreach(rpl_neighbour_t, n, &instance->candidate_neighbours) {
00413         if (!n->dodag_parent) {
00414             ns_list_add_before(&instance->candidate_neighbours, n, neighbour);
00415             return neighbour;
00416         }
00417     }
00418     ns_list_add_to_end(&instance->candidate_neighbours, neighbour);
00419     return neighbour;
00420 }
00421 
00422 void rpl_delete_neighbour(rpl_instance_t *instance, rpl_neighbour_t *neighbour)
00423 {
00424     rpl_downward_neighbour_gone(instance, neighbour);
00425     ns_list_remove(&instance->candidate_neighbours, neighbour);
00426     if (neighbour->dao_path_control) {
00427     }
00428     if (neighbour->dodag_parent) {
00429         rpl_instance_remove_system_routes_through_parent(instance, neighbour);
00430         rpl_instance_neighbours_changed(instance);
00431     }
00432 
00433     rpl_free(neighbour, sizeof *neighbour);
00434 }
00435 
00436 const uint8_t *rpl_neighbour_ll_address(const rpl_neighbour_t *neighbour)
00437 {
00438     return neighbour->ll_address;
00439 }
00440 
00441 const uint8_t *rpl_neighbour_global_address(const rpl_neighbour_t *neighbour)
00442 {
00443     return neighbour->have_global_address ? neighbour->global_address : NULL;
00444 }
00445 
00446 void rpl_neighbour_update_global_address(rpl_neighbour_t *neighbour, const uint8_t *addr)
00447 {
00448     if (!addr_ipv6_equal(neighbour->global_address, addr)) {
00449         memcpy(neighbour->global_address, addr, 16);
00450         neighbour->have_global_address = true;
00451         //neighbour->down_change = true;
00452     }
00453 }
00454 
00455 void rpl_neighbour_update_dodag_version(rpl_neighbour_t *neighbour, rpl_dodag_version_t *version, uint16_t rank, uint8_t g_mop_prf)
00456 {
00457     /* DODAG follows G/MOP/Prf of preferred parent if it isn't moving */
00458     if (g_mop_prf != version->dodag->g_mop_prf &&
00459             (rpl_dodag_version_compare(version, neighbour->dodag_version) & (RPL_CMP_GREATER|RPL_CMP_EQUAL)) &&
00460             neighbour == rpl_instance_preferred_parent(version->dodag->instance)) {
00461         version->dodag->g_mop_prf = g_mop_prf;
00462         rpl_dodag_inconsistency(version->dodag);
00463     }
00464     neighbour->g_mop_prf = g_mop_prf;
00465     neighbour->dodag_version = version;
00466     neighbour->rank = rank;
00467     neighbour->dio_timestamp = protocol_core_monotonic_time;
00468 }
00469 
00470 bool rpl_neighbour_update_dtsn(rpl_neighbour_t *neighbour, uint8_t dtsn)
00471 {
00472     uint8_t old_dtsn = neighbour->dtsn;
00473 
00474     neighbour->dtsn = dtsn;
00475 
00476     return neighbour->dodag_parent && (rpl_seq_compare(dtsn, old_dtsn) & RPL_CMP_GREATER);
00477 }
00478 
00479 rpl_instance_t *rpl_neighbour_instance(const rpl_neighbour_t *neighbour)
00480 {
00481     return neighbour->dodag_version->dodag->instance;
00482 }
00483 
00484 rpl_dodag_version_t *rpl_create_dodag_version(rpl_dodag_t *dodag, uint8_t version_num)
00485 {
00486     rpl_dodag_version_t *version = rpl_alloc(sizeof(rpl_dodag_version_t));
00487     if (!version) {
00488         return NULL;
00489     }
00490 
00491     version->dodag = dodag;
00492     version->number = version_num;
00493     version->last_advertised_rank = RPL_RANK_INFINITE;
00494     version->lowest_advertised_rank = RPL_RANK_INFINITE;
00495     version->greediness_rank_limit = RPL_RANK_INFINITE;
00496     version->hard_rank_limit = RPL_RANK_INFINITE;
00497 
00498     if (!ns_list_is_empty(&dodag->versions)) {
00499         protocol_stats_update(STATS_RPL_GLOBAL_REPAIR, 1);
00500     }
00501 
00502     /* Maintain the version list newest first, taking care on ordering */
00503     bool inserted = false;
00504     ns_list_foreach_safe(rpl_dodag_version_t, v, &dodag->versions) {
00505         rpl_cmp_t cmp = rpl_seq_compare(version_num, v->number);
00506         if (cmp & (RPL_CMP_GREATER|RPL_CMP_UNORDERED)) {
00507             /* "Unordered" is treated as newest (as per RFC 6550 7.2 rule 4?) */
00508             ns_list_add_before(&dodag->versions, v, version);
00509             inserted = true;
00510             break;
00511         }
00512     }
00513     if (!inserted) {
00514         ns_list_add_to_end(&dodag->versions, version);
00515     }
00516 
00517     /* Now a clean-up to guarantee we have a completely comparable list.
00518      * Starting from the newest, check every other element is "less" than the
00519      * newest. If we hit one that isn't, chuck that, and all subsequent ones.
00520      */
00521     rpl_dodag_version_t *newest = ns_list_get_first(&dodag->versions);
00522     for (rpl_dodag_version_t *v = ns_list_get_next(&dodag->versions, newest); v; v = ns_list_get_next(&dodag->versions, v)) {
00523         if (rpl_seq_compare(v->number, newest->number) & RPL_CMP_LESS) {
00524             continue;
00525         } else {
00526             do {
00527                 rpl_dodag_version_t *next = ns_list_get_next(&dodag->versions, v);
00528                 rpl_delete_dodag_version(v);
00529                 v = next;
00530             } while (v);
00531             break;
00532         }
00533     }
00534 
00535 
00536 
00537     return version;
00538 }
00539 
00540 void rpl_delete_dodag_version(rpl_dodag_version_t *version)
00541 {
00542     rpl_dodag_t *dodag = version->dodag;
00543     rpl_instance_t *instance = dodag->instance;
00544 
00545     if (instance->current_dodag_version == version) {
00546         // Don't call rpl_instance_set_dodag_version(NULL) - that would pre-empt parent reselection,
00547         // triggering poison immediately.
00548         // Give parent selection a chance to select another version (but will it have any info on-hand?)
00549         instance->current_dodag_version = NULL;
00550         rpl_instance_trigger_parent_selection(instance, 5);
00551     }
00552 
00553     ns_list_foreach_safe(rpl_neighbour_t, neighbour, &instance->candidate_neighbours) {
00554         if (neighbour->dodag_version == version) {
00555             rpl_delete_neighbour(instance, neighbour);
00556         }
00557     }
00558     ns_list_remove(&dodag->versions, version);
00559     rpl_free(version, sizeof(*version));
00560 }
00561 
00562 bool rpl_dodag_version_is_current(const rpl_dodag_version_t *version)
00563 {
00564     return version->dodag->instance->current_dodag_version == version;
00565 }
00566 
00567 bool rpl_dodag_version_rank_indicates_possible_sub_dodag(const rpl_dodag_version_t *version, uint16_t rank)
00568 {
00569     if (!rpl_dodag_version_is_current(version)) {
00570         return false;
00571     }
00572     rpl_cmp_t cmp = rpl_rank_compare(version->dodag, rank, version->dodag->instance->current_rank);
00573     return cmp & RPL_CMP_GREATER;
00574 }
00575 
00576 rpl_cmp_t rpl_dodag_version_compare(const rpl_dodag_version_t *a, const rpl_dodag_version_t *b)
00577 {
00578     if (a == NULL || b == NULL) {
00579         return RPL_CMP_UNORDERED;
00580     }
00581     if (a == b) {
00582         return RPL_CMP_EQUAL;
00583     }
00584     if (a->dodag != b->dodag) {
00585         return RPL_CMP_UNORDERED;
00586     }
00587     return rpl_seq_compare(a->number, b->number);
00588 }
00589 
00590 void rpl_dodag_version_limit_greediness(rpl_dodag_version_t *version, uint16_t rank)
00591 {
00592     /* Apply RPL greediness limit rule - after we've joined a DODAG version,
00593      * we don't increase rank unless required to by an existing parent.
00594      */
00595     if (rank != RPL_RANK_INFINITE && version->greediness_rank_limit == RPL_RANK_INFINITE) {
00596         version->greediness_rank_limit = rpl_rank_max_at_level(version->dodag, rank);
00597     }
00598 }
00599 
00600 void rpl_dodag_version_raise_greediness(rpl_dodag_version_t *version, uint16_t pref_rank)
00601 {
00602     /* This can be called during parent selection, after preferred parent is chosen,
00603      * to potentially increase the greediness limit considering the new parent circumstance.
00604      * Eg, if our initial and current Rank was 1, our greediness limit would have
00605      * been 1.99. But if we've just had to increase Rank to 2 for an existing
00606      * parent, then we can raise the limit.
00607      */
00608     if (version->greediness_rank_limit < pref_rank) {
00609         version->greediness_rank_limit = rpl_rank_max_at_level(version->dodag, pref_rank);
00610     }
00611 }
00612 
00613 rpl_dodag_t *rpl_create_dodag(rpl_instance_t *instance, const uint8_t *dodagid, uint8_t g_mop_prf)
00614 {
00615     rpl_dodag_t *dodag = rpl_alloc(sizeof(rpl_dodag_t));
00616     if (!dodag) {
00617         return NULL;
00618     }
00619 
00620     dodag->instance = instance;
00621     dodag->timestamp = protocol_core_monotonic_time;
00622     memcpy(dodag->id, dodagid, 16);
00623     dodag->leaf = false;
00624     dodag->root = false;
00625     dodag->have_config = false;
00626     dodag->used = false;
00627     dodag->g_mop_prf = g_mop_prf;
00628     // Default timer parameters and trickle start should never normally
00629     // be used - we would set the parameters from the DODAG Config and start
00630     // as we join a version. But initialising here catches odd cases where
00631     // we end up sending poison DIOs before we get any config.
00632     dodag->dio_timer_params.Imin = RPL_DEFAULT_IMIN_TICKS;
00633     dodag->dio_timer_params.Imax = (trickle_time_t) (RPL_DEFAULT_IMAX_TICKS < TRICKLE_TIME_MAX ? RPL_DEFAULT_IMAX_TICKS : TRICKLE_TIME_MAX);
00634     dodag->dio_timer_params.k = 10;
00635     trickle_start(&instance->dio_timer, &dodag->dio_timer_params);
00636     ns_list_init(&dodag->versions);
00637     ns_list_init(&dodag->routes);
00638     ns_list_init(&dodag->prefixes);
00639 
00640     ns_list_add_to_start(&instance->dodags, dodag);
00641     return dodag;
00642 }
00643 
00644 void rpl_delete_dodag(rpl_dodag_t *dodag)
00645 {
00646     rpl_instance_t *instance = dodag->instance;
00647     ns_list_foreach_safe(rpl_dodag_version_t, version, &dodag->versions) {
00648         rpl_delete_dodag_version(version);
00649     }
00650     ns_list_foreach_safe(rpl_dio_route_t, route, &dodag->routes) {
00651         rpl_dodag_delete_dio_route(dodag, route);
00652     }
00653     ns_list_foreach_safe(prefix_entry_t, prefix, &dodag->prefixes) {
00654         rpl_dodag_delete_dio_prefix(dodag, prefix);
00655     }
00656     ns_list_remove(&instance->dodags, dodag);
00657     rpl_free(dodag, sizeof(*dodag));
00658 }
00659 
00660 void rpl_delete_dodag_root(rpl_dodag_t *dodag)
00661 {
00662     // This should trigger immediate poison
00663     rpl_instance_set_dodag_version(dodag->instance, NULL, RPL_RANK_INFINITE);
00664     // Deleting DODAG is not ideal - we will just pick up adverts from our
00665     // former children, and recreate, possibly violating the MaxRankIncrease.
00666     // Should retain DODAG version info and just unset root flag, which will
00667     // limit what happens when we hear adverts.
00668     // Problem is rpl_control_create_dodag_root which can't handle the
00669     // case where DODAG already exists. This would always be a problem if
00670     // we'd heard adverts in between delete and create, but would be an instant
00671     // problem without this delete. Need to fix.
00672     rpl_delete_dodag(dodag);
00673 }
00674 
00675 /* Convert RPL configuration to generic trickle parameters. Returns true if
00676  * the value in the generic object has changed.
00677  */
00678 static bool rpl_dodag_conf_convert_trickle_parameters(trickle_params_t *params_out, const rpl_dodag_conf_t *conf)
00679 {
00680     /* Convert trickle parameters into 100ms ticks */
00681     uint32_t Imin_ms = conf->dio_interval_min < 32 ? (1ul << conf->dio_interval_min) : 0xfffffffful;
00682     uint32_t Imin_ticks = (Imin_ms + 99) / 100;
00683     uint32_t Imax_ms = (conf->dio_interval_min + conf->dio_interval_doublings) < 32 ?
00684                         (1ul << (conf->dio_interval_min + conf->dio_interval_doublings)) : 0xfffffffful;
00685     uint32_t Imax_ticks = (Imax_ms + 99) / 100;
00686     trickle_time_t Imin = (trickle_time_t) (Imin_ticks <= TRICKLE_TIME_MAX ?  Imin_ticks : TRICKLE_TIME_MAX);
00687     trickle_time_t Imax = (trickle_time_t) (Imax_ticks <= TRICKLE_TIME_MAX ?  Imax_ticks : TRICKLE_TIME_MAX);
00688     uint8_t k = conf->dio_redundancy_constant;
00689     if (params_out->Imin != Imin || params_out->Imax != Imax || params_out->k != k) {
00690         params_out->Imin = Imin;
00691         params_out->Imax = Imax;
00692         params_out->k = k;
00693         params_out->TimerExpirations = TRICKLE_EXPIRATIONS_INFINITE;
00694         return true;
00695     } else {
00696         return false;
00697     }
00698 }
00699 
00700 uint8_t rpl_dodag_mop(const rpl_dodag_t *dodag)
00701 {
00702     return dodag->g_mop_prf & RPL_MODE_MASK;
00703 }
00704 
00705 bool rpl_dodag_update_config(rpl_dodag_t *dodag, const rpl_dodag_conf_t *conf, const uint8_t *src, bool *become_leaf)
00706 {
00707     /* If already have config, don't update unless it's coming from preferred parent */
00708     if (dodag->have_config) {
00709         rpl_neighbour_t *parent = rpl_instance_preferred_parent(dodag->instance);
00710         if (parent && src && !addr_ipv6_equal(src, parent->ll_address)) {
00711             return true;
00712         }
00713     }
00714     dodag->config = *conf;
00715     bool restart_timer = rpl_dodag_conf_convert_trickle_parameters(&dodag->dio_timer_params, conf);
00716     dodag->have_config = true;
00717     if (restart_timer && rpl_instance_current_dodag(dodag->instance) == dodag) {
00718         /* They've changed the timing parameters for our currently-in-use trickle timer! */
00719         tr_warn("Trickle parameters changed");
00720         trickle_start(&dodag->instance->dio_timer, &dodag->dio_timer_params);
00721     }
00722     dodag->instance->of = rpl_objective_lookup(conf->objective_code_point);
00723     /* We could be a leaf of an unknown OCP. Still need an OF to choose parents */
00724     if (!dodag->instance->of) {
00725         dodag->instance->of = rpl_objective_lookup(RPL_OCP_OF0);
00726         if (!dodag->instance->of) {
00727             return false;
00728         }
00729         if (become_leaf) {
00730             *become_leaf = true;
00731         }
00732     }
00733     return true;
00734 }
00735 
00736 /* If we're currently a member of this DODAG, kick the DIO timers */
00737 void rpl_dodag_inconsistency(rpl_dodag_t *dodag)
00738 {
00739     if (rpl_instance_current_dodag(dodag->instance) == dodag) {
00740         trickle_inconsistent_heard(&dodag->instance->dio_timer, &dodag->dio_timer_params);
00741     }
00742 }
00743 
00744 void rpl_dodag_increment_dtsn(rpl_dodag_t *dodag)
00745 {
00746     if (rpl_instance_current_dodag(dodag->instance) == dodag) {
00747         rpl_instance_increment_dtsn(dodag->instance);
00748     }
00749 }
00750 
00751 void rpl_dodag_set_pref(rpl_dodag_t *dodag, uint8_t pref)
00752 {
00753     dodag->g_mop_prf &= ~RPL_DODAG_PREF_MASK;
00754     dodag->g_mop_prf |= pref;
00755 }
00756 
00757 rpl_cmp_t rpl_dodag_pref_compare(const rpl_dodag_t *a, const rpl_dodag_t *b)
00758 {
00759     uint8_t pref_a, pref_b;
00760     pref_a = a->g_mop_prf & RPL_DODAG_PREF_MASK;
00761     pref_b = b->g_mop_prf & RPL_DODAG_PREF_MASK;
00762     if (pref_a == pref_b) {
00763         return RPL_CMP_EQUAL;
00764     } else if (pref_a < pref_b) {
00765         return RPL_CMP_LESS;
00766     } else {
00767         return RPL_CMP_GREATER;
00768     }
00769 }
00770 
00771 void rpl_dodag_set_root(rpl_dodag_t *dodag, bool root)
00772 {
00773     if (root != dodag->root) {
00774         dodag->root = root;
00775         if (root) {
00776             rpl_instance_remove_parents(dodag->instance);
00777         } else {
00778             rpl_instance_run_parent_selection(dodag->instance);
00779         }
00780     }
00781 }
00782 
00783 #ifdef HAVE_RPL_ROOT
00784 bool rpl_dodag_am_root(const rpl_dodag_t *dodag)
00785 {
00786     return dodag->root;
00787 }
00788 #endif
00789 
00790 void rpl_dodag_set_leaf(rpl_dodag_t *dodag, bool leaf)
00791 {
00792     dodag->leaf = leaf;
00793 }
00794 
00795 bool rpl_dodag_am_leaf(const rpl_dodag_t *dodag)
00796 {
00797     return dodag->leaf || dodag->instance->domain->force_leaf;
00798 }
00799 
00800 bool rpl_dodag_is_current(const rpl_dodag_t *dodag)
00801 {
00802     return dodag->instance->current_dodag_version && dodag->instance->current_dodag_version->dodag == dodag;
00803 }
00804 
00805 const rpl_dodag_conf_t *rpl_dodag_get_config(const rpl_dodag_t *dodag)
00806 {
00807     return dodag->have_config ? &dodag->config : NULL;
00808 }
00809 
00810 uint8_t rpl_dodag_get_version_number_as_root(const rpl_dodag_t *dodag)
00811 {
00812     return dodag->instance->current_dodag_version->number;
00813 }
00814 
00815 void rpl_dodag_set_version_number_as_root(rpl_dodag_t *dodag, uint8_t number)
00816 {
00817     /* As root, unlike other nodes, we don't need to worry about maintaining
00818      * multiple rpl_dodag_version_t to track neighbours in each version and
00819      * our ranks in each version. We'll just quietly change the version number
00820      * of our only, always-current version.
00821      */
00822     rpl_dodag_version_t *version = dodag->instance->current_dodag_version;
00823     version->number = number;
00824     rpl_dodag_inconsistency(dodag);
00825 #if defined FEA_TRACE_SUPPORT && defined EXTRA_CONSISTENCY_CHECKS
00826     /* Sanity check that the above assertion is true - as root we shouldn't have
00827      * any neighbours referring to this version.
00828      */
00829     ns_list_foreach(rpl_neighbour_t, n, &dodag->instance->candidate_neighbours) {
00830         if (n->dodag_version == version) {
00831             tr_err("Root DODAG version had neighbour");
00832             break;
00833         }
00834     }
00835 #endif
00836     protocol_stats_update(STATS_RPL_GLOBAL_REPAIR, 1);
00837 }
00838 
00839 static bool rpl_dodag_in_use(const rpl_dodag_t *dodag)
00840 {
00841     if (dodag->root) {
00842         return true;
00843     }
00844 
00845     if (!dodag->have_config) {
00846         return false;
00847     }
00848 
00849     rpl_instance_t *instance = dodag->instance;
00850     ns_list_foreach(rpl_neighbour_t, neighbour, &instance->candidate_neighbours) {
00851         if (neighbour->dodag_version->dodag == dodag) {
00852             return true;
00853         }
00854     }
00855 
00856     return false;
00857 }
00858 
00859 const rpl_dio_route_list_t *rpl_dodag_get_route_list(const rpl_dodag_t *dodag)
00860 {
00861     return &dodag->routes;
00862 }
00863 
00864 rpl_dio_route_t *rpl_dodag_update_dio_route(rpl_dodag_t *dodag, const uint8_t *prefix, uint8_t prefix_len, uint8_t flags, uint32_t lifetime, bool age)
00865 {
00866     rpl_dio_route_t *route = NULL;
00867     bool update = false;
00868 
00869     /* First look for matching prefix */
00870     ns_list_foreach(rpl_dio_route_t, r, &dodag->routes) {
00871         if (r->prefix_len == prefix_len && bitsequal(r->prefix, prefix, prefix_len)) {
00872             route = r;
00873             break;
00874         }
00875     }
00876 
00877     /* If not found, create a new one */
00878     if (!route) {
00879         uint_fast8_t prefix_bytes = (prefix_len + 7u) / 8u;
00880         route = rpl_alloc(sizeof(rpl_dio_route_t) + prefix_bytes);
00881         if (!route) {
00882             return NULL;
00883         }
00884 
00885         route->prefix_len = prefix_len;
00886         bitcopy0(route->prefix, prefix, prefix_len);
00887         ns_list_add_to_end(&dodag->routes, route);
00888         update = true;
00889     } else {
00890         if (lifetime != route->lifetime || route->flags != flags) {
00891             update = true;
00892         }
00893     }
00894 
00895     /* Update other info */
00896     route->lifetime = lifetime;
00897     route->hold_count = lifetime ? 0 : RPL_MAX_FINAL_RTR_ADVERTISEMENTS;
00898     route->flags = flags;
00899     route->age = age;
00900     if (update) {
00901         rpl_dodag_update_system_route(dodag, route);
00902     }
00903     return route;
00904 }
00905 
00906 void rpl_dodag_delete_dio_route(rpl_dodag_t *dodag, rpl_dio_route_t *route)
00907 {
00908     ns_list_remove(&dodag->routes, route);
00909     uint_fast8_t prefix_bytes = (route->prefix_len + 7u) / 8u;
00910     rpl_free(route, sizeof *route + prefix_bytes);
00911 }
00912 
00913 static void rpl_dodag_age_routes(rpl_dodag_t *dodag, uint16_t seconds)
00914 {
00915     ns_list_foreach_safe(rpl_dio_route_t, route, &dodag->routes) {
00916         if (route->age && route->lifetime != 0xFFFFFFFF) {
00917             if (route->lifetime > seconds) {
00918                 route->lifetime -= seconds;
00919             } else {
00920                 route->lifetime = 0;
00921                 if (route->hold_count == 0) {
00922                     rpl_dodag_delete_dio_route(dodag, route);
00923                 }
00924             }
00925         }
00926     }
00927 }
00928 
00929 const prefix_list_t *rpl_dodag_get_prefix_list(const rpl_dodag_t *dodag)
00930 {
00931     return &dodag->prefixes;
00932 }
00933 prefix_entry_t *rpl_dodag_update_dio_prefix(rpl_dodag_t *dodag, const uint8_t *prefix, uint8_t prefix_len, uint8_t flags, uint32_t lifetime, uint32_t preftime, bool publish, bool age)
00934 {
00935     /* Don't let them set funny flags - we won't propagate them either.
00936      * Is not propagating them sensible? RFC isn't clear. Anyway, we use
00937      * the spare 5 flag bits internally...
00938      */
00939     flags &= (PIO_R|PIO_A|PIO_L);
00940 
00941     if (publish) {
00942         flags |= RPL_PIO_PUBLISHED;
00943     }
00944 
00945     if (age) {
00946         flags |= RPL_PIO_AGE;
00947     }
00948 
00949     if (lifetime == 0) {
00950         flags |= RPL_MAX_FINAL_RTR_ADVERTISEMENTS;
00951     }
00952 
00953     prefix_entry_t *entry = icmpv6_prefix_add(&dodag->prefixes, prefix, prefix_len, lifetime, preftime, flags);
00954     /* icmpv6_prefix_add indicates a new entry by leaving options set to 0xFF */
00955     if (entry) {
00956         entry->options = flags;
00957     }
00958     return entry;
00959 }
00960 
00961 void rpl_dodag_delete_dio_prefix(rpl_dodag_t *dodag, prefix_entry_t *prefix)
00962 {
00963     ns_list_remove(&dodag->prefixes, prefix);
00964     ns_dyn_mem_free(prefix);
00965 }
00966 
00967 static void rpl_dodag_age_prefixes(rpl_dodag_t *dodag, uint16_t seconds)
00968 {
00969     ns_list_foreach_safe(prefix_entry_t, prefix, &dodag->prefixes) {
00970         if (!(prefix->options & RPL_PIO_AGE)) {
00971             continue;
00972         }
00973         if (prefix->preftime != 0xFFFFFFFF) {
00974             if (prefix->preftime > seconds) {
00975                 prefix->preftime -= seconds;
00976             } else {
00977                 prefix->preftime = 0;
00978             }
00979         }
00980         if (prefix->lifetime != 0xFFFFFFFF) {
00981             if (prefix->lifetime > seconds) {
00982                 prefix->lifetime -= seconds;
00983             } else {
00984                 prefix->lifetime = 0;
00985                 if ((prefix->options & RPL_PIO_HOLD_MASK) == 0) {
00986                     rpl_dodag_delete_dio_prefix(dodag, prefix);
00987                 }
00988             }
00989         }
00990     }
00991 }
00992 
00993 static void rpl_dodag_slow_timer(rpl_dodag_t *dodag, uint16_t seconds)
00994 {
00995     rpl_dodag_age_routes(dodag, seconds);
00996     rpl_dodag_age_prefixes(dodag, seconds);
00997 }
00998 
00999 
01000 /* Look up a RPL instance by identifier. If the ID is local, addr must
01001  * identify the root - eg by providing either the source destination address of
01002  * a packet (depending on the 'O' flag).
01003  */
01004 rpl_instance_t *rpl_lookup_instance(const rpl_domain_t *domain, uint8_t instance_id, const uint8_t *addr)
01005 {
01006     if (rpl_instance_id_is_local(instance_id)) {
01007         instance_id &= ~RPL_INSTANCE_DEST;
01008     }
01009 
01010     ns_list_foreach(rpl_instance_t, instance, &domain->instances) {
01011         /* First match the instance ID */
01012         if (instance->id != instance_id) {
01013             continue;
01014         }
01015         /* If it's a global ID, this is a match */
01016         if (rpl_instance_id_is_global(instance_id)) {
01017             return instance;
01018         }
01019         /* If it's a local ID, address must match */
01020         const rpl_dodag_t *dodag = ns_list_get_first(&instance->dodags);
01021         if (addr && dodag && addr_ipv6_equal(dodag->id, addr)) {
01022             return instance;
01023         }
01024     }
01025     return NULL;
01026 }
01027 
01028 rpl_instance_t *rpl_create_instance(rpl_domain_t *domain, uint8_t instance_id)
01029 {
01030     rpl_instance_t *instance = rpl_alloc(sizeof(rpl_instance_t));
01031     if (!instance) {
01032         return NULL;
01033     }
01034     memset(instance, 0, sizeof(rpl_instance_t));
01035     ns_list_init(&instance->dodags);
01036     ns_list_init(&instance->candidate_neighbours);
01037     ns_list_init(&instance->dao_targets);
01038     instance->dtsn = rpl_seq_init();
01039     instance->last_dao_trigger_time = protocol_core_monotonic_time;
01040     instance->dao_sequence = rpl_seq_init();
01041     instance->id = instance_id;
01042     instance->domain = domain;
01043 
01044     ns_list_add_to_start(&domain->instances, instance);
01045     return instance;
01046 }
01047 
01048 void rpl_delete_instance(rpl_instance_t *instance) {
01049     rpl_domain_t *domain = instance->domain;
01050     ns_list_foreach_safe(rpl_neighbour_t, neighbour, &instance->candidate_neighbours) {
01051         rpl_delete_neighbour(instance, neighbour);
01052     }
01053     ns_list_foreach_safe(rpl_dodag_t, dodag, &instance->dodags) {
01054         rpl_delete_dodag(dodag);
01055     }
01056     ns_list_foreach_safe(rpl_dao_target_t, target, &instance->dao_targets) {
01057         rpl_delete_dao_target(instance, target);
01058     }
01059     ns_list_remove(&domain->instances, instance);
01060     rpl_free(instance, sizeof *instance);
01061 }
01062 
01063 /* Don't purge a DODAG we've used unless it's been gone for 15 minutes */
01064 #define DODAG_MIN_PURGE_AGE (15*60*10) // 15 minutes
01065 
01066 /* Choose worst DODAG in an instance - will be purgeable */
01067 static rpl_dodag_t *rpl_instance_choose_dodag_to_purge(const rpl_instance_t *instance)
01068 {
01069     rpl_dodag_t *worst = NULL;
01070 
01071     ns_list_foreach_reverse(rpl_dodag_t, dodag, &instance->dodags) {
01072         uint32_t dodag_age = protocol_core_monotonic_time - dodag->timestamp;
01073         if (rpl_dodag_in_use(dodag)) {
01074             continue;
01075         }
01076 
01077         if (dodag_age < DODAG_MIN_PURGE_AGE && dodag->used) {
01078             continue;
01079         }
01080 
01081         if (!worst) {
01082             goto new_worst;
01083         }
01084 
01085         /* Prefer to purge least-recently-heard-from */
01086         uint32_t worst_age = protocol_core_monotonic_time - worst->timestamp;
01087         if (dodag_age <= worst_age) {
01088             continue;
01089         } else {
01090             goto new_worst;
01091         }
01092 
01093     new_worst:
01094         worst = dodag;
01095         worst_age = dodag_age;
01096     }
01097 
01098     return worst;
01099 }
01100 
01101 /* Choose worst neighbour in an instance - may be a candidate for purging */
01102 static rpl_neighbour_t *rpl_instance_choose_worst_neighbour(const rpl_instance_t *instance)
01103 {
01104     rpl_neighbour_t *worst = NULL;
01105     bool worst_acceptable = false;
01106 
01107     /* Parents will be first - loop backwards so we take non-parents first */
01108     ns_list_foreach_reverse(rpl_neighbour_t, neighbour, &instance->candidate_neighbours) {
01109         bool acceptable = instance->of->neighbour_acceptable(instance, neighbour);
01110         if (!worst) {
01111             goto new_worst;
01112         }
01113 
01114         /* Stop if crossing from non-parents to parents - parents can't be worse */
01115         if (neighbour->dodag_parent && !worst->dodag_parent) {
01116             break;
01117         }
01118 
01119         /* Prefer to purge "unacceptable" neighbours according to OF */
01120         if (acceptable && !worst_acceptable) {
01121             continue;
01122         } else if (!acceptable && worst_acceptable) {
01123             goto new_worst;
01124         }
01125 
01126         /* Prefer to purge least-recently-heard-from */
01127         uint32_t neighbour_age = protocol_core_monotonic_time - neighbour->dio_timestamp;
01128         uint32_t worst_age = protocol_core_monotonic_time - worst->dio_timestamp;
01129         if (neighbour_age <= worst_age) {
01130             continue;
01131         } else {
01132             goto new_worst;
01133         }
01134 
01135     new_worst:
01136         worst = neighbour;
01137         worst_acceptable = acceptable;
01138     }
01139 
01140     return worst;
01141 }
01142 
01143 /* Purge one item from an instance */
01144 bool rpl_instance_purge(rpl_instance_t *instance)
01145 {
01146     /* Purge this instance itself if no remaining neighbours or DODAGs
01147      * (time restrictions on those are sufficient - no need for extra)
01148      */
01149     if (ns_list_is_empty(&instance->candidate_neighbours) &&
01150             ns_list_is_empty(&instance->dodags)) {
01151         rpl_delete_instance(instance);
01152         return true;
01153     }
01154 
01155     /* May purge a DODAG if not used for some time, and no remaining neighbours */
01156     rpl_dodag_t *dodag = rpl_instance_choose_dodag_to_purge(instance);
01157     if (dodag) {
01158         rpl_delete_dodag(dodag);
01159         return true;
01160     }
01161 
01162     /* Otherwise, delete a non-parent neighbour we've considered at least once.
01163      * (We don't want to delete a new neighbour before it gets a chance to
01164      * become a new parent)
01165      */
01166     rpl_neighbour_t *neighbour = rpl_instance_choose_worst_neighbour(instance);
01167     if (neighbour && neighbour->considered && !neighbour->dodag_parent && neighbour->dao_path_control == 0) {
01168         rpl_delete_neighbour(instance, neighbour);
01169         return true;
01170     }
01171     return false;
01172 }
01173 
01174 void rpl_instance_neighbours_changed(rpl_instance_t *instance)
01175 {
01176     instance->neighbours_changed = true;
01177     if (!rpl_instance_preferred_parent(instance)) {
01178         rpl_instance_set_local_repair(instance, true);
01179     }
01180     rpl_instance_trigger_parent_selection(instance, rpl_policy_dio_parent_selection_delay(instance->domain));
01181 }
01182 
01183 static void rpl_instance_remove_parents(rpl_instance_t *instance)
01184 {
01185     ns_list_foreach(rpl_neighbour_t, n, &instance->candidate_neighbours) {
01186         n->dodag_parent = false;
01187     }
01188 }
01189 
01190 /* Convert RPL lifetime (8-bit in units) to core lifetime (32-bit seconds) */
01191 static uint32_t rpl_lifetime(uint8_t lifetime, uint16_t lifetime_unit)
01192 {
01193     return lifetime == 0xff ? 0xffffffff : lifetime * (uint32_t) lifetime_unit;
01194 }
01195 
01196 static uint32_t rpl_default_lifetime(rpl_dodag_t *dodag)
01197 {
01198     return rpl_lifetime(dodag->config.default_lifetime, dodag->config.lifetime_unit);
01199 }
01200 
01201 /* Adjust a lifetime (in seconds) downwards to account for its age */
01202 static uint32_t rpl_aged_lifetime(uint32_t lifetime, uint32_t timestamp)
01203 {
01204     if (lifetime != 0xffffffff) {
01205         uint32_t age = (protocol_core_monotonic_time - timestamp) / 10;
01206         if (age < lifetime) {
01207             lifetime -= age;
01208         } else {
01209             lifetime = 0;
01210         }
01211     }
01212     return lifetime;
01213 }
01214 
01215 static void rpl_instance_update_system_dio_route(rpl_instance_t *instance, rpl_neighbour_t *parent, rpl_dio_route_t *route)
01216 {
01217     int8_t pref;
01218     switch (route->flags & RA_PRF_MASK) {
01219         case RA_PRF_LOW: pref = -1; break;
01220         case RA_PRF_MEDIUM: pref = 0; break;
01221         case RA_PRF_HIGH: pref = 1; break;
01222         default: return;
01223     }
01224 
01225     uint8_t metric = ipv6_route_pref_to_metric(pref) + parent->dodag_pref;
01226 
01227     ipv6_route_add_metric(route->prefix, route->prefix_len, parent->interface_id, parent->ll_address, ROUTE_RPL_DIO, parent, instance->id, rpl_aged_lifetime(route->lifetime, parent->dio_timestamp), metric);
01228 }
01229 
01230 /* Called when a DIO has been received */
01231 void rpl_dodag_update_implicit_system_routes(rpl_dodag_t *dodag, rpl_neighbour_t *parent)
01232 {
01233     if (!rpl_dodag_is_current(dodag) || !parent->dodag_parent) {
01234         return;
01235     }
01236 
01237     uint32_t aged_default = rpl_aged_lifetime(rpl_default_lifetime(dodag), parent->dio_timestamp);
01238     uint8_t metric = IPV6_ROUTE_DEFAULT_METRIC + parent->dodag_pref;
01239 
01240     /* Always add the "root" default route - only used for per-instance lookup */
01241     ipv6_route_add_metric(NULL, 0, parent->interface_id, parent->ll_address, ROUTE_RPL_INSTANCE, parent, dodag->instance->id, aged_default, metric);
01242 
01243     /* Also add a specific route to the DODAGID */
01244     ipv6_route_add_metric(dodag->id, 128, parent->interface_id, parent->ll_address, ROUTE_RPL_ROOT, parent, dodag->instance->id, aged_default, metric);
01245 
01246 }
01247 
01248 /* Called when a DIO RIO route has been updated (but not the parent list) */
01249 static void rpl_dodag_update_system_route(rpl_dodag_t *dodag, rpl_dio_route_t *route)
01250 {
01251     if (!rpl_dodag_is_current(dodag)) {
01252         return;
01253     }
01254 
01255     rpl_instance_t *instance = dodag->instance;
01256 
01257     ns_list_foreach(rpl_neighbour_t, neighbour, &instance->candidate_neighbours) {
01258         if (neighbour->dodag_parent) {
01259             rpl_instance_update_system_dio_route(instance, neighbour, route);
01260         }
01261     }
01262 }
01263 
01264 /* Called when a parent has been added/updated (but not the DIO route list) */
01265 static void rpl_instance_update_system_routes_through_parent(rpl_instance_t *instance, rpl_neighbour_t *parent)
01266 {
01267     rpl_dodag_t *dodag = parent->dodag_version->dodag;
01268 
01269     rpl_dodag_update_implicit_system_routes(dodag, parent);
01270 
01271     /* Then add the specific routes listed in the DIO as ROUTE_RPL_DIO */
01272     ns_list_foreach(rpl_dio_route_t, route, &dodag->routes) {
01273         rpl_instance_update_system_dio_route(instance, parent, route);
01274     }
01275 }
01276 
01277 static void rpl_instance_remove_system_routes_through_parent(rpl_instance_t *instance, rpl_neighbour_t *parent)
01278 {
01279     (void)instance;
01280 
01281     ipv6_route_table_remove_info(parent->interface_id, ROUTE_RPL_INSTANCE, parent);
01282     ipv6_route_table_remove_info(parent->interface_id, ROUTE_RPL_DIO, parent);
01283     ipv6_route_table_remove_info(parent->interface_id, ROUTE_RPL_ROOT, parent);
01284 }
01285 
01286 static void trace_info_print(const char *fmt, ...)
01287 {
01288     va_list ap;
01289     va_start(ap, fmt);
01290     vtracef(TRACE_LEVEL_INFO, TRACE_GROUP, fmt, ap);
01291     va_end(ap);
01292 }
01293 
01294 
01295 void rpl_instance_run_parent_selection(rpl_instance_t *instance)
01296 {
01297     bool parent_set_change = false;
01298     rpl_dodag_version_t *original_version = instance->current_dodag_version;
01299     uint16_t original_rank = instance->current_rank;
01300 
01301     instance->parent_selection_timer = rpl_policy_parent_selection_period(instance->domain);
01302 
01303     if (!instance->of) {
01304         return;
01305     }
01306 
01307     if (instance->current_dodag_version && instance->current_dodag_version->dodag->root) {
01308         return;
01309     }
01310 
01311     ns_list_foreach_safe(rpl_neighbour_t, n, &instance->candidate_neighbours) {
01312         if (rpl_aged_lifetime(rpl_default_lifetime(n->dodag_version->dodag), n->dio_timestamp) == 0) {
01313             rpl_delete_neighbour(instance, n);
01314             continue;
01315         }
01316         n->old_dao_path_control = n->dao_path_control;
01317         n->dao_path_control = 0;
01318         n->was_dodag_parent = n->dodag_parent;
01319         n->dodag_parent = false;
01320         n->considered = true;
01321     }
01322 
01323     rpl_neighbour_t *original_preferred = rpl_instance_preferred_parent(instance);
01324 
01325     instance->of->parent_selection(instance);
01326 
01327     ns_list_foreach(rpl_neighbour_t, n, &instance->candidate_neighbours) {
01328         if (n->was_dodag_parent != n->dodag_parent) {
01329             tr_info("%s parent %s", n->dodag_parent ? "Added" : "Removed", trace_ipv6(n->ll_address));
01330             parent_set_change = true;
01331             /* Remove routes through a deselected parent */
01332             if (!n->dodag_parent) {
01333                 rpl_instance_remove_system_routes_through_parent(instance, n);
01334             }
01335         }
01336         /* Always re-run route update (in case of changed preference values) */
01337         if (n->dodag_parent) {
01338             rpl_instance_update_system_routes_through_parent(instance, n);
01339         }
01340         n->was_dodag_parent = false;
01341     }
01342 
01343     rpl_neighbour_t *preferred_parent = rpl_instance_preferred_parent(instance);
01344 
01345     if (preferred_parent) {
01346         rpl_control_disable_ra_routes(instance->domain);
01347     } else {
01348         rpl_instance_poison(instance, rpl_policy_repair_poison_count(instance->domain));
01349     }
01350 
01351     if (original_preferred != preferred_parent) {
01352         protocol_stats_update(STATS_RPL_PARENT_CHANGE, 1);
01353     }
01354 
01355     // Sets new preferred parent
01356     if (preferred_parent) {
01357         ipv6_map_ip_to_ll_and_call_ll_addr_handler(NULL, preferred_parent->interface_id, NULL, preferred_parent->ll_address,
01358             protocol_6lowpan_neighbor_priority_set);
01359         // Sets secondary parent
01360         rpl_neighbour_t *sec_parent = ns_list_get_next(&instance->candidate_neighbours, preferred_parent);
01361         if (sec_parent && sec_parent->dodag_parent) {
01362             ipv6_map_ip_to_ll_and_call_ll_addr_handler(NULL, sec_parent->interface_id, NULL, sec_parent->ll_address,
01363                 protocol_6lowpan_neighbor_second_priority_set);
01364         } else {
01365             protocol_6lowpan_neighbor_priority_clear_all(preferred_parent->interface_id, PRIORITY_2ND);
01366         }
01367     }
01368 
01369     rpl_instance_set_local_repair(instance, preferred_parent == NULL);
01370 
01371     if (rpl_instance_mop(instance) != RPL_MODE_NO_DOWNWARD) {
01372         rpl_downward_process_dao_parent_changes(instance);
01373     }
01374 
01375     /* Anyone who's not a parent can be pruned now (eg bad link cost) */
01376     ns_list_foreach_safe(rpl_neighbour_t, n, &instance->candidate_neighbours) {
01377         if (n->dodag_parent) {
01378             continue;
01379         }
01380         if (!instance->of->neighbour_acceptable(instance, n)) {
01381             rpl_delete_neighbour(instance, n);
01382         }
01383     }
01384     rpl_control_print(trace_info_print);
01385     /* Changing DODAG version is an inconsistency */
01386     if (original_version != instance->current_dodag_version) {
01387         rpl_instance_inconsistency(instance);
01388         return;
01389     }
01390 
01391     /* Check for "consistent" indication as per RFC 6550 8.3 - if not
01392      * "consistent" by this definition, we reset Trickle consistency counter,
01393      * and do not process any more consistency events this interval.
01394      * See comments in rpl_control_dio_handler() for more info.
01395      */
01396     if (parent_set_change ||
01397         original_preferred != preferred_parent ||
01398         !(instance->current_dodag_version &&
01399           (rpl_rank_compare(instance->current_dodag_version->dodag, original_rank, instance->current_rank) & RPL_CMP_EQUAL))) {
01400         instance->dio_not_consistent = true;
01401         instance->dio_timer.c = 0;
01402     }
01403 }
01404 
01405 void rpl_instance_remove_interface(rpl_instance_t *instance, int8_t if_id)
01406 {
01407     ns_list_foreach_safe(rpl_neighbour_t, neighbour, &instance->candidate_neighbours) {
01408         if (neighbour->interface_id == if_id) {
01409             rpl_delete_neighbour(instance, neighbour);
01410         }
01411     }
01412 }
01413 
01414 /* Trigger DIO transmission - all interfaces multicast if addr+cur are NULL, else unicast */
01415 void rpl_instance_dio_trigger(rpl_instance_t *instance, protocol_interface_info_entry_t *cur, const uint8_t *addr)
01416 {
01417     uint16_t rank = instance->current_rank;
01418     rpl_dodag_version_t *dodag_version = instance->current_dodag_version;
01419     rpl_dodag_t *dodag;
01420     if (dodag_version) {
01421         dodag = dodag_version->dodag;
01422     } else if (instance->poison_count) {
01423         /* When poisoning, we can send using data from an arbitrary DODAG/version */
01424         dodag = ns_list_get_first(&instance->dodags);
01425         if (dodag) {
01426             dodag_version = ns_list_get_first(&dodag->versions);
01427         }
01428     } else {
01429         dodag = NULL;
01430     }
01431 
01432     if (!dodag || !dodag_version) {
01433         return;
01434     }
01435 
01436     if (instance->poison_count) {
01437         instance->poison_count--;
01438         rank = RPL_RANK_INFINITE;
01439         tr_debug("Poison count -> set RPL_RANK_INFINITE");
01440     }
01441 
01442     // Always send config in unicasts (as required), never in multicasts (optional)
01443     rpl_dodag_conf_t *conf = addr ? &dodag->config : NULL;
01444 
01445     rpl_control_transmit_dio(instance->domain, cur, instance->id, dodag_version->number, rank, dodag->g_mop_prf, instance->dtsn, dodag, dodag->id, conf, addr);
01446 
01447     dodag_version->last_advertised_rank = rank;
01448 
01449     /* When we advertise a new lowest rank, need to re-evaluate our rank limits */
01450     if (rank < dodag_version->lowest_advertised_rank) {
01451         dodag_version->lowest_advertised_rank = rank;
01452         dodag_version->hard_rank_limit = rpl_rank_add(rank, dodag->config.dag_max_rank_increase);
01453     }
01454     rpl_dodag_version_limit_greediness(dodag_version, rank);
01455 
01456     instance->last_advertised_dodag_version = dodag_version;
01457 }
01458 
01459 static void rpl_instance_dis_timer(rpl_instance_t *instance, uint16_t seconds)
01460 {
01461     if (instance->repair_dis_timer > seconds) {
01462          instance->repair_dis_timer -= seconds;
01463      } else if (instance->repair_dis_timer != 0){
01464          tr_debug("Timed repair DIS %d", instance->id);
01465          instance->repair_dis_timer = 0;
01466          instance->repair_dis_count++;
01467          rpl_control_transmit_dis(instance->domain, NULL, RPL_SOLINFO_PRED_INSTANCEID, instance->id, NULL, 0, NULL);
01468          if (instance->repair_dis_count < rpl_policy_repair_dis_count(instance->domain)) {
01469              uint16_t t = rpl_policy_repair_initial_dis_delay(instance->domain);
01470              uint16_t max = rpl_policy_repair_maximum_dis_interval(instance->domain);
01471              for (uint_fast8_t n = instance->repair_dis_count; n; n--) {
01472                  if (t < 0x8000 && t < max) {
01473                      t <<= 1;
01474                  } else {
01475                      t = max;
01476                      break;
01477                  }
01478              }
01479              if (t > max) {
01480                  t = max;
01481              }
01482              instance->repair_dis_timer = t;
01483          } else {
01484              rpl_control_event(instance->domain, RPL_EVENT_LOCAL_REPAIR_NO_MORE_DIS);
01485          }
01486      }
01487 }
01488 
01489 void rpl_instance_set_local_repair(rpl_instance_t *instance, bool repair)
01490 {
01491     if (instance->local_repair == repair) {
01492         return;
01493     }
01494 
01495     instance->local_repair = repair;
01496 
01497     if (repair) {
01498         /* Notify RPL user to state switch */
01499         rpl_control_event(instance->domain, RPL_EVENT_LOCAL_REPAIR_START);
01500         protocol_stats_update(STATS_RPL_LOCAL_REPAIR, 1);
01501         instance->repair_dis_timer = rpl_policy_repair_initial_dis_delay(instance->domain);
01502         instance->repair_dis_count = 0;
01503     } else {
01504         instance->repair_dis_timer = 0;
01505     }
01506 
01507     /* When repair ends, eliminate all higher-rank neighbours (potential sub-DODAG) from table */
01508     if (!repair && instance->current_dodag_version) {
01509         ns_list_foreach_safe(rpl_neighbour_t, neighbour, &instance->candidate_neighbours) {
01510             if (rpl_dodag_version_rank_indicates_possible_sub_dodag(neighbour->dodag_version, neighbour->rank)) {
01511                 rpl_delete_neighbour(instance, neighbour);
01512             }
01513         }
01514     }
01515 }
01516 
01517 bool rpl_instance_local_repair(const rpl_instance_t *instance)
01518 {
01519     return instance->local_repair;
01520 }
01521 
01522 uint16_t rpl_instance_current_rank(const rpl_instance_t *instance)
01523 {
01524     return instance->current_rank;
01525 }
01526 
01527 void rpl_instance_slow_timer(rpl_instance_t *instance, uint16_t seconds)
01528 {
01529     ns_list_foreach(rpl_dodag_t, dodag, &instance->dodags) {
01530         rpl_dodag_slow_timer(dodag, seconds);
01531     }
01532     rpl_instance_parent_selection_timer(instance, seconds);
01533     if (!rpl_instance_preferred_parent(instance)) {
01534         protocol_stats_update(STATS_RPL_TIME_NO_NEXT_HOP, 1);
01535         rpl_instance_dis_timer(instance, seconds);
01536     }
01537 }
01538 
01539 void rpl_upward_dio_timer(rpl_instance_t *instance, uint16_t ticks)
01540 {
01541     rpl_dodag_version_t *dodag_version = instance->current_dodag_version;
01542     rpl_dodag_t *dodag;
01543     if (dodag_version) {
01544         dodag = dodag_version->dodag;
01545     } else if (instance->poison_count) {
01546         /* When poisoning, we can send using data from an arbitrary DODAG/version */
01547         dodag = ns_list_get_first(&instance->dodags);
01548         if (dodag) {
01549             dodag_version = ns_list_get_first(&dodag->versions);
01550         }
01551     } else {
01552         dodag = NULL;
01553     }
01554 
01555     if (!dodag || !dodag_version) {
01556         return;
01557     }
01558 
01559     /* Leaves don't send normal periodic DIOs */
01560     if (rpl_dodag_am_leaf(dodag) && !instance->poison_count) {
01561         return;
01562     }
01563     if (trickle_timer(&instance->dio_timer, &dodag->dio_timer_params, ticks)) {
01564         instance->dio_not_consistent = false;
01565         rpl_instance_dio_trigger(instance, NULL, NULL);
01566     }
01567 }
01568 
01569 void rpl_upward_print_neighbour(const rpl_neighbour_t *neighbour, route_print_fn_t *print_fn)
01570 {
01571     uint16_t path_cost;
01572     if (neighbour->dodag_version->dodag->instance->of) {
01573         path_cost = neighbour->dodag_version->dodag->instance->of->path_cost(neighbour);
01574     } else {
01575         path_cost = 0xFFFF;
01576     }
01577 
01578     ROUTE_PRINT_ADDR_STR_BUFFER_INIT(addr_str_ll);
01579     ROUTE_PRINT_ADDR_STR_BUFFER_INIT(addr_str_global);
01580     print_fn("   %2.0d%c%04x %04x %02x %s%%%d (%s)",
01581            neighbour->dodag_parent ? neighbour->dodag_pref + 1 : 0,
01582            neighbour->dodag_version && rpl_instance_preferred_parent(neighbour->dodag_version->dodag->instance) == neighbour ? '*' : ' ',
01583            neighbour->rank,
01584            path_cost,
01585            neighbour->dao_path_control,
01586            ROUTE_PRINT_ADDR_STR_FORMAT(addr_str_ll, neighbour->ll_address), neighbour->interface_id,
01587            ROUTE_PRINT_ADDR_STR_FORMAT(addr_str_global, neighbour->global_address));
01588 }
01589 
01590 void rpl_upward_print_neighbours_in_version(const rpl_neighbour_list_t *list, const rpl_dodag_version_t *version, route_print_fn_t *print_fn)
01591 {
01592     ns_list_foreach(rpl_neighbour_t, neighbour, list) {
01593         if (neighbour->dodag_version == version) {
01594             rpl_upward_print_neighbour(neighbour, print_fn);
01595         }
01596     }
01597 }
01598 
01599 void rpl_upward_print_dodag(rpl_instance_t *instance, rpl_dodag_t *dodag, route_print_fn_t *print_fn)
01600 {
01601     /* Summary */
01602     ROUTE_PRINT_ADDR_STR_BUFFER_INIT(addr_str);
01603     print_fn("DODAG %s", ROUTE_PRINT_ADDR_STR_FORMAT(addr_str, dodag->id));
01604     print_fn("  G=%d MOP=%d Prf=%d", dodag->g_mop_prf & RPL_GROUNDED ? 1 : 0,
01605                                    (dodag->g_mop_prf & RPL_MODE_MASK) >> RPL_MODE_SHIFT,
01606                                    (dodag->g_mop_prf & RPL_DODAG_PREF_MASK));
01607     /* Routes */
01608     ns_list_foreach(rpl_dio_route_t, route, &dodag->routes) {
01609         uint8_t addr[16] = { 0 } ;
01610         int_fast8_t pref;
01611         switch (route->flags & RA_PRF_MASK) {
01612             case RA_PRF_LOW:    pref = -1; break;
01613             case RA_PRF_MEDIUM: pref = 0; break;
01614             case RA_PRF_HIGH:   pref = 1; break;
01615             default:            pref = 2; break;
01616         }
01617         bitcopy(addr, route->prefix, route->prefix_len);
01618         if (route->lifetime == 0xFFFFFFFF) {
01619             print_fn("%24s/%-3u lifetime:infinite pref:%"PRIdFAST8,
01620                     ROUTE_PRINT_ADDR_STR_FORMAT(addr_str, addr), route->prefix_len, pref);
01621 
01622         } else {
01623             print_fn("%24s/%-3u lifetime:%"PRIu32" pref:%"PRIdFAST8,
01624                     ROUTE_PRINT_ADDR_STR_FORMAT(addr_str, addr), route->prefix_len, route->lifetime, pref);
01625         }
01626     }
01627     /* Prefixes */
01628     ns_list_foreach(prefix_entry_t, prefix, &dodag->prefixes) {
01629         uint8_t addr[16] = { 0 } ;
01630         bitcopy(addr, prefix->prefix, prefix->prefix_len);
01631         if (prefix->lifetime == 0xFFFFFFFF) {
01632             print_fn("%24s/%-3u lifetime:infinite flags:%c%c%c",
01633                    ROUTE_PRINT_ADDR_STR_FORMAT(addr_str, addr), prefix->prefix_len,
01634                    prefix->options & PIO_L ? 'L' : '-',
01635                    prefix->options & PIO_A ? 'A' : '-',
01636                    prefix->options & RPL_PIO_PUBLISHED ? '*' : ' '
01637                   );
01638         } else {
01639             print_fn("%24s/%-3u lifetime:%"PRIu32" flags:%c%c%c",
01640                    ROUTE_PRINT_ADDR_STR_FORMAT(addr_str, addr), prefix->prefix_len, prefix->lifetime,
01641                    prefix->options & PIO_L ? 'L' : '-',
01642                    prefix->options & PIO_A ? 'A' : '-',
01643                    prefix->options & RPL_PIO_PUBLISHED ? '*' : ' '
01644                   );
01645         }
01646     }
01647     /* Versions */
01648     ns_list_foreach(rpl_dodag_version_t, version, &dodag->versions) {
01649         print_fn("  Version %d", version->number);
01650         if (version == instance->current_dodag_version) {
01651             print_fn ("  *Current version* Rank=%04x", instance->current_rank);
01652         }
01653         rpl_upward_print_neighbours_in_version(&instance->candidate_neighbours, version, print_fn);
01654     }
01655 }
01656 
01657 void rpl_upward_print_instance(rpl_instance_t *instance, route_print_fn_t *print_fn)
01658 {
01659     print_fn("RPL Instance %d", instance->id);
01660     print_fn("---------------");
01661     ns_list_foreach(rpl_dodag_t, dodag, &instance->dodags) {
01662         rpl_upward_print_dodag(instance, dodag, print_fn);
01663     }
01664     if (instance->current_dodag_version) {
01665         const trickle_params_t *params = &instance->current_dodag_version->dodag->dio_timer_params;
01666         const trickle_t *timer = &instance->dio_timer;
01667 
01668         print_fn("DIO trickle Imin=%d, Imax=%d, k=%d", params->Imin, params->Imax, params->k);
01669         print_fn("            I=%d, now=%d, t=%d, c=%d", timer->I, timer->now, timer->t, timer->c);
01670     }
01671 }
01672 
01673 uint16_t rpl_upward_read_dao_target_list_size(const rpl_instance_t *instance)
01674 {
01675     return ns_list_count(&instance->dao_targets);
01676 }
01677 
01678 /* Backwards-compatibility implementation of net_rpl.h API designed for old implementation */
01679 bool rpl_upward_read_dodag_info(const rpl_instance_t *instance, rpl_dodag_info_t *dodag_info)
01680 {
01681     rpl_dodag_version_t *version = rpl_instance_current_dodag_version(instance);
01682     if (!version) {
01683         return false;
01684     }
01685 
01686     rpl_dodag_t *dodag = version->dodag;
01687 
01688     memcpy(dodag_info->dodag_id, dodag->id, 16);
01689     dodag_info->instance_id = instance->id;
01690     dodag_info->flags = dodag->g_mop_prf;
01691     dodag_info->version_num = version->number;
01692     dodag_info->DTSN = instance->dtsn;
01693     dodag_info->curent_rank = instance->current_rank;
01694     dodag_info->parent_flags = 0;
01695     dodag_info->dag_min_hop_rank_inc = dodag->config.min_hop_rank_increase;
01696 
01697     rpl_neighbour_t *pref_parent = rpl_instance_preferred_parent(instance);
01698     if (!pref_parent) {
01699         return true;
01700     }
01701 
01702     dodag_info->parent_flags |= RPL_PRIMARY_PARENT_SET;
01703     memcpy(dodag_info->primary_parent,
01704            pref_parent->have_global_address ? pref_parent->global_address
01705                                             : pref_parent->ll_address,
01706            16);
01707     dodag_info->primary_parent_rank = pref_parent->rank;
01708 
01709     rpl_neighbour_t *sec_parent = ns_list_get_next(&instance->candidate_neighbours, pref_parent);
01710     if (!sec_parent || !sec_parent->dodag_parent) {
01711         return true;
01712     }
01713 
01714     dodag_info->parent_flags |= RPL_SECONDARY_PARENT_SET;
01715     memcpy(dodag_info->secondary_parent,
01716            sec_parent->have_global_address ? sec_parent->global_address
01717                                             : sec_parent->ll_address,
01718            16);
01719     dodag_info->secondary_parent_rank = sec_parent->rank;
01720 
01721     return true;
01722 }
01723 
01724 #endif /* HAVE_RPL */