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  expiration_time = (clock_time_t)instance->default_lifetime *
245  (clock_time_t)instance->lifetime_unit *
246  CLOCK_SECOND / 2;
247  /* make the time for the re registration be betwen 1/2 - 3/4 of lifetime */
248  expiration_time = expiration_time + (random_rand() % (expiration_time / 2));
249  LOG_DBG("Scheduling DAO lifetime timer %u ticks in the future\n",
250  (unsigned)expiration_time);
251  ctimer_set(&instance->dao_lifetime_timer, expiration_time,
252  handle_dao_timer, instance);
253  }
254 }
255 /*---------------------------------------------------------------------------*/
256 static void
257 handle_dao_timer(void *ptr)
258 {
259  rpl_instance_t *instance;
260 #if RPL_WITH_MULTICAST
261  uip_mcast6_route_t *mcast_route;
262  uint8_t i;
263 #endif
264 
265  instance = (rpl_instance_t *)ptr;
266 
267  if(!dio_send_ok && uip_ds6_get_link_local(ADDR_PREFERRED) == NULL) {
268  LOG_INFO("Postpone DAO transmission\n");
269  ctimer_set(&instance->dao_timer, CLOCK_SECOND, handle_dao_timer, instance);
270  return;
271  }
272 
273  /* Send the DAO to the DAO parent set -- the preferred parent in our case. */
274  if(instance->current_dag->preferred_parent != NULL) {
275  LOG_INFO("handle_dao_timer - sending DAO\n");
276  /* Set the route lifetime to the default value. */
277  dao_output(instance->current_dag->preferred_parent, instance->default_lifetime);
278 
279 #if RPL_WITH_MULTICAST
280  /* Send DAOs for multicast prefixes only if the instance is in MOP 3 */
281  if(instance->mop == RPL_MOP_STORING_MULTICAST) {
282  /* Send a DAO for own multicast addresses */
283  for(i = 0; i < UIP_DS6_MADDR_NB; i++) {
284  if(uip_ds6_if.maddr_list[i].isused
285  && uip_is_addr_mcast_global(&uip_ds6_if.maddr_list[i].ipaddr)) {
286  dao_output_target(instance->current_dag->preferred_parent,
287  &uip_ds6_if.maddr_list[i].ipaddr, instance->default_lifetime);
288  }
289  }
290 
291  /* Iterate over multicast routes and send DAOs */
292  mcast_route = uip_mcast6_route_list_head();
293  while(mcast_route != NULL) {
294  /* Don't send if it's also our own address, done that already */
295  if(uip_ds6_maddr_lookup(&mcast_route->group) == NULL) {
296  dao_output_target(instance->current_dag->preferred_parent,
297  &mcast_route->group, instance->default_lifetime);
298  }
299  mcast_route = list_item_next(mcast_route);
300  }
301  }
302 #endif
303  } else {
304  LOG_INFO("No suitable DAO parent\n");
305  }
306 
307  ctimer_stop(&instance->dao_timer);
308 
309  if(etimer_expired(&instance->dao_lifetime_timer.etimer)) {
310  set_dao_lifetime_timer(instance);
311  }
312 }
313 /*---------------------------------------------------------------------------*/
314 static void
315 schedule_dao(rpl_instance_t *instance, clock_time_t latency)
316 {
317  clock_time_t expiration_time;
318 
319  if(rpl_get_mode() == RPL_MODE_FEATHER) {
320  return;
321  }
322 
323  expiration_time = etimer_expiration_time(&instance->dao_timer.etimer);
324 
325  if(!etimer_expired(&instance->dao_timer.etimer)) {
326  LOG_DBG("DAO timer already scheduled\n");
327  } else {
328  if(latency != 0) {
329  expiration_time = latency / 2 +
330  (random_rand() % (latency));
331  } else {
332  expiration_time = 0;
333  }
334  LOG_DBG("Scheduling DAO timer %u ticks in the future\n",
335  (unsigned)expiration_time);
336  ctimer_set(&instance->dao_timer, expiration_time,
337  handle_dao_timer, instance);
338 
339  set_dao_lifetime_timer(instance);
340  }
341 }
342 /*---------------------------------------------------------------------------*/
343 void
344 rpl_schedule_dao(rpl_instance_t *instance)
345 {
346  schedule_dao(instance, RPL_DAO_DELAY);
347 }
348 /*---------------------------------------------------------------------------*/
349 void
350 rpl_schedule_dao_immediately(rpl_instance_t *instance)
351 {
352  schedule_dao(instance, 0);
353 }
354 /*---------------------------------------------------------------------------*/
355 void
356 rpl_cancel_dao(rpl_instance_t *instance)
357 {
358  ctimer_stop(&instance->dao_timer);
359  ctimer_stop(&instance->dao_lifetime_timer);
360 }
361 /*---------------------------------------------------------------------------*/
362 static void
363 handle_unicast_dio_timer(void *ptr)
364 {
365  rpl_instance_t *instance = (rpl_instance_t *)ptr;
366  uip_ipaddr_t *target_ipaddr = rpl_parent_get_ipaddr(instance->unicast_dio_target);
367 
368  if(target_ipaddr != NULL) {
369  dio_output(instance, target_ipaddr);
370  }
371 }
372 /*---------------------------------------------------------------------------*/
373 void
374 rpl_schedule_unicast_dio_immediately(rpl_instance_t *instance)
375 {
376  ctimer_set(&instance->unicast_dio_timer, 0,
377  handle_unicast_dio_timer, instance);
378 }
379 /*---------------------------------------------------------------------------*/
380 #if RPL_WITH_PROBING
381 clock_time_t
382 get_probing_delay(rpl_dag_t *dag)
383 {
384  return ((RPL_PROBING_INTERVAL) / 2) + random_rand() % (RPL_PROBING_INTERVAL);
385 }
386 /*---------------------------------------------------------------------------*/
387 rpl_parent_t *
388 get_probing_target(rpl_dag_t *dag)
389 {
390  /* Returns the next probing target. The current implementation probes the urgent
391  * probing target if any, or the preferred parent if its link statistics need refresh.
392  * Otherwise, it picks at random between:
393  * (1) selecting the best parent with non-fresh link statistics
394  * (2) selecting the least recently updated parent
395  */
396 
397  rpl_parent_t *p;
398  rpl_parent_t *probing_target = NULL;
399  rpl_rank_t probing_target_rank = RPL_INFINITE_RANK;
400  clock_time_t probing_target_age = 0;
401  clock_time_t clock_now = clock_time();
402 
403  if(dag == NULL ||
404  dag->instance == NULL) {
405  return NULL;
406  }
407 
408  /* There is an urgent probing target */
409  if(dag->instance->urgent_probing_target != NULL) {
410  return dag->instance->urgent_probing_target;
411  }
412 
413  /* The preferred parent needs probing */
414  if(dag->preferred_parent != NULL && !rpl_parent_is_fresh(dag->preferred_parent)) {
415  return dag->preferred_parent;
416  }
417 
418  /* With 50% probability: probe best non-fresh parent */
419  if(random_rand() % 2 == 0) {
420  p = nbr_table_head(rpl_parents);
421  while(p != NULL) {
422  if(p->dag == dag && !rpl_parent_is_fresh(p)) {
423  /* p is in our dag and needs probing */
424  rpl_rank_t p_rank = rpl_rank_via_parent(p);
425  if(probing_target == NULL
426  || p_rank < probing_target_rank) {
427  probing_target = p;
428  probing_target_rank = p_rank;
429  }
430  }
431  p = nbr_table_next(rpl_parents, p);
432  }
433  }
434 
435  /* If we still do not have a probing target: pick the least recently updated parent */
436  if(probing_target == NULL) {
437  p = nbr_table_head(rpl_parents);
438  while(p != NULL) {
439  const struct link_stats *stats =rpl_get_parent_link_stats(p);
440  if(p->dag == dag && stats != NULL) {
441  if(probing_target == NULL
442  || clock_now - stats->last_tx_time > probing_target_age) {
443  probing_target = p;
444  probing_target_age = clock_now - stats->last_tx_time;
445  }
446  }
447  p = nbr_table_next(rpl_parents, p);
448  }
449  }
450 
451  return probing_target;
452 }
453 /*---------------------------------------------------------------------------*/
454 static rpl_dag_t *
455 get_next_dag(rpl_instance_t *instance)
456 {
457  rpl_dag_t *dag = NULL;
458  int new_dag = instance->last_dag;
459  do {
460  new_dag++;
461  if(new_dag >= RPL_MAX_DAG_PER_INSTANCE) {
462  new_dag = 0;
463  }
464  if(instance->dag_table[new_dag].used) {
465  dag = &instance->dag_table[new_dag];
466  }
467  } while(new_dag != instance->last_dag && dag == NULL);
468  instance->last_dag = new_dag;
469  return dag;
470 }
471 /*---------------------------------------------------------------------------*/
472 static void
473 handle_probing_timer(void *ptr)
474 {
475  rpl_instance_t *instance = (rpl_instance_t *)ptr;
476  rpl_parent_t *probing_target = RPL_PROBING_SELECT_FUNC(get_next_dag(instance));
477  uip_ipaddr_t *target_ipaddr = rpl_parent_get_ipaddr(probing_target);
478 
479  /* Perform probing */
480  if(target_ipaddr != NULL) {
481  const struct link_stats *stats = rpl_get_parent_link_stats(probing_target);
482  const linkaddr_t *lladdr = rpl_get_parent_lladdr(probing_target);
483  LOG_INFO("probing %u %s last tx %u min ago\n",
484  lladdr != NULL ? lladdr->u8[7] : 0x0,
485  instance->urgent_probing_target != NULL ? "(urgent)" : "",
486  probing_target != NULL && stats != NULL ?
487  (unsigned)((clock_time() - stats->last_tx_time) / (60 * CLOCK_SECOND)) : 0
488  );
489 
490  /* Send probe, e.g. unicast DIO or DIS */
491  RPL_PROBING_SEND_FUNC(instance, target_ipaddr);
492  }
493 
494  /* Schedule next probing */
495  rpl_schedule_probing(instance);
496 
497  if(LOG_DBG_ENABLED) {
498  rpl_print_neighbor_list();
499  }
500 }
501 /*---------------------------------------------------------------------------*/
502 void
504 {
505  ctimer_set(&instance->probing_timer, RPL_PROBING_DELAY_FUNC(instance->current_dag),
506  handle_probing_timer, instance);
507 }
508 /*---------------------------------------------------------------------------*/
509 void
511 {
512  ctimer_set(&instance->probing_timer, random_rand() % (CLOCK_SECOND * 4),
513  handle_probing_timer, instance);
514 }
515 #endif /* RPL_WITH_PROBING */
516 /** @}*/
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:1968
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:206
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:1073
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
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.
void * list_item_next(void *item)
Get the next item following this item.
Definition: list.c:322
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