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