Contiki-Inga 3.x
rpl-timers.c
Go to the documentation of this file.
1 /**
2  * \addtogroup uip6
3  * @{
4  */
5 /*
6  * Copyright (c) 2010, Swedish Institute of Computer Science.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in the
16  * documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the Institute nor the names of its contributors
18  * may be used to endorse or promote products derived from this software
19  * without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * This file is part of the Contiki operating system.
34  */
35 /**
36  * \file
37  * RPL timer management.
38  *
39  * \author Joakim Eriksson <joakime@sics.se>, Nicolas Tsiftes <nvt@sics.se>
40  */
41 
42 #include "contiki-conf.h"
43 #include "net/rpl/rpl-private.h"
45 #include "lib/random.h"
46 #include "sys/ctimer.h"
47 
48 #if UIP_CONF_IPV6
49 
50 #define DEBUG DEBUG_NONE
51 #include "net/ip/uip-debug.h"
52 
53 /*---------------------------------------------------------------------------*/
54 static struct ctimer periodic_timer;
55 
56 static void handle_periodic_timer(void *ptr);
57 static void new_dio_interval(rpl_instance_t *instance);
58 static void handle_dio_timer(void *ptr);
59 
60 static uint16_t next_dis;
61 
62 /* dio_send_ok is true if the node is ready to send DIOs */
63 static uint8_t dio_send_ok;
64 
65 /*---------------------------------------------------------------------------*/
66 static void
67 handle_periodic_timer(void *ptr)
68 {
69  rpl_purge_routes();
70  rpl_recalculate_ranks();
71 
72  /* handle DIS */
73 #if RPL_DIS_SEND
74  next_dis++;
75  if(rpl_get_any_dag() == NULL && next_dis >= RPL_DIS_INTERVAL) {
76  next_dis = 0;
77  dis_output(NULL);
78  }
79 #endif
80  ctimer_reset(&periodic_timer);
81 }
82 /*---------------------------------------------------------------------------*/
83 static void
84 new_dio_interval(rpl_instance_t *instance)
85 {
86  uint32_t time;
87  clock_time_t ticks;
88 
89  /* TODO: too small timer intervals for many cases */
90  time = 1UL << instance->dio_intcurrent;
91 
92  /* Convert from milliseconds to CLOCK_TICKS. */
93  ticks = (time * CLOCK_SECOND) / 1000;
94  instance->dio_next_delay = ticks;
95 
96  /* random number between I/2 and I */
97  ticks = ticks / 2 + (ticks / 2 * (uint32_t)random_rand()) / RANDOM_RAND_MAX;
98 
99  /*
100  * The intervals must be equally long among the nodes for Trickle to
101  * operate efficiently. Therefore we need to calculate the delay between
102  * the randomized time and the start time of the next interval.
103  */
104  instance->dio_next_delay -= ticks;
105  instance->dio_send = 1;
106 
107 #if RPL_CONF_STATS
108  /* keep some stats */
109  instance->dio_totint++;
110  instance->dio_totrecv += instance->dio_counter;
111  ANNOTATE("#A rank=%u.%u(%u),stats=%d %d %d %d,color=%s\n",
112  DAG_RANK(instance->current_dag->rank, instance),
113  (10 * (instance->current_dag->rank % instance->min_hoprankinc)) / instance->min_hoprankinc,
114  instance->current_dag->version,
115  instance->dio_totint, instance->dio_totsend,
116  instance->dio_totrecv,instance->dio_intcurrent,
117  instance->current_dag->rank == ROOT_RANK(instance) ? "BLUE" : "ORANGE");
118 #endif /* RPL_CONF_STATS */
119 
120  /* reset the redundancy counter */
121  instance->dio_counter = 0;
122 
123  /* schedule the timer */
124  PRINTF("RPL: Scheduling DIO timer %lu ticks in future (Interval)\n", ticks);
125  ctimer_set(&instance->dio_timer, ticks, &handle_dio_timer, instance);
126 }
127 /*---------------------------------------------------------------------------*/
128 static void
129 handle_dio_timer(void *ptr)
130 {
131  rpl_instance_t *instance;
132 
133  instance = (rpl_instance_t *)ptr;
134 
135  PRINTF("RPL: DIO Timer triggered\n");
136  if(!dio_send_ok) {
137  if(uip_ds6_get_link_local(ADDR_PREFERRED) != NULL) {
138  dio_send_ok = 1;
139  } else {
140  PRINTF("RPL: Postponing DIO transmission since link local address is not ok\n");
141  ctimer_set(&instance->dio_timer, CLOCK_SECOND, &handle_dio_timer, instance);
142  return;
143  }
144  }
145 
146  if(instance->dio_send) {
147  /* send DIO if counter is less than desired redundancy */
148  if(instance->dio_counter < instance->dio_redundancy) {
149 #if RPL_CONF_STATS
150  instance->dio_totsend++;
151 #endif /* RPL_CONF_STATS */
152  dio_output(instance, NULL);
153  } else {
154  PRINTF("RPL: Supressing DIO transmission (%d >= %d)\n",
155  instance->dio_counter, instance->dio_redundancy);
156  }
157  instance->dio_send = 0;
158  PRINTF("RPL: Scheduling DIO timer %lu ticks in future (sent)\n",
159  instance->dio_next_delay);
160  ctimer_set(&instance->dio_timer, instance->dio_next_delay, handle_dio_timer, instance);
161  } else {
162  /* check if we need to double interval */
163  if(instance->dio_intcurrent < instance->dio_intmin + instance->dio_intdoubl) {
164  instance->dio_intcurrent++;
165  PRINTF("RPL: DIO Timer interval doubled %d\n", instance->dio_intcurrent);
166  }
167  new_dio_interval(instance);
168  }
169 }
170 /*---------------------------------------------------------------------------*/
171 void
172 rpl_reset_periodic_timer(void)
173 {
174  next_dis = RPL_DIS_INTERVAL / 2 +
175  ((uint32_t)RPL_DIS_INTERVAL * (uint32_t)random_rand()) / RANDOM_RAND_MAX -
176  RPL_DIS_START_DELAY;
177  ctimer_set(&periodic_timer, CLOCK_SECOND, handle_periodic_timer, NULL);
178 }
179 /*---------------------------------------------------------------------------*/
180 /* Resets the DIO timer in the instance to its minimal interval. */
181 void
182 rpl_reset_dio_timer(rpl_instance_t *instance)
183 {
184 #if !RPL_LEAF_ONLY
185  /* Do not reset if we are already on the minimum interval,
186  unless forced to do so. */
187  if(instance->dio_intcurrent > instance->dio_intmin) {
188  instance->dio_counter = 0;
189  instance->dio_intcurrent = instance->dio_intmin;
190  new_dio_interval(instance);
191  }
192 #if RPL_CONF_STATS
193  rpl_stats.resets++;
194 #endif /* RPL_CONF_STATS */
195 #endif /* RPL_LEAF_ONLY */
196 }
197 /*---------------------------------------------------------------------------*/
198 static void handle_dao_timer(void *ptr);
199 static void
200 set_dao_lifetime_timer(rpl_instance_t *instance)
201 {
202  if(rpl_get_mode() == RPL_MODE_FEATHER) {
203  return;
204  }
205 
206  /* Set up another DAO within half the expiration time, if such a
207  time has been configured */
208  if(instance->lifetime_unit != 0xffff && instance->default_lifetime != 0xff) {
209  clock_time_t expiration_time;
210  expiration_time = (clock_time_t)instance->default_lifetime *
211  (clock_time_t)instance->lifetime_unit *
212  CLOCK_SECOND / 2;
213  PRINTF("RPL: Scheduling DAO lifetime timer %u ticks in the future\n",
214  (unsigned)expiration_time);
215  ctimer_set(&instance->dao_lifetime_timer, expiration_time,
216  handle_dao_timer, instance);
217  }
218 }
219 /*---------------------------------------------------------------------------*/
220 static void
221 handle_dao_timer(void *ptr)
222 {
223  rpl_instance_t *instance;
224 #if RPL_CONF_MULTICAST
225  uip_mcast6_route_t *mcast_route;
226  uint8_t i;
227 #endif
228 
229  instance = (rpl_instance_t *)ptr;
230 
231  if(!dio_send_ok && uip_ds6_get_link_local(ADDR_PREFERRED) == NULL) {
232  PRINTF("RPL: Postpone DAO transmission\n");
233  ctimer_set(&instance->dao_timer, CLOCK_SECOND, handle_dao_timer, instance);
234  return;
235  }
236 
237  /* Send the DAO to the DAO parent set -- the preferred parent in our case. */
238  if(instance->current_dag->preferred_parent != NULL) {
239  PRINTF("RPL: handle_dao_timer - sending DAO\n");
240  /* Set the route lifetime to the default value. */
241  dao_output(instance->current_dag->preferred_parent, instance->default_lifetime);
242 
243 #if RPL_CONF_MULTICAST
244  /* Send DAOs for multicast prefixes only if the instance is in MOP 3 */
245  if(instance->mop == RPL_MOP_STORING_MULTICAST) {
246  /* Send a DAO for own multicast addresses */
247  for(i = 0; i < UIP_DS6_MADDR_NB; i++) {
248  if(uip_ds6_if.maddr_list[i].isused
249  && uip_is_addr_mcast_global(&uip_ds6_if.maddr_list[i].ipaddr)) {
250  dao_output_target(instance->current_dag->preferred_parent,
251  &uip_ds6_if.maddr_list[i].ipaddr, RPL_MCAST_LIFETIME);
252  }
253  }
254 
255  /* Iterate over multicast routes and send DAOs */
256  mcast_route = uip_mcast6_route_list_head();
257  while(mcast_route != NULL) {
258  /* Don't send if it's also our own address, done that already */
259  if(uip_ds6_maddr_lookup(&mcast_route->group) == NULL) {
260  dao_output_target(instance->current_dag->preferred_parent,
261  &mcast_route->group, RPL_MCAST_LIFETIME);
262  }
263  mcast_route = list_item_next(mcast_route);
264  }
265  }
266 #endif
267  } else {
268  PRINTF("RPL: No suitable DAO parent\n");
269  }
270 
271  ctimer_stop(&instance->dao_timer);
272 
273  if(etimer_expired(&instance->dao_lifetime_timer.etimer)) {
274  set_dao_lifetime_timer(instance);
275  }
276 }
277 /*---------------------------------------------------------------------------*/
278 static void
279 schedule_dao(rpl_instance_t *instance, clock_time_t latency)
280 {
281  clock_time_t expiration_time;
282 
283  if(rpl_get_mode() == RPL_MODE_FEATHER) {
284  return;
285  }
286 
287  expiration_time = etimer_expiration_time(&instance->dao_timer.etimer);
288 
289  if(!etimer_expired(&instance->dao_timer.etimer)) {
290  PRINTF("RPL: DAO timer already scheduled\n");
291  } else {
292  if(latency != 0) {
293  expiration_time = latency / 2 +
294  (random_rand() % (latency));
295  } else {
296  expiration_time = 0;
297  }
298  PRINTF("RPL: Scheduling DAO timer %u ticks in the future\n",
299  (unsigned)expiration_time);
300  ctimer_set(&instance->dao_timer, expiration_time,
301  handle_dao_timer, instance);
302 
303  set_dao_lifetime_timer(instance);
304  }
305 }
306 /*---------------------------------------------------------------------------*/
307 void
308 rpl_schedule_dao(rpl_instance_t *instance)
309 {
310  schedule_dao(instance, RPL_DAO_LATENCY);
311 }
312 /*---------------------------------------------------------------------------*/
313 void
314 rpl_schedule_dao_immediately(rpl_instance_t *instance)
315 {
316  schedule_dao(instance, 0);
317 }
318 /*---------------------------------------------------------------------------*/
319 void
320 rpl_cancel_dao(rpl_instance_t *instance)
321 {
322  ctimer_stop(&instance->dao_timer);
323  ctimer_stop(&instance->dao_lifetime_timer);
324 }
325 /*---------------------------------------------------------------------------*/
326 #endif /* UIP_CONF_IPV6 */