Contiki-NG
rpl-neighbor.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 DAG neighbors 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 "contiki.h"
46 #include "net/routing/rpl-lite/rpl.h"
47 #include "net/link-stats.h"
48 #include "net/nbr-table.h"
49 #include "net/ipv6/uiplib.h"
50 
51 /* Log configuration */
52 #include "sys/log.h"
53 #define LOG_MODULE "RPL"
54 #define LOG_LEVEL LOG_LEVEL_RPL
55 
56 /* A configurable function called after every RPL parent switch */
57 #ifdef RPL_CALLBACK_PARENT_SWITCH
58 void RPL_CALLBACK_PARENT_SWITCH(rpl_nbr_t *old, rpl_nbr_t *new);
59 #endif /* RPL_CALLBACK_PARENT_SWITCH */
60 
61 static rpl_nbr_t * best_parent(int fresh_only);
62 
63 /*---------------------------------------------------------------------------*/
64 /* Per-neighbor RPL information */
65 NBR_TABLE_GLOBAL(rpl_nbr_t, rpl_neighbors);
66 
67 /*---------------------------------------------------------------------------*/
68 static int
69 max_acceptable_rank(void)
70 {
71  if(curr_instance.max_rankinc == 0) {
72  /* There is no max rank increment */
73  return RPL_INFINITE_RANK;
74  } else {
75  /* Make sure not to exceed RPL_INFINITE_RANK */
76  return MIN((uint32_t)curr_instance.dag.lowest_rank + curr_instance.max_rankinc, RPL_INFINITE_RANK);
77  }
78 }
79 /*---------------------------------------------------------------------------*/
80 /* As per RFC 6550, section 8.2.2.4 */
81 static int
82 acceptable_rank(rpl_rank_t rank)
83 {
84  return rank != RPL_INFINITE_RANK
85  && rank >= ROOT_RANK
86  && rank <= max_acceptable_rank();
87 }
88 /*---------------------------------------------------------------------------*/
89 int
90 rpl_neighbor_snprint(char *buf, int buflen, rpl_nbr_t *nbr)
91 {
92  int index = 0;
93  rpl_nbr_t *best = best_parent(0);
94  const struct link_stats *stats = rpl_neighbor_get_link_stats(nbr);
95  clock_time_t clock_now = clock_time();
96 
97  if(LOG_WITH_COMPACT_ADDR) {
98  index += log_6addr_compact_snprint(buf+index, buflen-index, rpl_neighbor_get_ipaddr(nbr));
99  } else {
100  index += uiplib_ipaddr_snprint(buf+index, buflen-index, rpl_neighbor_get_ipaddr(nbr));
101  }
102  if(index >= buflen) {
103  return index;
104  }
105  index += snprintf(buf+index, buflen-index,
106  "%5u, %5u => %5u -- %2u %c%c%c%c%c",
107  nbr->rank,
110  stats != NULL ? stats->freshness : 0,
111  (nbr->rank == ROOT_RANK) ? 'r' : ' ',
112  nbr == best ? 'b' : ' ',
113  (acceptable_rank(rpl_neighbor_rank_via_nbr(nbr)) && rpl_neighbor_is_acceptable_parent(nbr)) ? 'a' : ' ',
114  link_stats_is_fresh(stats) ? 'f' : ' ',
115  nbr == curr_instance.dag.preferred_parent ? 'p' : ' '
116  );
117  if(index >= buflen) {
118  return index;
119  }
120  if(stats->last_tx_time > 0) {
121  index += snprintf(buf+index, buflen-index,
122  " (last tx %u min ago",
123  (unsigned)((clock_now - stats->last_tx_time) / (60 * CLOCK_SECOND)));
124  } else {
125  index += snprintf(buf+index, buflen-index,
126  " (no tx");
127  }
128  if(index >= buflen) {
129  return index;
130  }
131  if(nbr->better_parent_since > 0) {
132  index += snprintf(buf+index, buflen-index,
133  ", better since %u min)",
134  (unsigned)((clock_now - nbr->better_parent_since) / (60 * CLOCK_SECOND)));
135  } else {
136  index += snprintf(buf+index, buflen-index,
137  ")");
138  }
139  return index;
140 }
141 /*---------------------------------------------------------------------------*/
142 void
143 rpl_neighbor_print_list(const char *str)
144 {
145  if(curr_instance.used) {
146  int curr_dio_interval = curr_instance.dag.dio_intcurrent;
147  int curr_rank = curr_instance.dag.rank;
148  rpl_nbr_t *nbr = nbr_table_head(rpl_neighbors);
149 
150  LOG_INFO("nbr: own state, addr ");
151  LOG_INFO_6ADDR(rpl_get_global_address());
152  LOG_INFO_(", DAG state: %s, MOP %u OCP %u rank %u max-rank %u, dioint %u, nbr count %u (%s)\n",
153  rpl_dag_state_to_str(curr_instance.dag.state),
154  curr_instance.mop, curr_instance.of->ocp, curr_rank,
155  max_acceptable_rank(),
156  curr_dio_interval, rpl_neighbor_count(), str);
157  while(nbr != NULL) {
158  char buf[120];
159  rpl_neighbor_snprint(buf, sizeof(buf), nbr);
160  LOG_INFO("nbr: %s\n", buf);
161  nbr = nbr_table_next(rpl_neighbors, nbr);
162  }
163  LOG_INFO("nbr: end of list\n");
164  }
165 }
166 /*---------------------------------------------------------------------------*/
167 int
169 {
170  int count = 0;
171  rpl_nbr_t *nbr = nbr_table_head(rpl_neighbors);
172  for(nbr = nbr_table_head(rpl_neighbors);
173  nbr != NULL;
174  nbr = nbr_table_next(rpl_neighbors, nbr)) {
175  count++;
176  }
177  return count;
178 }
179 /*---------------------------------------------------------------------------*/
180 #if UIP_ND6_SEND_NS
181 static uip_ds6_nbr_t *
182 rpl_get_ds6_nbr(rpl_nbr_t *nbr)
183 {
184  const uip_lladdr_t *lladdr = (const uip_lladdr_t *)rpl_neighbor_get_lladdr(nbr);
185  if(lladdr != NULL) {
186  return uip_ds6_nbr_ll_lookup(lladdr);
187  } else {
188  return NULL;
189  }
190 }
191 #endif /* UIP_ND6_SEND_NS */
192 /*---------------------------------------------------------------------------*/
193 static void
194 remove_neighbor(rpl_nbr_t *nbr)
195 {
196  /* Make sure we don't point to a removed neighbor. Note that we do not need
197  to worry about preferred_parent here, as it is locked in the the table
198  and will never be removed by external modules. */
199 #if RPL_WITH_PROBING
200  if(nbr == curr_instance.dag.urgent_probing_target) {
201  curr_instance.dag.urgent_probing_target = NULL;
202  }
203 #endif
204 
205  if(nbr == curr_instance.dag.unicast_dio_target) {
206  curr_instance.dag.unicast_dio_target = NULL;
207  }
208  nbr_table_remove(rpl_neighbors, nbr);
209  rpl_timers_schedule_state_update(); /* Updating from here is unsafe; postpone */
210 }
211 /*---------------------------------------------------------------------------*/
212 rpl_nbr_t *
214 {
215  return nbr_table_get_from_lladdr(rpl_neighbors, (linkaddr_t *)addr);
216 }
217 /*---------------------------------------------------------------------------*/
218 int
220 {
221  if(nbr != NULL && curr_instance.of->nbr_is_acceptable_parent != NULL) {
222  return curr_instance.of->nbr_is_acceptable_parent(nbr);
223  }
224  return 0xffff;
225 }
226 /*---------------------------------------------------------------------------*/
227 uint16_t
229 {
230  if(nbr != NULL && curr_instance.of->nbr_link_metric != NULL) {
231  return curr_instance.of->nbr_link_metric(nbr);
232  }
233  return 0xffff;
234 }
235 /*---------------------------------------------------------------------------*/
236 rpl_rank_t
238 {
239  if(nbr != NULL && curr_instance.of->rank_via_nbr != NULL) {
240  return curr_instance.of->rank_via_nbr(nbr);
241  }
242  return RPL_INFINITE_RANK;
243 }
244 /*---------------------------------------------------------------------------*/
245 const linkaddr_t *
247 {
248  return nbr_table_get_lladdr(rpl_neighbors, nbr);
249 }
250 /*---------------------------------------------------------------------------*/
251 uip_ipaddr_t *
253 {
254  const linkaddr_t *lladdr = rpl_neighbor_get_lladdr(nbr);
255  return uip_ds6_nbr_ipaddr_from_lladdr((uip_lladdr_t *)lladdr);
256 }
257 /*---------------------------------------------------------------------------*/
258 const struct link_stats *
260 {
261  const linkaddr_t *lladdr = rpl_neighbor_get_lladdr(nbr);
262  return link_stats_from_lladdr(lladdr);
263 }
264 /*---------------------------------------------------------------------------*/
265 int
267 {
268  const struct link_stats *stats = rpl_neighbor_get_link_stats(nbr);
269  return link_stats_is_fresh(stats);
270 }
271 /*---------------------------------------------------------------------------*/
272 int
274  if(nbr == NULL) {
275  return 0;
276  } else {
277 #if UIP_ND6_SEND_NS
278  uip_ds6_nbr_t *ds6_nbr = rpl_get_ds6_nbr(nbr);
279  /* Exclude links to a neighbor that is not reachable at a NUD level */
280  if(ds6_nbr == NULL || ds6_nbr->state != NBR_REACHABLE) {
281  return 0;
282  }
283 #endif /* UIP_ND6_SEND_NS */
284  /* If we don't have fresh link information, assume the nbr is reachable. */
285  return !rpl_neighbor_is_fresh(nbr) || curr_instance.of->nbr_has_usable_link(nbr);
286  }
287 }
288 /*---------------------------------------------------------------------------*/
289 int
291 {
292  return nbr != NULL && nbr->rank < curr_instance.dag.rank;
293 }
294 /*---------------------------------------------------------------------------*/
295 void
297 {
298  if(curr_instance.dag.preferred_parent != nbr) {
299  LOG_INFO("parent switch: ");
300  LOG_INFO_6ADDR(rpl_neighbor_get_ipaddr(curr_instance.dag.preferred_parent));
301  LOG_INFO_(" -> ");
302  LOG_INFO_6ADDR(rpl_neighbor_get_ipaddr(nbr));
303  LOG_INFO_("\n");
304 
305 #ifdef RPL_CALLBACK_PARENT_SWITCH
306  RPL_CALLBACK_PARENT_SWITCH(curr_instance.dag.preferred_parent, nbr);
307 #endif /* RPL_CALLBACK_PARENT_SWITCH */
308 
309  /* Always keep the preferred parent locked, so it remains in the
310  * neighbor table. */
311  nbr_table_unlock(rpl_neighbors, curr_instance.dag.preferred_parent);
312  nbr_table_lock(rpl_neighbors, nbr);
313 
314  /* Update DS6 default route. Use an infinite lifetime */
315  uip_ds6_defrt_rm(uip_ds6_defrt_lookup(
316  rpl_neighbor_get_ipaddr(curr_instance.dag.preferred_parent)));
317  uip_ds6_defrt_add(rpl_neighbor_get_ipaddr(nbr), 0);
318 
319  curr_instance.dag.preferred_parent = nbr;
320  curr_instance.dag.unprocessed_parent_switch = true;
321  }
322 }
323 /*---------------------------------------------------------------------------*/
324 /* Remove all DAG neighbors */
325 void
327 {
328  rpl_nbr_t *nbr;
329 
330  LOG_INFO("removing all neighbors\n");
331 
332  /* Unset preferred parent before we de-allocate it. This will set
333  * unprocessed_parent_switch which will make sure rpl_dag_update_state takes
334  * all actions necessary after losing the preferred parent */
336 
337  nbr = nbr_table_head(rpl_neighbors);
338  while(nbr != NULL) {
339  remove_neighbor(nbr);
340  nbr = nbr_table_next(rpl_neighbors, nbr);
341  }
342 
343  /* Update needed immediately. As we have lost the preferred parent this will
344  * enter poisoining and set timers accordingly. */
346 }
347 /*---------------------------------------------------------------------------*/
348 rpl_nbr_t *
350 {
351  uip_ds6_nbr_t *ds6_nbr = uip_ds6_nbr_lookup(addr);
352  const uip_lladdr_t *lladdr = uip_ds6_nbr_get_ll(ds6_nbr);
353  return nbr_table_get_from_lladdr(rpl_neighbors, (linkaddr_t *)lladdr);
354 }
355 /*---------------------------------------------------------------------------*/
356 static rpl_nbr_t *
357 best_parent(int fresh_only)
358 {
359  rpl_nbr_t *nbr;
360  rpl_nbr_t *best = NULL;
361 
362  if(curr_instance.used == 0) {
363  return NULL;
364  }
365 
366  /* Search for the best parent according to the OF */
367  for(nbr = nbr_table_head(rpl_neighbors); nbr != NULL; nbr = nbr_table_next(rpl_neighbors, nbr)) {
368 
369  if(!acceptable_rank(rpl_neighbor_rank_via_nbr(nbr))
370  || !curr_instance.of->nbr_is_acceptable_parent(nbr)) {
371  /* Exclude neighbors with a rank that is not acceptable */
372  continue;
373  }
374 
375  if(fresh_only && !rpl_neighbor_is_fresh(nbr)) {
376  /* Filter out non-fresh nerighbors if fresh_only is set */
377  continue;
378  }
379 
380 #if UIP_ND6_SEND_NS
381  {
382  uip_ds6_nbr_t *ds6_nbr = rpl_get_ds6_nbr(nbr);
383  /* Exclude links to a neighbor that is not reachable at a NUD level */
384  if(ds6_nbr == NULL || ds6_nbr->state != NBR_REACHABLE) {
385  continue;
386  }
387  }
388 #endif /* UIP_ND6_SEND_NS */
389 
390  /* Now we have an acceptable parent, check if it is the new best */
391  best = curr_instance.of->best_parent(best, nbr);
392  }
393 
394  return best;
395 }
396 /*---------------------------------------------------------------------------*/
397 rpl_nbr_t *
399 {
400  rpl_nbr_t *best;
401 
402  if(rpl_dag_root_is_root()) {
403  return NULL; /* The root has no parent */
404  }
405 
406  /* Look for best parent (regardless of freshness) */
407  best = best_parent(0);
408 
409 #if RPL_WITH_PROBING
410  if(best != NULL) {
411  if(rpl_neighbor_is_fresh(best)) {
412  /* Unschedule any already scheduled urgent probing */
413  curr_instance.dag.urgent_probing_target = NULL;
414  /* Return best if it is fresh */
415  return best;
416  } else {
417  rpl_nbr_t *best_fresh;
418 
419  /* The best is not fresh. Probe it (unless there is already an urgent
420  probing target). We will be called back after the probing anyway. */
421  if(curr_instance.dag.urgent_probing_target == NULL) {
422  LOG_INFO("best parent is not fresh, schedule urgent probing to ");
423  LOG_INFO_6ADDR(rpl_neighbor_get_ipaddr(best));
424  LOG_INFO_("\n");
425  curr_instance.dag.urgent_probing_target = best;
427  }
428 
429  /* The best is our preferred parent. It is not fresh but used to be,
430  else we would not have selected it in the first place. Stick to it
431  for a little while and rely on urgent probing to make a call. */
432  if(best == curr_instance.dag.preferred_parent) {
433  return best;
434  }
435 
436  /* Look for the best fresh parent. */
437  best_fresh = best_parent(1);
438  if(best_fresh == NULL) {
439  if(curr_instance.dag.preferred_parent == NULL) {
440  /* We will wait to find a fresh node before selecting our first parent */
441  return NULL;
442  } else {
443  /* We already have a parent, now stick to the best and count on
444  urgent probing to get a fresh parent soon */
445  return best;
446  }
447  } else {
448  /* Select best fresh */
449  return best_fresh;
450  }
451  }
452  } else {
453  /* No acceptable parent */
454  return NULL;
455  }
456 #else /* RPL_WITH_PROBING */
457  return best;
458 #endif /* RPL_WITH_PROBING */
459 }
460 /*---------------------------------------------------------------------------*/
461 void
463 {
464  nbr_table_register(rpl_neighbors, (nbr_table_callback *)remove_neighbor);
465 }
466 /** @} */
const linkaddr_t * rpl_neighbor_get_lladdr(rpl_nbr_t *nbr)
Returns a neighbors&#39;s link-layer address.
Definition: rpl-neighbor.c:246
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
#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
void rpl_neighbor_remove_all(void)
Empty the RPL neighbor table.
Definition: rpl-neighbor.c:326
void rpl_schedule_probing_now(void)
Schedule probing within a few seconds.
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
int rpl_neighbor_is_fresh(rpl_nbr_t *nbr)
Tells wether we have fresh link information towards a given neighbor.
Definition: rpl-neighbor.c:266
uint16_t rpl_neighbor_get_link_metric(rpl_nbr_t *nbr)
Returns a neighbor&#39;s link metric.
Definition: rpl-neighbor.c:228
int rpl_neighbor_is_parent(rpl_nbr_t *nbr)
Tells whether a neighbor is in the parent set.
Definition: rpl-neighbor.c:290
int rpl_dag_root_is_root(void)
Tells whether we are DAG root or not.
Definition: rpl-dag-root.c:152
int rpl_neighbor_count(void)
Returns the number of nodes in the RPL neighbor table.
Definition: rpl-neighbor.c:168
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
void rpl_timers_schedule_state_update(void)
Schedule a state update ASAP.
Definition: rpl-timers.c:563
void rpl_neighbor_init(void)
Initialize rpl-dag-neighbor module.
Definition: rpl-neighbor.c:462
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82
Header file for the IP address manipulation library.
uip_ipaddr_t * uip_ds6_nbr_ipaddr_from_lladdr(const uip_lladdr_t *lladdr)
Get an IPv6 address associated with a specified link-layer address.
Definition: uip-ds6-nbr.c:505
uip_ds6_nbr_t * uip_ds6_nbr_lookup(const uip_ipaddr_t *ipaddr)
Get the neighbor cache associated with a specified IPv6 address.
Definition: uip-ds6-nbr.c:467
const struct link_stats * rpl_neighbor_get_link_stats(rpl_nbr_t *nbr)
Returns a neighbor&#39;s link statistics.
Definition: rpl-neighbor.c:259
All information related to a RPL neighbor.
Definition: rpl-types.h:136
clock_time_t clock_time(void)
Get the current clock time.
Definition: clock.c:118
void rpl_neighbor_print_list(const char *str)
Prints a summary of all RPL neighbors and their properties.
Definition: rpl-neighbor.c:143
const uip_ipaddr_t * rpl_get_global_address(void)
Get one of the node&#39;s global addresses.
Definition: rpl.c:71
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
int rpl_neighbor_is_acceptable_parent(rpl_nbr_t *nbr)
Tells whether a nbr is acceptable as per the OF&#39;s definition.
Definition: rpl-neighbor.c:219
int log_6addr_compact_snprint(char *buf, size_t size, const uip_ipaddr_t *ipaddr)
Write at most size - 1 characters of the IP address to the output string, in a compact representation...
Definition: log.c:97
uip_ds6_nbr_t * uip_ds6_nbr_ll_lookup(const uip_lladdr_t *lladdr)
Get the neighbor cache associated with a specified link-layer address.
Definition: uip-ds6-nbr.c:482
const uip_lladdr_t * uip_ds6_nbr_get_ll(const uip_ds6_nbr_t *nbr)
Get the link-layer address associated with a specified nbr cache.
Definition: uip-ds6-nbr.c:392
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_neighbor_set_preferred_parent(rpl_nbr_t *nbr)
Set current RPL preferred parent and update DS6 default route accordingly.
Definition: rpl-neighbor.c:296
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
int rpl_neighbor_snprint(char *buf, int buflen, rpl_nbr_t *nbr)
Print a textual description of RPL neighbor into a string.
Definition: rpl-neighbor.c:90
rpl_nbr_t * rpl_neighbor_get_from_lladdr(uip_lladdr_t *addr)
Returns a neighbor from its link-layer address.
Definition: rpl-neighbor.c:213
The default nbr_table entry (when UIP_DS6_NBR_MULTI_IPV6_ADDRS is disabled), that implements nbr cach...
Definition: uip-ds6-nbr.h:105
int rpl_neighbor_is_reachable(rpl_nbr_t *nbr)
Tells wether we a given neighbor is reachable.
Definition: rpl-neighbor.c:273
int uiplib_ipaddr_snprint(char *buf, size_t size, const uip_ipaddr_t *addr)
Write at most size - 1 characters of the IP address to the output string.
Definition: uiplib.c:166