Contiki-NG
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 /*---------------------------------------------------------------------------*/
56 extern rpl_of_t rpl_of0, rpl_mrhof;
57 static rpl_of_t * const objective_functions[] = RPL_SUPPORTED_OFS;
58 static int init_dag_from_dio(rpl_dio_t *dio);
59 
60 /*---------------------------------------------------------------------------*/
61 /* Allocate instance table. */
62 rpl_instance_t curr_instance;
63 
64 /*---------------------------------------------------------------------------*/
65 
66 #ifdef RPL_VALIDATE_DIO_FUNC
67 int RPL_VALIDATE_DIO_FUNC(rpl_dio_t *dio);
68 #endif /* RPL_PROBING_SELECT_FUNC */
69 
70 /*---------------------------------------------------------------------------*/
71 const 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 /*---------------------------------------------------------------------------*/
88 int
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 /*---------------------------------------------------------------------------*/
98 void
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 and lnks */
116  uip_sr_free_all();
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 /*---------------------------------------------------------------------------*/
130 void
132 {
133  curr_instance.dag.state = DAG_POISONING;
135 }
136 /*---------------------------------------------------------------------------*/
137 void
138 rpl_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 /*---------------------------------------------------------------------------*/
157 int
158 rpl_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 /*---------------------------------------------------------------------------*/
166 {
167  return curr_instance.used ? &curr_instance : NULL;
168 }
169 /*---------------------------------------------------------------------------*/
170 rpl_dag_t *
172 {
173  return curr_instance.used ? &curr_instance.dag : NULL;
174 }
175 /*---------------------------------------------------------------------------*/
176 static rpl_of_t *
177 find_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 /*---------------------------------------------------------------------------*/
188 void
189 rpl_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 /*---------------------------------------------------------------------------*/
203 void
204 rpl_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 /*---------------------------------------------------------------------------*/
221 static void
222 global_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 */
232  init_dag_from_dio(dio);
233  rpl_local_repair("Global repair");
234  }
235 }
236 /*---------------------------------------------------------------------------*/
237 void
238 rpl_local_repair(const char *str)
239 {
240  if(curr_instance.used) { /* Check needed because this is a public function */
241  LOG_WARN("local repair (%s)\n", str);
242  if(!rpl_dag_root_is_root()) {
243  curr_instance.dag.state = DAG_INITIALIZED; /* Reset DAG state */
244  }
245  curr_instance.of->reset(); /* Reset OF */
246  rpl_neighbor_remove_all(); /* Remove all neighbors */
247  rpl_timers_dio_reset("Local repair"); /* Reset Trickle timer */
249  }
250 }
251 /*---------------------------------------------------------------------------*/
252 int
254 {
255  if(curr_instance.mop == RPL_MOP_NO_DOWNWARD_ROUTES) {
256  return curr_instance.used && curr_instance.dag.state >= DAG_INITIALIZED;
257  } else {
258  return curr_instance.used && curr_instance.dag.state >= DAG_REACHABLE;
259  }
260 }
261 /*---------------------------------------------------------------------------*/
262 /* Updates rank and parent */
263 void
265 {
266  rpl_rank_t old_rank;
267 
268  if(!curr_instance.used) {
269  return;
270  }
271 
272  old_rank = curr_instance.dag.rank;
273  /* Any scheduled state update is no longer needed */
275 
276  if(curr_instance.dag.state == DAG_POISONING) {
278  curr_instance.dag.rank = RPL_INFINITE_RANK;
279  if(old_rank != RPL_INFINITE_RANK) {
280  /* Advertise that we are leaving, and leave after a delay */
281  LOG_WARN("poisoning and leaving after a delay\n");
282  rpl_timers_dio_reset("Poison routes");
284  }
285  } else if(!rpl_dag_root_is_root()) {
286  rpl_nbr_t *old_parent = curr_instance.dag.preferred_parent;
287  rpl_nbr_t *nbr;
288 
289  /* Select and set preferred parent */
291  /* Update rank */
292  curr_instance.dag.rank = rpl_neighbor_rank_via_nbr(curr_instance.dag.preferred_parent);
293 
294  /* Update better_parent_since flag for each neighbor */
295  nbr = nbr_table_head(rpl_neighbors);
296  while(nbr != NULL) {
297  if(rpl_neighbor_rank_via_nbr(nbr) < curr_instance.dag.rank) {
298  /* This neighbor would be a better parent than our current.
299  Set 'better_parent_since' if not already set. */
300  if(nbr->better_parent_since == 0) {
301  nbr->better_parent_since = clock_time(); /* Initialize */
302  }
303  } else {
304  nbr->better_parent_since = 0; /* Not a better parent */
305  }
306  nbr = nbr_table_next(rpl_neighbors, nbr);
307  }
308 
309  if(old_parent == NULL || curr_instance.dag.rank < curr_instance.dag.lowest_rank) {
310  /* This is a slight departure from RFC6550: if we had no preferred parent before,
311  * reset lowest_rank. This helps recovering from temporary bad link conditions. */
312  curr_instance.dag.lowest_rank = curr_instance.dag.rank;
313  }
314 
315  /* Reset DIO timer in case of significant rank update */
316  if(curr_instance.dag.last_advertised_rank != RPL_INFINITE_RANK
317  && curr_instance.dag.rank != RPL_INFINITE_RANK
318  && ABS((int32_t)curr_instance.dag.rank - curr_instance.dag.last_advertised_rank) > RPL_SIGNIFICANT_CHANGE_THRESHOLD) {
319  LOG_WARN("significant rank update %u->%u\n",
320  curr_instance.dag.last_advertised_rank, curr_instance.dag.rank);
321  /* Update already here to avoid multiple resets in a row */
322  curr_instance.dag.last_advertised_rank = curr_instance.dag.rank;
323  rpl_timers_dio_reset("Significant rank update");
324  }
325 
326  /* Parent switch */
327  if(curr_instance.dag.preferred_parent != old_parent) {
328  /* We just got a parent (was NULL), reset trickle timer to advertise this */
329  if(old_parent == NULL) {
330  curr_instance.dag.state = DAG_JOINED;
331  rpl_timers_dio_reset("Got parent");
332  LOG_WARN("found parent: ");
333  LOG_WARN_6ADDR(rpl_neighbor_get_ipaddr(curr_instance.dag.preferred_parent));
334  LOG_WARN_(", staying in DAG\n");
336  }
337 
338  /* Schedule a DAO */
339  if(curr_instance.dag.preferred_parent != NULL) {
341  } else {
342  /* We have no more parent, schedule DIS to get a chance to hear updated state */
343  curr_instance.dag.state = DAG_INITIALIZED;
344  LOG_WARN("no parent, scheduling periodic DIS, will leave if no parent is found\n");
345  rpl_timers_dio_reset("Poison routes");
348  }
349 
350  if(LOG_INFO_ENABLED) {
351  rpl_neighbor_print_list("Parent switch");
352  }
353  }
354  }
355 
356  /* Finally, update metric container */
357  curr_instance.of->update_metric_container();
358 }
359 /*---------------------------------------------------------------------------*/
360 static rpl_nbr_t *
361 update_nbr_from_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
362 {
363  rpl_nbr_t *nbr = NULL;
364  const uip_lladdr_t *lladdr;
365 
366  nbr = rpl_neighbor_get_from_ipaddr(from);
367  /* Neighbor not in RPL neighbor table, add it */
368  if(nbr == NULL) {
369  /* Is the neighbor known by ds6? Drop this request if not.
370  * Typically, the neighbor is added upon receiving a DIO. */
371  lladdr = uip_ds6_nbr_lladdr_from_ipaddr(from);
372  if(lladdr == NULL) {
373  return NULL;
374  }
375 
376  /* Add neighbor to RPL table */
377  nbr = nbr_table_add_lladdr(rpl_neighbors, (linkaddr_t *)lladdr,
378  NBR_TABLE_REASON_RPL_DIO, dio);
379  if(nbr == NULL) {
380  LOG_ERR("failed to add neighbor\n");
381  return NULL;
382  }
383  }
384 
385  /* Update neighbor info from DIO */
386  nbr->rank = dio->rank;
387  nbr->dtsn = dio->dtsn;
388 #if RPL_WITH_MC
389  memcpy(&nbr->mc, &dio->mc, sizeof(nbr->mc));
390 #endif /* RPL_WITH_MC */
391 
392  return nbr;
393 }
394 /*---------------------------------------------------------------------------*/
395 static void
396 process_dio_from_current_dag(uip_ipaddr_t *from, rpl_dio_t *dio)
397 {
398  rpl_nbr_t *nbr;
399  uint8_t last_dtsn;
400 
401  /* Does the rank make sense at all? */
402  if(dio->rank < ROOT_RANK) {
403  return;
404  }
405 
406  /* If the DIO sender is on an older version of the DAG, do not process it
407  * further. The sender will eventually hear the global repair and catch up. */
408  if(rpl_lollipop_greater_than(curr_instance.dag.version, dio->version)) {
409  if(dio->rank == ROOT_RANK) {
410  /* Before returning, if the DIO was from the root, an old DAG versions
411  * likely incidates a root reboot. Reset our DIO timer to make sure the
412  * root hears our version ASAP, and in turn triggers a global repair. */
413  rpl_timers_dio_reset("Heard old version from root");
414  }
415  return;
416  }
417 
418  /* The DIO is valid, proceed further */
419 
420  /* Update DIO counter for redundancy mngt */
421  if(dio->rank != RPL_INFINITE_RANK) {
422  curr_instance.dag.dio_counter++;
423  }
424 
425  /* The DIO has a newer version: global repair.
426  * Must come first, as it might remove all neighbors, and we then need
427  * to re-add this source of the DIO to the neighbor table */
428  if(rpl_lollipop_greater_than(dio->version, curr_instance.dag.version)) {
429  if(curr_instance.dag.rank == ROOT_RANK) {
430  /* The root should not hear newer versions unless it just rebooted */
431  LOG_ERR("inconsistent DIO version (current: %u, received: %u), initiate global repair\n",
432  curr_instance.dag.version, dio->version);
433  /* Update version and trigger global repair */
434  curr_instance.dag.version = dio->version;
435  rpl_global_repair("Inconsistent DIO version");
436  } else {
437  LOG_WARN("new DIO version (current: %u, received: %u), apply global repair\n",
438  curr_instance.dag.version, dio->version);
439  global_repair_non_root(dio);
440  }
441  }
442 
443  /* Update IPv6 neighbor cache */
444  if(!rpl_icmp6_update_nbr_table(from, NBR_TABLE_REASON_RPL_DIO, dio)) {
445  LOG_ERR("IPv6 cache full, dropping DIO\n");
446  return;
447  }
448 
449  /* Add neighbor to RPL neighbor table */
450  nbr = rpl_neighbor_get_from_ipaddr(from);
451  last_dtsn = nbr != NULL ? nbr->dtsn : RPL_LOLLIPOP_INIT;
452 
453  if(!update_nbr_from_dio(from, dio)) {
454  LOG_ERR("neighbor table full, dropping DIO\n");
455  return;
456  }
457 
458  /* Init lifetime if not set yet. Refresh it at every DIO from preferred parent. */
459  if(curr_instance.dag.lifetime == 0 ||
460  (nbr != NULL && nbr == curr_instance.dag.preferred_parent)) {
461  LOG_INFO("refreshing lifetime\n");
462  curr_instance.dag.lifetime = RPL_LIFETIME(RPL_DAG_LIFETIME);
463  }
464 
465  /* If the source is our preferred parent and it increased DTSN, we increment
466  * our DTSN in turn and schedule a DAO (see RFC6550 section 9.6.) */
467  if(curr_instance.mop != RPL_MOP_NO_DOWNWARD_ROUTES) {
468  if(nbr != NULL && nbr == curr_instance.dag.preferred_parent && rpl_lollipop_greater_than(dio->dtsn, last_dtsn)) {
469  RPL_LOLLIPOP_INCREMENT(curr_instance.dtsn_out);
470  LOG_WARN("DTSN increment %u->%u, schedule new DAO with DTSN %u\n",
471  last_dtsn, dio->dtsn, curr_instance.dtsn_out);
473  }
474  }
475 }
476 /*---------------------------------------------------------------------------*/
477 static int
478 init_dag(uint8_t instance_id, uip_ipaddr_t *dag_id, rpl_ocp_t ocp,
479  uip_ipaddr_t *prefix, unsigned prefix_len, uint8_t prefix_flags)
480 {
481  rpl_of_t *of;
482 
483  memset(&curr_instance, 0, sizeof(curr_instance));
484 
485  /* OF */
486  of = find_objective_function(ocp);
487  if(of == NULL) {
488  LOG_ERR("ignoring DIO with an unsupported OF: %u\n", ocp);
489  return 0;
490  }
491 
492  /* Prefix */
493  if(!rpl_set_prefix_from_addr(prefix, prefix_len, prefix_flags)) {
494  LOG_ERR("failed to set prefix\n");
495  return 0;
496  }
497 
498  /* Instnace */
499  curr_instance.instance_id = instance_id;
500  curr_instance.of = of;
501  curr_instance.dtsn_out = RPL_LOLLIPOP_INIT;
502  curr_instance.used = 1;
503 
504  /* DAG */
505  curr_instance.dag.rank = RPL_INFINITE_RANK;
506  curr_instance.dag.last_advertised_rank = RPL_INFINITE_RANK;
507  curr_instance.dag.lowest_rank = RPL_INFINITE_RANK;
508  curr_instance.dag.dao_last_seqno = RPL_LOLLIPOP_INIT;
509  curr_instance.dag.dao_last_acked_seqno = RPL_LOLLIPOP_INIT;
510  curr_instance.dag.dao_last_seqno = RPL_LOLLIPOP_INIT;
511  memcpy(&curr_instance.dag.dag_id, dag_id, sizeof(curr_instance.dag.dag_id));
512 
513  return 1;
514 }
515 /*---------------------------------------------------------------------------*/
516 static int
517 init_dag_from_dio(rpl_dio_t *dio)
518 {
519  if(!init_dag(dio->instance_id, &dio->dag_id, dio->ocp,
520  &dio->prefix_info.prefix, dio->prefix_info.length, dio->prefix_info.flags)) {
521  return 0;
522  }
523 
524  /* Instnace */
525  curr_instance.mop = dio->mop;
526  curr_instance.mc.type = dio->mc.type;
527  curr_instance.mc.flags = dio->mc.flags;
528  curr_instance.mc.aggr = dio->mc.aggr;
529  curr_instance.mc.prec = dio->mc.prec;
530  curr_instance.max_rankinc = dio->dag_max_rankinc;
531  curr_instance.min_hoprankinc = dio->dag_min_hoprankinc;
532  curr_instance.dio_intdoubl = dio->dag_intdoubl;
533  curr_instance.dio_intmin = dio->dag_intmin;
534  curr_instance.dio_redundancy = dio->dag_redund;
535  curr_instance.default_lifetime = dio->default_lifetime;
536  curr_instance.lifetime_unit = dio->lifetime_unit;
537 
538  /* DAG */
539  curr_instance.dag.state = DAG_INITIALIZED;
540  curr_instance.dag.preference = dio->preference;
541  curr_instance.dag.grounded = dio->grounded;
542  curr_instance.dag.version = dio->version;
543  curr_instance.dag.dio_intcurrent = dio->dag_intmin;
544 
545  return 1;
546 }
547 /*---------------------------------------------------------------------------*/
548 static int
549 process_dio_init_dag(uip_ipaddr_t *from, rpl_dio_t *dio)
550 {
551 #ifdef RPL_VALIDATE_DIO_FUNC
552  if(!RPL_VALIDATE_DIO_FUNC(dio)) {
553  LOG_WARN("DIO validation failed\n");
554  return 0;
555  }
556 #endif
557 
558  /* Check MOP */
559  if(dio->mop != RPL_MOP_NO_DOWNWARD_ROUTES && dio->mop != RPL_MOP_NON_STORING) {
560  LOG_WARN("ignoring DIO with an unsupported MOP: %d\n", dio->mop);
561  return 0;
562  }
563 
564  /* Initialize instance and DAG data structures */
565  if(!init_dag_from_dio(dio)) {
566  LOG_WARN("failed to initialize DAG\n");
567  return 0;
568  }
569 
570  /* Init OF and timers */
571  curr_instance.of->reset();
572  rpl_timers_dio_reset("Join");
573 #if RPL_WITH_PROBING
575 #endif /* RPL_WITH_PROBING */
576  /* Leave the network after RPL_DELAY_BEFORE_LEAVING in case we do not
577  find a parent */
578  LOG_INFO("initialized DAG with instance ID %u, DAG ID ",
579  curr_instance.instance_id);
580  LOG_INFO_6ADDR(&curr_instance.dag.dag_id);
581  LOG_INFO_(", prexix ");
582  LOG_INFO_6ADDR(&dio->prefix_info.prefix);
583  LOG_INFO_("/%u, rank %u\n", dio->prefix_info.length, curr_instance.dag.rank);
584 
585  LOG_ANNOTATE("#A init=%u\n", curr_instance.dag.dag_id.u8[sizeof(curr_instance.dag.dag_id) - 1]);
586 
587  LOG_WARN("just joined, no parent yet, setting timer for leaving\n");
589 
590  return 1;
591 }
592 /*---------------------------------------------------------------------------*/
593 void
594 rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
595 {
596  if(!curr_instance.used && !rpl_dag_root_is_root()) {
597  /* Attempt to init our DAG from this DIO */
598  if(!process_dio_init_dag(from, dio)) {
599  LOG_WARN("failed to init DAG\n");
600  return;
601  }
602  }
603 
604  if(curr_instance.used
605  && curr_instance.instance_id == dio->instance_id
606  && uip_ipaddr_cmp(&curr_instance.dag.dag_id, &dio->dag_id)) {
607  process_dio_from_current_dag(from, dio);
609  }
610 }
611 /*---------------------------------------------------------------------------*/
612 void
613 rpl_process_dis(uip_ipaddr_t *from, int is_multicast)
614 {
615  if(is_multicast) {
616  rpl_timers_dio_reset("Multicast DIS");
617  } else {
618  /* Add neighbor to cache and reply to the unicast DIS with a unicast DIO*/
619  if(rpl_icmp6_update_nbr_table(from, NBR_TABLE_REASON_RPL_DIS, NULL) != NULL) {
620  LOG_INFO("unicast DIS, reply to sender\n");
621  rpl_icmp6_dio_output(from);
622  }
623  }
624 }
625 /*---------------------------------------------------------------------------*/
626 void
627 rpl_process_dao(uip_ipaddr_t *from, rpl_dao_t *dao)
628 {
629  if(dao->lifetime == 0) {
630  uip_sr_expire_parent(NULL, from, &dao->parent_addr);
631  } else {
632  if(!uip_sr_update_node(NULL, from, &dao->parent_addr, RPL_LIFETIME(dao->lifetime))) {
633  LOG_ERR("failed to add link on incoming DAO\n");
634  return;
635  }
636  }
637 
638 #if RPL_WITH_DAO_ACK
639  if(dao->flags & RPL_DAO_K_FLAG) {
640  rpl_timers_schedule_dao_ack(from, dao->sequence);
641  }
642 #endif /* RPL_WITH_DAO_ACK */
643 }
644 /*---------------------------------------------------------------------------*/
645 #if RPL_WITH_DAO_ACK
646 void
647 rpl_process_dao_ack(uint8_t sequence, uint8_t status)
648 {
649  /* Update dao_last_acked_seqno */
650  if(rpl_lollipop_greater_than(sequence, curr_instance.dag.dao_last_acked_seqno)) {
651  curr_instance.dag.dao_last_acked_seqno = sequence;
652  }
653  /* Is this an ACK for our last DAO? */
654  if(sequence == curr_instance.dag.dao_last_seqno) {
655  int status_ok = status < RPL_DAO_ACK_UNABLE_TO_ACCEPT;
656  if(curr_instance.dag.state == DAG_JOINED && status_ok) {
657  curr_instance.dag.state = DAG_REACHABLE;
658  rpl_timers_dio_reset("Reachable");
659  }
660  /* Let the rpl-timers module know that we got an ACK for the last DAO */
662 
663  if(!status_ok) {
664  /* We got a NACK, start poisoning and leave */
665  LOG_WARN("DAO-NACK received with seqno %u, status %u, poison and leave\n",
666  sequence, status);
667  curr_instance.dag.state = DAG_POISONING;
668  }
669  }
670 }
671 #endif /* RPL_WITH_DAO_ACK */
672 /*---------------------------------------------------------------------------*/
673 int
674 rpl_process_hbh(rpl_nbr_t *sender, uint16_t sender_rank, int loop_detected, int rank_error_signaled)
675 {
676  int drop = 0;
677 
678  if(loop_detected) {
679  if(rank_error_signaled) {
680 #if RPL_LOOP_ERROR_DROP
681  /* Drop packet and reset trickle timer, as per RFC6550 - 11.2.2.2 */
682  rpl_timers_dio_reset("HBH error");
683  LOG_WARN("rank error and loop detected, dropping\n");
684  drop = 1;
685 #endif /* RPL_LOOP_ERROR_DROP */
686  }
687  /* Attempt to repair the loop by sending a unicast DIO back to the sender
688  * so that it gets a fresh update of our rank. */
690  }
691 
692  if(rank_error_signaled) {
693  /* A rank error was signalled, attempt to repair it by updating
694  * the sender's rank from ext header */
695  if(sender != NULL) {
696  sender->rank = sender_rank;
697  /* Select DAG and preferred parent. In case of a parent switch,
698  the new parent will be used to forward the current packet. */
700  }
701  }
702 
703  return !drop;
704 }
705 /*---------------------------------------------------------------------------*/
706 void
707 rpl_dag_init_root(uint8_t instance_id, uip_ipaddr_t *dag_id,
708  uip_ipaddr_t *prefix, unsigned prefix_len, uint8_t prefix_flags)
709 {
710  uint8_t version = RPL_LOLLIPOP_INIT;
711 
712  /* If we're in an instance, first leave it */
713  if(curr_instance.used) {
714  /* We were already root. Increment version */
715  if(uip_ipaddr_cmp(&curr_instance.dag.dag_id, dag_id)) {
716  version = curr_instance.dag.version;
717  RPL_LOLLIPOP_INCREMENT(version);
718  }
719  rpl_dag_leave();
720  }
721 
722  /* Init DAG and instance */
723  init_dag(instance_id, dag_id, RPL_OF_OCP, prefix, prefix_len, prefix_flags);
724 
725  /* Instance */
726  curr_instance.mop = RPL_MOP_DEFAULT;
727  curr_instance.max_rankinc = RPL_MAX_RANKINC;
728  curr_instance.min_hoprankinc = RPL_MIN_HOPRANKINC;
729  curr_instance.dio_intdoubl = RPL_DIO_INTERVAL_DOUBLINGS;
730  curr_instance.dio_intmin = RPL_DIO_INTERVAL_MIN;
731  curr_instance.dio_redundancy = RPL_DIO_REDUNDANCY;
732  curr_instance.default_lifetime = RPL_DEFAULT_LIFETIME;
733  curr_instance.lifetime_unit = RPL_DEFAULT_LIFETIME_UNIT;
734 
735  /* DAG */
736  curr_instance.dag.preference = RPL_PREFERENCE;
737  curr_instance.dag.grounded = RPL_GROUNDED;
738  curr_instance.dag.version = version;
739  curr_instance.dag.rank = ROOT_RANK;
740  curr_instance.dag.lifetime = RPL_LIFETIME(RPL_INFINITE_LIFETIME);
741  curr_instance.dag.dio_intcurrent = RPL_DIO_INTERVAL_MIN;
742  curr_instance.dag.state = DAG_REACHABLE;
743 
744  rpl_timers_dio_reset("Init root");
745 
746  LOG_INFO("created DAG with instance ID %u, DAG ID ",
747  curr_instance.instance_id);
748  LOG_INFO_6ADDR(&curr_instance.dag.dag_id);
749  LOG_INFO_(", rank %u\n", curr_instance.dag.rank);
750 
751  LOG_ANNOTATE("#A root=%u\n", curr_instance.dag.dag_id.u8[sizeof(curr_instance.dag.dag_id) - 1]);
752 }
753 /*---------------------------------------------------------------------------*/
754 void
756 {
757  memset(&curr_instance, 0, sizeof(curr_instance));
758 }
759 /*---------------------------------------------------------------------------*/
760 /** @} */
static uip_ipaddr_t ipaddr
Pointer to prefix information option in uip_buf.
Definition: uip-nd6.c:116
void rpl_dag_poison_and_leave(void)
Start poisoning and leave the DAG after a delay.
Definition: rpl-dag.c:131
RPL DAG structure.
Definition: rpl.h:135
uip_ipaddr_t * rpl_neighbor_get_ipaddr(rpl_nbr_t *nbr)
Returns a neighbor&#39;s (link-local) IPv6 address.
Definition: rpl-neighbor.c:252
static uip_ds6_nbr_t * nbr
Pointer to llao option in uip_buf.
Definition: uip-nd6.c:106
void rpl_process_dao(uip_ipaddr_t *from, rpl_dao_t *dao)
Processes incoming DAO.
Definition: rpl-dag.c:627
RPL instance structure.
Definition: rpl.h:219
void rpl_timers_schedule_unicast_dio(rpl_nbr_t *target)
Schedule unicast DIO with no delay.
Definition: rpl-timers.c:202
#define ROOT_RANK
Rank of a root node.
Definition: rpl-types.h:78
static uip_ds6_addr_t * addr
Pointer to a nbr cache entry.
Definition: uip-nd6.c:107
int rpl_lollipop_greater_than(int a, int b)
Greater-than function for a lollipop counter.
Definition: rpl.c:57
void rpl_timers_notify_dao_ack(void)
Let the rpl-timers module know that the last DAO was ACKed.
void rpl_global_repair(const char *str)
Triggers a RPL global repair.
Definition: rpl-dag.c:204
void rpl_neighbor_remove_all(void)
Empty the RPL neighbor table.
Definition: rpl-neighbor.c:325
rpl_nbr_t * rpl_neighbor_get_from_ipaddr(uip_ipaddr_t *addr)
Returns a neighbor from its link-local IPv6 address.
Definition: rpl-neighbor.c:343
void uip_sr_free_all(void)
Deallocate all neighbors.
Definition: uip-sr.c:239
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
#define RPL_LIFETIME(lifetime)
Compute lifetime, accounting for the lifetime unit.
Definition: rpl-types.h:72
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:707
void uip_sr_expire_parent(void *graph, const uip_ipaddr_t *child, const uip_ipaddr_t *parent)
Expires a given child-parent link.
Definition: uip-sr.c:113
int rpl_dag_root_is_root(void)
Tells whether we are DAG root or not.
Definition: rpl-dag-root.c:152
void rpl_timers_schedule_dao_ack(uip_ipaddr_t *target, uint16_t sequence)
Schedule a DAO-ACK with no delay.
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:543
Source routing support.
void rpl_process_dao_ack(uint8_t sequence, uint8_t status)
Processes incoming DAO-ACK.
API for RPL objective functions (OF)
Definition: rpl.h:201
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
int rpl_set_prefix_from_addr(uip_ipaddr_t *addr, unsigned len, uint8_t flags)
Set prefix from an IPv6 address.
Definition: rpl.c:156
void rpl_timers_schedule_state_update(void)
Schedule a state update ASAP.
Definition: rpl-timers.c:557
void rpl_timers_dio_reset(const char *str)
Reset DIO Trickle timer.
Definition: rpl-timers.c:150
void rpl_timers_unschedule_leaving(void)
Cancel scheduled leaving if any.
Definition: rpl-timers.c:477
void rpl_timers_stop_dag_timers(void)
Stop all timers related to the DAG.
Definition: rpl-timers.c:532
void rpl_dag_leave(void)
Leaves the current DAG.
Definition: rpl-dag.c:99
void rpl_process_dis(uip_ipaddr_t *from, int is_multicast)
Processes incoming DIS.
Definition: rpl-dag.c:613
rpl_dag_state
RPL DAG states.
Definition: rpl-types.h:177
All information related to a RPL neighbor.
Definition: rpl-types.h:136
rpl_dag_t * rpl_get_any_dag(void)
Returns pointer to any DAG (for compatibility with legagy RPL code)
Definition: rpl-dag.c:1055
#define uip_ipaddr_copy(dest, src)
Copy an IP address from one place to another.
Definition: uip.h:1015
clock_time_t clock_time(void)
Get the current clock time.
Definition: clock.c:118
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.
Definition: uip-ds6-nbr.c:513
int rpl_dag_ready_to_advertise(void)
Tells whether RPL is ready to advertise the DAG.
Definition: rpl-dag.c:253
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
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:194
void rpl_timers_schedule_dao(void)
Schedule a DAO with random delay based on RPL_DAO_DELAY.
Definition: rpl-timers.c:257
void rpl_dag_periodic(unsigned seconds)
A function called periodically.
Definition: rpl-dag.c:138
void rpl_neighbor_print_list(const char *str)
Prints a summary of all RPL neighbors and their properties.
Definition: rpl-neighbor.c:143
void rpl_refresh_routes(const char *str)
Triggers a route fresh via DTSN increment.
Definition: rpl-dag.c:189
void rpl_dag_update_state(void)
Updates RPL internal state: selects preferred parent, updates rank & metreic container, triggers control traffic accordingly and updates uIP6 internal state.
Definition: rpl-dag.c:264
void rpl_timers_schedule_leaving(void)
Schedule leaving after RPL_DELAY_BEFORE_LEAVING.
Definition: rpl-timers.c:487
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
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:123
void rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
Processes incoming DIO.
Definition: rpl-dag.c:1459
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:674
void rpl_timers_unschedule_state_update(void)
Cancelled any scheduled state update.
Definition: rpl-timers.c:549
rpl_rank_t rpl_neighbor_rank_via_nbr(rpl_nbr_t *nbr)
Returns our rank if selecting a given parent as preferred parent.
Definition: rpl-neighbor.c:237
void rpl_local_repair(const char *str)
Triggers a RPL local repair.
Definition: rpl-dag.c:238
void rpl_reset_prefix(rpl_prefix_t *last_prefix)
Removes current prefx.
Definition: rpl.c:140
void rpl_neighbor_set_preferred_parent(rpl_nbr_t *nbr)
Set current RPL preferred parent and update DS6 default route accordingly.
Definition: rpl-neighbor.c:296
void rpl_icmp6_dio_output(uip_ipaddr_t *uc_addr)
Creates an ICMPv6 DIO packet and sends it.
Definition: rpl-icmp6.c:332
Header file for the logging system
rpl_nbr_t * rpl_neighbor_select_best(void)
Returns the best candidate for preferred parent.
Definition: rpl-neighbor.c:392
rpl_instance_t * rpl_get_default_instance(void)
Returns pointer to the default instance (for compatibility with legagy RPL code)
Definition: rpl-dag.c:628
void rpl_dag_init(void)
Initializes rpl-dag module.
Definition: rpl-dag.c:143
void rpl_icmp6_dis_output(uip_ipaddr_t *addr)
Creates an ICMPv6 DIS packet and sends it.
Definition: rpl-icmp6.c:150
void rpl_schedule_probing(void)
Schedule probing with delay RPL_PROBING_DELAY_FUNC()