Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of OmniWheels by
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 Fri Jul 22 2022 04:53:59 by
 1.7.2
 1.7.2 
    