BA
/
BaBoRo1
Embed:
(wiki syntax)
Show/hide line numbers
rpl_upward.c
00001 /* 00002 * Copyright (c) 2015-2017, 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 ns_list_foreach_safe(rpl_neighbour_t, neighbour, &instance->candidate_neighbours) { 00546 if (neighbour->dodag_version == version) { 00547 rpl_delete_neighbour(instance, neighbour); 00548 } 00549 } 00550 ns_list_remove(&dodag->versions, version); 00551 rpl_free(version, sizeof(*version)); 00552 } 00553 00554 bool rpl_dodag_version_is_current(const rpl_dodag_version_t *version) 00555 { 00556 return version->dodag->instance->current_dodag_version == version; 00557 } 00558 00559 bool rpl_dodag_version_rank_indicates_possible_sub_dodag(const rpl_dodag_version_t *version, uint16_t rank) 00560 { 00561 if (!rpl_dodag_version_is_current(version)) { 00562 return false; 00563 } 00564 rpl_cmp_t cmp = rpl_rank_compare(version->dodag, rank, version->dodag->instance->current_rank); 00565 return cmp & RPL_CMP_GREATER; 00566 } 00567 00568 rpl_cmp_t rpl_dodag_version_compare(const rpl_dodag_version_t *a, const rpl_dodag_version_t *b) 00569 { 00570 if (a == NULL || b == NULL) { 00571 return RPL_CMP_UNORDERED; 00572 } 00573 if (a == b) { 00574 return RPL_CMP_EQUAL; 00575 } 00576 if (a->dodag != b->dodag) { 00577 return RPL_CMP_UNORDERED; 00578 } 00579 return rpl_seq_compare(a->number, b->number); 00580 } 00581 00582 void rpl_dodag_version_limit_greediness(rpl_dodag_version_t *version, uint16_t rank) 00583 { 00584 /* Apply RPL greediness limit rule - after we've joined a DODAG version, 00585 * we don't increase rank unless required to by an existing parent. 00586 */ 00587 if (rank != RPL_RANK_INFINITE && version->greediness_rank_limit == RPL_RANK_INFINITE) { 00588 version->greediness_rank_limit = rpl_rank_max_at_level(version->dodag, rank); 00589 } 00590 } 00591 00592 void rpl_dodag_version_raise_greediness(rpl_dodag_version_t *version, uint16_t pref_rank) 00593 { 00594 /* This can be called during parent selection, after preferred parent is chosen, 00595 * to potentially increase the greediness limit considering the new parent circumstance. 00596 * Eg, if our initial and current Rank was 1, our greediness limit would have 00597 * been 1.99. But if we've just had to increase Rank to 2 for an existing 00598 * parent, then we can raise the limit. 00599 */ 00600 if (version->greediness_rank_limit < pref_rank) { 00601 version->greediness_rank_limit = rpl_rank_max_at_level(version->dodag, pref_rank); 00602 } 00603 } 00604 00605 rpl_dodag_t *rpl_create_dodag(rpl_instance_t *instance, const uint8_t *dodagid, uint8_t g_mop_prf) 00606 { 00607 rpl_dodag_t *dodag = rpl_alloc(sizeof(rpl_dodag_t)); 00608 if (!dodag) { 00609 return NULL; 00610 } 00611 00612 dodag->instance = instance; 00613 dodag->timestamp = protocol_core_monotonic_time; 00614 memcpy(dodag->id, dodagid, 16); 00615 dodag->leaf = false; 00616 dodag->root = false; 00617 dodag->have_config = false; 00618 dodag->used = false; 00619 dodag->g_mop_prf = g_mop_prf; 00620 // Default timer parameters and trickle start should never normally 00621 // be used - we would set the parameters from the DODAG Config and start 00622 // as we join a version. But initialising here catches odd cases where 00623 // we end up sending poison DIOs before we get any config. 00624 dodag->dio_timer_params.Imin = RPL_DEFAULT_IMIN_TICKS; 00625 dodag->dio_timer_params.Imax = (trickle_time_t) (RPL_DEFAULT_IMAX_TICKS < TRICKLE_TIME_MAX ? RPL_DEFAULT_IMAX_TICKS : TRICKLE_TIME_MAX); 00626 dodag->dio_timer_params.k = 10; 00627 trickle_start(&instance->dio_timer, &dodag->dio_timer_params); 00628 ns_list_init(&dodag->versions); 00629 ns_list_init(&dodag->routes); 00630 ns_list_init(&dodag->prefixes); 00631 00632 ns_list_add_to_start(&instance->dodags, dodag); 00633 return dodag; 00634 } 00635 00636 void rpl_delete_dodag(rpl_dodag_t *dodag) 00637 { 00638 rpl_instance_t *instance = dodag->instance; 00639 ns_list_foreach_safe(rpl_dodag_version_t, version, &dodag->versions) { 00640 rpl_delete_dodag_version(version); 00641 } 00642 ns_list_foreach_safe(rpl_dio_route_t, route, &dodag->routes) { 00643 rpl_dodag_delete_dio_route(dodag, route); 00644 } 00645 ns_list_foreach_safe(prefix_entry_t, prefix, &dodag->prefixes) { 00646 rpl_dodag_delete_dio_prefix(dodag, prefix); 00647 } 00648 ns_list_remove(&instance->dodags, dodag); 00649 rpl_free(dodag, sizeof(*dodag)); 00650 } 00651 00652 /* Convert RPL configuration to generic trickle parameters. Returns true if 00653 * the value in the generic object has changed. 00654 */ 00655 static bool rpl_dodag_conf_convert_trickle_parameters(trickle_params_t *params_out, const rpl_dodag_conf_t *conf) 00656 { 00657 /* Convert trickle parameters into 100ms ticks */ 00658 uint32_t Imin_ms = conf->dio_interval_min < 32 ? (1ul << conf->dio_interval_min) : 0xfffffffful; 00659 uint32_t Imin_ticks = (Imin_ms + 99) / 100; 00660 uint32_t Imax_ms = (conf->dio_interval_min + conf->dio_interval_doublings) < 32 ? 00661 (1ul << (conf->dio_interval_min + conf->dio_interval_doublings)) : 0xfffffffful; 00662 uint32_t Imax_ticks = (Imax_ms + 99) / 100; 00663 trickle_time_t Imin = (trickle_time_t) (Imin_ticks <= TRICKLE_TIME_MAX ? Imin_ticks : TRICKLE_TIME_MAX); 00664 trickle_time_t Imax = (trickle_time_t) (Imax_ticks <= TRICKLE_TIME_MAX ? Imax_ticks : TRICKLE_TIME_MAX); 00665 uint8_t k = conf->dio_redundancy_constant; 00666 if (params_out->Imin != Imin || params_out->Imax != Imax || params_out->k != k) { 00667 params_out->Imin = Imin; 00668 params_out->Imax = Imax; 00669 params_out->k = k; 00670 params_out->TimerExpirations = TRICKLE_EXPIRATIONS_INFINITE; 00671 return true; 00672 } else { 00673 return false; 00674 } 00675 } 00676 00677 uint8_t rpl_dodag_mop(const rpl_dodag_t *dodag) 00678 { 00679 return dodag->g_mop_prf & RPL_MODE_MASK; 00680 } 00681 00682 bool rpl_dodag_update_config(rpl_dodag_t *dodag, const rpl_dodag_conf_t *conf, const uint8_t *src, bool *become_leaf) 00683 { 00684 /* If already have config, don't update unless it's coming from preferred parent */ 00685 if (dodag->have_config) { 00686 rpl_neighbour_t *parent = rpl_instance_preferred_parent(dodag->instance); 00687 if (parent && src && !addr_ipv6_equal(src, parent->ll_address)) { 00688 return true; 00689 } 00690 } 00691 dodag->config = *conf; 00692 bool restart_timer = rpl_dodag_conf_convert_trickle_parameters(&dodag->dio_timer_params, conf); 00693 dodag->have_config = true; 00694 if (restart_timer && rpl_instance_current_dodag(dodag->instance) == dodag) { 00695 /* They've changed the timing parameters for our currently-in-use trickle timer! */ 00696 tr_warn("Trickle parameters changed"); 00697 trickle_start(&dodag->instance->dio_timer, &dodag->dio_timer_params); 00698 } 00699 dodag->instance->of = rpl_objective_lookup(conf->objective_code_point); 00700 /* We could be a leaf of an unknown OCP. Still need an OF to choose parents */ 00701 if (!dodag->instance->of) { 00702 dodag->instance->of = rpl_objective_lookup(RPL_OCP_OF0); 00703 if (!dodag->instance->of) { 00704 return false; 00705 } 00706 if (become_leaf) { 00707 *become_leaf = true; 00708 } 00709 } 00710 return true; 00711 } 00712 00713 /* If we're currently a member of this DODAG, kick the DIO timers */ 00714 void rpl_dodag_inconsistency(rpl_dodag_t *dodag) 00715 { 00716 if (rpl_instance_current_dodag(dodag->instance) == dodag) { 00717 trickle_inconsistent_heard(&dodag->instance->dio_timer, &dodag->dio_timer_params); 00718 } 00719 } 00720 00721 void rpl_dodag_increment_dtsn(rpl_dodag_t *dodag) 00722 { 00723 if (rpl_instance_current_dodag(dodag->instance) == dodag) { 00724 rpl_instance_increment_dtsn(dodag->instance); 00725 } 00726 } 00727 00728 void rpl_dodag_set_pref(rpl_dodag_t *dodag, uint8_t pref) 00729 { 00730 dodag->g_mop_prf &= ~RPL_DODAG_PREF_MASK; 00731 dodag->g_mop_prf |= pref; 00732 } 00733 00734 rpl_cmp_t rpl_dodag_pref_compare(const rpl_dodag_t *a, const rpl_dodag_t *b) 00735 { 00736 uint8_t pref_a, pref_b; 00737 pref_a = a->g_mop_prf & RPL_DODAG_PREF_MASK; 00738 pref_b = b->g_mop_prf & RPL_DODAG_PREF_MASK; 00739 if (pref_a == pref_b) { 00740 return RPL_CMP_EQUAL; 00741 } else if (pref_a < pref_b) { 00742 return RPL_CMP_LESS; 00743 } else { 00744 return RPL_CMP_GREATER; 00745 } 00746 } 00747 00748 void rpl_dodag_set_root(rpl_dodag_t *dodag, bool root) 00749 { 00750 if (root != dodag->root) { 00751 dodag->root = root; 00752 if (root) { 00753 rpl_instance_remove_parents(dodag->instance); 00754 } else { 00755 rpl_instance_run_parent_selection(dodag->instance); 00756 } 00757 } 00758 } 00759 00760 #ifdef HAVE_RPL_ROOT 00761 bool rpl_dodag_am_root(const rpl_dodag_t *dodag) 00762 { 00763 return dodag->root; 00764 } 00765 #endif 00766 00767 void rpl_dodag_set_leaf(rpl_dodag_t *dodag, bool leaf) 00768 { 00769 dodag->leaf = leaf; 00770 } 00771 00772 bool rpl_dodag_am_leaf(const rpl_dodag_t *dodag) 00773 { 00774 return dodag->leaf || dodag->instance->domain->force_leaf; 00775 } 00776 00777 bool rpl_dodag_is_current(const rpl_dodag_t *dodag) 00778 { 00779 return dodag->instance->current_dodag_version && dodag->instance->current_dodag_version->dodag == dodag; 00780 } 00781 00782 const rpl_dodag_conf_t *rpl_dodag_get_config(const rpl_dodag_t *dodag) 00783 { 00784 return dodag->have_config ? &dodag->config : NULL; 00785 } 00786 00787 uint8_t rpl_dodag_get_version_number_as_root(const rpl_dodag_t *dodag) 00788 { 00789 return dodag->instance->current_dodag_version->number; 00790 } 00791 00792 void rpl_dodag_set_version_number_as_root(rpl_dodag_t *dodag, uint8_t number) 00793 { 00794 /* As root, unlike other nodes, we don't need to worry about maintaining 00795 * multiple rpl_dodag_version_t to track neighbours in each version and 00796 * our ranks in each version. We'll just quietly change the version number 00797 * of our only, always-current version. 00798 */ 00799 rpl_dodag_version_t *version = dodag->instance->current_dodag_version; 00800 version->number = number; 00801 rpl_dodag_inconsistency(dodag); 00802 #if defined FEA_TRACE_SUPPORT && defined EXTRA_CONSISTENCY_CHECKS 00803 /* Sanity check that the above assertion is true - as root we shouldn't have 00804 * any neighbours referring to this version. 00805 */ 00806 ns_list_foreach(rpl_neighbour_t, n, &dodag->instance->candidate_neighbours) { 00807 if (n->dodag_version == version) { 00808 tr_err("Root DODAG version had neighbour"); 00809 break; 00810 } 00811 } 00812 #endif 00813 protocol_stats_update(STATS_RPL_GLOBAL_REPAIR, 1); 00814 } 00815 00816 static bool rpl_dodag_in_use(const rpl_dodag_t *dodag) 00817 { 00818 if (dodag->root) { 00819 return true; 00820 } 00821 00822 if (!dodag->have_config) { 00823 return false; 00824 } 00825 00826 rpl_instance_t *instance = dodag->instance; 00827 ns_list_foreach(rpl_neighbour_t, neighbour, &instance->candidate_neighbours) { 00828 if (neighbour->dodag_version->dodag == dodag) { 00829 return true; 00830 } 00831 } 00832 00833 return false; 00834 } 00835 00836 const rpl_dio_route_list_t *rpl_dodag_get_route_list(const rpl_dodag_t *dodag) 00837 { 00838 return &dodag->routes; 00839 } 00840 00841 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) 00842 { 00843 rpl_dio_route_t *route = NULL; 00844 bool update = false; 00845 00846 /* First look for matching prefix */ 00847 ns_list_foreach(rpl_dio_route_t, r, &dodag->routes) { 00848 if (r->prefix_len == prefix_len && bitsequal(r->prefix, prefix, prefix_len)) { 00849 route = r; 00850 break; 00851 } 00852 } 00853 00854 /* If not found, create a new one */ 00855 if (!route) { 00856 uint_fast8_t prefix_bytes = (prefix_len + 7u) / 8u; 00857 route = rpl_alloc(sizeof(rpl_dio_route_t) + prefix_bytes); 00858 if (!route) { 00859 return NULL; 00860 } 00861 00862 route->prefix_len = prefix_len; 00863 bitcopy0(route->prefix, prefix, prefix_len); 00864 ns_list_add_to_end(&dodag->routes, route); 00865 update = true; 00866 } else { 00867 if (lifetime != route->lifetime || route->flags != flags) { 00868 update = true; 00869 } 00870 } 00871 00872 /* Update other info */ 00873 route->lifetime = lifetime; 00874 route->hold_count = lifetime ? 0 : RPL_MAX_FINAL_RTR_ADVERTISEMENTS; 00875 route->flags = flags; 00876 route->age = age; 00877 if (update) { 00878 rpl_dodag_update_system_route(dodag, route); 00879 } 00880 return route; 00881 } 00882 00883 void rpl_dodag_delete_dio_route(rpl_dodag_t *dodag, rpl_dio_route_t *route) 00884 { 00885 ns_list_remove(&dodag->routes, route); 00886 uint_fast8_t prefix_bytes = (route->prefix_len + 7u) / 8u; 00887 rpl_free(route, sizeof *route + prefix_bytes); 00888 } 00889 00890 static void rpl_dodag_age_routes(rpl_dodag_t *dodag, uint16_t seconds) 00891 { 00892 ns_list_foreach_safe(rpl_dio_route_t, route, &dodag->routes) { 00893 if (route->age && route->lifetime != 0xFFFFFFFF) { 00894 if (route->lifetime > seconds) { 00895 route->lifetime -= seconds; 00896 } else { 00897 route->lifetime = 0; 00898 if (route->hold_count == 0) { 00899 rpl_dodag_delete_dio_route(dodag, route); 00900 } 00901 } 00902 } 00903 } 00904 } 00905 00906 const prefix_list_t *rpl_dodag_get_prefix_list(const rpl_dodag_t *dodag) 00907 { 00908 return &dodag->prefixes; 00909 } 00910 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) 00911 { 00912 /* Don't let them set funny flags - we won't propagate them either. 00913 * Is not propagating them sensible? RFC isn't clear. Anyway, we use 00914 * the spare 5 flag bits internally... 00915 */ 00916 flags &= (PIO_R|PIO_A|PIO_L); 00917 00918 if (publish) { 00919 flags |= RPL_PIO_PUBLISHED; 00920 } 00921 00922 if (age) { 00923 flags |= RPL_PIO_AGE; 00924 } 00925 00926 if (lifetime == 0) { 00927 flags |= RPL_MAX_FINAL_RTR_ADVERTISEMENTS; 00928 } 00929 00930 prefix_entry_t *entry = icmpv6_prefix_add(&dodag->prefixes, prefix, prefix_len, lifetime, preftime, flags); 00931 /* icmpv6_prefix_add indicates a new entry by leaving options set to 0xFF */ 00932 if (entry) { 00933 entry->options = flags; 00934 } 00935 return entry; 00936 } 00937 00938 void rpl_dodag_delete_dio_prefix(rpl_dodag_t *dodag, prefix_entry_t *prefix) 00939 { 00940 ns_list_remove(&dodag->prefixes, prefix); 00941 ns_dyn_mem_free(prefix); 00942 } 00943 00944 static void rpl_dodag_age_prefixes(rpl_dodag_t *dodag, uint16_t seconds) 00945 { 00946 ns_list_foreach_safe(prefix_entry_t, prefix, &dodag->prefixes) { 00947 if (!(prefix->options & RPL_PIO_AGE)) { 00948 continue; 00949 } 00950 if (prefix->preftime != 0xFFFFFFFF) { 00951 if (prefix->preftime > seconds) { 00952 prefix->preftime -= seconds; 00953 } else { 00954 prefix->preftime = 0; 00955 } 00956 } 00957 if (prefix->lifetime != 0xFFFFFFFF) { 00958 if (prefix->lifetime > seconds) { 00959 prefix->lifetime -= seconds; 00960 } else { 00961 prefix->lifetime = 0; 00962 if ((prefix->options & RPL_PIO_HOLD_MASK) == 0) { 00963 rpl_dodag_delete_dio_prefix(dodag, prefix); 00964 } 00965 } 00966 } 00967 } 00968 } 00969 00970 static void rpl_dodag_slow_timer(rpl_dodag_t *dodag, uint16_t seconds) 00971 { 00972 rpl_dodag_age_routes(dodag, seconds); 00973 rpl_dodag_age_prefixes(dodag, seconds); 00974 } 00975 00976 00977 /* Look up a RPL instance by identifier. If the ID is local, addr must 00978 * identify the root - eg by providing either the source destination address of 00979 * a packet (depending on the 'O' flag). 00980 */ 00981 rpl_instance_t *rpl_lookup_instance(const rpl_domain_t *domain, uint8_t instance_id, const uint8_t *addr) 00982 { 00983 if (rpl_instance_id_is_local(instance_id)) { 00984 instance_id &= ~RPL_INSTANCE_DEST; 00985 } 00986 00987 ns_list_foreach(rpl_instance_t, instance, &domain->instances) { 00988 /* First match the instance ID */ 00989 if (instance->id != instance_id) { 00990 continue; 00991 } 00992 /* If it's a global ID, this is a match */ 00993 if (rpl_instance_id_is_global(instance_id)) { 00994 return instance; 00995 } 00996 /* If it's a local ID, address must match */ 00997 const rpl_dodag_t *dodag = ns_list_get_first(&instance->dodags); 00998 if (addr && dodag && addr_ipv6_equal(dodag->id, addr)) { 00999 return instance; 01000 } 01001 } 01002 return NULL; 01003 } 01004 01005 rpl_instance_t *rpl_create_instance(rpl_domain_t *domain, uint8_t instance_id) 01006 { 01007 rpl_instance_t *instance = rpl_alloc(sizeof(rpl_instance_t)); 01008 if (!instance) { 01009 return NULL; 01010 } 01011 ns_list_init(&instance->dodags); 01012 ns_list_init(&instance->candidate_neighbours); 01013 ns_list_init(&instance->dao_targets); 01014 instance->dtsn = rpl_seq_init(); 01015 instance->srh_error_count = 0; 01016 instance->poison_count = 0; 01017 instance->repair_dis_timer = 0; 01018 instance->repair_dis_count = 0; 01019 instance->last_dao_trigger_time = protocol_core_monotonic_time; 01020 instance->root_paths_valid = false; 01021 instance->root_topo_sort_valid = false; 01022 instance->dao_sequence = rpl_seq_init(); 01023 instance->dao_sequence_in_transit = 0; 01024 instance->dao_in_transit = false; 01025 instance->dao_retry_timer = 0; 01026 instance->dao_attempt = 0; 01027 instance->delay_dao_timer = 0; 01028 instance->parent_selection_timer = 0; 01029 instance->neighbours_changed = false; 01030 instance->local_repair = false; 01031 instance->id = instance_id; 01032 instance->domain = domain; 01033 instance->current_dodag_version = NULL; 01034 instance->dio_not_consistent = false; 01035 instance->of = NULL; 01036 01037 ns_list_add_to_start(&domain->instances, instance); 01038 return instance; 01039 } 01040 01041 void rpl_delete_instance(rpl_instance_t *instance) { 01042 rpl_domain_t *domain = instance->domain; 01043 ns_list_foreach_safe(rpl_neighbour_t, neighbour, &instance->candidate_neighbours) { 01044 rpl_delete_neighbour(instance, neighbour); 01045 } 01046 ns_list_foreach_safe(rpl_dodag_t, dodag, &instance->dodags) { 01047 rpl_delete_dodag(dodag); 01048 } 01049 ns_list_foreach_safe(rpl_dao_target_t, target, &instance->dao_targets) { 01050 rpl_delete_dao_target(instance, target); 01051 } 01052 ns_list_remove(&domain->instances, instance); 01053 rpl_free(instance, sizeof *instance); 01054 } 01055 01056 /* Don't purge a DODAG we've used unless it's been gone for 15 minutes */ 01057 #define DODAG_MIN_PURGE_AGE (15*60*10) // 15 minutes 01058 01059 /* Choose worst DODAG in an instance - will be purgeable */ 01060 static rpl_dodag_t *rpl_instance_choose_dodag_to_purge(const rpl_instance_t *instance) 01061 { 01062 rpl_dodag_t *worst = NULL; 01063 01064 ns_list_foreach_reverse(rpl_dodag_t, dodag, &instance->dodags) { 01065 uint32_t dodag_age = protocol_core_monotonic_time - dodag->timestamp; 01066 if (rpl_dodag_in_use(dodag)) { 01067 continue; 01068 } 01069 01070 if (dodag_age < DODAG_MIN_PURGE_AGE && dodag->used) { 01071 continue; 01072 } 01073 01074 if (!worst) { 01075 goto new_worst; 01076 } 01077 01078 /* Prefer to purge least-recently-heard-from */ 01079 uint32_t worst_age = protocol_core_monotonic_time - worst->timestamp; 01080 if (dodag_age <= worst_age) { 01081 continue; 01082 } else { 01083 goto new_worst; 01084 } 01085 01086 new_worst: 01087 worst = dodag; 01088 worst_age = dodag_age; 01089 } 01090 01091 return worst; 01092 } 01093 01094 /* Choose worst neighbour in an instance - may be a candidate for purging */ 01095 static rpl_neighbour_t *rpl_instance_choose_worst_neighbour(const rpl_instance_t *instance) 01096 { 01097 rpl_neighbour_t *worst = NULL; 01098 bool worst_acceptable = false; 01099 01100 /* Parents will be first - loop backwards so we take non-parents first */ 01101 ns_list_foreach_reverse(rpl_neighbour_t, neighbour, &instance->candidate_neighbours) { 01102 bool acceptable = instance->of->neighbour_acceptable(instance, neighbour); 01103 if (!worst) { 01104 goto new_worst; 01105 } 01106 01107 /* Stop if crossing from non-parents to parents - parents can't be worse */ 01108 if (neighbour->dodag_parent && !worst->dodag_parent) { 01109 break; 01110 } 01111 01112 /* Prefer to purge "unacceptable" neighbours according to OF */ 01113 if (acceptable && !worst_acceptable) { 01114 continue; 01115 } else if (!acceptable && worst_acceptable) { 01116 goto new_worst; 01117 } 01118 01119 /* Prefer to purge least-recently-heard-from */ 01120 uint32_t neighbour_age = protocol_core_monotonic_time - neighbour->dio_timestamp; 01121 uint32_t worst_age = protocol_core_monotonic_time - worst->dio_timestamp; 01122 if (neighbour_age <= worst_age) { 01123 continue; 01124 } else { 01125 goto new_worst; 01126 } 01127 01128 new_worst: 01129 worst = neighbour; 01130 worst_acceptable = acceptable; 01131 } 01132 01133 return worst; 01134 } 01135 01136 /* Purge one item from an instance */ 01137 bool rpl_instance_purge(rpl_instance_t *instance) 01138 { 01139 /* Purge this instance itself if no remaining neighbours or DODAGs 01140 * (time restrictions on those are sufficient - no need for extra) 01141 */ 01142 if (ns_list_is_empty(&instance->candidate_neighbours) && 01143 ns_list_is_empty(&instance->dodags)) { 01144 rpl_delete_instance(instance); 01145 return true; 01146 } 01147 01148 /* May purge a DODAG if not used for some time, and no remaining neighbours */ 01149 rpl_dodag_t *dodag = rpl_instance_choose_dodag_to_purge(instance); 01150 if (dodag) { 01151 rpl_delete_dodag(dodag); 01152 return true; 01153 } 01154 01155 /* Otherwise, delete a non-parent neighbour we've considered at least once. 01156 * (We don't want to delete a new neighbour before it gets a chance to 01157 * become a new parent) 01158 */ 01159 rpl_neighbour_t *neighbour = rpl_instance_choose_worst_neighbour(instance); 01160 if (neighbour && neighbour->considered && !neighbour->dodag_parent && neighbour->dao_path_control == 0) { 01161 rpl_delete_neighbour(instance, neighbour); 01162 return true; 01163 } 01164 return false; 01165 } 01166 01167 void rpl_instance_neighbours_changed(rpl_instance_t *instance) 01168 { 01169 instance->neighbours_changed = true; 01170 if (!rpl_instance_preferred_parent(instance)) { 01171 rpl_instance_set_local_repair(instance, true); 01172 } 01173 rpl_instance_trigger_parent_selection(instance, rpl_policy_dio_parent_selection_delay(instance->domain)); 01174 } 01175 01176 static void rpl_instance_remove_parents(rpl_instance_t *instance) 01177 { 01178 ns_list_foreach(rpl_neighbour_t, n, &instance->candidate_neighbours) { 01179 n->dodag_parent = false; 01180 } 01181 } 01182 01183 /* Convert RPL lifetime (8-bit in units) to core lifetime (32-bit seconds) */ 01184 static uint32_t rpl_lifetime(uint8_t lifetime, uint16_t lifetime_unit) 01185 { 01186 return lifetime == 0xff ? 0xffffffff : lifetime * (uint32_t) lifetime_unit; 01187 } 01188 01189 static uint32_t rpl_default_lifetime(rpl_dodag_t *dodag) 01190 { 01191 return rpl_lifetime(dodag->config.default_lifetime, dodag->config.lifetime_unit); 01192 } 01193 01194 /* Adjust a lifetime (in seconds) downwards to account for its age */ 01195 static uint32_t rpl_aged_lifetime(uint32_t lifetime, uint32_t timestamp) 01196 { 01197 if (lifetime != 0xffffffff) { 01198 uint32_t age = (protocol_core_monotonic_time - timestamp) / 10; 01199 if (age < lifetime) { 01200 lifetime -= age; 01201 } else { 01202 lifetime = 0; 01203 } 01204 } 01205 return lifetime; 01206 } 01207 01208 static void rpl_instance_update_system_dio_route(rpl_instance_t *instance, rpl_neighbour_t *parent, rpl_dio_route_t *route) 01209 { 01210 int8_t pref; 01211 switch (route->flags & RA_PRF_MASK) { 01212 case RA_PRF_LOW: pref = -1; break; 01213 case RA_PRF_MEDIUM: pref = 0; break; 01214 case RA_PRF_HIGH: pref = 1; break; 01215 default: return; 01216 } 01217 01218 uint8_t metric = ipv6_route_pref_to_metric(pref) + parent->dodag_pref; 01219 01220 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); 01221 } 01222 01223 /* Called when a DIO has been received */ 01224 void rpl_dodag_update_implicit_system_routes(rpl_dodag_t *dodag, rpl_neighbour_t *parent) 01225 { 01226 if (!rpl_dodag_is_current(dodag) || !parent->dodag_parent) { 01227 return; 01228 } 01229 01230 uint32_t aged_default = rpl_aged_lifetime(rpl_default_lifetime(dodag), parent->dio_timestamp); 01231 uint8_t metric = IPV6_ROUTE_DEFAULT_METRIC + parent->dodag_pref; 01232 01233 /* Always add the "root" default route - only used for per-instance lookup */ 01234 ipv6_route_add_metric(NULL, 0, parent->interface_id, parent->ll_address, ROUTE_RPL_INSTANCE, parent, dodag->instance->id, aged_default, metric); 01235 01236 /* Also add a specific route to the DODAGID */ 01237 ipv6_route_add_metric(dodag->id, 128, parent->interface_id, parent->ll_address, ROUTE_RPL_ROOT, parent, dodag->instance->id, aged_default, metric); 01238 01239 } 01240 01241 /* Called when a DIO RIO route has been updated (but not the parent list) */ 01242 static void rpl_dodag_update_system_route(rpl_dodag_t *dodag, rpl_dio_route_t *route) 01243 { 01244 if (!rpl_dodag_is_current(dodag)) { 01245 return; 01246 } 01247 01248 rpl_instance_t *instance = dodag->instance; 01249 01250 ns_list_foreach(rpl_neighbour_t, neighbour, &instance->candidate_neighbours) { 01251 if (neighbour->dodag_parent) { 01252 rpl_instance_update_system_dio_route(instance, neighbour, route); 01253 } 01254 } 01255 } 01256 01257 /* Called when a parent has been added/updated (but not the DIO route list) */ 01258 static void rpl_instance_update_system_routes_through_parent(rpl_instance_t *instance, rpl_neighbour_t *parent) 01259 { 01260 rpl_dodag_t *dodag = parent->dodag_version->dodag; 01261 01262 rpl_dodag_update_implicit_system_routes(dodag, parent); 01263 01264 /* Then add the specific routes listed in the DIO as ROUTE_RPL_DIO */ 01265 ns_list_foreach(rpl_dio_route_t, route, &dodag->routes) { 01266 rpl_instance_update_system_dio_route(instance, parent, route); 01267 } 01268 } 01269 01270 static void rpl_instance_remove_system_routes_through_parent(rpl_instance_t *instance, rpl_neighbour_t *parent) 01271 { 01272 (void)instance; 01273 01274 ipv6_route_table_remove_info(parent->interface_id, ROUTE_RPL_INSTANCE, parent); 01275 ipv6_route_table_remove_info(parent->interface_id, ROUTE_RPL_DIO, parent); 01276 ipv6_route_table_remove_info(parent->interface_id, ROUTE_RPL_ROOT, parent); 01277 } 01278 01279 static void trace_info_print(const char *fmt, ...) 01280 { 01281 va_list ap; 01282 va_start(ap, fmt); 01283 vtracef(TRACE_LEVEL_INFO, TRACE_GROUP, fmt, ap); 01284 va_end(ap); 01285 } 01286 01287 01288 void rpl_instance_run_parent_selection(rpl_instance_t *instance) 01289 { 01290 bool parent_set_change = false; 01291 rpl_dodag_version_t *original_version = instance->current_dodag_version; 01292 uint16_t original_rank = instance->current_rank; 01293 01294 instance->parent_selection_timer = rpl_policy_parent_selection_period(instance->domain); 01295 01296 if (!instance->of) { 01297 return; 01298 } 01299 01300 if (instance->current_dodag_version && instance->current_dodag_version->dodag->root) { 01301 return; 01302 } 01303 01304 ns_list_foreach_safe(rpl_neighbour_t, n, &instance->candidate_neighbours) { 01305 if (rpl_aged_lifetime(rpl_default_lifetime(n->dodag_version->dodag), n->dio_timestamp) == 0) { 01306 rpl_delete_neighbour(instance, n); 01307 continue; 01308 } 01309 n->old_dao_path_control = n->dao_path_control; 01310 n->dao_path_control = 0; 01311 n->was_dodag_parent = n->dodag_parent; 01312 n->dodag_parent = false; 01313 n->considered = true; 01314 } 01315 01316 rpl_neighbour_t *original_preferred = rpl_instance_preferred_parent(instance); 01317 01318 instance->of->parent_selection(instance); 01319 01320 ns_list_foreach(rpl_neighbour_t, n, &instance->candidate_neighbours) { 01321 if (n->was_dodag_parent != n->dodag_parent) { 01322 tr_info("%s parent %s", n->dodag_parent ? "Added" : "Removed", trace_ipv6(n->ll_address)); 01323 parent_set_change = true; 01324 /* Remove routes through a deselected parent */ 01325 if (!n->dodag_parent) { 01326 rpl_instance_remove_system_routes_through_parent(instance, n); 01327 } 01328 } 01329 /* Always re-run route update (in case of changed preference values) */ 01330 if (n->dodag_parent) { 01331 rpl_instance_update_system_routes_through_parent(instance, n); 01332 } 01333 n->was_dodag_parent = false; 01334 } 01335 01336 rpl_neighbour_t *preferred_parent = rpl_instance_preferred_parent(instance); 01337 01338 if (preferred_parent) { 01339 rpl_control_disable_ra_routes(instance->domain); 01340 } else { 01341 rpl_instance_poison(instance, rpl_policy_repair_poison_count(instance->domain)); 01342 } 01343 01344 if (original_preferred != preferred_parent) { 01345 protocol_stats_update(STATS_RPL_PARENT_CHANGE, 1); 01346 } 01347 01348 #ifndef NO_MLE 01349 // Sets new preferred parent 01350 if (preferred_parent) { 01351 ipv6_map_ip_to_ll_and_call_ll_addr_handler(NULL, preferred_parent->interface_id, NULL, preferred_parent->ll_address, 01352 protocol_6lowpan_neighbor_priority_set); 01353 // Sets secondary parent 01354 rpl_neighbour_t *sec_parent = ns_list_get_next(&instance->candidate_neighbours, preferred_parent); 01355 if (sec_parent && sec_parent->dodag_parent) { 01356 ipv6_map_ip_to_ll_and_call_ll_addr_handler(NULL, sec_parent->interface_id, NULL, sec_parent->ll_address, 01357 protocol_6lowpan_neighbor_second_priority_set); 01358 } else { 01359 protocol_6lowpan_neighbor_priority_clear_all(preferred_parent->interface_id, PRIORITY_2ND); 01360 } 01361 } 01362 #endif 01363 01364 rpl_instance_set_local_repair(instance, preferred_parent == NULL); 01365 01366 if (rpl_instance_mop(instance) != RPL_MODE_NO_DOWNWARD) { 01367 rpl_downward_process_dao_parent_changes(instance); 01368 } 01369 01370 /* Anyone who's not a parent can be pruned now (eg bad link cost) */ 01371 ns_list_foreach_safe(rpl_neighbour_t, n, &instance->candidate_neighbours) { 01372 if (n->dodag_parent) { 01373 continue; 01374 } 01375 if (!instance->of->neighbour_acceptable(instance, n)) { 01376 rpl_delete_neighbour(instance, n); 01377 } 01378 } 01379 rpl_control_print(trace_info_print); 01380 /* Changing DODAG version is an inconsistency */ 01381 if (original_version != instance->current_dodag_version) { 01382 rpl_instance_inconsistency(instance); 01383 return; 01384 } 01385 01386 /* Check for "consistent" indication as per RFC 6550 8.3 - if not 01387 * "consistent" by this definition, we reset Trickle consistency counter, 01388 * and do not process any more consistency events this interval. 01389 * See comments in rpl_control_dio_handler() for more info. 01390 */ 01391 if (parent_set_change || 01392 original_preferred != preferred_parent || 01393 !(instance->current_dodag_version && 01394 (rpl_rank_compare(instance->current_dodag_version->dodag, original_rank, instance->current_rank) & RPL_CMP_EQUAL))) { 01395 instance->dio_not_consistent = true; 01396 instance->dio_timer.c = 0; 01397 } 01398 } 01399 01400 void rpl_instance_remove_interface(rpl_instance_t *instance, int8_t if_id) 01401 { 01402 ns_list_foreach_safe(rpl_neighbour_t, neighbour, &instance->candidate_neighbours) { 01403 if (neighbour->interface_id == if_id) { 01404 rpl_delete_neighbour(instance, neighbour); 01405 } 01406 } 01407 } 01408 01409 /* Trigger DIO transmission - all interfaces multicast if addr+cur are NULL, else unicast */ 01410 void rpl_instance_dio_trigger(rpl_instance_t *instance, protocol_interface_info_entry_t *cur, const uint8_t *addr) 01411 { 01412 uint16_t rank = instance->current_rank; 01413 rpl_dodag_version_t *dodag_version = instance->current_dodag_version; 01414 rpl_dodag_t *dodag; 01415 if (dodag_version) { 01416 dodag = dodag_version->dodag; 01417 } else if (instance->poison_count) { 01418 /* When poisoning, we can send using data from an arbitrary DODAG/version */ 01419 dodag = ns_list_get_first(&instance->dodags); 01420 if (dodag) { 01421 dodag_version = ns_list_get_first(&dodag->versions); 01422 } 01423 } else { 01424 dodag = NULL; 01425 } 01426 01427 if (!dodag || !dodag_version) { 01428 return; 01429 } 01430 01431 if (instance->poison_count) { 01432 instance->poison_count--; 01433 rank = RPL_RANK_INFINITE; 01434 tr_debug("Poison count -> set RPL_RANK_INFINITE"); 01435 } 01436 01437 // Always send config in unicasts (as required), never in multicasts (optional) 01438 rpl_dodag_conf_t *conf = addr ? &dodag->config : NULL; 01439 01440 rpl_control_transmit_dio(instance->domain, cur, instance->id, dodag_version->number, rank, dodag->g_mop_prf, instance->dtsn, dodag, dodag->id, conf, addr); 01441 01442 dodag_version->last_advertised_rank = rank; 01443 01444 /* When we advertise a new lowest rank, need to re-evaluate our rank limits */ 01445 if (rank < dodag_version->lowest_advertised_rank) { 01446 dodag_version->lowest_advertised_rank = rank; 01447 dodag_version->hard_rank_limit = rpl_rank_add(rank, dodag->config.dag_max_rank_increase); 01448 } 01449 rpl_dodag_version_limit_greediness(dodag_version, rank); 01450 01451 instance->last_advertised_dodag_version = dodag_version; 01452 } 01453 01454 static void rpl_instance_dis_timer(rpl_instance_t *instance, uint16_t seconds) 01455 { 01456 if (instance->repair_dis_timer > seconds) { 01457 instance->repair_dis_timer -= seconds; 01458 } else if (instance->repair_dis_timer != 0){ 01459 tr_debug("Timed repair DIS %d", instance->id); 01460 instance->repair_dis_timer = 0; 01461 instance->repair_dis_count++; 01462 rpl_control_transmit_dis(instance->domain, NULL, RPL_SOLINFO_PRED_INSTANCEID, instance->id, NULL, 0, NULL); 01463 if (instance->repair_dis_count < rpl_policy_repair_dis_count(instance->domain)) { 01464 uint16_t t = rpl_policy_repair_initial_dis_delay(instance->domain); 01465 uint16_t max = rpl_policy_repair_maximum_dis_interval(instance->domain); 01466 for (uint_fast8_t n = instance->repair_dis_count; n; n--) { 01467 if (t < 0x8000 && t < max) { 01468 t <<= 1; 01469 } else { 01470 t = max; 01471 break; 01472 } 01473 } 01474 if (t > max) { 01475 t = max; 01476 } 01477 instance->repair_dis_timer = t; 01478 } else { 01479 rpl_control_event(instance->domain, RPL_EVENT_LOCAL_REPAIR_NO_MORE_DIS); 01480 } 01481 } 01482 } 01483 01484 void rpl_instance_set_local_repair(rpl_instance_t *instance, bool repair) 01485 { 01486 if (instance->local_repair == repair) { 01487 return; 01488 } 01489 01490 instance->local_repair = repair; 01491 01492 if (repair) { 01493 /* Notify RPL user to state switch */ 01494 rpl_control_event(instance->domain, RPL_EVENT_LOCAL_REPAIR_START); 01495 protocol_stats_update(STATS_RPL_LOCAL_REPAIR, 1); 01496 instance->repair_dis_timer = rpl_policy_repair_initial_dis_delay(instance->domain); 01497 instance->repair_dis_count = 0; 01498 } else { 01499 instance->repair_dis_timer = 0; 01500 } 01501 01502 /* When repair ends, eliminate all higher-rank neighbours (potential sub-DODAG) from table */ 01503 if (!repair && instance->current_dodag_version) { 01504 ns_list_foreach_safe(rpl_neighbour_t, neighbour, &instance->candidate_neighbours) { 01505 if (rpl_dodag_version_rank_indicates_possible_sub_dodag(neighbour->dodag_version, neighbour->rank)) { 01506 rpl_delete_neighbour(instance, neighbour); 01507 } 01508 } 01509 } 01510 } 01511 01512 bool rpl_instance_local_repair(const rpl_instance_t *instance) 01513 { 01514 return instance->local_repair; 01515 } 01516 01517 uint16_t rpl_instance_current_rank(const rpl_instance_t *instance) 01518 { 01519 return instance->current_rank; 01520 } 01521 01522 void rpl_instance_slow_timer(rpl_instance_t *instance, uint16_t seconds) 01523 { 01524 ns_list_foreach(rpl_dodag_t, dodag, &instance->dodags) { 01525 rpl_dodag_slow_timer(dodag, seconds); 01526 } 01527 rpl_instance_parent_selection_timer(instance, seconds); 01528 if (!rpl_instance_preferred_parent(instance)) { 01529 protocol_stats_update(STATS_RPL_TIME_NO_NEXT_HOP, 1); 01530 rpl_instance_dis_timer(instance, seconds); 01531 } 01532 } 01533 01534 void rpl_upward_dio_timer(rpl_instance_t *instance, uint16_t ticks) 01535 { 01536 rpl_dodag_version_t *dodag_version = instance->current_dodag_version; 01537 rpl_dodag_t *dodag; 01538 if (dodag_version) { 01539 dodag = dodag_version->dodag; 01540 } else if (instance->poison_count) { 01541 /* When poisoning, we can send using data from an arbitrary DODAG/version */ 01542 dodag = ns_list_get_first(&instance->dodags); 01543 if (dodag) { 01544 dodag_version = ns_list_get_first(&dodag->versions); 01545 } 01546 } else { 01547 dodag = NULL; 01548 } 01549 01550 if (!dodag || !dodag_version) { 01551 return; 01552 } 01553 01554 /* Leaves don't send normal periodic DIOs */ 01555 if (rpl_dodag_am_leaf(dodag) && !instance->poison_count) { 01556 return; 01557 } 01558 if (trickle_timer(&instance->dio_timer, &dodag->dio_timer_params, ticks)) { 01559 instance->dio_not_consistent = false; 01560 rpl_instance_dio_trigger(instance, NULL, NULL); 01561 } 01562 } 01563 01564 void rpl_upward_print_neighbour(const rpl_neighbour_t *neighbour, route_print_fn_t *print_fn) 01565 { 01566 uint16_t path_cost; 01567 if (neighbour->dodag_version->dodag->instance->of) { 01568 path_cost = neighbour->dodag_version->dodag->instance->of->path_cost(neighbour); 01569 } else { 01570 path_cost = 0xFFFF; 01571 } 01572 01573 ROUTE_PRINT_ADDR_STR_BUFFER_INIT(addr_str_ll); 01574 ROUTE_PRINT_ADDR_STR_BUFFER_INIT(addr_str_global); 01575 print_fn(" %2.0d%c%04x %04x %02x %s%%%d (%s)", 01576 neighbour->dodag_parent ? neighbour->dodag_pref + 1 : 0, 01577 neighbour->dodag_version && rpl_instance_preferred_parent(neighbour->dodag_version->dodag->instance) == neighbour ? '*' : ' ', 01578 neighbour->rank, 01579 path_cost, 01580 neighbour->dao_path_control, 01581 ROUTE_PRINT_ADDR_STR_FORMAT(addr_str_ll, neighbour->ll_address), neighbour->interface_id, 01582 ROUTE_PRINT_ADDR_STR_FORMAT(addr_str_global, neighbour->global_address)); 01583 } 01584 01585 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) 01586 { 01587 ns_list_foreach(rpl_neighbour_t, neighbour, list) { 01588 if (neighbour->dodag_version == version) { 01589 rpl_upward_print_neighbour(neighbour, print_fn); 01590 } 01591 } 01592 } 01593 01594 void rpl_upward_print_dodag(rpl_instance_t *instance, rpl_dodag_t *dodag, route_print_fn_t *print_fn) 01595 { 01596 /* Summary */ 01597 ROUTE_PRINT_ADDR_STR_BUFFER_INIT(addr_str); 01598 print_fn("DODAG %s", ROUTE_PRINT_ADDR_STR_FORMAT(addr_str, dodag->id)); 01599 print_fn(" G=%d MOP=%d Prf=%d", dodag->g_mop_prf & RPL_GROUNDED ? 1 : 0, 01600 (dodag->g_mop_prf & RPL_MODE_MASK) >> RPL_MODE_SHIFT, 01601 (dodag->g_mop_prf & RPL_DODAG_PREF_MASK)); 01602 /* Routes */ 01603 ns_list_foreach(rpl_dio_route_t, route, &dodag->routes) { 01604 uint8_t addr[16] = { 0 } ; 01605 int_fast8_t pref; 01606 switch (route->flags & RA_PRF_MASK) { 01607 case RA_PRF_LOW: pref = -1; break; 01608 case RA_PRF_MEDIUM: pref = 0; break; 01609 case RA_PRF_HIGH: pref = 1; break; 01610 default: pref = 2; break; 01611 } 01612 bitcopy(addr, route->prefix, route->prefix_len); 01613 if (route->lifetime == 0xFFFFFFFF) { 01614 print_fn("%24s/%-3u lifetime:infinite pref:%"PRIdFAST8, 01615 ROUTE_PRINT_ADDR_STR_FORMAT(addr_str, addr), route->prefix_len, pref); 01616 01617 } else { 01618 print_fn("%24s/%-3u lifetime:%"PRIu32" pref:%"PRIdFAST8, 01619 ROUTE_PRINT_ADDR_STR_FORMAT(addr_str, addr), route->prefix_len, route->lifetime, pref); 01620 } 01621 } 01622 /* Prefixes */ 01623 ns_list_foreach(prefix_entry_t, prefix, &dodag->prefixes) { 01624 uint8_t addr[16] = { 0 } ; 01625 bitcopy(addr, prefix->prefix, prefix->prefix_len); 01626 if (prefix->lifetime == 0xFFFFFFFF) { 01627 print_fn("%24s/%-3u lifetime:infinite flags:%c%c%c", 01628 ROUTE_PRINT_ADDR_STR_FORMAT(addr_str, addr), prefix->prefix_len, 01629 prefix->options & PIO_L ? 'L' : '-', 01630 prefix->options & PIO_A ? 'A' : '-', 01631 prefix->options & RPL_PIO_PUBLISHED ? '*' : ' ' 01632 ); 01633 } else { 01634 print_fn("%24s/%-3u lifetime:%"PRIu32" flags:%c%c%c", 01635 ROUTE_PRINT_ADDR_STR_FORMAT(addr_str, addr), prefix->prefix_len, prefix->lifetime, 01636 prefix->options & PIO_L ? 'L' : '-', 01637 prefix->options & PIO_A ? 'A' : '-', 01638 prefix->options & RPL_PIO_PUBLISHED ? '*' : ' ' 01639 ); 01640 } 01641 } 01642 /* Versions */ 01643 ns_list_foreach(rpl_dodag_version_t, version, &dodag->versions) { 01644 print_fn(" Version %d", version->number); 01645 if (version == instance->current_dodag_version) { 01646 print_fn (" *Current version* Rank=%04x", instance->current_rank); 01647 } 01648 rpl_upward_print_neighbours_in_version(&instance->candidate_neighbours, version, print_fn); 01649 } 01650 } 01651 01652 void rpl_upward_print_instance(rpl_instance_t *instance, route_print_fn_t *print_fn) 01653 { 01654 print_fn("RPL Instance %d", instance->id); 01655 print_fn("---------------"); 01656 ns_list_foreach(rpl_dodag_t, dodag, &instance->dodags) { 01657 rpl_upward_print_dodag(instance, dodag, print_fn); 01658 } 01659 if (instance->current_dodag_version) { 01660 const trickle_params_t *params = &instance->current_dodag_version->dodag->dio_timer_params; 01661 const trickle_t *timer = &instance->dio_timer; 01662 01663 print_fn("DIO trickle Imin=%d, Imax=%d, k=%d", params->Imin, params->Imax, params->k); 01664 print_fn(" I=%d, now=%d, t=%d, c=%d", timer->I, timer->now, timer->t, timer->c); 01665 } 01666 } 01667 01668 uint16_t rpl_upward_read_dao_target_list_size(const rpl_instance_t *instance) 01669 { 01670 return ns_list_count(&instance->dao_targets); 01671 } 01672 01673 /* Backwards-compatibility implementation of net_rpl.h API designed for old implementation */ 01674 bool rpl_upward_read_dodag_info(const rpl_instance_t *instance, rpl_dodag_info_t *dodag_info) 01675 { 01676 rpl_dodag_version_t *version = rpl_instance_current_dodag_version(instance); 01677 if (!version) { 01678 return false; 01679 } 01680 01681 rpl_dodag_t *dodag = version->dodag; 01682 01683 memcpy(dodag_info->dodag_id, dodag->id, 16); 01684 dodag_info->instance_id = instance->id; 01685 dodag_info->flags = dodag->g_mop_prf; 01686 dodag_info->version_num = version->number; 01687 dodag_info->DTSN = instance->dtsn; 01688 dodag_info->curent_rank = instance->current_rank; 01689 dodag_info->parent_flags = 0; 01690 dodag_info->dag_min_hop_rank_inc = dodag->config.min_hop_rank_increase; 01691 01692 rpl_neighbour_t *pref_parent = rpl_instance_preferred_parent(instance); 01693 if (!pref_parent) { 01694 return true; 01695 } 01696 01697 dodag_info->parent_flags |= RPL_PRIMARY_PARENT_SET; 01698 memcpy(dodag_info->primary_parent, 01699 pref_parent->have_global_address ? pref_parent->global_address 01700 : pref_parent->ll_address, 01701 16); 01702 dodag_info->primary_parent_rank = pref_parent->rank; 01703 01704 rpl_neighbour_t *sec_parent = ns_list_get_next(&instance->candidate_neighbours, pref_parent); 01705 if (!sec_parent || !sec_parent->dodag_parent) { 01706 return true; 01707 } 01708 01709 dodag_info->parent_flags |= RPL_SECONDARY_PARENT_SET; 01710 memcpy(dodag_info->secondary_parent, 01711 sec_parent->have_global_address ? sec_parent->global_address 01712 : sec_parent->ll_address, 01713 16); 01714 dodag_info->secondary_parent_rank = sec_parent->rank; 01715 01716 return true; 01717 } 01718 01719 #endif /* HAVE_RPL */
Generated on Tue Jul 12 2022 12:22:19 by
