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#include "sys/cc.h"
50
51/* Log configuration */
52#include "sys/log.h"
53#define LOG_MODULE "RPL"
54#define LOG_LEVEL LOG_LEVEL_RPL
55
56/*---------------------------------------------------------------------------*/
57extern rpl_of_t rpl_of0, rpl_mrhof;
58static rpl_of_t * const objective_functions[] = RPL_SUPPORTED_OFS;
59static int process_dio_init_dag(rpl_dio_t *dio);
60
61/*---------------------------------------------------------------------------*/
62/* Allocate instance table. */
63rpl_instance_t curr_instance;
64
65/*---------------------------------------------------------------------------*/
66
67#ifdef RPL_VALIDATE_DIO_FUNC
68int RPL_VALIDATE_DIO_FUNC(rpl_dio_t *dio);
69#endif /* RPL_PROBING_SELECT_FUNC */
70
71/*---------------------------------------------------------------------------*/
72const char *
74{
75 switch(state) {
76 case DAG_INITIALIZED:
77 return "initialized";
78 case DAG_JOINED:
79 return "joined";
80 case DAG_REACHABLE:
81 return "reachable";
82 case DAG_POISONING:
83 return "poisoning";
84 default:
85 return "unknown";
86 }
87}
88/*---------------------------------------------------------------------------*/
89int
91{
92 if(curr_instance.used && ipaddr != NULL) {
93 uip_ipaddr_copy(ipaddr, &curr_instance.dag.dag_id);
94 return 1;
95 }
96 return 0;
97}
98/*---------------------------------------------------------------------------*/
99void
101{
102 LOG_INFO("leaving DAG ");
103 LOG_INFO_6ADDR(&curr_instance.dag.dag_id);
104 LOG_INFO_(", instance %u\n", curr_instance.instance_id);
105
106 /* Issue a no-path DAO */
107 if(!rpl_dag_root_is_root()) {
108 RPL_LOLLIPOP_INCREMENT(curr_instance.dag.dao_last_seqno);
110 }
111
112 /* Forget past link statistics */
113 link_stats_reset();
114
115 /* Remove all neighbors, links and default route */
118
119 /* Stop all timers */
121
122 /* Remove autoconfigured address */
123 if((curr_instance.dag.prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS)) {
124 rpl_reset_prefix(&curr_instance.dag.prefix_info);
125 }
126
127 /* Mark instance as unused */
128 curr_instance.used = 0;
129}
130/*---------------------------------------------------------------------------*/
131void
133{
134 curr_instance.dag.state = DAG_POISONING;
136}
137/*---------------------------------------------------------------------------*/
138void
139rpl_dag_periodic(unsigned seconds)
140{
141 if(curr_instance.used) {
142 if(curr_instance.dag.lifetime != RPL_LIFETIME(RPL_INFINITE_LIFETIME)) {
143 curr_instance.dag.lifetime =
144 curr_instance.dag.lifetime > seconds ? curr_instance.dag.lifetime - seconds : 0;
145 if(curr_instance.dag.lifetime == 0) {
146 LOG_WARN("DAG expired, poison and leave\n");
147 curr_instance.dag.state = DAG_POISONING;
149 } else if(curr_instance.dag.lifetime < 300 && curr_instance.dag.preferred_parent != NULL) {
150 /* Five minutes before expiring, start sending unicast DIS to get an update */
151 LOG_WARN("DAG expiring in %u seconds, send DIS to preferred parent\n", (unsigned)curr_instance.dag.lifetime);
152 rpl_icmp6_dis_output(rpl_neighbor_get_ipaddr(curr_instance.dag.preferred_parent));
153 }
154 }
155 }
156}
157/*---------------------------------------------------------------------------*/
158int
159rpl_is_addr_in_our_dag(const uip_ipaddr_t *addr)
160{
161 return curr_instance.used
162 && uip_ipaddr_prefixcmp(&curr_instance.dag.dag_id, addr, curr_instance.dag.prefix_info.length);
163}
164/*---------------------------------------------------------------------------*/
165rpl_instance_t *
167{
168 return curr_instance.used ? &curr_instance : NULL;
169}
170/*---------------------------------------------------------------------------*/
171rpl_dag_t *
173{
174 return curr_instance.used ? &curr_instance.dag : NULL;
175}
176/*---------------------------------------------------------------------------*/
177static rpl_of_t *
178find_objective_function(rpl_ocp_t ocp)
179{
180 unsigned int i;
181 for(i = 0; i < CC_ARRAY_LENGTH(objective_functions); i++) {
182 if(objective_functions[i]->ocp == ocp) {
183 return objective_functions[i];
184 }
185 }
186 return NULL;
187}
188/*---------------------------------------------------------------------------*/
189void
190rpl_refresh_routes(const char *str)
191{
193 /* Increment DTSN */
194 RPL_LOLLIPOP_INCREMENT(curr_instance.dtsn_out);
195
196 LOG_WARN("incremented DTSN (%s), current %u\n",
197 str, curr_instance.dtsn_out);
198 if(LOG_INFO_ENABLED) {
199 rpl_neighbor_print_list("Refresh routes (before)");
200 }
201 }
202}
203/*---------------------------------------------------------------------------*/
204void
205rpl_global_repair(const char *str)
206{
208 RPL_LOLLIPOP_INCREMENT(curr_instance.dag.version); /* New DAG version */
209 curr_instance.dtsn_out = RPL_LOLLIPOP_INIT; /* Re-initialize DTSN */
210
211 LOG_WARN("initiating global repair (%s), version %u, rank %u\n",
212 str, curr_instance.dag.version, curr_instance.dag.rank);
213 if(LOG_INFO_ENABLED) {
214 rpl_neighbor_print_list("Global repair (before)");
215 }
216
217 /* Now do a local repair to disseminate the new version */
218 rpl_local_repair("Global repair");
219 }
220}
221/*---------------------------------------------------------------------------*/
222static void
223global_repair_non_root(rpl_dio_t *dio)
224{
225 if(!rpl_dag_root_is_root()) {
226 LOG_WARN("participating in global repair, version %u, rank %u\n",
227 dio->version, curr_instance.dag.rank);
228 if(LOG_INFO_ENABLED) {
229 rpl_neighbor_print_list("Global repair (before)");
230 }
231 /* Re-initialize configuration from DIO */
234 /* This will both re-init the DAG and schedule required timers */
235 process_dio_init_dag(dio);
236 rpl_local_repair("Global repair");
237 }
238}
239/*---------------------------------------------------------------------------*/
240void
241rpl_local_repair(const char *str)
242{
243 if(curr_instance.used) { /* Check needed because this is a public function */
244 LOG_WARN("local repair (%s)\n", str);
245 if(!rpl_dag_root_is_root()) {
246 curr_instance.dag.state = DAG_INITIALIZED; /* Reset DAG state */
247 }
248 curr_instance.of->reset(); /* Reset OF */
249 rpl_neighbor_remove_all(); /* Remove all neighbors */
250 rpl_timers_dio_reset("Local repair"); /* Reset Trickle timer */
252 }
253}
254/*---------------------------------------------------------------------------*/
255int
257{
258 if(curr_instance.mop == RPL_MOP_NO_DOWNWARD_ROUTES) {
259 return curr_instance.used && curr_instance.dag.state >= DAG_INITIALIZED;
260 } else {
261 return curr_instance.used && curr_instance.dag.state >= DAG_REACHABLE;
262 }
263}
264/*---------------------------------------------------------------------------*/
265/* Updates rank and parent */
266void
268{
269 rpl_rank_t old_rank;
270
271 if(!curr_instance.used) {
272 return;
273 }
274
275 old_rank = curr_instance.dag.rank;
276 /* Any scheduled state update is no longer needed */
278
279 if(curr_instance.dag.state == DAG_POISONING) {
281 curr_instance.dag.rank = RPL_INFINITE_RANK;
282 if(old_rank != RPL_INFINITE_RANK) {
283 /* Advertise that we are leaving, and leave after a delay */
284 LOG_WARN("poisoning and leaving after a delay\n");
285 rpl_timers_dio_reset("Poison routes");
287 }
288 } else if(!rpl_dag_root_is_root()) {
289 rpl_nbr_t *old_parent = curr_instance.dag.preferred_parent;
290 rpl_nbr_t *nbr;
291
292 /* Select and set preferred parent */
294 /* Update rank */
295 curr_instance.dag.rank = rpl_neighbor_rank_via_nbr(curr_instance.dag.preferred_parent);
296
297 /* Update better_parent_since flag for each neighbor */
298 nbr = nbr_table_head(rpl_neighbors);
299 while(nbr != NULL) {
300 if(rpl_neighbor_rank_via_nbr(nbr) < curr_instance.dag.rank) {
301 /* This neighbor would be a better parent than our current.
302 Set 'better_parent_since' if not already set. */
303 if(nbr->better_parent_since == 0) {
304 nbr->better_parent_since = clock_time(); /* Initialize */
305 }
306 } else {
307 nbr->better_parent_since = 0; /* Not a better parent */
308 }
309 nbr = nbr_table_next(rpl_neighbors, nbr);
310 }
311
312 if(old_parent == NULL || curr_instance.dag.rank < curr_instance.dag.lowest_rank) {
313 /* This is a slight departure from RFC6550: if we had no preferred parent before,
314 * reset lowest_rank. This helps recovering from temporary bad link conditions. */
315 curr_instance.dag.lowest_rank = curr_instance.dag.rank;
316 }
317
318 /* Reset DIO timer in case of significant rank update */
319 if(curr_instance.dag.last_advertised_rank != RPL_INFINITE_RANK
320 && curr_instance.dag.rank != RPL_INFINITE_RANK
321 && ABS((int32_t)curr_instance.dag.rank - curr_instance.dag.last_advertised_rank) > RPL_SIGNIFICANT_CHANGE_THRESHOLD) {
322 LOG_WARN("significant rank update %u->%u\n",
323 curr_instance.dag.last_advertised_rank, curr_instance.dag.rank);
324 /* Update already here to avoid multiple resets in a row */
325 curr_instance.dag.last_advertised_rank = curr_instance.dag.rank;
326 rpl_timers_dio_reset("Significant rank update");
327 }
328
329 /* Parent switch */
330 if(curr_instance.dag.unprocessed_parent_switch) {
331
332 if(curr_instance.dag.preferred_parent != NULL) {
333 /* We just got a parent (was NULL), reset trickle timer to advertise this */
334 if(old_parent == NULL) {
335 curr_instance.dag.state = DAG_JOINED;
336 rpl_timers_dio_reset("Got parent");
337 LOG_WARN("found parent: ");
338 LOG_WARN_6ADDR(rpl_neighbor_get_ipaddr(curr_instance.dag.preferred_parent));
339 LOG_WARN_(", staying in DAG\n");
341 }
342 /* Schedule a DAO */
344 } else {
345 /* We have no more parent, schedule DIS to get a chance to hear updated state */
346 curr_instance.dag.state = DAG_INITIALIZED;
347 LOG_WARN("no parent, scheduling periodic DIS, will leave if no parent is found\n");
348 rpl_timers_dio_reset("Poison routes");
351 }
352
353 if(LOG_INFO_ENABLED) {
354 rpl_neighbor_print_list("Parent switch");
355 }
356
357 /* Clear unprocessed_parent_switch now that we have processed it */
358 curr_instance.dag.unprocessed_parent_switch = false;
359 }
360 }
361
362 /* Finally, update metric container */
363 curr_instance.of->update_metric_container();
364}
365/*---------------------------------------------------------------------------*/
366static rpl_nbr_t *
367update_nbr_from_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
368{
369 rpl_nbr_t *nbr = NULL;
370 const uip_lladdr_t *lladdr;
371
373 /* Neighbor not in RPL neighbor table, add it */
374 if(nbr == NULL) {
375 /* Is the neighbor known by ds6? Drop this request if not.
376 * Typically, the neighbor is added upon receiving a DIO. */
377 lladdr = uip_ds6_nbr_lladdr_from_ipaddr(from);
378 if(lladdr == NULL) {
379 return NULL;
380 }
381
382 /* Add neighbor to RPL table */
383 nbr = nbr_table_add_lladdr(rpl_neighbors, (linkaddr_t *)lladdr,
384 NBR_TABLE_REASON_RPL_DIO, dio);
385 if(nbr == NULL) {
386 LOG_ERR("failed to add neighbor\n");
387 return NULL;
388 }
389 }
390
391 /* Update neighbor info from DIO */
392 nbr->rank = dio->rank;
393 nbr->dtsn = dio->dtsn;
394#if RPL_WITH_MC
395 memcpy(&nbr->mc, &dio->mc, sizeof(nbr->mc));
396#endif /* RPL_WITH_MC */
397
398 return nbr;
399}
400/*---------------------------------------------------------------------------*/
401static void
402process_dio_from_current_dag(uip_ipaddr_t *from, rpl_dio_t *dio)
403{
404 rpl_nbr_t *nbr;
405 uint8_t last_dtsn;
406
407 /* Does the rank make sense at all? */
408 if(dio->rank < ROOT_RANK) {
409 return;
410 }
411
412 /* If the DIO sender is on an older version of the DAG, do not process it
413 * further. The sender will eventually hear the global repair and catch up. */
414 if(rpl_lollipop_greater_than(curr_instance.dag.version, dio->version)) {
415 if(dio->rank == ROOT_RANK) {
416 /* Before returning, if the DIO was from the root, an old DAG versions
417 * likely incidates a root reboot. Reset our DIO timer to make sure the
418 * root hears our version ASAP, and in turn triggers a global repair. */
419 rpl_timers_dio_reset("Heard old version from root");
420 }
421 return;
422 }
423
424 /* The DIO is valid, proceed further */
425
426 /* Update DIO counter for redundancy mngt */
427 if(dio->rank != RPL_INFINITE_RANK) {
428 curr_instance.dag.dio_counter++;
429 }
430
431 /* The DIO has a newer version: global repair.
432 * Must come first, as it might remove all neighbors, and we then need
433 * to re-add this source of the DIO to the neighbor table */
434 if(rpl_lollipop_greater_than(dio->version, curr_instance.dag.version)) {
435 if(curr_instance.dag.rank == ROOT_RANK) {
436 /* The root should not hear newer versions unless it just rebooted */
437 LOG_ERR("inconsistent DIO version (current: %u, received: %u), initiate global repair\n",
438 curr_instance.dag.version, dio->version);
439 /* Update version and trigger global repair */
440 curr_instance.dag.version = dio->version;
441 rpl_global_repair("Inconsistent DIO version");
442 } else {
443 LOG_WARN("new DIO version (current: %u, received: %u), apply global repair\n",
444 curr_instance.dag.version, dio->version);
445 global_repair_non_root(dio);
446 }
447 }
448
449 /* Update IPv6 neighbor cache */
450 if(!rpl_icmp6_update_nbr_table(from, NBR_TABLE_REASON_RPL_DIO, dio)) {
451 LOG_ERR("IPv6 cache full, dropping DIO\n");
452 return;
453 }
454
456 last_dtsn = nbr != NULL ? nbr->dtsn : RPL_LOLLIPOP_INIT;
457
458 /* Add neighbor to RPL neighbor table */
459 if(!update_nbr_from_dio(from, dio)) {
460 LOG_ERR("neighbor table full, dropping DIO\n");
461 return;
462 }
463
464 /* Init lifetime if not set yet. Refresh it at every DIO from preferred parent. */
465 if(curr_instance.dag.lifetime == 0 ||
466 (nbr != NULL && nbr == curr_instance.dag.preferred_parent)) {
467 LOG_INFO("refreshing lifetime\n");
468 curr_instance.dag.lifetime = RPL_LIFETIME(RPL_DAG_LIFETIME);
469 }
470
471 /* If the source is our preferred parent and it increased DTSN, we increment
472 * our DTSN in turn and schedule a DAO (see RFC6550 section 9.6.) */
473 if(curr_instance.mop != RPL_MOP_NO_DOWNWARD_ROUTES) {
474 if(nbr != NULL && nbr == curr_instance.dag.preferred_parent && rpl_lollipop_greater_than(dio->dtsn, last_dtsn)) {
475 RPL_LOLLIPOP_INCREMENT(curr_instance.dtsn_out);
476 LOG_WARN("DTSN increment %u->%u, schedule new DAO with DTSN %u\n",
477 last_dtsn, dio->dtsn, curr_instance.dtsn_out);
479 }
480 }
481}
482/*---------------------------------------------------------------------------*/
483static int
484init_dag(uint8_t instance_id, uip_ipaddr_t *dag_id, rpl_ocp_t ocp,
485 uip_ipaddr_t *prefix, unsigned prefix_len, uint8_t prefix_flags)
486{
487 rpl_of_t *of;
488
489 memset(&curr_instance, 0, sizeof(curr_instance));
490
491 /* OF */
492 of = find_objective_function(ocp);
493 if(of == NULL) {
494 LOG_ERR("ignoring DIO with an unsupported OF: %u\n", ocp);
495 return 0;
496 }
497
498 /* Prefix */
499 if(!rpl_set_prefix_from_addr(prefix, prefix_len, prefix_flags)) {
500 LOG_ERR("failed to set prefix\n");
501 return 0;
502 }
503
504 /* Instnace */
505 curr_instance.instance_id = instance_id;
506 curr_instance.of = of;
507 curr_instance.dtsn_out = RPL_LOLLIPOP_INIT;
508 curr_instance.used = 1;
509
510 /* DAG */
511 curr_instance.dag.rank = RPL_INFINITE_RANK;
512 curr_instance.dag.last_advertised_rank = RPL_INFINITE_RANK;
513 curr_instance.dag.lowest_rank = RPL_INFINITE_RANK;
514 curr_instance.dag.dao_last_seqno = RPL_LOLLIPOP_INIT;
515 curr_instance.dag.dao_last_acked_seqno = RPL_LOLLIPOP_INIT;
516 curr_instance.dag.dao_last_seqno = RPL_LOLLIPOP_INIT;
517 memcpy(&curr_instance.dag.dag_id, dag_id, sizeof(curr_instance.dag.dag_id));
518
519 return 1;
520}
521/*---------------------------------------------------------------------------*/
522static int
523init_dag_from_dio(rpl_dio_t *dio)
524{
525 if(!init_dag(dio->instance_id, &dio->dag_id, dio->ocp,
526 &dio->prefix_info.prefix, dio->prefix_info.length, dio->prefix_info.flags)) {
527 return 0;
528 }
529
530 /* Instnace */
531 curr_instance.mop = dio->mop;
532 curr_instance.mc.type = dio->mc.type;
533 curr_instance.mc.flags = dio->mc.flags;
534 curr_instance.mc.aggr = dio->mc.aggr;
535 curr_instance.mc.prec = dio->mc.prec;
536 curr_instance.max_rankinc = dio->dag_max_rankinc;
537 curr_instance.min_hoprankinc = dio->dag_min_hoprankinc;
538 curr_instance.dio_intdoubl = dio->dag_intdoubl;
539 curr_instance.dio_intmin = dio->dag_intmin;
540 curr_instance.dio_redundancy = dio->dag_redund;
541 curr_instance.default_lifetime = dio->default_lifetime;
542 curr_instance.lifetime_unit = dio->lifetime_unit;
543
544 /* DAG */
545 curr_instance.dag.state = DAG_INITIALIZED;
546 curr_instance.dag.preference = dio->preference;
547 curr_instance.dag.grounded = dio->grounded;
548 curr_instance.dag.version = dio->version;
549 /* dio_intcurrent will be reset by rpl_timers_dio_reset() */
550 curr_instance.dag.dio_intcurrent = 0;
551
552 return 1;
553}
554/*---------------------------------------------------------------------------*/
555static int
556process_dio_init_dag(rpl_dio_t *dio)
557{
558#ifdef RPL_VALIDATE_DIO_FUNC
559 if(!RPL_VALIDATE_DIO_FUNC(dio)) {
560 LOG_WARN("DIO validation failed\n");
561 return 0;
562 }
563#endif
564
565 /* Check MOP */
566 if(dio->mop != RPL_MOP_NO_DOWNWARD_ROUTES && dio->mop != RPL_MOP_NON_STORING) {
567 LOG_WARN("ignoring DIO with an unsupported MOP: %d\n", dio->mop);
568 return 0;
569 }
570
571 /* Initialize instance and DAG data structures */
572 if(!init_dag_from_dio(dio)) {
573 LOG_WARN("failed to initialize DAG\n");
574 return 0;
575 }
576
577 /* Init OF and timers */
578 curr_instance.of->reset();
579 rpl_timers_dio_reset("Join");
580#if RPL_WITH_PROBING
582#endif /* RPL_WITH_PROBING */
583 /* Leave the network after RPL_DELAY_BEFORE_LEAVING in case we do not
584 find a parent */
585 LOG_INFO("initialized DAG with instance ID %u, DAG ID ",
586 curr_instance.instance_id);
587 LOG_INFO_6ADDR(&curr_instance.dag.dag_id);
588 LOG_INFO_(", prexix ");
589 LOG_INFO_6ADDR(&dio->prefix_info.prefix);
590 LOG_INFO_("/%u, rank %u\n", dio->prefix_info.length, curr_instance.dag.rank);
591
592 LOG_ANNOTATE("#A init=%u\n", curr_instance.dag.dag_id.u8[sizeof(curr_instance.dag.dag_id) - 1]);
593
594 LOG_WARN("just joined, no parent yet, setting timer for leaving\n");
596
597 return 1;
598}
599/*---------------------------------------------------------------------------*/
600void
601rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
602{
603 if(!curr_instance.used && !rpl_dag_root_is_root()) {
604 /* Attempt to init our DAG from this DIO */
605 if(!process_dio_init_dag(dio)) {
606 LOG_WARN("failed to init DAG\n");
607 return;
608 }
609 }
610
611 if(curr_instance.used
612 && curr_instance.instance_id == dio->instance_id
613 && uip_ipaddr_cmp(&curr_instance.dag.dag_id, &dio->dag_id)) {
614 process_dio_from_current_dag(from, dio);
616 }
617}
618/*---------------------------------------------------------------------------*/
619void
620rpl_process_dis(uip_ipaddr_t *from, int is_multicast)
621{
622 if(is_multicast) {
623 rpl_timers_dio_reset("Multicast DIS");
624 } else {
625 /* Add neighbor to cache and reply to the unicast DIS with a unicast DIO*/
626 if(rpl_icmp6_update_nbr_table(from, NBR_TABLE_REASON_RPL_DIS, NULL) != NULL) {
627 LOG_INFO("unicast DIS, reply to sender\n");
629 }
630 }
631}
632/*---------------------------------------------------------------------------*/
633void
634rpl_process_dao(uip_ipaddr_t *from, rpl_dao_t *dao)
635{
636 if(dao->lifetime == 0) {
637 uip_sr_expire_parent(NULL, from, &dao->parent_addr);
638 } else {
639 if(!uip_sr_update_node(NULL, from, &dao->parent_addr, RPL_LIFETIME(dao->lifetime))) {
640 LOG_ERR("failed to add link on incoming DAO\n");
641 return;
642 }
643 }
644
645#if RPL_WITH_DAO_ACK
646 if(dao->flags & RPL_DAO_K_FLAG) {
647 rpl_timers_schedule_dao_ack(from, dao->sequence);
648 }
649#endif /* RPL_WITH_DAO_ACK */
650}
651/*---------------------------------------------------------------------------*/
652#if RPL_WITH_DAO_ACK
653void
654rpl_process_dao_ack(uint8_t sequence, uint8_t status)
655{
656 /* Update dao_last_acked_seqno */
657 if(rpl_lollipop_greater_than(sequence, curr_instance.dag.dao_last_acked_seqno)) {
658 curr_instance.dag.dao_last_acked_seqno = sequence;
659 }
660 /* Is this an ACK for our last DAO? */
661 if(sequence == curr_instance.dag.dao_last_seqno) {
662 int status_ok = status < RPL_DAO_ACK_UNABLE_TO_ACCEPT;
663 if(curr_instance.dag.state == DAG_JOINED && status_ok) {
664 curr_instance.dag.state = DAG_REACHABLE;
665 rpl_timers_dio_reset("Reachable");
666 }
667 /* Let the rpl-timers module know that we got an ACK for the last DAO */
669
670 if(!status_ok) {
671 /* We got a NACK, start poisoning and leave */
672 LOG_WARN("DAO-NACK received with seqno %u, status %u, poison and leave\n",
673 sequence, status);
674 curr_instance.dag.state = DAG_POISONING;
675 }
676 }
677}
678#endif /* RPL_WITH_DAO_ACK */
679/*---------------------------------------------------------------------------*/
680int
681rpl_process_hbh(rpl_nbr_t *sender, uint16_t sender_rank, int loop_detected, int rank_error_signaled)
682{
683 int drop = 0;
684
685 if(loop_detected) {
686 if(rank_error_signaled) {
687#if RPL_LOOP_ERROR_DROP
688 /* Drop packet and reset trickle timer, as per RFC6550 - 11.2.2.2 */
689 rpl_timers_dio_reset("HBH error");
690 LOG_WARN("rank error and loop detected, dropping\n");
691 drop = 1;
692#endif /* RPL_LOOP_ERROR_DROP */
693 }
694 /* Attempt to repair the loop by sending a unicast DIO back to the sender
695 * so that it gets a fresh update of our rank. */
697 }
698
699 if(rank_error_signaled) {
700 /* A rank error was signalled, attempt to repair it by updating
701 * the sender's rank from ext header */
702 if(sender != NULL) {
703 sender->rank = sender_rank;
704 /* Select DAG and preferred parent. In case of a parent switch,
705 the new parent will be used to forward the current packet. */
707 }
708 }
709
710 return !drop;
711}
712/*---------------------------------------------------------------------------*/
713void
714rpl_dag_init_root(uint8_t instance_id, uip_ipaddr_t *dag_id,
715 uip_ipaddr_t *prefix, unsigned prefix_len, uint8_t prefix_flags)
716{
717 uint8_t version = RPL_LOLLIPOP_INIT;
718
719 /* If we're in an instance, first leave it */
720 if(curr_instance.used) {
721 /* We were already root. Increment version */
722 if(uip_ipaddr_cmp(&curr_instance.dag.dag_id, dag_id)) {
723 version = curr_instance.dag.version;
724 RPL_LOLLIPOP_INCREMENT(version);
725 }
727 }
728
729 /* Init DAG and instance */
730 init_dag(instance_id, dag_id, RPL_OF_OCP, prefix, prefix_len, prefix_flags);
731
732 /* Instance */
733 curr_instance.mop = RPL_MOP_DEFAULT;
734 curr_instance.max_rankinc = RPL_MAX_RANKINC;
735 curr_instance.min_hoprankinc = RPL_MIN_HOPRANKINC;
736 curr_instance.dio_intdoubl = RPL_DIO_INTERVAL_DOUBLINGS;
737 curr_instance.dio_intmin = RPL_DIO_INTERVAL_MIN;
738 curr_instance.dio_redundancy = RPL_DIO_REDUNDANCY;
739 curr_instance.default_lifetime = RPL_DEFAULT_LIFETIME;
740 curr_instance.lifetime_unit = RPL_DEFAULT_LIFETIME_UNIT;
741
742 /* DAG */
743 curr_instance.dag.preference = RPL_PREFERENCE;
744 curr_instance.dag.grounded = RPL_GROUNDED;
745 curr_instance.dag.version = version;
746 curr_instance.dag.rank = ROOT_RANK;
747 curr_instance.dag.lifetime = RPL_LIFETIME(RPL_INFINITE_LIFETIME);
748 /* dio_intcurrent will be reset by rpl_timers_dio_reset() */
749 curr_instance.dag.dio_intcurrent = 0;
750 curr_instance.dag.state = DAG_REACHABLE;
751
752 rpl_timers_dio_reset("Init root");
753
754 LOG_INFO("created DAG with instance ID %u, DAG ID ",
755 curr_instance.instance_id);
756 LOG_INFO_6ADDR(&curr_instance.dag.dag_id);
757 LOG_INFO_(", rank %u\n", curr_instance.dag.rank);
758
759 LOG_ANNOTATE("#A root=%u\n", curr_instance.dag.dag_id.u8[sizeof(curr_instance.dag.dag_id) - 1]);
760}
761/*---------------------------------------------------------------------------*/
762void
764{
765 memset(&curr_instance, 0, sizeof(curr_instance));
766}
767/*---------------------------------------------------------------------------*/
768/** @} */
Default definitions of C compiler quirk work-arounds.
#define CC_ARRAY_LENGTH(array)
Counts the number of elements of an array.
Definition cc.h:122
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:90
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:267
void rpl_dag_periodic(unsigned seconds)
A function called periodically.
Definition rpl-dag.c:139
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:714
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
#define RPL_LIFETIME(lifetime)
Compute lifetime, accounting for the lifetime unit.
Definition rpl-types.h:72
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:620
void rpl_dag_poison_and_leave(void)
Start poisoning and leave the DAG after a delay.
Definition rpl-dag.c:132
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:599
void rpl_process_dao(uip_ipaddr_t *from, rpl_dao_t *dao)
Processes incoming DAO.
Definition rpl-dag.c:634
void rpl_global_repair(const char *str)
Triggers a RPL global repair.
Definition rpl-dag.c:205
int rpl_dag_ready_to_advertise(void)
Tells whether RPL is ready to advertise the DAG.
Definition rpl-dag.c:256
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:681
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:73
void rpl_dag_leave(void)
Leaves the current DAG.
Definition rpl-dag.c:100
void rpl_refresh_routes(const char *str)
Triggers a route fresh via DTSN increment.
Definition rpl-dag.c:190
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:159
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
rpl_dag_t * rpl_get_any_dag(void)
Returns pointer to any DAG (for compatibility with legagy RPL code).
Definition rpl-dag.c:1091
int rpl_dag_root_is_root(void)
Tells whether we are DAG root or not.
void uip_sr_free_all(void)
Deallocate all neighbors.
Definition uip-sr.c:248
uip_ds6_nbr_t * rpl_icmp6_update_nbr_table(uip_ipaddr_t *from, nbr_table_reason_t reason, void *data)
Updates IPv6 neighbor cache on incoming link-local RPL ICMPv6 messages.
Definition rpl-icmp6.c:190
void rpl_dag_init(void)
Initializes rpl-dag module.
Definition rpl-dag.c:141
void rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
Processes incoming DIO.
Definition rpl-dag.c:1490
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
rpl_instance_t * rpl_get_default_instance(void)
Returns pointer to the default instance (for compatibility with legagy RPL code).
Definition rpl-dag.c:635
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.
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.