BA
/
BaBoRo1
Embed:
(wiki syntax)
Show/hide line numbers
rpl_control.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_control.c deals with packet handling, and management of domains. The 00019 * workings of individual instances are dealt elsewhere. 00020 * 00021 * rpl_domain_t is accessible. 00022 * rpl_instance_t, rpl_dodag_t, rpl_dodag_version_t, rpl_neighbout_t are all opaque 00023 */ 00024 00025 /* 00026 * State machine still not fully developed - we're basically free-running, 00027 * with a simple kick to advance the bootstrap when we first get a DAO-ACK 00028 * on any instance. 00029 * 00030 * A larger set of events is needed, including some sort of policy interaction 00031 * to establish which instances "count" for triggering an overall system (or 00032 * domain) state machine. 00033 */ 00034 #include "nsconfig.h" 00035 00036 #ifdef HAVE_RPL 00037 00038 #include <string.h> 00039 #include "ns_trace.h" 00040 #include "common_functions.h" 00041 #include "nsdynmemLIB.h" 00042 00043 #include "Core/include/ns_buffer.h" 00044 #include "NWK_INTERFACE/Include/protocol.h" 00045 #include "NWK_INTERFACE/Include/protocol_stats.h" 00046 #include "Common_Protocols/ipv6_constants.h" 00047 #include "Common_Protocols/icmpv6.h" 00048 00049 #include "Service_Libs/etx/etx.h" /* slight ick */ 00050 00051 #include "net_rpl.h" 00052 #include "RPL/rpl_protocol.h" 00053 #include "RPL/rpl_upward.h" 00054 #include "RPL/rpl_downward.h" 00055 #include "RPL/rpl_policy.h" 00056 #include "RPL/rpl_control.h" 00057 00058 #define TRACE_GROUP "rplc" 00059 00060 const uint8_t ADDR_LINK_LOCAL_ALL_RPL_NODES[16] = { 0xff, 0x02, [15] = 0x1a }; 00061 00062 /* Sensible default limits for a 6LoWPAN-ND node */ 00063 static size_t rpl_purge_threshold = 1 * 1024; 00064 static size_t rpl_alloc_limit = 2 * 1024; // 0 means no limit 00065 00066 static size_t rpl_alloc_total; 00067 #define RPL_ALLOC_OVERHEAD 8 00068 00069 static NS_LIST_DEFINE(rpl_domains, rpl_domain_t, link); 00070 00071 void rpl_control_set_memory_limits(size_t soft_limit, size_t hard_limit) 00072 { 00073 rpl_purge_threshold = soft_limit; 00074 rpl_alloc_limit = hard_limit; 00075 } 00076 00077 /* Send all RPL allocations through central point - gives a way of counting and 00078 * limiting RPL allocations. Allocation rejected if total would go above the 00079 * hard limit rpl_alloc_limit. This should normally be avoided by the timers 00080 * trying to keep allocations below the soft limit rpl_purge_threshold. 00081 */ 00082 void *rpl_realloc(void *p, uint16_t old_size, uint16_t new_size) 00083 { 00084 if (old_size == new_size) { 00085 return p; 00086 } 00087 00088 /* If increasing memory, check if we're going above the hard limit */ 00089 if (rpl_alloc_limit != 0 && new_size > old_size) { 00090 uint16_t size_diff = p ? new_size - old_size : new_size + RPL_ALLOC_OVERHEAD; 00091 00092 if (rpl_alloc_total + size_diff > rpl_alloc_limit) { 00093 protocol_stats_update(STATS_RPL_MEMORY_OVERFLOW, size_diff); 00094 return NULL; 00095 } 00096 } 00097 void *n = ns_dyn_mem_alloc(new_size); 00098 if (n) { 00099 /* rpl_free() below will subtract (old_size + RPL_ALLOC_OVERHEAD) if reallocing */ 00100 rpl_alloc_total += (size_t) new_size + RPL_ALLOC_OVERHEAD; 00101 protocol_stats_update(STATS_RPL_MEMORY_ALLOC, new_size); 00102 if (p) { 00103 memcpy(n, p, old_size < new_size ? old_size : new_size); 00104 } 00105 } else { 00106 protocol_stats_update(STATS_RPL_MEMORY_OVERFLOW, new_size); 00107 } 00108 rpl_free(p, old_size); 00109 return n; 00110 } 00111 00112 void *rpl_alloc(uint16_t size) 00113 { 00114 return rpl_realloc(NULL, 0, size); 00115 } 00116 00117 void rpl_free(void *p, uint16_t size) 00118 { 00119 if (p) { 00120 rpl_alloc_total -= (size_t) size + RPL_ALLOC_OVERHEAD; 00121 protocol_stats_update(STATS_RPL_MEMORY_FREE, size); 00122 } 00123 ns_dyn_mem_free(p); 00124 } 00125 00126 void rpl_control_event(struct rpl_domain *domain, rpl_event_t event) 00127 { 00128 if (domain->callback) { 00129 domain->callback(event, domain->cb_handle); 00130 } 00131 } 00132 00133 00134 /* When we join a new instance, we need to publish existing addresses. 00135 * Later addresses additions/removals are handled by rpl_control_addr_notifier. 00136 */ 00137 static void rpl_control_publish_own_addresses(rpl_domain_t *domain, rpl_instance_t *instance) 00138 { 00139 int8_t last_id = -1; 00140 protocol_interface_info_entry_t *cur; 00141 while ((cur = protocol_stack_interface_info_get_by_rpl_domain(domain, last_id)) != NULL) { 00142 ns_list_foreach(if_address_entry_t, addr, &cur->ip_addresses) { 00143 if (!addr->tentative && !addr_is_ipv6_link_local(addr->address)) { 00144 /* TODO - wouldn't need to publish address if within a prefix published by parent */ 00145 uint32_t descriptor = 0; 00146 bool want_descriptor = rpl_policy_target_descriptor_for_own_address(domain, addr->address, addr->source, addr->data, &descriptor); 00147 rpl_instance_publish_dao_target(instance, addr->address, 128, addr->valid_lifetime, true, want_descriptor, descriptor); 00148 } 00149 } 00150 last_id = cur->id; 00151 } 00152 } 00153 00154 static void rpl_control_publish_own_address(rpl_domain_t *domain, const if_address_entry_t *addr) 00155 { 00156 ns_list_foreach(rpl_instance_t, instance, &domain->instances) { 00157 if (!rpl_instance_am_root(instance)) { 00158 uint32_t descriptor = 0; 00159 bool want_descriptor = rpl_policy_target_descriptor_for_own_address(domain, addr->address, addr->source, addr->data, &descriptor); 00160 rpl_instance_publish_dao_target(instance, addr->address, 128, addr->valid_lifetime, true, want_descriptor, descriptor); 00161 } 00162 } 00163 } 00164 00165 void rpl_control_publish_host_address(rpl_domain_t *domain, const uint8_t addr[16], uint32_t lifetime) 00166 { 00167 ns_list_foreach(rpl_instance_t, instance, &domain->instances) { 00168 if (!rpl_instance_am_root(instance)) { 00169 /* TODO - Wouldn't need to publish host address if within a published prefix */ 00170 uint32_t descriptor = 0; 00171 bool want_descriptor = rpl_policy_target_descriptor_for_host_address(domain, addr, &descriptor); 00172 rpl_instance_publish_dao_target(instance, addr, 128, lifetime, false, want_descriptor, descriptor); 00173 } 00174 } 00175 } 00176 00177 /* Is unpublish a word? */ 00178 void rpl_control_unpublish_address(rpl_domain_t *domain, const uint8_t addr[16]) 00179 { 00180 ns_list_foreach(rpl_instance_t, instance, &domain->instances) { 00181 rpl_instance_delete_published_dao_target(instance, addr, 128); 00182 } 00183 } 00184 00185 /* Address changes need to trigger DAO target re-evaluation */ 00186 static void rpl_control_addr_notifier(struct protocol_interface_info_entry *interface, const if_address_entry_t *addr, if_address_callback_t reason) 00187 { 00188 /* Don't care about link-local addresses */ 00189 if (addr_is_ipv6_link_local(addr->address)) { 00190 return; 00191 } 00192 00193 /* Only publish addresses on the domain attached to their interface */ 00194 if (!interface->rpl_domain) { 00195 return; 00196 } 00197 00198 switch (reason) { 00199 case ADDR_CALLBACK_DAD_COMPLETE: 00200 case ADDR_CALLBACK_REFRESHED: 00201 rpl_control_publish_own_address(interface->rpl_domain, addr); 00202 break; 00203 case ADDR_CALLBACK_DELETED: 00204 rpl_control_unpublish_address(interface->rpl_domain, addr->address); 00205 break; 00206 default: 00207 break; 00208 00209 } 00210 } 00211 00212 static void rpl_control_etx_change_callback(int8_t nwk_id, uint16_t previous_etx, uint16_t current_etx, const uint8_t *mac64_addr_ptr, uint16_t mac16_addr) 00213 { 00214 (void)previous_etx; 00215 (void)current_etx; 00216 (void)mac16_addr; 00217 00218 protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(nwk_id); 00219 if (!cur || !cur->rpl_domain) { 00220 return; 00221 } 00222 rpl_domain_t *domain = cur->rpl_domain; 00223 uint16_t delay = rpl_policy_etx_change_parent_selection_delay(domain); 00224 tr_debug("Triggering parent selection due to ETX change on %s", trace_array(mac64_addr_ptr, 8)); 00225 00226 ns_list_foreach(rpl_instance_t, instance, &domain->instances) { 00227 rpl_instance_trigger_parent_selection(instance, delay); 00228 if (rpl_instance_am_root(instance)) { 00229 rpl_downward_paths_invalidate(instance); 00230 } 00231 } 00232 } 00233 00234 /* Create a RPL domain - it can be associated with one or more interfaces */ 00235 rpl_domain_t *rpl_control_create_domain(void) 00236 { 00237 rpl_domain_t *domain = rpl_alloc(sizeof(rpl_domain_t)); 00238 if (!domain) { 00239 return NULL; 00240 } 00241 00242 ns_list_init(&domain->instances); 00243 domain->non_storing_downstream_interface = -1; 00244 domain->callback = NULL; 00245 domain->cb_handle = NULL; 00246 domain->force_leaf = false; 00247 ns_list_add_to_start(&rpl_domains, domain); 00248 00249 addr_notification_register(rpl_control_addr_notifier); 00250 00251 return domain; 00252 } 00253 00254 void rpl_control_delete_domain(rpl_domain_t *domain) 00255 { 00256 ns_list_foreach_safe(rpl_instance_t, instance, &domain->instances) { 00257 rpl_delete_instance(instance); 00258 } 00259 ns_list_remove(&rpl_domains, domain); 00260 rpl_free(domain, sizeof *domain); 00261 } 00262 00263 static void rpl_control_remove_interface_from_domain(protocol_interface_info_entry_t *cur, rpl_domain_t *domain) 00264 { 00265 ns_list_foreach(rpl_instance_t, instance, &domain->instances) { 00266 rpl_instance_remove_interface(instance, cur->id); 00267 } 00268 ns_list_foreach(if_address_entry_t, addr, &cur->ip_addresses) { 00269 if (!addr_is_ipv6_link_local(addr->address)) { 00270 rpl_control_unpublish_address(domain, addr->address); 00271 } 00272 } 00273 if (domain->non_storing_downstream_interface == cur->id) { 00274 domain->non_storing_downstream_interface = -1; 00275 } 00276 } 00277 00278 void rpl_control_set_domain_on_interface(protocol_interface_info_entry_t *cur, rpl_domain_t *domain, bool downstream) 00279 { 00280 if (cur->rpl_domain != domain) { 00281 rpl_control_remove_domain_from_interface(cur); 00282 cur->rpl_domain = domain; 00283 addr_add_group(cur, ADDR_LINK_LOCAL_ALL_RPL_NODES); 00284 } 00285 ns_list_foreach(if_address_entry_t, addr, &cur->ip_addresses) { 00286 if (!addr_is_ipv6_link_local(addr->address)) { 00287 rpl_control_publish_own_address(domain, addr); 00288 } 00289 } 00290 if (downstream) { 00291 domain->non_storing_downstream_interface = cur->id; 00292 } 00293 /* This is a bit icky - why assume that our Objective Functions use ETX? */ 00294 /* But this is the easiest place to add an interface registration */ 00295 etx_value_change_callback_register(cur->nwk_id, cur->id, rpl_policy_etx_hysteresis(domain), rpl_control_etx_change_callback); 00296 } 00297 00298 void rpl_control_remove_domain_from_interface(protocol_interface_info_entry_t *cur) 00299 { 00300 if (cur->rpl_domain) { 00301 rpl_control_remove_interface_from_domain(cur, cur->rpl_domain); 00302 addr_delete_group(cur, ADDR_LINK_LOCAL_ALL_RPL_NODES); 00303 cur->rpl_domain = NULL; 00304 } 00305 } 00306 00307 void rpl_control_set_callback(rpl_domain_t *domain, rpl_domain_callback_t callback, void *cb_handle) 00308 { 00309 domain->callback = callback; 00310 domain->cb_handle = cb_handle; 00311 } 00312 00313 /* To do - this should live somewhere nicer. Basically a bootstrap 00314 * thing to stop being a host as soon as we get our first RPL parent 00315 * (any instance?) 00316 */ 00317 void rpl_control_disable_ra_routes(struct rpl_domain *domain) 00318 { 00319 int8_t last_id = -1; 00320 protocol_interface_info_entry_t *cur; 00321 while ((cur = protocol_stack_interface_info_get_by_rpl_domain(domain, last_id)) != NULL) { 00322 icmpv6_recv_ra_routes(cur, false); 00323 last_id = cur->id; 00324 } 00325 } 00326 00327 bool rpl_control_have_dodag(rpl_domain_t *domain) 00328 { 00329 ns_list_foreach(rpl_instance_t, instance, &domain->instances) { 00330 if (rpl_instance_current_dodag(instance)) { 00331 return true; 00332 } 00333 } 00334 return false; 00335 } 00336 00337 typedef void rpl_control_predicate_loop_fn_t(rpl_instance_t *instance, rpl_dodag_version_t *version, void *arg); 00338 00339 /* Callbacks for rpl_control_predicate_loop */ 00340 00341 static void rpl_loopfn_reset_dio_timer(rpl_instance_t *instance, rpl_dodag_version_t *dodag_version, void *handle) 00342 { 00343 (void)dodag_version; 00344 (void)handle; 00345 00346 rpl_instance_inconsistency(instance); 00347 } 00348 00349 typedef struct rpl_loopfn_trigger_unicast_dio_arg { 00350 struct protocol_interface_info_entry *interface; 00351 const uint8_t *dst; 00352 } rpl_loopfn_trigger_unicast_dio_arg_t; 00353 00354 static void rpl_loopfn_trigger_unicast_dio(rpl_instance_t *instance, rpl_dodag_version_t *dodag_version, void *handle) 00355 { 00356 (void)dodag_version; 00357 00358 rpl_loopfn_trigger_unicast_dio_arg_t *arg = handle; 00359 rpl_instance_dio_trigger(instance, arg->interface, arg->dst); 00360 } 00361 00362 /* For each DODAG Version we're a member of, matching the predicates, call the 00363 * provided function. Mainly used for handling DODAG Information Solicitations. 00364 */ 00365 static void rpl_control_predicate_loop(rpl_domain_t *domain, rpl_control_predicate_loop_fn_t *fn, void *fn_arg, uint8_t pred, uint8_t instance_id, const uint8_t *dodagid, uint8_t version_num) 00366 { 00367 ns_list_foreach(rpl_instance_t, instance, &domain->instances) { 00368 rpl_dodag_version_t *version = rpl_instance_predicate_match(instance, pred, instance_id, dodagid, version_num); 00369 if (version) { 00370 fn(instance, version, fn_arg); 00371 } 00372 } 00373 } 00374 00375 /* Manipulation of roots */ 00376 rpl_dodag_t *rpl_control_create_dodag_root(rpl_domain_t *domain, uint8_t instance_id, const uint8_t *dodagid, const rpl_dodag_conf_t *conf, uint16_t rank, uint8_t g_mop_prf) 00377 { 00378 rpl_instance_t *instance = rpl_lookup_instance(domain, instance_id, dodagid); 00379 if (!instance) { 00380 instance = rpl_create_instance(domain, instance_id); 00381 if (!instance) { 00382 return NULL; 00383 } 00384 } 00385 00386 rpl_dodag_t *dodag = rpl_lookup_dodag(instance, dodagid); 00387 if (dodag) { 00388 tr_error("Root DODAG already exists"); 00389 return NULL; 00390 } 00391 dodag = rpl_create_dodag(instance, dodagid, g_mop_prf); 00392 if (!dodag) { 00393 tr_error("No mem for DODAG root"); 00394 return NULL; 00395 } 00396 00397 rpl_dodag_update_config(dodag, conf, NULL, NULL); 00398 rpl_dodag_set_root(dodag, true); 00399 rpl_dodag_version_t *version = rpl_create_dodag_version(dodag, rpl_seq_init()); 00400 if (!version) { 00401 rpl_delete_dodag(dodag); 00402 tr_error("No mem for DODAG root"); 00403 return NULL; 00404 } 00405 rpl_instance_set_dodag_version(instance, version, rank); 00406 00407 return dodag; 00408 } 00409 00410 void rpl_control_delete_dodag_root(rpl_domain_t *domain, rpl_dodag_t *dodag) 00411 { 00412 (void)domain; 00413 00414 rpl_delete_dodag(dodag); 00415 } 00416 00417 void rpl_control_update_dodag_route(rpl_dodag_t *dodag, const uint8_t *prefix, uint8_t prefix_len, uint8_t flags, uint32_t lifetime, bool age) 00418 { 00419 /* Not clear if non-root nodes should be able to publish routes */ 00420 if (rpl_dodag_am_root(dodag)) { 00421 rpl_dodag_update_dio_route(dodag, prefix, prefix_len, flags, lifetime, age); 00422 } 00423 } 00424 00425 void rpl_control_update_dodag_prefix(rpl_dodag_t *dodag, const uint8_t *prefix, uint8_t prefix_len, uint8_t flags, uint32_t lifetime, uint32_t preftime, bool age) 00426 { 00427 /* Don't let them set weird flags. We do allow them to add prefixes if not 00428 * a root though - they might want this to add a L prefix? 00429 */ 00430 flags &= (PIO_A|PIO_L); 00431 rpl_dodag_update_dio_prefix(dodag, prefix, prefix_len, flags, lifetime, preftime, /*publish=*/true, age); 00432 } 00433 00434 void rpl_control_increment_dtsn(rpl_dodag_t *dodag) 00435 { 00436 rpl_dodag_increment_dtsn(dodag); 00437 //rpl_dodag_inconsistency(dodag); currently implied by rpl_dodag_increment_dtsn 00438 } 00439 00440 void rpl_control_increment_dodag_version(rpl_dodag_t *dodag) 00441 { 00442 if (rpl_dodag_am_root(dodag)) { 00443 uint8_t new_version = rpl_seq_inc(rpl_dodag_get_version_number_as_root(dodag)); 00444 rpl_dodag_set_version_number_as_root(dodag, new_version); 00445 } 00446 } 00447 00448 void rpl_control_set_dodag_pref(rpl_dodag_t *dodag, uint8_t pref) 00449 { 00450 if (rpl_dodag_am_root(dodag)) { 00451 rpl_dodag_set_pref(dodag, pref); 00452 } 00453 } 00454 00455 void rpl_control_poison(rpl_domain_t *domain, uint8_t count) 00456 { 00457 ns_list_foreach(rpl_instance_t, instance, &domain->instances) { 00458 rpl_instance_poison(instance, count); 00459 } 00460 } 00461 00462 void rpl_control_force_leaf(rpl_domain_t *domain, bool leaf) 00463 { 00464 domain->force_leaf = leaf; 00465 if (leaf) { 00466 ns_list_foreach(rpl_instance_t, instance, &domain->instances) { 00467 rpl_instance_force_leaf(instance); 00468 } 00469 } 00470 } 00471 00472 /* Check whether the options section of a RPL control message is well-formed */ 00473 static bool rpl_control_options_well_formed(const uint8_t *dptr, uint_fast16_t dlen) 00474 { 00475 while (dlen) { 00476 uint_fast16_t opt_len; 00477 00478 if (dptr[0] == RPL_PAD1_OPTION) { 00479 opt_len = 1; 00480 } else { 00481 if (dlen < 2) { 00482 return false; 00483 } 00484 opt_len = 2 + dptr[1]; 00485 } 00486 00487 if (opt_len > dlen) { 00488 return false; 00489 } 00490 00491 dptr += opt_len; 00492 dlen -= opt_len; 00493 } 00494 00495 return true; 00496 } 00497 00498 static bool rpl_control_options_well_formed_in_buffer(const buffer_t *buf, uint16_t offset) 00499 { 00500 if (buffer_data_length(buf) < offset) { 00501 return false; 00502 } 00503 00504 return rpl_control_options_well_formed(buffer_data_pointer(buf) + offset, 00505 buffer_data_length(buf) - offset); 00506 } 00507 00508 /* 00509 * Search for the first option of the specified type (and optionally length). 00510 * Caller must have already checked the options are well-formed. 00511 * Returns pointer to type byte, so length is at +1, data at +2. 00512 */ 00513 static const uint8_t *rpl_control_find_option(const uint8_t *dptr, uint_fast16_t dlen, uint8_t option, uint8_t optlen) 00514 { 00515 while (dlen) { 00516 uint8_t type = dptr[0]; 00517 if (type == RPL_PAD1_OPTION) { 00518 dptr++, dlen--; 00519 continue; 00520 } 00521 uint_fast16_t len = dptr[1]; 00522 if (type == option && (optlen == 0 || optlen == len)) { 00523 return dptr; 00524 } 00525 00526 dlen -= 2 + len; 00527 dptr += 2 + len; 00528 } 00529 return NULL; 00530 } 00531 00532 static const uint8_t *rpl_control_find_option_in_buffer(const buffer_t *buf, uint_fast16_t offset, uint8_t option, uint8_t optlen) 00533 { 00534 return rpl_control_find_option(buffer_data_pointer(buf) + offset, 00535 buffer_data_length(buf) - offset, 00536 option, optlen); 00537 } 00538 00539 /* 0 1 2 3 00540 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 00541 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00542 * | Type = 0x04 |Opt Length = 14| Flags |A| PCS | DIOIntDoubl. | 00543 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00544 * | DIOIntMin. | DIORedun. | MaxRankIncrease | 00545 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00546 * | MinHopRankIncrease | OCP | 00547 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00548 * | Reserved | Def. Lifetime | Lifetime Unit | 00549 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00550 * 00551 * Figure 24: Format of the DODAG Configuration Option 00552 */ 00553 static const uint8_t *rpl_control_read_conf(rpl_dodag_conf_t *conf_out, const uint8_t *opt) 00554 { 00555 conf_out->authentication = opt[2] & 0x08; 00556 conf_out->path_control_size = opt[2] & 0x07; 00557 conf_out->dio_interval_doublings = opt[3]; 00558 conf_out->dio_interval_min = opt[4]; 00559 conf_out->dio_redundancy_constant = opt[5]; 00560 conf_out->dag_max_rank_increase = common_read_16_bit(opt + 6); 00561 conf_out->min_hop_rank_increase = common_read_16_bit(opt + 8); 00562 conf_out->objective_code_point = common_read_16_bit(opt + 10); 00563 conf_out->default_lifetime = opt[13]; 00564 conf_out->lifetime_unit = common_read_16_bit(opt + 14); 00565 return opt + 16; 00566 } 00567 00568 static uint8_t *rpl_control_write_conf(uint8_t *opt_out, const rpl_dodag_conf_t *conf) 00569 { 00570 opt_out[0] = RPL_DODAG_CONF_OPTION; 00571 opt_out[1] = 14; 00572 opt_out[2] = conf->authentication ? RPL_CONF_FLAG_AUTH : 0; 00573 opt_out[2] |= conf->path_control_size; 00574 opt_out[3] = conf->dio_interval_doublings; 00575 opt_out[4] = conf->dio_interval_min; 00576 opt_out[5] = conf->dio_redundancy_constant; 00577 common_write_16_bit(conf->dag_max_rank_increase, opt_out + 6); 00578 common_write_16_bit(conf->min_hop_rank_increase, opt_out + 8); 00579 common_write_16_bit(conf->objective_code_point, opt_out + 10); 00580 opt_out[12] = 0; // reserved 00581 opt_out[13] = conf->default_lifetime; 00582 common_write_16_bit(conf->lifetime_unit, opt_out + 14); 00583 return opt_out + 16; 00584 } 00585 00586 static uint_fast8_t rpl_control_conf_length(void) 00587 { 00588 return 16; 00589 } 00590 00591 /* 00592 * 0 1 2 3 00593 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 00594 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00595 * | Type = 0x08 |Opt Length = 30| Prefix Length |L|A|R|Reserved1| 00596 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00597 * | Valid Lifetime | 00598 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00599 * | Preferred Lifetime | 00600 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00601 * | Reserved2 | 00602 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00603 * | | 00604 * + + 00605 * | | 00606 * + Prefix + 00607 * | | 00608 * + + 00609 * | | 00610 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00611 * 00612 * Figure 29: Format of the Prefix Information Option 00613 */ 00614 static void rpl_control_process_prefix_options(protocol_interface_info_entry_t *cur, rpl_instance_t *instance, rpl_dodag_t *dodag, rpl_neighbour_t *neighbour, const uint8_t *start, const uint8_t *end) 00615 { 00616 (void)cur; 00617 00618 bool router_addr_set = false; 00619 00620 rpl_neighbour_t *pref_parent = rpl_instance_preferred_parent(instance); 00621 00622 // const rpl_dodag_conf_t *conf = rpl_dodag_get_config(dodag); 00623 00624 for (;;) { 00625 const uint8_t *ptr = rpl_control_find_option(start, end-start, RPL_PREFIX_INFO_OPTION, 30); 00626 if (!ptr) { 00627 break; 00628 } 00629 uint8_t prefix_len = ptr[2]; 00630 uint8_t flags = ptr[3]; 00631 uint32_t preferred = common_read_32_bit(ptr + 4); 00632 uint32_t valid = common_read_32_bit(ptr + 8); 00633 const uint8_t *prefix = ptr + 16; 00634 00635 if (!pref_parent || neighbour == pref_parent) { 00636 /* XXX We don't yet locally handle A and L flags. Presumably should 00637 * only locally process for DODAG's we're a member of? Should we 00638 * process now, or later? 00639 */ 00640 00641 /* Store prefixes for possible forwarding */ 00642 /* XXX if leaf - don't bother? Or do we want to remember them for 00643 * when we switch DODAG, as mentioned above? 00644 */ 00645 00646 rpl_dodag_update_dio_prefix(dodag, prefix, prefix_len, flags, valid, preferred, false, true); 00647 } 00648 00649 if ((flags & PIO_R) && !router_addr_set) { 00650 /* Routers can have multiple global addresses - one for each 00651 * prefix being advertised. We don't attempt to track this; 00652 * we just remember their first-listed address. 00653 * ( 00654 */ 00655 router_addr_set = true; 00656 rpl_neighbour_update_global_address(neighbour, ptr + 16); 00657 } 00658 00659 start = ptr + 32; 00660 } 00661 } 00662 00663 /* 00664 * 0 1 2 3 00665 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 00666 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00667 * | Type = 0x03 | Option Length | Prefix Length |Resvd|Prf|Resvd| 00668 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00669 * | Route Lifetime | 00670 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00671 * | | 00672 * . Prefix (Variable Length) . 00673 * . . 00674 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00675 * 00676 * Figure 23: Format of the Route Information Option 00677 */ 00678 static void rpl_control_process_route_options(rpl_instance_t *instance, rpl_dodag_t *dodag, rpl_dodag_version_t *version, rpl_neighbour_t *neighbour, uint16_t rank, const uint8_t *start, const uint8_t *end) 00679 { 00680 (void)neighbour; 00681 00682 /* For non-current DODAGs, it's simplest to accept routes from anyone. */ 00683 if (rpl_instance_current_dodag(instance) == dodag) { 00684 /* It's for the current DODAG - more care required. Check versions */ 00685 rpl_cmp_t v_cmp = rpl_dodag_version_compare(version, rpl_instance_current_dodag_version(instance)); 00686 if (v_cmp & RPL_CMP_EQUAL) { 00687 /* If coming from a neighbour in the same version, make sure they have lower rank */ 00688 if (!(rpl_rank_compare(dodag, rank, rpl_instance_current_rank(instance)) & RPL_CMP_LESS)) { 00689 return; 00690 } 00691 } else if (v_cmp & RPL_CMP_GREATER) { 00692 /* If coming from a neighbour in a newer version, we'll accept it */ 00693 } else { 00694 /* From older or unknown version, we reject it */ 00695 return; 00696 } 00697 } 00698 00699 for (;;) { 00700 const uint8_t *ptr = rpl_control_find_option(start, end-start, RPL_ROUTE_INFO_OPTION, 0); 00701 if (!ptr) { 00702 break; 00703 } 00704 uint8_t opt_len = ptr[1]; 00705 start = ptr + 2 + opt_len; 00706 if (opt_len < 6) { 00707 tr_warn("Malformed RIO"); 00708 continue; 00709 } 00710 uint8_t prefix_len = ptr[2]; 00711 uint8_t flags = ptr[3]; 00712 uint32_t lifetime = common_read_32_bit(ptr + 4); 00713 const uint8_t *prefix = ptr + 8; 00714 if (opt_len < 6 + (prefix_len + 7u) / 8) { 00715 tr_warn("Malformed RIO"); 00716 continue; 00717 } 00718 rpl_dodag_update_dio_route(dodag, prefix, prefix_len, flags, lifetime, true); 00719 } 00720 00721 /* We do not purge unadvertised routes. Thus if the root wants to purge 00722 * a route before its lifetime is up, stopping advertising it is not 00723 * sufficient, it has to advertise it with low or zero lifetime. This fits 00724 * with ND Router Advertisement behaviour. 00725 */ 00726 } 00727 00728 static void rpl_control_dao_trigger_request(rpl_instance_t *instance, rpl_dodag_t *dodag, rpl_neighbour_t *neighbour) 00729 { 00730 switch (rpl_dodag_mop(dodag)) { 00731 case RPL_MODE_NON_STORING: 00732 rpl_instance_dao_request(instance, NULL); 00733 rpl_dodag_increment_dtsn(dodag); 00734 break; 00735 case RPL_MODE_STORING: 00736 case RPL_MODE_STORING_MULTICAST: 00737 rpl_instance_dao_request(instance, neighbour); 00738 break; 00739 default: 00740 /* Nothing */ 00741 break; 00742 } 00743 } 00744 00745 /* 00746 * 0 1 2 3 00747 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 00748 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00749 * | RPLInstanceID |Version Number | Rank | 00750 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00751 * |G|0| MOP | Prf | DTSN | Flags | Reserved | 00752 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00753 * | | 00754 * + + 00755 * | | 00756 * + DODAGID + 00757 * | | 00758 * + + 00759 * | | 00760 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00761 * | Option(s)... 00762 * +-+-+-+-+-+-+-+-+ 00763 * 00764 * Figure 14: The DIO Base Object 00765 */ 00766 static buffer_t *rpl_control_dio_handler(protocol_interface_info_entry_t *cur, rpl_domain_t *domain, buffer_t *buf) 00767 { 00768 if (!rpl_control_options_well_formed_in_buffer(buf, 24)) { 00769 tr_error("DIO format"); 00770 malformed: 00771 protocol_stats_update(STATS_RPL_MALFORMED_MESSAGE, 1); 00772 return buffer_free(buf); 00773 } 00774 00775 /* Read the base object */ 00776 const uint8_t *ptr = buffer_data_pointer(buf); 00777 uint8_t instance_id, version_num, g_mop_prf, dtsn; 00778 uint16_t rank; 00779 const uint8_t *dodagid; 00780 bool become_leaf = false; 00781 00782 instance_id = ptr[0]; 00783 version_num = ptr[1]; 00784 rank = common_read_16_bit(ptr + 2); 00785 g_mop_prf = ptr[4]; 00786 dtsn = ptr[5]; 00787 dodagid = ptr+8; 00788 ptr += 24; 00789 tr_info("DIO from %s, rank %x", trace_ipv6(buf->src_sa .address ), rank); 00790 if (addr_is_ipv6_link_local(dodagid) || addr_is_ipv6_multicast(dodagid)) { 00791 tr_error("DIO DODAGID"); 00792 goto malformed; 00793 } 00794 00795 /* Check if this is for an existing RPL Instance - create if necessary */ 00796 rpl_instance_t *instance = rpl_lookup_instance(domain, instance_id, dodagid); 00797 if (!instance) { 00798 /* Policy can gate at this point, by instance ID and DODAG ID */ 00799 if (!rpl_policy_join_instance(domain, instance_id, dodagid)) { 00800 tr_info("Policy ignoring instance (%d,%s)", instance_id, trace_ipv6(dodagid)); 00801 return buffer_free(buf); 00802 } 00803 00804 instance = rpl_create_instance(domain, instance_id); 00805 if (!instance) { 00806 return buffer_free(buf); 00807 } 00808 00809 if ((g_mop_prf & RPL_MODE_MASK) != RPL_MODE_NO_DOWNWARD) { 00810 rpl_control_publish_own_addresses(domain, instance); 00811 } 00812 } 00813 00814 /* Lookup any existing neighbour entry */ 00815 rpl_neighbour_t *neighbour = rpl_lookup_neighbour_by_ll_address(instance, buf->src_sa .address , cur->id); 00816 00817 /* From this point on, we are potentially updating an existing neighbour */ 00818 /* Any decision to reject the DIO, or failure to handle it, means removing that neighbour */ 00819 00820 /* Any DIO with infinite rank is immediate poison - no further analysis required */ 00821 if (rank == RPL_RANK_INFINITE) { 00822 goto invalid_parent; 00823 } 00824 00825 /* Policy can gate by DODAGID and other bits */ 00826 if (!rpl_policy_join_dodag(domain, g_mop_prf, instance_id, dodagid)) { 00827 tr_info("Policy ignoring DODAG (%02x,%s)", g_mop_prf, trace_ipv6(dodagid)); 00828 goto invalid_parent; 00829 } 00830 00831 /* Look up and create the DODAG structure */ 00832 rpl_dodag_t *dodag = rpl_lookup_dodag(instance, dodagid); 00833 if (!dodag) { 00834 /* Policy can gate by DODAGID and other bits */ 00835 if (!rpl_policy_join_dodag(domain, g_mop_prf, instance_id, dodagid)) { 00836 tr_info("Policy ignoring DODAG (%02x,%s)", g_mop_prf, trace_ipv6(dodagid)); 00837 goto invalid_parent; 00838 } 00839 dodag = rpl_create_dodag(instance, dodagid, g_mop_prf); 00840 if (!dodag) { 00841 goto invalid_parent; 00842 } 00843 } 00844 00845 /* Never listen to nodes in a DODAG we're rooting */ 00846 if (rpl_dodag_am_root(dodag)) { 00847 /* TODO - if version is newer or unordered, increment our version to be higher? */ 00848 /* Old code had this trick - actually, would need to go further. Want to listen first, then use a higher 00849 * than existing. */ 00850 /* 00851 uint8_t our_version = rpl_dodag_get_version_number_as_root(dodag); 00852 if (rpl_seq_compare(version_num, our_version) & (RPL_CMP_UNORDERED|RPL_CMP_GREATER)) 00853 */ 00854 goto invalid_parent; 00855 } 00856 00857 /* Even if we're not currently rooting - what if it's our address? Ignore stale info on network */ 00858 if (protcol_interface_address_compare(NULL, dodagid) == 0) { 00859 tr_info("DIO our DODAGID %s", trace_ipv6(dodagid)); 00860 /* Should we transmit poison? */ 00861 goto invalid_parent; 00862 } 00863 00864 00865 /* Update DODAG config information, if option present, and either we don't have it or version is newer */ 00866 const uint8_t *dodag_conf_ptr = rpl_control_find_option_in_buffer(buf, 24, RPL_DODAG_CONF_OPTION, 14); 00867 if (dodag_conf_ptr) { 00868 rpl_dodag_conf_t conf_buf; 00869 rpl_control_read_conf(&conf_buf, dodag_conf_ptr); 00870 if (!rpl_dodag_update_config(dodag, &conf_buf, buf->src_sa .address , &become_leaf)) { 00871 goto invalid_parent; 00872 } 00873 } 00874 00875 /* If we don't have any DODAG config information, ask by unicast DIS */ 00876 const rpl_dodag_conf_t *conf = rpl_dodag_get_config(dodag); 00877 if (!conf) { 00878 /* TODO - rate limit DIS? */ 00879 rpl_control_transmit_dis(domain, cur, RPL_SOLINFO_PRED_DODAGID|RPL_SOLINFO_PRED_INSTANCEID, instance_id, dodagid, 0, buf->src_sa .address ); 00880 goto invalid_parent; 00881 } 00882 00883 /* Check whether config is acceptable */ 00884 if (!rpl_policy_join_config(domain, conf, &become_leaf)) { 00885 goto invalid_parent; 00886 } 00887 00888 /* Lookup or create DODAG Version state info */ 00889 rpl_dodag_version_t *version = rpl_lookup_dodag_version(dodag, version_num); 00890 if (!version) { 00891 version = rpl_create_dodag_version(dodag, version_num); 00892 if (!version) { 00893 goto invalid_parent; 00894 } 00895 } 00896 00897 const uint8_t *metric_ptr = rpl_control_find_option_in_buffer(buf, 24, RPL_DAG_METRIC_OPTION, 0); 00898 /* We currently don't understand anything about metrics, so to be on the safe side, we don't join */ 00899 if (metric_ptr) { 00900 become_leaf = true; 00901 } 00902 00903 /* We mustn't process DIOs from our potential sub-DODAG, unless local repair is ongoing */ 00904 if (!rpl_instance_local_repair(instance) && rpl_dodag_version_rank_indicates_possible_sub_dodag(version, rank)) { 00905 goto invalid_parent; 00906 } 00907 00908 /* Now we create the neighbour, if we don't already have a record */ 00909 if (!neighbour) { 00910 neighbour = rpl_create_neighbour(version, buf->src_sa .address , cur->id, g_mop_prf, dtsn); 00911 if (!neighbour) { 00912 goto invalid_parent; 00913 } 00914 } 00915 00916 /* Update neighbour info */ 00917 rpl_neighbour_update_dodag_version(neighbour, version, rank, g_mop_prf); 00918 00919 if (rpl_neighbour_update_dtsn(neighbour, dtsn)) { 00920 tr_info("Parent %s incremented DTSN", trace_ipv6(buf->src_sa .address )); 00921 rpl_control_dao_trigger_request(instance, dodag, neighbour); 00922 } 00923 00924 00925 rpl_control_process_prefix_options(cur, instance, dodag, neighbour, ptr, buffer_data_end(buf)); 00926 rpl_dodag_update_implicit_system_routes(dodag, neighbour); 00927 rpl_control_process_route_options(instance, dodag, version, neighbour, rank, ptr, buffer_data_end(buf)); 00928 00929 //rpl_control_process_metric_containers(neighbour, ptr, buffer_data_end(buf)) 00930 00931 if (become_leaf) { 00932 rpl_dodag_set_leaf(dodag, true); 00933 } 00934 00935 /* RFC 6550 8.3: A DIO from a sender with lesser DAGRank that causes no 00936 * changes to the recipient's parent set, preferred parent, or Rank SHOULD 00937 * be considered consistent with respect to the Trickle timer. 00938 * 00939 * Now, if we don't run parent selection immediately, how do we know if it's 00940 * consistent or not? Compromise is to treat all lower ranked DIOs as 00941 * consistent, and reset (and hold) the consistent counter to 0 if any of 00942 * the above change. This actually seems better than the RFC 6550 rule, as 00943 * it guarantees we will transmit if those change. The rule as stated 00944 * would mean a large number of parent messages would stop us advertising 00945 * a Rank change. 00946 */ 00947 if (version == rpl_instance_current_dodag_version(instance) && 00948 (rpl_rank_compare(dodag, rank, rpl_instance_current_rank(instance)) & RPL_CMP_LESS)) { 00949 rpl_instance_consistent_rx(instance); 00950 } 00951 00952 rpl_instance_neighbours_changed(instance); 00953 00954 return buffer_free(buf); 00955 00956 invalid_parent: 00957 if (neighbour) { 00958 rpl_delete_neighbour(instance, neighbour); 00959 } 00960 return buffer_free(buf); 00961 } 00962 00963 static void rpl_control_transmit_one_interface(protocol_interface_info_entry_t *cur, buffer_t *buf) 00964 { 00965 /* If destination is global, this will end up routed, regardless of original interface */ 00966 /* (But we do currently need an interface specified anyway?) */ 00967 if (cur) { 00968 buf->interface = cur; 00969 } 00970 00971 /* RPL requires us to use link-local source for all messages except DAO/DAO-ACK 00972 * in storing mode, which use global source. Default address selection in 00973 * icmpv6_down should do the right thing, as long as the interface does have 00974 * both LL and global, so don't bother to set source here. 00975 */ 00976 00977 protocol_push(buf); 00978 } 00979 00980 static void rpl_control_transmit_all_interfaces(rpl_domain_t *domain, buffer_t *buf) 00981 { 00982 protocol_interface_info_entry_t *first_if = NULL, *cur; 00983 int8_t last_id = -1; 00984 00985 while ((cur = protocol_stack_interface_info_get_by_rpl_domain(domain, last_id)) != NULL) { 00986 if (!first_if) { 00987 /* Note first interface - it will get original buffer after loop */ 00988 first_if = cur; 00989 } else { 00990 /* This is a subsequent interface - send a clone */ 00991 buffer_t *clone = buffer_clone(buf); 00992 if (clone) { 00993 rpl_control_transmit_one_interface(cur, clone); 00994 } 00995 } 00996 last_id = cur->id; 00997 } 00998 00999 if (first_if) { 01000 rpl_control_transmit_one_interface(first_if, buf); 01001 } else { 01002 // RPL domain with no interfaces? Odd... 01003 buffer_free(buf); 01004 } 01005 } 01006 01007 /* Complete and send a RPL control message - all interfaces multicast if dst+cur are NULL, else unicast */ 01008 void rpl_control_transmit(rpl_domain_t *domain, protocol_interface_info_entry_t *cur, uint8_t code, buffer_t *buf, const uint8_t *dst) 01009 { 01010 buf->info = (buffer_info_t)(B_FROM_ICMP | B_TO_ICMP | B_DIR_DOWN); 01011 buf->options .type = ICMPV6_TYPE_INFO_RPL_CONTROL; 01012 buf->options .code = code; 01013 buf->dst_sa .addr_type = ADDR_IPV6 ; 01014 memcpy(buf->dst_sa .address , dst ? dst : ADDR_LINK_LOCAL_ALL_RPL_NODES, 16); 01015 01016 /* Use 255 hop limit for link-local stuff, akin to other ICMP */ 01017 /* Others set "0", which means use interface default */ 01018 buf->options .hop_limit = addr_ipv6_scope(buf->dst_sa .address , cur) <= IPV6_SCOPE_LINK_LOCAL ? 255 : 0; 01019 01020 01021 if (dst == NULL && cur == NULL) { 01022 rpl_control_transmit_all_interfaces(domain, buf); 01023 } else { 01024 /* Fudge - need a dummy interface for non-storing DAO to root - won't 01025 * actually be used as it'll get globally routed. 01026 */ 01027 if (!cur) { 01028 cur = protocol_stack_interface_info_get_by_id(domain->non_storing_downstream_interface); 01029 } 01030 rpl_control_transmit_one_interface(cur, buf); 01031 } 01032 } 01033 01034 01035 /* Transmit a DIO (unicast or multicast); cur may be NULL if multicast */ 01036 void rpl_control_transmit_dio(rpl_domain_t *domain, protocol_interface_info_entry_t *cur, uint8_t instance_id, uint8_t dodag_version, uint16_t rank, uint8_t g_mop_prf, uint8_t dtsn, rpl_dodag_t *dodag, const uint8_t dodagid[16], const rpl_dodag_conf_t *conf, const uint8_t *dst) 01037 { 01038 uint16_t length; 01039 01040 const rpl_dio_route_list_t *routes = rpl_dodag_get_route_list(dodag); 01041 const prefix_list_t *prefixes = rpl_dodag_get_prefix_list(dodag); 01042 01043 tr_debug("transmit dio, rank: %x", rank); 01044 protocol_interface_info_entry_t *downstream_if = protocol_stack_interface_info_get_by_id(domain->non_storing_downstream_interface); 01045 length = 24; 01046 if (conf) { 01047 length += rpl_control_conf_length(); 01048 } 01049 01050 ns_list_foreach(prefix_entry_t, prefix, prefixes) { 01051 /* We must not forward 'L' prefixes */ 01052 if ((prefix->options & (PIO_L|RPL_PIO_PUBLISHED)) == PIO_L) { 01053 continue; 01054 } 01055 01056 /* In storing mode, do not forward a prefix until we have an 01057 * address for it. (RFC 6550 6.7.10 - "A non-storing node SHOULD 01058 * refrain from advertising a prefix until it owns an address of 01059 * that prefix, and then it SHOULD advertise its full address in 01060 * this field, with the 'R' flag set. 01061 */ 01062 /* XXX We must also be advertising the corresponding address as a DAO target */ 01063 const uint8_t *addr = downstream_if ? addr_select_with_prefix(downstream_if, prefix->prefix, prefix->prefix_len, 0) : NULL; 01064 if (addr) { 01065 prefix->options |= PIO_R; 01066 memcpy(prefix->prefix, addr, 16); 01067 } else { 01068 prefix->options &=~ PIO_R; 01069 01070 if (rpl_dodag_mop(dodag) == RPL_MODE_NON_STORING) { 01071 continue; 01072 } 01073 } 01074 length += 32; 01075 } 01076 01077 ns_list_foreach(rpl_dio_route_t, route, routes) { 01078 length += 8 + (route->prefix_len + 7u) / 8; 01079 } 01080 01081 /* Add metric lengths here */ 01082 01083 buffer_t *buf = buffer_get(length); 01084 if (!buf) { 01085 return; 01086 } 01087 uint8_t *ptr = buffer_data_pointer(buf); 01088 ptr[0] = instance_id; 01089 ptr[1] = dodag_version; 01090 common_write_16_bit(rank, ptr + 2); 01091 ptr[4] = g_mop_prf; 01092 ptr[5] = dtsn; 01093 ptr[6] = 0; 01094 ptr[7] = 0; 01095 memcpy(ptr + 8, dodagid, 16); 01096 ptr += 24; 01097 01098 if (conf) { 01099 ptr = rpl_control_write_conf(ptr, conf); 01100 } 01101 01102 /* Write prefix/route/metric options here */ 01103 01104 01105 ns_list_foreach_safe(prefix_entry_t, prefix, prefixes) { 01106 /* See equivalent checks in length calculation above */ 01107 if ((prefix->options & (PIO_L|RPL_PIO_PUBLISHED)) == PIO_L || 01108 (!(prefix->options & PIO_R) && rpl_dodag_mop(dodag) == RPL_MODE_NON_STORING)) { 01109 continue; 01110 } 01111 01112 ptr[0] = RPL_PREFIX_INFO_OPTION; 01113 ptr[1] = 30; 01114 ptr[2] = prefix->prefix_len; 01115 ptr[3] = prefix->options & (PIO_R|PIO_A|PIO_L); 01116 common_write_32_bit(prefix->lifetime, ptr + 4); 01117 common_write_32_bit(prefix->preftime, ptr + 8); 01118 common_write_32_bit(0, ptr + 12); // reserved 01119 memcpy(ptr + 16, prefix->prefix, 16); 01120 ptr += 32; 01121 } 01122 01123 ns_list_foreach_safe(rpl_dio_route_t, route, routes) { 01124 uint8_t prefix_bytes = (route->prefix_len + 7u) / 8u; 01125 ptr[0] = RPL_ROUTE_INFO_OPTION; 01126 ptr[1] = 6 + prefix_bytes; 01127 ptr[2] = route->prefix_len; 01128 ptr[3] = route->flags; 01129 common_write_32_bit(route->lifetime, ptr + 4); 01130 bitcopy0(ptr + 8, route->prefix, route->prefix_len); 01131 ptr += 8 + prefix_bytes; 01132 /* Transmitting a multicast DIO decrements the hold count for 0 lifetime routes */ 01133 if (dst == NULL && route->lifetime == 0) { 01134 if (route->hold_count) { 01135 route->hold_count--; 01136 } 01137 if (route->hold_count == 0) { 01138 rpl_dodag_delete_dio_route(dodag, route); 01139 } 01140 } 01141 } 01142 01143 buffer_data_end_set(buf, ptr); 01144 01145 rpl_control_transmit(domain, cur, ICMPV6_CODE_RPL_DIO, buf, dst); 01146 } 01147 01148 /* 0 1 2 01149 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 01150 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 01151 * | Flags | Reserved | Option(s)... 01152 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 01153 * 01154 * Figure 13: The DIS Base Object 01155 * 01156 * 0 1 2 3 01157 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 01158 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 01159 * | Type = 0x07 |Opt Length = 19| RPLInstanceID |V|I|D| Flags | 01160 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 01161 * | | 01162 * + + 01163 * | | 01164 * + DODAGID + 01165 * | | 01166 * + + 01167 * | | 01168 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 01169 * |Version Number | 01170 * +-+-+-+-+-+-+-+-+ 01171 * 01172 * Figure 28: Format of the Solicited Information Option 01173 */ 01174 static buffer_t *rpl_control_dis_handler(protocol_interface_info_entry_t *cur, rpl_domain_t *domain, buffer_t *buf, bool multicast) 01175 { 01176 if (!rpl_control_options_well_formed_in_buffer(buf, 2)) { 01177 tr_error("DIS format"); 01178 protocol_stats_update(STATS_RPL_MALFORMED_MESSAGE, 1); 01179 return buffer_free(buf); 01180 } 01181 01182 /* Base object irrelevant - find the Solicited Information option, if any */ 01183 const uint8_t *sol = rpl_control_find_option_in_buffer(buf, 2, RPL_SOL_INFO_OPTION, 19); 01184 uint8_t preds, sol_instance_id, sol_version; 01185 const uint8_t *sol_dodagid; 01186 if (sol) { 01187 preds = sol[3]; 01188 sol_instance_id = sol[2]; 01189 sol_dodagid = sol + 4; 01190 sol_version = sol[20]; 01191 } else { 01192 /* No option present means "match any" */ 01193 preds = 0; 01194 sol_instance_id = 0; 01195 sol_dodagid = NULL; 01196 sol_version = 0; 01197 } 01198 01199 /* If it's a multicast DIS, then we reset trickle timer for each matching 01200 * instances. 01201 * 01202 * If it's a unicast DIS, we unicast a DIO back to the sender for each 01203 * matching instance. 01204 */ 01205 rpl_loopfn_trigger_unicast_dio_arg_t arg; 01206 arg.interface = cur; 01207 arg.dst = buf->src_sa .address ; 01208 01209 rpl_control_predicate_loop(domain, 01210 multicast ? rpl_loopfn_reset_dio_timer : rpl_loopfn_trigger_unicast_dio, 01211 &arg, 01212 preds, sol_instance_id, sol_dodagid, sol_version); 01213 01214 return buffer_free(buf); 01215 } 01216 01217 void rpl_control_transmit_dis(rpl_domain_t *domain, protocol_interface_info_entry_t *cur, uint8_t pred, uint8_t instance_id, const uint8_t *dodagid, const uint8_t version, const uint8_t *dst) 01218 { 01219 uint16_t length = 2; 01220 if (pred) { 01221 length += 2 + 19; 01222 } 01223 buffer_t *buf = buffer_get(length); 01224 if (!buf) { 01225 return; 01226 } 01227 uint8_t *ptr = buffer_data_pointer(buf); 01228 *ptr++ = 0; // flags 01229 *ptr++ = 0; // reserved 01230 if (pred) { 01231 ptr[0] = RPL_SOL_INFO_OPTION; 01232 ptr[1] = 19; 01233 if (pred & RPL_SOLINFO_PRED_INSTANCEID) { 01234 ptr[2] = instance_id; 01235 } else { 01236 ptr[2] = 0; 01237 } 01238 ptr[3] = pred; 01239 if (pred & RPL_SOLINFO_PRED_DODAGID) { 01240 memcpy(ptr + 4, dodagid, 16); 01241 } else { 01242 memset(ptr + 4, 0, 16); 01243 } 01244 if (pred & RPL_SOLINFO_PRED_VERSION) { 01245 ptr[20] = version; 01246 } else { 01247 ptr[20] = 0; 01248 } 01249 ptr += 21; 01250 } 01251 01252 buffer_data_end_set(buf, ptr); 01253 rpl_control_transmit(domain, cur, ICMPV6_CODE_RPL_DIS, buf, dst); 01254 } 01255 01256 #ifdef HAVE_RPL_DAO_HANDLING 01257 /* 01258 * 0 1 2 3 01259 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 01260 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 01261 * | RPLInstanceID |D| Reserved | DAOSequence | Status | 01262 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 01263 * | | 01264 * + + 01265 * | | 01266 * + DODAGID* + 01267 * | | 01268 * + + 01269 * | | 01270 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 01271 * | Option(s)... 01272 * +-+-+-+-+-+-+-+-+ 01273 * 01274 * Figure 17: The DAO ACK Base Object 01275 */ 01276 01277 static void rpl_control_transmit_dao_ack(rpl_domain_t *domain, protocol_interface_info_entry_t *cur, uint8_t instance_id, uint8_t dao_sequence, uint8_t status, const uint8_t dodagid[16], const uint8_t *dst) 01278 { 01279 buffer_t *buf = buffer_get(dodagid ? 4 + 20 : 4); 01280 if (!buf) { 01281 return; 01282 } 01283 uint8_t *ptr = buffer_data_pointer(buf); 01284 ptr[0] = instance_id; 01285 ptr[1] = dodagid ? RPL_DAO_ACK_FLAG_DODAGID : 0; 01286 ptr[2] = dao_sequence; 01287 ptr[3] = status; 01288 ptr += 4; 01289 if (dodagid) { 01290 memcpy(ptr, dodagid, 16); 01291 ptr += 16; 01292 } 01293 buffer_data_end_set(buf, ptr); 01294 rpl_control_transmit(domain, cur, ICMPV6_CODE_RPL_DAO_ACK, buf, dst); 01295 tr_info("Transmit DAO-ACK to: %s", trace_ipv6(dst)); 01296 } 01297 #endif // HAVE_RPL_DAO_HANDLING 01298 01299 static buffer_t *rpl_control_dao_ack_handler(protocol_interface_info_entry_t *cur, rpl_domain_t *domain, buffer_t *buf) 01300 { 01301 (void)cur; 01302 01303 if (buffer_data_length(buf) < 4) { 01304 format_error: 01305 tr_error("DAO-ACK format"); 01306 protocol_stats_update(STATS_RPL_MALFORMED_MESSAGE, 1); 01307 return buffer_free(buf); 01308 } 01309 const uint8_t *ptr = buffer_data_pointer(buf); 01310 uint8_t instance_id = ptr[0]; 01311 const uint8_t *dodagid; 01312 01313 if (ptr[1] & RPL_DAO_ACK_FLAG_DODAGID) { 01314 if (buffer_data_length(buf) < 4 + 16) { 01315 goto format_error; 01316 } 01317 dodagid = ptr + 4; 01318 } else { 01319 dodagid = NULL; 01320 } 01321 01322 rpl_instance_t *instance = rpl_lookup_instance(domain, instance_id, dodagid); 01323 if (!instance) { 01324 protocol_stats_update(STATS_RPL_UNKNOWN_INSTANCE, 1); 01325 return buffer_free(buf); 01326 } 01327 01328 uint8_t dao_sequence = ptr[2]; 01329 uint8_t status = ptr[3]; 01330 rpl_instance_dao_acked(instance, buf->src_sa .address , buf->interface ->id, dao_sequence, status); 01331 return buffer_free(buf); 01332 } 01333 01334 /* 01335 * 0 1 2 3 01336 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 01337 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 01338 * | RPLInstanceID |K|D| Flags | Reserved | DAOSequence | 01339 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 01340 * | | 01341 * + + 01342 * | | 01343 * + DODAGID* + 01344 * | | 01345 * + + 01346 * | | 01347 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 01348 * | Option(s)... 01349 * +-+-+-+-+-+-+-+-+ 01350 * 01351 * Figure 16: The DAO Base Object 01352 */ 01353 bool rpl_control_transmit_dao(rpl_domain_t *domain, protocol_interface_info_entry_t *cur, rpl_instance_t *instance, uint8_t instance_id, uint8_t dao_sequence, const uint8_t dodagid[16], const uint8_t *opts, uint16_t opts_size, const uint8_t *dst) 01354 { 01355 uint8_t dodagid_flag = 0; 01356 if (rpl_instance_id_is_local(instance_id)) { 01357 dodagid_flag = RPL_DAO_FLAG_DODAGID; 01358 } 01359 01360 uint8_t base_size = dodagid_flag ? 4 + 16 : 4; 01361 buffer_t *buf = buffer_get(base_size + opts_size); 01362 if (!buf) { 01363 return true; 01364 } 01365 uint8_t *ptr = buffer_data_pointer(buf); 01366 ptr[0] = instance_id; 01367 ptr[1] = dodagid_flag; 01368 if (rpl_policy_request_dao_acks(domain, rpl_instance_mop(instance))) { 01369 ptr[1] |= RPL_DAO_FLAG_ACK_REQ; 01370 } 01371 ptr[2] = 0; 01372 ptr[3] = dao_sequence; 01373 if (dodagid_flag) { 01374 memcpy(ptr + 4, dodagid, 16); 01375 } 01376 memcpy(ptr + base_size, opts, opts_size); 01377 buffer_data_end_set(buf, ptr + base_size + opts_size); 01378 01379 rpl_control_transmit(domain, cur, ICMPV6_CODE_RPL_DAO, buf, dst); 01380 01381 return ptr[1] & RPL_DAO_FLAG_ACK_REQ; 01382 } 01383 01384 #ifdef HAVE_RPL_DAO_HANDLING 01385 static buffer_t *rpl_control_dao_handler(protocol_interface_info_entry_t *cur, rpl_domain_t *domain, buffer_t *buf, bool multicast) 01386 { 01387 if (buffer_data_length(buf) < 4) { 01388 format_error: 01389 tr_error("DAO format"); 01390 protocol_stats_update(STATS_RPL_MALFORMED_MESSAGE, 1); 01391 return buffer_free(buf); 01392 } 01393 const uint8_t *ptr = buffer_data_pointer(buf); 01394 uint8_t instance_id = ptr[0]; 01395 uint8_t flags = ptr[1]; 01396 uint8_t dao_sequence = ptr[3]; 01397 const uint8_t *dodagid; 01398 tr_info("DAO from %s", trace_ipv6(buf->src_sa .address )); 01399 ptr += 4; 01400 if (flags & RPL_DAO_FLAG_DODAGID) { 01401 if (buffer_data_length(buf) < 4 + 16) { 01402 goto format_error; 01403 } 01404 dodagid = ptr; 01405 ptr += 16; 01406 } else { 01407 dodagid = NULL; 01408 } 01409 01410 rpl_instance_t *instance = rpl_lookup_instance(domain, instance_id, dodagid); 01411 if (!instance) { 01412 protocol_stats_update(STATS_RPL_UNKNOWN_INSTANCE, 1); 01413 return buffer_free(buf); 01414 } 01415 01416 uint16_t opts_len = buffer_data_end(buf) - ptr; 01417 if (!rpl_control_options_well_formed(ptr, opts_len)) { 01418 goto format_error; 01419 } 01420 01421 #if 0 01422 rpl_dodag_t *dodag = rpl_instance_current_dodag(instance); 01423 if (!dodag) { 01424 return buffer_free(buf); 01425 01426 } 01427 uint8_t mode = rpl_dodag_mop(dodag); 01428 switch (mo)!rpl_instance_am_root(instance)) 01429 01430 /* No current processing - pretend to accept */ 01431 uint8_t status = 0; 01432 01433 01434 } 01435 #endif 01436 uint8_t status; 01437 bool reply_ok = rpl_instance_dao_received(instance, buf->src_sa .address , buf->interface ->id, multicast, ptr, opts_len, &status); 01438 01439 /* Ack if requested or non-zero status */ 01440 if (reply_ok && ((flags & RPL_DAO_FLAG_ACK_REQ) || status != 0)) { 01441 rpl_control_transmit_dao_ack(domain, cur, instance_id, dao_sequence, status, dodagid, buf->src_sa .address ); 01442 } 01443 return buffer_free(buf); 01444 } 01445 #endif // HAVE_RPL_DAO_HANDLING 01446 01447 buffer_t *rpl_control_handler(buffer_t *buf) 01448 { 01449 protocol_interface_info_entry_t *cur = buf->interface ; 01450 rpl_domain_t *domain = cur ? cur->rpl_domain : NULL; 01451 if (!domain) { 01452 tr_warning("RPL control on non-RPL interface"); 01453 return buffer_free(buf); 01454 } 01455 bool multicast = addr_is_ipv6_multicast(buf->dst_sa .address ); 01456 01457 if (addr_is_ipv6_multicast(buf->src_sa .address )) { 01458 protocol_stats_update(STATS_RPL_MALFORMED_MESSAGE, 1); 01459 return buffer_free(buf); 01460 } 01461 01462 switch (buf->options .code ) { 01463 case ICMPV6_CODE_RPL_DIS: 01464 return rpl_control_dis_handler(cur, domain, buf, multicast); 01465 case ICMPV6_CODE_RPL_DIO: 01466 return rpl_control_dio_handler(cur, domain, buf); 01467 #ifdef HAVE_RPL_DAO_HANDLING 01468 case ICMPV6_CODE_RPL_DAO: 01469 return rpl_control_dao_handler(cur, domain, buf, multicast); 01470 #endif 01471 case ICMPV6_CODE_RPL_DAO_ACK: 01472 return rpl_control_dao_ack_handler(cur, domain, buf); 01473 default: 01474 tr_warning("Unknown code 0x%02x", buf->options .code ); 01475 protocol_stats_update(STATS_RPL_MALFORMED_MESSAGE, 1); 01476 return buffer_free(buf); 01477 } 01478 } 01479 01480 #ifdef HAVE_RPL_ROOT 01481 /* Buffer contains ICMP payload, so 4 bytes unused, followed by invoking packet */ 01482 buffer_t *rpl_control_source_route_error_handler(buffer_t *buf, protocol_interface_info_entry_t *cur) 01483 { 01484 if (buffer_data_length(buf) < 40 || !cur->rpl_domain) { 01485 return buf; 01486 } 01487 01488 const uint8_t *target = buffer_data_pointer(buf) + 4 + 24; // Dest in IP header in ICMP invoking packet payload 01489 const uint8_t *transit = buf->src_sa .address ; // Source in IP header of ICMP packet 01490 01491 tr_warn("Source route error: %s->%s", trace_ipv6(transit), trace_ipv6(target)); 01492 /* We can't identify the instance - logically though it's instance-independent. 01493 * If transit can't reach target, that applies to all instances. 01494 */ 01495 ns_list_foreach(rpl_instance_t, instance, &cur->rpl_domain->instances) { 01496 rpl_downward_transit_error(instance, target, transit); 01497 } 01498 01499 return buf; 01500 } 01501 #endif 01502 01503 void rpl_control_fast_timer(uint16_t ticks) 01504 { 01505 ns_list_foreach(rpl_domain_t, domain, &rpl_domains) { 01506 ns_list_foreach(rpl_instance_t, instance, &domain->instances) { 01507 rpl_upward_dio_timer(instance, ticks); 01508 rpl_downward_dao_timer(instance, ticks); 01509 } 01510 } 01511 01512 } 01513 01514 #if 0 01515 static void trace_info_print(const char *fmt, ...) 01516 { 01517 va_list ap; 01518 va_start(ap, fmt); 01519 vtracef(TRACE_LEVEL_INFO, TRACE_GROUP, fmt, ap); 01520 va_end(ap); 01521 } 01522 #endif 01523 void rpl_control_slow_timer(uint16_t seconds) 01524 { 01525 bool purge = rpl_alloc_total > rpl_purge_threshold; 01526 01527 ns_list_foreach(rpl_domain_t, domain, &rpl_domains) { 01528 ns_list_foreach_safe(rpl_instance_t, instance, &domain->instances) { 01529 rpl_instance_slow_timer(instance, seconds); 01530 rpl_downward_dao_slow_timer(instance, seconds); 01531 /* We purge one item from each instance, so as not to favour one domain or instance */ 01532 if (purge) { 01533 rpl_instance_purge(instance); 01534 } 01535 } 01536 } 01537 01538 #if 0 // If including this, make sure to include the above trace_info_print helper function as well. 01539 static int rpl_print_timer; 01540 if ((rpl_print_timer += seconds) >= 50) { 01541 rpl_print_timer = 0; 01542 void arm_print_routing_table2(void (*print_fn)(const char *fmt, ...)); 01543 protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get(IF_6LoWPAN); 01544 if (cur) { 01545 ipv6_neighbour_cache_print(&cur->ipv6_neighbour_cache, trace_info_print); 01546 } 01547 arm_print_routing_table2(trace_info_print); 01548 rpl_control_print(trace_info_print); 01549 } 01550 #endif 01551 } 01552 01553 rpl_instance_t *rpl_control_enumerate_instances(rpl_domain_t *domain, rpl_instance_t *instance) 01554 { 01555 if (instance) { 01556 return ns_list_get_next(&domain->instances, instance); 01557 } else { 01558 return ns_list_get_first(&domain->instances); 01559 } 01560 } 01561 01562 rpl_instance_t *rpl_control_lookup_instance(rpl_domain_t *domain, uint8_t instance_id, const uint8_t *dodagid) 01563 { 01564 return rpl_lookup_instance(domain, instance_id, dodagid); 01565 } 01566 01567 bool rpl_control_get_instance_dao_target_count(rpl_domain_t *domain, uint8_t instance_id, const uint8_t *dodagid, uint16_t *target_count) 01568 { 01569 rpl_instance_t *instance = rpl_lookup_instance(domain, instance_id, dodagid); 01570 if (!instance) { 01571 return false; 01572 } 01573 *target_count = rpl_upward_read_dao_target_list_size(instance); 01574 return true; 01575 } 01576 01577 /* Backwards-compatibility implementation of net_rpl.h API designed for old implementation */ 01578 bool rpl_control_read_dodag_info(const rpl_instance_t *instance, rpl_dodag_info_t *dodag_info) 01579 { 01580 return rpl_upward_read_dodag_info(instance, dodag_info); 01581 } 01582 01583 const rpl_dodag_conf_t *rpl_control_get_dodag_config(const rpl_instance_t *instance) 01584 { 01585 rpl_dodag_t *dodag = rpl_instance_current_dodag(instance); 01586 if (!dodag) { 01587 return NULL; 01588 } 01589 return rpl_dodag_get_config(dodag); 01590 } 01591 01592 const uint8_t *rpl_control_preferred_parent_addr(const rpl_instance_t *instance, bool global) 01593 { 01594 const rpl_neighbour_t *parent = rpl_instance_preferred_parent(instance); 01595 if (!parent) { 01596 return NULL; 01597 } 01598 if (global) { 01599 return rpl_neighbour_global_address(parent); 01600 } else { 01601 return rpl_neighbour_ll_address(parent); 01602 } 01603 } 01604 01605 static void rpl_domain_print(const rpl_domain_t *domain, route_print_fn_t *print_fn) 01606 { 01607 print_fn("RPL Domain %p", (void *) domain); 01608 ns_list_foreach(rpl_instance_t, instance, &domain->instances) { 01609 rpl_upward_print_instance(instance, print_fn); 01610 rpl_downward_print_instance(instance, print_fn); 01611 } 01612 } 01613 01614 void rpl_control_print(route_print_fn_t *print_fn) 01615 { 01616 print_fn("RPL memory usage %zu", rpl_alloc_total); 01617 ns_list_foreach(rpl_domain_t, domain, &rpl_domains) { 01618 rpl_domain_print(domain, print_fn); 01619 } 01620 } 01621 01622 #ifdef RPL_STRUCTURES_H_ 01623 #error "rpl_structures.h should not be included by rpl_control.c" 01624 #endif 01625 01626 #endif /* HAVE_RPL */
Generated on Tue Jul 12 2022 12:22:18 by
