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 process_dio_init_dag(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, links and default route */
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 */
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 /*---------------------------------------------------------------------------*/
239 void
240 rpl_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 /*---------------------------------------------------------------------------*/
254 int
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 */
265 void
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 /*---------------------------------------------------------------------------*/
365 static rpl_nbr_t *
366 update_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 
371  nbr = rpl_neighbor_get_from_ipaddr(from);
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 /*---------------------------------------------------------------------------*/
400 static void
401 process_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 
454  nbr = rpl_neighbor_get_from_ipaddr(from);
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 /*---------------------------------------------------------------------------*/
482 static int
483 init_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 /*---------------------------------------------------------------------------*/
521 static int
522 init_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 /*---------------------------------------------------------------------------*/
554 static int
555 process_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 /*---------------------------------------------------------------------------*/
599 void
600 rpl_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 /*---------------------------------------------------------------------------*/
618 void
619 rpl_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");
627  rpl_icmp6_dio_output(from);
628  }
629  }
630 }
631 /*---------------------------------------------------------------------------*/
632 void
633 rpl_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
652 void
653 rpl_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 /*---------------------------------------------------------------------------*/
679 int
680 rpl_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 /*---------------------------------------------------------------------------*/
712 void
713 rpl_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  }
725  rpl_dag_leave();
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 /*---------------------------------------------------------------------------*/
761 void
763 {
764  memset(&curr_instance, 0, sizeof(curr_instance));
765 }
766 /*---------------------------------------------------------------------------*/
767 /** @} */
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:633
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:208
#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:326
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:349
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:713
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:153
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:563
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:483
void rpl_timers_stop_dag_timers(void)
Stop all timers related to the DAG.
Definition: rpl-timers.c:538
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:619
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:1073
#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:255
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:263
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:266
void rpl_timers_schedule_leaving(void)
Schedule leaving after RPL_DELAY_BEFORE_LEAVING.
Definition: rpl-timers.c:493
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:1470
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
void rpl_timers_unschedule_state_update(void)
Cancelled any scheduled state update.
Definition: rpl-timers.c:555
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:240
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:398
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()