Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers rpl_control.c Source File

rpl_control.c

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