Contiki-NG
Loading...
Searching...
No Matches
rpl-dag.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2010, Swedish Institute of Computer Science.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the Institute nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * This file is part of the Contiki operating system.
30 *
31 */
32
33/**
34 * \addtogroup rpl-lite
35 * @{
36 *
37 * \file
38 * Logic for Directed Acyclic Graphs in RPL.
39 *
40 * \author Joakim Eriksson <joakime@sics.se>, Nicolas Tsiftes <nvt@sics.se>,
41 * Simon Duquennoy <simon.duquennoy@inria.fr>
42 * Contributors: George Oikonomou <oikonomou@users.sourceforge.net> (multicast)
43 */
44
45#include "net/routing/rpl-lite/rpl.h"
46#include "net/ipv6/uip-sr.h"
47#include "net/nbr-table.h"
48#include "net/link-stats.h"
49
50/* Log configuration */
51#include "sys/log.h"
52#define LOG_MODULE "RPL"
53#define LOG_LEVEL LOG_LEVEL_RPL
54
55/*---------------------------------------------------------------------------*/
56extern rpl_of_t rpl_of0, rpl_mrhof;
57static rpl_of_t * const objective_functions[] = RPL_SUPPORTED_OFS;
58static int process_dio_init_dag(rpl_dio_t *dio);
59
60/*---------------------------------------------------------------------------*/
61/* Allocate instance table. */
62rpl_instance_t curr_instance;
63
64/*---------------------------------------------------------------------------*/
65
66#ifdef RPL_VALIDATE_DIO_FUNC
67int RPL_VALIDATE_DIO_FUNC(rpl_dio_t *dio);
68#endif /* RPL_PROBING_SELECT_FUNC */
69
70/*---------------------------------------------------------------------------*/
71const char *
73{
74 switch(state) {
75 case DAG_INITIALIZED:
76 return "initialized";
77 case DAG_JOINED:
78 return "joined";
79 case DAG_REACHABLE:
80 return "reachable";
81 case DAG_POISONING:
82 return "poisoning";
83 default:
84 return "unknown";
85 }
86}
87/*---------------------------------------------------------------------------*/
88int
90{
91 if(curr_instance.used && ipaddr != NULL) {
92 uip_ipaddr_copy(ipaddr, &curr_instance.dag.dag_id);
93 return 1;
94 }
95 return 0;
96}
97/*---------------------------------------------------------------------------*/
98void
100{
101 LOG_INFO("leaving DAG ");
102 LOG_INFO_6ADDR(&curr_instance.dag.dag_id);
103 LOG_INFO_(", instance %u\n", curr_instance.instance_id);
104
105 /* Issue a no-path DAO */
106 if(!rpl_dag_root_is_root()) {
107 RPL_LOLLIPOP_INCREMENT(curr_instance.dag.dao_last_seqno);
109 }
110
111 /* Forget past link statistics */
112 link_stats_reset();
113
114 /* Remove all neighbors, links and default route */
117
118 /* Stop all timers */
120
121 /* Remove autoconfigured address */
122 if((curr_instance.dag.prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS)) {
123 rpl_reset_prefix(&curr_instance.dag.prefix_info);
124 }
125
126 /* Mark instance as unused */
127 curr_instance.used = 0;
128}
129/*---------------------------------------------------------------------------*/
130void
132{
133 curr_instance.dag.state = DAG_POISONING;
135}
136/*---------------------------------------------------------------------------*/
137void
138rpl_dag_periodic(unsigned seconds)
139{
140 if(curr_instance.used) {
141 if(curr_instance.dag.lifetime != RPL_LIFETIME(RPL_INFINITE_LIFETIME)) {
142 curr_instance.dag.lifetime =
143 curr_instance.dag.lifetime > seconds ? curr_instance.dag.lifetime - seconds : 0;
144 if(curr_instance.dag.lifetime == 0) {
145 LOG_WARN("DAG expired, poison and leave\n");
146 curr_instance.dag.state = DAG_POISONING;
148 } else if(curr_instance.dag.lifetime < 300 && curr_instance.dag.preferred_parent != NULL) {
149 /* Five minutes before expiring, start sending unicast DIS to get an update */
150 LOG_WARN("DAG expiring in %u seconds, send DIS to preferred parent\n", (unsigned)curr_instance.dag.lifetime);
151 rpl_icmp6_dis_output(rpl_neighbor_get_ipaddr(curr_instance.dag.preferred_parent));
152 }
153 }
154 }
155}
156/*---------------------------------------------------------------------------*/
157int
158rpl_is_addr_in_our_dag(const uip_ipaddr_t *addr)
159{
160 return curr_instance.used
161 && uip_ipaddr_prefixcmp(&curr_instance.dag.dag_id, addr, curr_instance.dag.prefix_info.length);
162}
163/*---------------------------------------------------------------------------*/
165rpl_get_default_instance(void)
166{
167 return curr_instance.used ? &curr_instance : NULL;
168}
169/*---------------------------------------------------------------------------*/
170rpl_dag_t *
171rpl_get_any_dag(void)
172{
173 return curr_instance.used ? &curr_instance.dag : NULL;
174}
175/*---------------------------------------------------------------------------*/
176static rpl_of_t *
177find_objective_function(rpl_ocp_t ocp)
178{
179 unsigned int i;
180 for(i = 0; i < sizeof(objective_functions) / sizeof(objective_functions[0]); i++) {
181 if(objective_functions[i]->ocp == ocp) {
182 return objective_functions[i];
183 }
184 }
185 return NULL;
186}
187/*---------------------------------------------------------------------------*/
188void
189rpl_refresh_routes(const char *str)
190{
191 if(rpl_dag_root_is_root()) {
192 /* Increment DTSN */
193 RPL_LOLLIPOP_INCREMENT(curr_instance.dtsn_out);
194
195 LOG_WARN("incremented DTSN (%s), current %u\n",
196 str, curr_instance.dtsn_out);
197 if(LOG_INFO_ENABLED) {
198 rpl_neighbor_print_list("Refresh routes (before)");
199 }
200 }
201}
202/*---------------------------------------------------------------------------*/
203void
204rpl_global_repair(const char *str)
205{
206 if(rpl_dag_root_is_root()) {
207 RPL_LOLLIPOP_INCREMENT(curr_instance.dag.version); /* New DAG version */
208 curr_instance.dtsn_out = RPL_LOLLIPOP_INIT; /* Re-initialize DTSN */
209
210 LOG_WARN("initiating global repair (%s), version %u, rank %u\n",
211 str, curr_instance.dag.version, curr_instance.dag.rank);
212 if(LOG_INFO_ENABLED) {
213 rpl_neighbor_print_list("Global repair (before)");
214 }
215
216 /* Now do a local repair to disseminate the new version */
217 rpl_local_repair("Global repair");
218 }
219}
220/*---------------------------------------------------------------------------*/
221static void
222global_repair_non_root(rpl_dio_t *dio)
223{
224 if(!rpl_dag_root_is_root()) {
225 LOG_WARN("participating in global repair, version %u, rank %u\n",
226 dio->version, curr_instance.dag.rank);
227 if(LOG_INFO_ENABLED) {
228 rpl_neighbor_print_list("Global repair (before)");
229 }
230 /* Re-initialize configuration from DIO */
233 /* This will both re-init the DAG and schedule required timers */
234 process_dio_init_dag(dio);
235 rpl_local_repair("Global repair");
236 }
237}
238/*---------------------------------------------------------------------------*/
239void
240rpl_local_repair(const char *str)
241{
242 if(curr_instance.used) { /* Check needed because this is a public function */
243 LOG_WARN("local repair (%s)\n", str);
244 if(!rpl_dag_root_is_root()) {
245 curr_instance.dag.state = DAG_INITIALIZED; /* Reset DAG state */
246 }
247 curr_instance.of->reset(); /* Reset OF */
248 rpl_neighbor_remove_all(); /* Remove all neighbors */
249 rpl_timers_dio_reset("Local repair"); /* Reset Trickle timer */
251 }
252}
253/*---------------------------------------------------------------------------*/
254int
256{
257 if(curr_instance.mop == RPL_MOP_NO_DOWNWARD_ROUTES) {
258 return curr_instance.used && curr_instance.dag.state >= DAG_INITIALIZED;
259 } else {
260 return curr_instance.used && curr_instance.dag.state >= DAG_REACHABLE;
261 }
262}
263/*---------------------------------------------------------------------------*/
264/* Updates rank and parent */
265void
267{
268 rpl_rank_t old_rank;
269
270 if(!curr_instance.used) {
271 return;
272 }
273
274 old_rank = curr_instance.dag.rank;
275 /* Any scheduled state update is no longer needed */
277
278 if(curr_instance.dag.state == DAG_POISONING) {
280 curr_instance.dag.rank = RPL_INFINITE_RANK;
281 if(old_rank != RPL_INFINITE_RANK) {
282 /* Advertise that we are leaving, and leave after a delay */
283 LOG_WARN("poisoning and leaving after a delay\n");
284 rpl_timers_dio_reset("Poison routes");
286 }
287 } else if(!rpl_dag_root_is_root()) {
288 rpl_nbr_t *old_parent = curr_instance.dag.preferred_parent;
289 rpl_nbr_t *nbr;
290
291 /* Select and set preferred parent */
293 /* Update rank */
294 curr_instance.dag.rank = rpl_neighbor_rank_via_nbr(curr_instance.dag.preferred_parent);
295
296 /* Update better_parent_since flag for each neighbor */
297 nbr = nbr_table_head(rpl_neighbors);
298 while(nbr != NULL) {
299 if(rpl_neighbor_rank_via_nbr(nbr) < curr_instance.dag.rank) {
300 /* This neighbor would be a better parent than our current.
301 Set 'better_parent_since' if not already set. */
302 if(nbr->better_parent_since == 0) {
303 nbr->better_parent_since = clock_time(); /* Initialize */
304 }
305 } else {
306 nbr->better_parent_since = 0; /* Not a better parent */
307 }
308 nbr = nbr_table_next(rpl_neighbors, nbr);
309 }
310
311 if(old_parent == NULL || curr_instance.dag.rank < curr_instance.dag.lowest_rank) {
312 /* This is a slight departure from RFC6550: if we had no preferred parent before,
313 * reset lowest_rank. This helps recovering from temporary bad link conditions. */
314 curr_instance.dag.lowest_rank = curr_instance.dag.rank;
315 }
316
317 /* Reset DIO timer in case of significant rank update */
318 if(curr_instance.dag.last_advertised_rank != RPL_INFINITE_RANK
319 && curr_instance.dag.rank != RPL_INFINITE_RANK
320 && ABS((int32_t)curr_instance.dag.rank - curr_instance.dag.last_advertised_rank) > RPL_SIGNIFICANT_CHANGE_THRESHOLD) {
321 LOG_WARN("significant rank update %u->%u\n",
322 curr_instance.dag.last_advertised_rank, curr_instance.dag.rank);
323 /* Update already here to avoid multiple resets in a row */
324 curr_instance.dag.last_advertised_rank = curr_instance.dag.rank;
325 rpl_timers_dio_reset("Significant rank update");
326 }
327
328 /* Parent switch */
329 if(curr_instance.dag.unprocessed_parent_switch) {
330
331 if(curr_instance.dag.preferred_parent != NULL) {
332 /* We just got a parent (was NULL), reset trickle timer to advertise this */
333 if(old_parent == NULL) {
334 curr_instance.dag.state = DAG_JOINED;
335 rpl_timers_dio_reset("Got parent");
336 LOG_WARN("found parent: ");
337 LOG_WARN_6ADDR(rpl_neighbor_get_ipaddr(curr_instance.dag.preferred_parent));
338 LOG_WARN_(", staying in DAG\n");
340 }
341 /* Schedule a DAO */
343 } else {
344 /* We have no more parent, schedule DIS to get a chance to hear updated state */
345 curr_instance.dag.state = DAG_INITIALIZED;
346 LOG_WARN("no parent, scheduling periodic DIS, will leave if no parent is found\n");
347 rpl_timers_dio_reset("Poison routes");
350 }
351
352 if(LOG_INFO_ENABLED) {
353 rpl_neighbor_print_list("Parent switch");
354 }
355
356 /* Clear unprocessed_parent_switch now that we have processed it */
357 curr_instance.dag.unprocessed_parent_switch = false;
358 }
359 }
360
361 /* Finally, update metric container */
362 curr_instance.of->update_metric_container();
363}
364/*---------------------------------------------------------------------------*/
365static rpl_nbr_t *
366update_nbr_from_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
367{
368 rpl_nbr_t *nbr = NULL;
369 const uip_lladdr_t *lladdr;
370
372 /* Neighbor not in RPL neighbor table, add it */
373 if(nbr == NULL) {
374 /* Is the neighbor known by ds6? Drop this request if not.
375 * Typically, the neighbor is added upon receiving a DIO. */
376 lladdr = uip_ds6_nbr_lladdr_from_ipaddr(from);
377 if(lladdr == NULL) {
378 return NULL;
379 }
380
381 /* Add neighbor to RPL table */
382 nbr = nbr_table_add_lladdr(rpl_neighbors, (linkaddr_t *)lladdr,
383 NBR_TABLE_REASON_RPL_DIO, dio);
384 if(nbr == NULL) {
385 LOG_ERR("failed to add neighbor\n");
386 return NULL;
387 }
388 }
389
390 /* Update neighbor info from DIO */
391 nbr->rank = dio->rank;
392 nbr->dtsn = dio->dtsn;
393#if RPL_WITH_MC
394 memcpy(&nbr->mc, &dio->mc, sizeof(nbr->mc));
395#endif /* RPL_WITH_MC */
396
397 return nbr;
398}
399/*---------------------------------------------------------------------------*/
400static void
401process_dio_from_current_dag(uip_ipaddr_t *from, rpl_dio_t *dio)
402{
403 rpl_nbr_t *nbr;
404 uint8_t last_dtsn;
405
406 /* Does the rank make sense at all? */
407 if(dio->rank < ROOT_RANK) {
408 return;
409 }
410
411 /* If the DIO sender is on an older version of the DAG, do not process it
412 * further. The sender will eventually hear the global repair and catch up. */
413 if(rpl_lollipop_greater_than(curr_instance.dag.version, dio->version)) {
414 if(dio->rank == ROOT_RANK) {
415 /* Before returning, if the DIO was from the root, an old DAG versions
416 * likely incidates a root reboot. Reset our DIO timer to make sure the
417 * root hears our version ASAP, and in turn triggers a global repair. */
418 rpl_timers_dio_reset("Heard old version from root");
419 }
420 return;
421 }
422
423 /* The DIO is valid, proceed further */
424
425 /* Update DIO counter for redundancy mngt */
426 if(dio->rank != RPL_INFINITE_RANK) {
427 curr_instance.dag.dio_counter++;
428 }
429
430 /* The DIO has a newer version: global repair.
431 * Must come first, as it might remove all neighbors, and we then need
432 * to re-add this source of the DIO to the neighbor table */
433 if(rpl_lollipop_greater_than(dio->version, curr_instance.dag.version)) {
434 if(curr_instance.dag.rank == ROOT_RANK) {
435 /* The root should not hear newer versions unless it just rebooted */
436 LOG_ERR("inconsistent DIO version (current: %u, received: %u), initiate global repair\n",
437 curr_instance.dag.version, dio->version);
438 /* Update version and trigger global repair */
439 curr_instance.dag.version = dio->version;
440 rpl_global_repair("Inconsistent DIO version");
441 } else {
442 LOG_WARN("new DIO version (current: %u, received: %u), apply global repair\n",
443 curr_instance.dag.version, dio->version);
444 global_repair_non_root(dio);
445 }
446 }
447
448 /* Update IPv6 neighbor cache */
449 if(!rpl_icmp6_update_nbr_table(from, NBR_TABLE_REASON_RPL_DIO, dio)) {
450 LOG_ERR("IPv6 cache full, dropping DIO\n");
451 return;
452 }
453
455 last_dtsn = nbr != NULL ? nbr->dtsn : RPL_LOLLIPOP_INIT;
456
457 /* Add neighbor to RPL neighbor table */
458 if(!update_nbr_from_dio(from, dio)) {
459 LOG_ERR("neighbor table full, dropping DIO\n");
460 return;
461 }
462
463 /* Init lifetime if not set yet. Refresh it at every DIO from preferred parent. */
464 if(curr_instance.dag.lifetime == 0 ||
465 (nbr != NULL && nbr == curr_instance.dag.preferred_parent)) {
466 LOG_INFO("refreshing lifetime\n");
467 curr_instance.dag.lifetime = RPL_LIFETIME(RPL_DAG_LIFETIME);
468 }
469
470 /* If the source is our preferred parent and it increased DTSN, we increment
471 * our DTSN in turn and schedule a DAO (see RFC6550 section 9.6.) */
472 if(curr_instance.mop != RPL_MOP_NO_DOWNWARD_ROUTES) {
473 if(nbr != NULL && nbr == curr_instance.dag.preferred_parent && rpl_lollipop_greater_than(dio->dtsn, last_dtsn)) {
474 RPL_LOLLIPOP_INCREMENT(curr_instance.dtsn_out);
475 LOG_WARN("DTSN increment %u->%u, schedule new DAO with DTSN %u\n",
476 last_dtsn, dio->dtsn, curr_instance.dtsn_out);
478 }
479 }
480}
481/*---------------------------------------------------------------------------*/
482static int
483init_dag(uint8_t instance_id, uip_ipaddr_t *dag_id, rpl_ocp_t ocp,
484 uip_ipaddr_t *prefix, unsigned prefix_len, uint8_t prefix_flags)
485{
486 rpl_of_t *of;
487
488 memset(&curr_instance, 0, sizeof(curr_instance));
489
490 /* OF */
491 of = find_objective_function(ocp);
492 if(of == NULL) {
493 LOG_ERR("ignoring DIO with an unsupported OF: %u\n", ocp);
494 return 0;
495 }
496
497 /* Prefix */
498 if(!rpl_set_prefix_from_addr(prefix, prefix_len, prefix_flags)) {
499 LOG_ERR("failed to set prefix\n");
500 return 0;
501 }
502
503 /* Instnace */
504 curr_instance.instance_id = instance_id;
505 curr_instance.of = of;
506 curr_instance.dtsn_out = RPL_LOLLIPOP_INIT;
507 curr_instance.used = 1;
508
509 /* DAG */
510 curr_instance.dag.rank = RPL_INFINITE_RANK;
511 curr_instance.dag.last_advertised_rank = RPL_INFINITE_RANK;
512 curr_instance.dag.lowest_rank = RPL_INFINITE_RANK;
513 curr_instance.dag.dao_last_seqno = RPL_LOLLIPOP_INIT;
514 curr_instance.dag.dao_last_acked_seqno = RPL_LOLLIPOP_INIT;
515 curr_instance.dag.dao_last_seqno = RPL_LOLLIPOP_INIT;
516 memcpy(&curr_instance.dag.dag_id, dag_id, sizeof(curr_instance.dag.dag_id));
517
518 return 1;
519}
520/*---------------------------------------------------------------------------*/
521static int
522init_dag_from_dio(rpl_dio_t *dio)
523{
524 if(!init_dag(dio->instance_id, &dio->dag_id, dio->ocp,
525 &dio->prefix_info.prefix, dio->prefix_info.length, dio->prefix_info.flags)) {
526 return 0;
527 }
528
529 /* Instnace */
530 curr_instance.mop = dio->mop;
531 curr_instance.mc.type = dio->mc.type;
532 curr_instance.mc.flags = dio->mc.flags;
533 curr_instance.mc.aggr = dio->mc.aggr;
534 curr_instance.mc.prec = dio->mc.prec;
535 curr_instance.max_rankinc = dio->dag_max_rankinc;
536 curr_instance.min_hoprankinc = dio->dag_min_hoprankinc;
537 curr_instance.dio_intdoubl = dio->dag_intdoubl;
538 curr_instance.dio_intmin = dio->dag_intmin;
539 curr_instance.dio_redundancy = dio->dag_redund;
540 curr_instance.default_lifetime = dio->default_lifetime;
541 curr_instance.lifetime_unit = dio->lifetime_unit;
542
543 /* DAG */
544 curr_instance.dag.state = DAG_INITIALIZED;
545 curr_instance.dag.preference = dio->preference;
546 curr_instance.dag.grounded = dio->grounded;
547 curr_instance.dag.version = dio->version;
548 /* dio_intcurrent will be reset by rpl_timers_dio_reset() */
549 curr_instance.dag.dio_intcurrent = 0;
550
551 return 1;
552}
553/*---------------------------------------------------------------------------*/
554static int
555process_dio_init_dag(rpl_dio_t *dio)
556{
557#ifdef RPL_VALIDATE_DIO_FUNC
558 if(!RPL_VALIDATE_DIO_FUNC(dio)) {
559 LOG_WARN("DIO validation failed\n");
560 return 0;
561 }
562#endif
563
564 /* Check MOP */
565 if(dio->mop != RPL_MOP_NO_DOWNWARD_ROUTES && dio->mop != RPL_MOP_NON_STORING) {
566 LOG_WARN("ignoring DIO with an unsupported MOP: %d\n", dio->mop);
567 return 0;
568 }
569
570 /* Initialize instance and DAG data structures */
571 if(!init_dag_from_dio(dio)) {
572 LOG_WARN("failed to initialize DAG\n");
573 return 0;
574 }
575
576 /* Init OF and timers */
577 curr_instance.of->reset();
578 rpl_timers_dio_reset("Join");
579#if RPL_WITH_PROBING
581#endif /* RPL_WITH_PROBING */
582 /* Leave the network after RPL_DELAY_BEFORE_LEAVING in case we do not
583 find a parent */
584 LOG_INFO("initialized DAG with instance ID %u, DAG ID ",
585 curr_instance.instance_id);
586 LOG_INFO_6ADDR(&curr_instance.dag.dag_id);
587 LOG_INFO_(", prexix ");
588 LOG_INFO_6ADDR(&dio->prefix_info.prefix);
589 LOG_INFO_("/%u, rank %u\n", dio->prefix_info.length, curr_instance.dag.rank);
590
591 LOG_ANNOTATE("#A init=%u\n", curr_instance.dag.dag_id.u8[sizeof(curr_instance.dag.dag_id) - 1]);
592
593 LOG_WARN("just joined, no parent yet, setting timer for leaving\n");
595
596 return 1;
597}
598/*---------------------------------------------------------------------------*/
599void
600rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
601{
602 if(!curr_instance.used && !rpl_dag_root_is_root()) {
603 /* Attempt to init our DAG from this DIO */
604 if(!process_dio_init_dag(dio)) {
605 LOG_WARN("failed to init DAG\n");
606 return;
607 }
608 }
609
610 if(curr_instance.used
611 && curr_instance.instance_id == dio->instance_id
612 && uip_ipaddr_cmp(&curr_instance.dag.dag_id, &dio->dag_id)) {
613 process_dio_from_current_dag(from, dio);
615 }
616}
617/*---------------------------------------------------------------------------*/
618void
619rpl_process_dis(uip_ipaddr_t *from, int is_multicast)
620{
621 if(is_multicast) {
622 rpl_timers_dio_reset("Multicast DIS");
623 } else {
624 /* Add neighbor to cache and reply to the unicast DIS with a unicast DIO*/
625 if(rpl_icmp6_update_nbr_table(from, NBR_TABLE_REASON_RPL_DIS, NULL) != NULL) {
626 LOG_INFO("unicast DIS, reply to sender\n");
628 }
629 }
630}
631/*---------------------------------------------------------------------------*/
632void
633rpl_process_dao(uip_ipaddr_t *from, rpl_dao_t *dao)
634{
635 if(dao->lifetime == 0) {
636 uip_sr_expire_parent(NULL, from, &dao->parent_addr);
637 } else {
638 if(!uip_sr_update_node(NULL, from, &dao->parent_addr, RPL_LIFETIME(dao->lifetime))) {
639 LOG_ERR("failed to add link on incoming DAO\n");
640 return;
641 }
642 }
643
644#if RPL_WITH_DAO_ACK
645 if(dao->flags & RPL_DAO_K_FLAG) {
646 rpl_timers_schedule_dao_ack(from, dao->sequence);
647 }
648#endif /* RPL_WITH_DAO_ACK */
649}
650/*---------------------------------------------------------------------------*/
651#if RPL_WITH_DAO_ACK
652void
653rpl_process_dao_ack(uint8_t sequence, uint8_t status)
654{
655 /* Update dao_last_acked_seqno */
656 if(rpl_lollipop_greater_than(sequence, curr_instance.dag.dao_last_acked_seqno)) {
657 curr_instance.dag.dao_last_acked_seqno = sequence;
658 }
659 /* Is this an ACK for our last DAO? */
660 if(sequence == curr_instance.dag.dao_last_seqno) {
661 int status_ok = status < RPL_DAO_ACK_UNABLE_TO_ACCEPT;
662 if(curr_instance.dag.state == DAG_JOINED && status_ok) {
663 curr_instance.dag.state = DAG_REACHABLE;
664 rpl_timers_dio_reset("Reachable");
665 }
666 /* Let the rpl-timers module know that we got an ACK for the last DAO */
668
669 if(!status_ok) {
670 /* We got a NACK, start poisoning and leave */
671 LOG_WARN("DAO-NACK received with seqno %u, status %u, poison and leave\n",
672 sequence, status);
673 curr_instance.dag.state = DAG_POISONING;
674 }
675 }
676}
677#endif /* RPL_WITH_DAO_ACK */
678/*---------------------------------------------------------------------------*/
679int
680rpl_process_hbh(rpl_nbr_t *sender, uint16_t sender_rank, int loop_detected, int rank_error_signaled)
681{
682 int drop = 0;
683
684 if(loop_detected) {
685 if(rank_error_signaled) {
686#if RPL_LOOP_ERROR_DROP
687 /* Drop packet and reset trickle timer, as per RFC6550 - 11.2.2.2 */
688 rpl_timers_dio_reset("HBH error");
689 LOG_WARN("rank error and loop detected, dropping\n");
690 drop = 1;
691#endif /* RPL_LOOP_ERROR_DROP */
692 }
693 /* Attempt to repair the loop by sending a unicast DIO back to the sender
694 * so that it gets a fresh update of our rank. */
696 }
697
698 if(rank_error_signaled) {
699 /* A rank error was signalled, attempt to repair it by updating
700 * the sender's rank from ext header */
701 if(sender != NULL) {
702 sender->rank = sender_rank;
703 /* Select DAG and preferred parent. In case of a parent switch,
704 the new parent will be used to forward the current packet. */
706 }
707 }
708
709 return !drop;
710}
711/*---------------------------------------------------------------------------*/
712void
713rpl_dag_init_root(uint8_t instance_id, uip_ipaddr_t *dag_id,
714 uip_ipaddr_t *prefix, unsigned prefix_len, uint8_t prefix_flags)
715{
716 uint8_t version = RPL_LOLLIPOP_INIT;
717
718 /* If we're in an instance, first leave it */
719 if(curr_instance.used) {
720 /* We were already root. Increment version */
721 if(uip_ipaddr_cmp(&curr_instance.dag.dag_id, dag_id)) {
722 version = curr_instance.dag.version;
723 RPL_LOLLIPOP_INCREMENT(version);
724 }
726 }
727
728 /* Init DAG and instance */
729 init_dag(instance_id, dag_id, RPL_OF_OCP, prefix, prefix_len, prefix_flags);
730
731 /* Instance */
732 curr_instance.mop = RPL_MOP_DEFAULT;
733 curr_instance.max_rankinc = RPL_MAX_RANKINC;
734 curr_instance.min_hoprankinc = RPL_MIN_HOPRANKINC;
735 curr_instance.dio_intdoubl = RPL_DIO_INTERVAL_DOUBLINGS;
736 curr_instance.dio_intmin = RPL_DIO_INTERVAL_MIN;
737 curr_instance.dio_redundancy = RPL_DIO_REDUNDANCY;
738 curr_instance.default_lifetime = RPL_DEFAULT_LIFETIME;
739 curr_instance.lifetime_unit = RPL_DEFAULT_LIFETIME_UNIT;
740
741 /* DAG */
742 curr_instance.dag.preference = RPL_PREFERENCE;
743 curr_instance.dag.grounded = RPL_GROUNDED;
744 curr_instance.dag.version = version;
745 curr_instance.dag.rank = ROOT_RANK;
746 curr_instance.dag.lifetime = RPL_LIFETIME(RPL_INFINITE_LIFETIME);
747 /* dio_intcurrent will be reset by rpl_timers_dio_reset() */
748 curr_instance.dag.dio_intcurrent = 0;
749 curr_instance.dag.state = DAG_REACHABLE;
750
751 rpl_timers_dio_reset("Init root");
752
753 LOG_INFO("created DAG with instance ID %u, DAG ID ",
754 curr_instance.instance_id);
755 LOG_INFO_6ADDR(&curr_instance.dag.dag_id);
756 LOG_INFO_(", rank %u\n", curr_instance.dag.rank);
757
758 LOG_ANNOTATE("#A root=%u\n", curr_instance.dag.dag_id.u8[sizeof(curr_instance.dag.dag_id) - 1]);
759}
760/*---------------------------------------------------------------------------*/
761void
762rpl_dag_init(void)
763{
764 memset(&curr_instance, 0, sizeof(curr_instance));
765}
766/*---------------------------------------------------------------------------*/
767/** @} */
clock_time_t clock_time(void)
Get the current clock time.
Definition clock.c:118
int rpl_dag_get_root_ipaddr(uip_ipaddr_t *ipaddr)
Returns the IPv6 address of the RPL DAG root, if any.
Definition rpl-dag.c:89
void rpl_timers_stop_dag_timers(void)
Stop all timers related to the DAG.
Definition rpl-timers.c:538
void rpl_schedule_probing(void)
Schedule probing with delay RPL_PROBING_DELAY_FUNC()
rpl_rank_t rpl_neighbor_rank_via_nbr(rpl_nbr_t *nbr)
Returns our rank if selecting a given parent as preferred parent.
static void rpl_timers_unschedule_state_update(void)
Cancelled any scheduled state update.
Definition rpl-timers.h:127
void rpl_process_dao_ack(uint8_t sequence, uint8_t status)
Processes incoming DAO-ACK.
void rpl_icmp6_dis_output(uip_ipaddr_t *addr)
Creates an ICMPv6 DIS packet and sends it.
Definition rpl-icmp6.c:151
int rpl_lollipop_greater_than(int a, int b)
Greater-than function for a lollipop counter.
Definition rpl.c:57
void rpl_neighbor_remove_all(void)
Empty the RPL neighbor table.
void rpl_icmp6_dio_output(uip_ipaddr_t *uc_addr)
Creates an ICMPv6 DIO packet and sends it.
Definition rpl-icmp6.c:358
void rpl_dag_update_state(void)
Updates RPL internal state: selects preferred parent, updates rank & metreic container,...
Definition rpl-dag.c:266
void rpl_dag_periodic(unsigned seconds)
A function called periodically.
Definition rpl-dag.c:138
void rpl_timers_notify_dao_ack(void)
Let the rpl-timers module know that the last DAO was ACKed.
void rpl_dag_init_root(uint8_t instance_id, uip_ipaddr_t *dag_id, uip_ipaddr_t *prefix, unsigned prefix_len, uint8_t prefix_flags)
Initializes DAG internal structure for a root node.
Definition rpl-dag.c:713
void rpl_reset_prefix(rpl_prefix_t *last_prefix)
Removes current prefx.
Definition rpl.c:141
void rpl_timers_schedule_dao(void)
Schedule a DAO with random delay based on RPL_DAO_DELAY.
Definition rpl-timers.c:263
void rpl_timers_schedule_leaving(void)
Schedule leaving after RPL_DELAY_BEFORE_LEAVING.
Definition rpl-timers.c:493
int rpl_set_prefix_from_addr(uip_ipaddr_t *addr, unsigned len, uint8_t flags)
Set prefix from an IPv6 address.
Definition rpl.c:157
void rpl_timers_dio_reset(const char *str)
Reset DIO Trickle timer.
Definition rpl-timers.c:150
void rpl_process_dis(uip_ipaddr_t *from, int is_multicast)
Processes incoming DIS.
Definition rpl-dag.c:619
void rpl_dag_poison_and_leave(void)
Start poisoning and leave the DAG after a delay.
Definition rpl-dag.c:131
rpl_nbr_t * rpl_neighbor_get_from_ipaddr(uip_ipaddr_t *addr)
Returns a neighbor from its link-local IPv6 address.
void rpl_icmp6_dao_output(uint8_t lifetime)
Creates an ICMPv6 DAO packet and sends it to the root, advertising the current preferred parent,...
Definition rpl-icmp6.c:594
void rpl_process_dao(uip_ipaddr_t *from, rpl_dao_t *dao)
Processes incoming DAO.
Definition rpl-dag.c:633
void rpl_global_repair(const char *str)
Triggers a RPL global repair.
Definition rpl-dag.c:204
int rpl_dag_ready_to_advertise(void)
Tells whether RPL is ready to advertise the DAG.
Definition rpl-dag.c:255
void rpl_neighbor_set_preferred_parent(rpl_nbr_t *nbr)
Set current RPL preferred parent and update DS6 default route accordingly.
void rpl_timers_schedule_dao_ack(uip_ipaddr_t *target, uint16_t sequence)
Schedule a DAO-ACK with no delay.
void rpl_timers_unschedule_leaving(void)
Cancel scheduled leaving if any.
Definition rpl-timers.c:483
rpl_dag_state
RPL DAG states.
Definition rpl-types.h:177
uip_ipaddr_t * rpl_neighbor_get_ipaddr(rpl_nbr_t *nbr)
Returns a neighbor's (link-local) IPv6 address.
int rpl_process_hbh(rpl_nbr_t *sender, uint16_t sender_rank, int loop_detected, int rank_error_signaled)
Processes Hop-by-Hop (HBH) Extension Header of a packet currently being forwrded.
Definition rpl-dag.c:680
const char * rpl_dag_state_to_str(enum rpl_dag_state state)
Returns a textual description of the current DAG state.
Definition rpl-dag.c:72
void rpl_dag_leave(void)
Leaves the current DAG.
Definition rpl-dag.c:99
void rpl_refresh_routes(const char *str)
Triggers a route fresh via DTSN increment.
Definition rpl-dag.c:189
void rpl_neighbor_print_list(const char *str)
Prints a summary of all RPL neighbors and their properties.
void rpl_timers_schedule_periodic_dis(void)
Schedule periodic DIS with a random delay based on RPL_DIS_INTERVAL, until we join a DAG.
Definition rpl-timers.c:93
rpl_nbr_t * rpl_neighbor_select_best(void)
Returns the best candidate for preferred parent.
int rpl_is_addr_in_our_dag(const uip_ipaddr_t *addr)
Tells whether a given global IPv6 address is in our current DAG.
Definition rpl-dag.c:158
void rpl_timers_schedule_state_update(void)
Schedule a state update ASAP.
Definition rpl-timers.c:555
void rpl_timers_schedule_unicast_dio(rpl_nbr_t *target)
Schedule unicast DIO with no delay.
Definition rpl-timers.c:208
void uip_sr_expire_parent(const void *graph, const uip_ipaddr_t *child, const uip_ipaddr_t *parent)
Expires a given child-parent link.
Definition uip-sr.c:114
void uip_sr_free_all(void)
Deallocate all neighbors.
Definition uip-sr.c:248
uip_sr_node_t * uip_sr_update_node(void *graph, const uip_ipaddr_t *child, const uip_ipaddr_t *parent, uint32_t lifetime)
Updates a child-parent link.
Definition uip-sr.c:127
const uip_lladdr_t * uip_ds6_nbr_lladdr_from_ipaddr(const uip_ipaddr_t *ipaddr)
Get the link-layer address associated with a specified IPv6 address.
#define uip_ipaddr_copy(dest, src)
Copy an IP address from one place to another.
Definition uip.h:969
Header file for the logging system.
RPL DAG structure.
Definition rpl.h:138
RPL instance structure.
Definition rpl.h:222
All information related to a RPL neighbor.
Definition rpl-types.h:136
API for RPL objective functions (OF)
Definition rpl.h:204
static uip_ds6_nbr_t * nbr
Pointer to llao option in uip_buf.
Definition uip-nd6.c:106
static uip_ipaddr_t ipaddr
Pointer to prefix information option in uip_buf.
Definition uip-nd6.c:116
static uip_ds6_addr_t * addr
Pointer to a nbr cache entry.
Definition uip-nd6.c:107
Source routing support.