takashi kadono / Mbed OS Nucleo_446

Dependencies:   ssd1331

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers rpl_control.c Source File

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 */