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