Contiki-NG
rpl-timers.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  * \file
34  * RPL timer management.
35  *
36  * \author Joakim Eriksson <joakime@sics.se>, Nicolas Tsiftes <nvt@sics.se>
37  */
38 
39 /**
40  * \addtogroup uip
41  * @{
42  */
43 
44 #include "contiki.h"
45 #include "net/routing/rpl-classic/rpl-private.h"
46 #include "net/link-stats.h"
48 #include "net/ipv6/uip-sr.h"
49 #include "lib/random.h"
50 #include "sys/ctimer.h"
51 #include "sys/log.h"
52 
53 #define LOG_MODULE "RPL"
54 #define LOG_LEVEL LOG_LEVEL_RPL
55 
56 /* A configurable function called after update of the RPL DIO interval */
57 #ifdef RPL_CALLBACK_NEW_DIO_INTERVAL
58 void RPL_CALLBACK_NEW_DIO_INTERVAL(clock_time_t dio_interval);
59 #endif /* RPL_CALLBACK_NEW_DIO_INTERVAL */
60 
61 #ifdef RPL_PROBING_SELECT_FUNC
62 rpl_parent_t *RPL_PROBING_SELECT_FUNC(rpl_dag_t *dag);
63 #endif /* RPL_PROBING_SELECT_FUNC */
64 
65 #ifdef RPL_PROBING_DELAY_FUNC
66 clock_time_t RPL_PROBING_DELAY_FUNC(rpl_dag_t *dag);
67 #endif /* RPL_PROBING_DELAY_FUNC */
68 
69 /*---------------------------------------------------------------------------*/
70 static struct ctimer periodic_timer;
71 
72 static void handle_periodic_timer(void *ptr);
73 static void new_dio_interval(rpl_instance_t *instance);
74 static void handle_dio_timer(void *ptr);
75 
76 static uint16_t next_dis;
77 
78 /* dio_send_ok is true if the node is ready to send DIOs */
79 static uint8_t dio_send_ok;
80 
81 /*---------------------------------------------------------------------------*/
82 static void
83 handle_periodic_timer(void *ptr)
84 {
85  rpl_dag_t *dag = rpl_get_any_dag();
86 
87  rpl_purge_dags();
88  if(dag != NULL) {
89  if(RPL_IS_STORING(dag->instance)) {
90  rpl_purge_routes();
91  }
92  if(RPL_IS_NON_STORING(dag->instance)) {
93  uip_sr_periodic(1);
94  }
95  }
96  rpl_recalculate_ranks();
97 
98  /* handle DIS */
99 #if RPL_DIS_SEND
100  next_dis++;
101  if((dag == NULL || dag->instance->current_dag->rank == RPL_INFINITE_RANK) && next_dis >= RPL_DIS_INTERVAL) {
102  next_dis = 0;
103  dis_output(NULL);
104  }
105 #endif
106  ctimer_reset(&periodic_timer);
107 }
108 /*---------------------------------------------------------------------------*/
109 static void
110 new_dio_interval(rpl_instance_t *instance)
111 {
112  uint32_t time;
113  clock_time_t ticks;
114 
115  /* TODO: too small timer intervals for many cases */
116  time = 1UL << instance->dio_intcurrent;
117 
118  /* Convert from milliseconds to CLOCK_TICKS. */
119  ticks = (time * CLOCK_SECOND) / 1000;
120  instance->dio_next_delay = ticks;
121 
122  /* random number between I/2 and I */
123  ticks = ticks / 2 + (ticks / 2 * (uint32_t)random_rand()) / RANDOM_RAND_MAX;
124 
125  /*
126  * The intervals must be equally long among the nodes for Trickle to
127  * operate efficiently. Therefore we need to calculate the delay between
128  * the randomized time and the start time of the next interval.
129  */
130  instance->dio_next_delay -= ticks;
131  instance->dio_send = 1;
132 
133 #if RPL_CONF_STATS
134  /* keep some stats */
135  instance->dio_totint++;
136  instance->dio_totrecv += instance->dio_counter;
137  LOG_ANNOTATE("#A rank=%u.%u(%u),stats=%d %d %d %d,color=%s\n",
138  DAG_RANK(instance->current_dag->rank, instance),
139  (10 * (instance->current_dag->rank % instance->min_hoprankinc)) / instance->min_hoprankinc,
140  instance->current_dag->version,
141  instance->dio_totint, instance->dio_totsend,
142  instance->dio_totrecv,instance->dio_intcurrent,
143  instance->current_dag->rank == ROOT_RANK(instance) ? "BLUE" : "ORANGE");
144 #endif /* RPL_CONF_STATS */
145 
146  /* reset the redundancy counter */
147  instance->dio_counter = 0;
148 
149  /* schedule the timer */
150  LOG_INFO("Scheduling DIO timer %lu ticks in future (Interval)\n", ticks);
151  ctimer_set(&instance->dio_timer, ticks, &handle_dio_timer, instance);
152 
153 #ifdef RPL_CALLBACK_NEW_DIO_INTERVAL
154  RPL_CALLBACK_NEW_DIO_INTERVAL((CLOCK_SECOND * 1UL << instance->dio_intcurrent) / 1000);
155 #endif /* RPL_CALLBACK_NEW_DIO_INTERVAL */
156 }
157 /*---------------------------------------------------------------------------*/
158 static void
159 handle_dio_timer(void *ptr)
160 {
161  rpl_instance_t *instance;
162 
163  instance = (rpl_instance_t *)ptr;
164 
165  LOG_DBG("DIO Timer triggered\n");
166  if(!dio_send_ok) {
167  if(uip_ds6_get_link_local(ADDR_PREFERRED) != NULL) {
168  dio_send_ok = 1;
169  } else {
170  LOG_WARN("Postponing DIO transmission since link local address is not ok\n");
171  ctimer_set(&instance->dio_timer, CLOCK_SECOND, &handle_dio_timer, instance);
172  return;
173  }
174  }
175 
176  if(instance->dio_send) {
177  /* send DIO if counter is less than desired redundancy */
178  if(instance->dio_redundancy == 0 || instance->dio_counter < instance->dio_redundancy) {
179 #if RPL_CONF_STATS
180  instance->dio_totsend++;
181 #endif /* RPL_CONF_STATS */
182  dio_output(instance, NULL);
183  } else {
184  LOG_DBG("Suppressing DIO transmission (%d >= %d)\n",
185  instance->dio_counter, instance->dio_redundancy);
186  }
187  instance->dio_send = 0;
188  LOG_DBG("Scheduling DIO timer %lu ticks in future (sent)\n",
189  instance->dio_next_delay);
190  ctimer_set(&instance->dio_timer, instance->dio_next_delay, handle_dio_timer, instance);
191  } else {
192  /* check if we need to double interval */
193  if(instance->dio_intcurrent < instance->dio_intmin + instance->dio_intdoubl) {
194  instance->dio_intcurrent++;
195  LOG_DBG("DIO Timer interval doubled %d\n", instance->dio_intcurrent);
196  }
197  new_dio_interval(instance);
198  }
199 
200  if(LOG_DBG_ENABLED) {
201  rpl_print_neighbor_list();
202  }
203 }
204 /*---------------------------------------------------------------------------*/
205 void
206 rpl_reset_periodic_timer(void)
207 {
208  next_dis = RPL_DIS_INTERVAL / 2 +
209  ((uint32_t)RPL_DIS_INTERVAL * (uint32_t)random_rand()) / RANDOM_RAND_MAX -
210  RPL_DIS_START_DELAY;
211  ctimer_set(&periodic_timer, CLOCK_SECOND, handle_periodic_timer, NULL);
212 }
213 /*---------------------------------------------------------------------------*/
214 /* Resets the DIO timer in the instance to its minimal interval. */
215 void
216 rpl_reset_dio_timer(rpl_instance_t *instance)
217 {
218 #if !RPL_LEAF_ONLY
219  /* Do not reset if we are already on the minimum interval,
220  unless forced to do so. */
221  if(instance->dio_intcurrent > instance->dio_intmin) {
222  instance->dio_counter = 0;
223  instance->dio_intcurrent = instance->dio_intmin;
224  new_dio_interval(instance);
225  }
226 #if RPL_CONF_STATS
227  rpl_stats.resets++;
228 #endif /* RPL_CONF_STATS */
229 #endif /* RPL_LEAF_ONLY */
230 }
231 /*---------------------------------------------------------------------------*/
232 static void handle_dao_timer(void *ptr);
233 static void
234 set_dao_lifetime_timer(rpl_instance_t *instance)
235 {
236  if(rpl_get_mode() == RPL_MODE_FEATHER) {
237  return;
238  }
239 
240  /* Set up another DAO within half the expiration time, if such a
241  time has been configured */
242  if(instance->default_lifetime != RPL_INFINITE_LIFETIME) {
243  clock_time_t expiration_time;
244 
245  /*
246  * If the lifetime is 0, we simply set the expiration time to
247  * half a second to get an interval that corresponds to a
248  * lifetime of 1 and a lifetime unit of 1.
249  */
250  if(instance->default_lifetime == 0 || instance->lifetime_unit == 0) {
251  expiration_time = CLOCK_SECOND / 2;
252  } else {
253  expiration_time = (clock_time_t)instance->default_lifetime *
254  (clock_time_t)instance->lifetime_unit * CLOCK_SECOND / 2;
255  }
256 
257  /* make the time for the re registration be betwen 1/2 - 3/4 of lifetime */
258  expiration_time = expiration_time + (random_rand() % (expiration_time / 2));
259  LOG_DBG("Scheduling DAO lifetime timer %u ticks in the future\n",
260  (unsigned)expiration_time);
261  ctimer_set(&instance->dao_lifetime_timer, expiration_time,
262  handle_dao_timer, instance);
263  }
264 }
265 /*---------------------------------------------------------------------------*/
266 static void
267 handle_dao_timer(void *ptr)
268 {
269  rpl_instance_t *instance;
270 #if RPL_WITH_MULTICAST
271  uip_mcast6_route_t *mcast_route;
272  uint8_t i;
273 #endif
274 
275  instance = (rpl_instance_t *)ptr;
276 
277  if(!dio_send_ok && uip_ds6_get_link_local(ADDR_PREFERRED) == NULL) {
278  LOG_INFO("Postpone DAO transmission\n");
279  ctimer_set(&instance->dao_timer, CLOCK_SECOND, handle_dao_timer, instance);
280  return;
281  }
282 
283  /* Send the DAO to the DAO parent set -- the preferred parent in our case. */
284  if(instance->current_dag->preferred_parent != NULL) {
285  LOG_INFO("handle_dao_timer - sending DAO\n");
286  /* Set the route lifetime to the default value. */
287  dao_output(instance->current_dag->preferred_parent, instance->default_lifetime);
288 
289 #if RPL_WITH_MULTICAST
290  /* Send DAOs for multicast prefixes only if the instance is in MOP 3 */
291  if(instance->mop == RPL_MOP_STORING_MULTICAST) {
292  /* Send a DAO for own multicast addresses */
293  for(i = 0; i < UIP_DS6_MADDR_NB; i++) {
294  if(uip_ds6_if.maddr_list[i].isused
295  && uip_is_addr_mcast_global(&uip_ds6_if.maddr_list[i].ipaddr)) {
296  dao_output_target(instance->current_dag->preferred_parent,
297  &uip_ds6_if.maddr_list[i].ipaddr, instance->default_lifetime);
298  }
299  }
300 
301  /* Iterate over multicast routes and send DAOs */
302  mcast_route = uip_mcast6_route_list_head();
303  while(mcast_route != NULL) {
304  /* Don't send if it's also our own address, done that already */
305  if(uip_ds6_maddr_lookup(&mcast_route->group) == NULL) {
306  dao_output_target(instance->current_dag->preferred_parent,
307  &mcast_route->group, instance->default_lifetime);
308  }
309  mcast_route = list_item_next(mcast_route);
310  }
311  }
312 #endif
313  } else {
314  LOG_INFO("No suitable DAO parent\n");
315  }
316 
317  ctimer_stop(&instance->dao_timer);
318 
319  if(etimer_expired(&instance->dao_lifetime_timer.etimer)) {
320  set_dao_lifetime_timer(instance);
321  }
322 }
323 /*---------------------------------------------------------------------------*/
324 static void
325 schedule_dao(rpl_instance_t *instance, clock_time_t latency)
326 {
327  clock_time_t expiration_time;
328 
329  if(rpl_get_mode() == RPL_MODE_FEATHER) {
330  return;
331  }
332 
333  expiration_time = etimer_expiration_time(&instance->dao_timer.etimer);
334 
335  if(!etimer_expired(&instance->dao_timer.etimer)) {
336  LOG_DBG("DAO timer already scheduled\n");
337  } else {
338  if(latency != 0) {
339  expiration_time = latency / 2 +
340  (random_rand() % (latency));
341  } else {
342  expiration_time = 0;
343  }
344  LOG_DBG("Scheduling DAO timer %u ticks in the future\n",
345  (unsigned)expiration_time);
346  ctimer_set(&instance->dao_timer, expiration_time,
347  handle_dao_timer, instance);
348 
349  set_dao_lifetime_timer(instance);
350  }
351 }
352 /*---------------------------------------------------------------------------*/
353 void
354 rpl_schedule_dao(rpl_instance_t *instance)
355 {
356  schedule_dao(instance, RPL_DAO_DELAY);
357 }
358 /*---------------------------------------------------------------------------*/
359 void
360 rpl_schedule_dao_immediately(rpl_instance_t *instance)
361 {
362  schedule_dao(instance, 0);
363 }
364 /*---------------------------------------------------------------------------*/
365 void
366 rpl_cancel_dao(rpl_instance_t *instance)
367 {
368  ctimer_stop(&instance->dao_timer);
369  ctimer_stop(&instance->dao_lifetime_timer);
370 }
371 /*---------------------------------------------------------------------------*/
372 static void
373 handle_unicast_dio_timer(void *ptr)
374 {
375  rpl_instance_t *instance = (rpl_instance_t *)ptr;
376  uip_ipaddr_t *target_ipaddr = rpl_parent_get_ipaddr(instance->unicast_dio_target);
377 
378  if(target_ipaddr != NULL) {
379  dio_output(instance, target_ipaddr);
380  }
381 }
382 /*---------------------------------------------------------------------------*/
383 void
384 rpl_schedule_unicast_dio_immediately(rpl_instance_t *instance)
385 {
386  ctimer_set(&instance->unicast_dio_timer, 0,
387  handle_unicast_dio_timer, instance);
388 }
389 /*---------------------------------------------------------------------------*/
390 #if RPL_WITH_PROBING
391 clock_time_t
392 get_probing_delay(rpl_dag_t *dag)
393 {
394  return ((RPL_PROBING_INTERVAL) / 2) + random_rand() % (RPL_PROBING_INTERVAL);
395 }
396 /*---------------------------------------------------------------------------*/
397 rpl_parent_t *
398 get_probing_target(rpl_dag_t *dag)
399 {
400  /* Returns the next probing target. The current implementation probes the urgent
401  * probing target if any, or the preferred parent if its link statistics need refresh.
402  * Otherwise, it picks at random between:
403  * (1) selecting the best parent with non-fresh link statistics
404  * (2) selecting the least recently updated parent
405  */
406 
407  rpl_parent_t *p;
408  rpl_parent_t *probing_target = NULL;
409  rpl_rank_t probing_target_rank = RPL_INFINITE_RANK;
410  clock_time_t probing_target_age = 0;
411  clock_time_t clock_now = clock_time();
412 
413  if(dag == NULL ||
414  dag->instance == NULL) {
415  return NULL;
416  }
417 
418  /* There is an urgent probing target */
419  if(dag->instance->urgent_probing_target != NULL) {
420  return dag->instance->urgent_probing_target;
421  }
422 
423  /* The preferred parent needs probing */
424  if(dag->preferred_parent != NULL && !rpl_parent_is_fresh(dag->preferred_parent)) {
425  return dag->preferred_parent;
426  }
427 
428  /* With 50% probability: probe best non-fresh parent */
429  if(random_rand() % 2 == 0) {
430  p = nbr_table_head(rpl_parents);
431  while(p != NULL) {
432  if(p->dag == dag && !rpl_parent_is_fresh(p)) {
433  /* p is in our dag and needs probing */
434  rpl_rank_t p_rank = rpl_rank_via_parent(p);
435  if(probing_target == NULL
436  || p_rank < probing_target_rank) {
437  probing_target = p;
438  probing_target_rank = p_rank;
439  }
440  }
441  p = nbr_table_next(rpl_parents, p);
442  }
443  }
444 
445  /* If we still do not have a probing target: pick the least recently updated parent */
446  if(probing_target == NULL) {
447  p = nbr_table_head(rpl_parents);
448  while(p != NULL) {
449  const struct link_stats *stats =rpl_get_parent_link_stats(p);
450  if(p->dag == dag && stats != NULL) {
451  if(probing_target == NULL
452  || clock_now - stats->last_tx_time > probing_target_age) {
453  probing_target = p;
454  probing_target_age = clock_now - stats->last_tx_time;
455  }
456  }
457  p = nbr_table_next(rpl_parents, p);
458  }
459  }
460 
461  return probing_target;
462 }
463 /*---------------------------------------------------------------------------*/
464 static rpl_dag_t *
465 get_next_dag(rpl_instance_t *instance)
466 {
467  rpl_dag_t *dag = NULL;
468  int new_dag = instance->last_dag;
469  do {
470  new_dag++;
471  if(new_dag >= RPL_MAX_DAG_PER_INSTANCE) {
472  new_dag = 0;
473  }
474  if(instance->dag_table[new_dag].used) {
475  dag = &instance->dag_table[new_dag];
476  }
477  } while(new_dag != instance->last_dag && dag == NULL);
478  instance->last_dag = new_dag;
479  return dag;
480 }
481 /*---------------------------------------------------------------------------*/
482 static void
483 handle_probing_timer(void *ptr)
484 {
485  rpl_instance_t *instance = (rpl_instance_t *)ptr;
486  rpl_parent_t *probing_target = RPL_PROBING_SELECT_FUNC(get_next_dag(instance));
487  uip_ipaddr_t *target_ipaddr = rpl_parent_get_ipaddr(probing_target);
488 
489  /* Perform probing */
490  if(target_ipaddr != NULL) {
491  const struct link_stats *stats = rpl_get_parent_link_stats(probing_target);
492  const linkaddr_t *lladdr = rpl_get_parent_lladdr(probing_target);
493  LOG_INFO("probing %u %s last tx %u min ago\n",
494  lladdr != NULL ? lladdr->u8[7] : 0x0,
495  instance->urgent_probing_target != NULL ? "(urgent)" : "",
496  probing_target != NULL && stats != NULL ?
497  (unsigned)((clock_time() - stats->last_tx_time) / (60 * CLOCK_SECOND)) : 0
498  );
499 
500  /* Send probe, e.g. unicast DIO or DIS */
501  RPL_PROBING_SEND_FUNC(instance, target_ipaddr);
502  }
503 
504  /* Schedule next probing */
505  rpl_schedule_probing(instance);
506 
507  if(LOG_DBG_ENABLED) {
508  rpl_print_neighbor_list();
509  }
510 }
511 /*---------------------------------------------------------------------------*/
512 void
514 {
515  ctimer_set(&instance->probing_timer, RPL_PROBING_DELAY_FUNC(instance->current_dag),
516  handle_probing_timer, instance);
517 }
518 /*---------------------------------------------------------------------------*/
519 void
521 {
522  ctimer_set(&instance->probing_timer, random_rand() % (CLOCK_SECOND * 4),
523  handle_probing_timer, instance);
524 }
525 #endif /* RPL_WITH_PROBING */
526 /** @}*/
void ctimer_stop(struct ctimer *c)
Stop a pending callback timer.
Definition: ctimer.c:149
RPL DAG structure.
Definition: rpl.h:135
RPL instance structure.
Definition: rpl.h:219
#define ROOT_RANK
Rank of a root node.
Definition: rpl-types.h:78
#define uip_is_addr_mcast_global(a)
is address a global multicast address (FFxE::/16), a is of type uip_ip6addr_t*
Definition: uip.h:1892
void rpl_schedule_probing_now(void)
Schedule probing within a few seconds.
enum rpl_mode rpl_get_mode(void)
Get the RPL mode.
Definition: rpl.c:70
void ctimer_reset(struct ctimer *c)
Reset a callback timer with the same interval as was previously set.
Definition: ctimer.c:125
Source routing support.
This header file contains configuration directives for uIPv6 multicast support.
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82
#define DAG_RANK(fixpt_rank)
Return DAG RANK as per RFC 6550 (rank divided by min_hoprankinc)
Definition: rpl-types.h:81
Header file for the callback timer
void uip_sr_periodic(unsigned seconds)
A function called periodically.
Definition: uip-sr.c:211
void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
Definition: ctimer.c:99
rpl_dag_t * rpl_get_any_dag(void)
Returns pointer to any DAG (for compatibility with legagy RPL code)
Definition: rpl-dag.c:1069
clock_time_t clock_time(void)
Get the current clock time.
Definition: clock.c:118
int etimer_expired(struct etimer *et)
Check if an event timer has expired.
Definition: etimer.c:213
An entry in the multicast routing table.
uip_ds6_netif_t uip_ds6_if
The single interface.
Definition: uip-ds6.c:75
void * list_item_next(const void *item)
Get the next item following this item.
Definition: list.c:322
unsigned short random_rand(void)
Generates a new random number using the cc2538 RNG.
Definition: random.c:58
Header file for the logging system
uip_ipaddr_t group
The multicast group.
uip_mcast6_route_t * uip_mcast6_route_list_head(void)
Retrieve a pointer to the start of the multicast routes list.
void rpl_schedule_probing(void)
Schedule probing with delay RPL_PROBING_DELAY_FUNC()
clock_time_t etimer_expiration_time(struct etimer *et)
Get the expiration time for the event timer.
Definition: etimer.c:219