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 linkaddr_t *lladdr = rpl_neighbor_get_lladdr(nbr);
185  if(lladdr != NULL) {
186  return nbr_table_get_from_lladdr(ds6_neighbors, 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(nbr == curr_instance.dag.urgent_probing_target) {
200  curr_instance.dag.urgent_probing_target = NULL;
201  }
202  if(nbr == curr_instance.dag.unicast_dio_target) {
203  curr_instance.dag.unicast_dio_target = NULL;
204  }
205  nbr_table_remove(rpl_neighbors, nbr);
206  rpl_timers_schedule_state_update(); /* Updating from here is unsafe; postpone */
207 }
208 /*---------------------------------------------------------------------------*/
209 rpl_nbr_t *
211 {
212  return nbr_table_get_from_lladdr(rpl_neighbors, (linkaddr_t *)addr);
213 }
214 /*---------------------------------------------------------------------------*/
215 int
217 {
218  if(nbr != NULL && curr_instance.of->nbr_is_acceptable_parent != NULL) {
219  return curr_instance.of->nbr_is_acceptable_parent(nbr);
220  }
221  return 0xffff;
222 }
223 /*---------------------------------------------------------------------------*/
224 uint16_t
226 {
227  if(nbr != NULL && curr_instance.of->nbr_link_metric != NULL) {
228  return curr_instance.of->nbr_link_metric(nbr);
229  }
230  return 0xffff;
231 }
232 /*---------------------------------------------------------------------------*/
233 rpl_rank_t
235 {
236  if(nbr != NULL && curr_instance.of->rank_via_nbr != NULL) {
237  return curr_instance.of->rank_via_nbr(nbr);
238  }
239  return RPL_INFINITE_RANK;
240 }
241 /*---------------------------------------------------------------------------*/
242 const linkaddr_t *
244 {
245  return nbr_table_get_lladdr(rpl_neighbors, nbr);
246 }
247 /*---------------------------------------------------------------------------*/
248 uip_ipaddr_t *
250 {
251  const linkaddr_t *lladdr = rpl_neighbor_get_lladdr(nbr);
252  return uip_ds6_nbr_ipaddr_from_lladdr((uip_lladdr_t *)lladdr);
253 }
254 /*---------------------------------------------------------------------------*/
255 const struct link_stats *
257 {
258  const linkaddr_t *lladdr = rpl_neighbor_get_lladdr(nbr);
259  return link_stats_from_lladdr(lladdr);
260 }
261 /*---------------------------------------------------------------------------*/
262 int
264 {
265  const struct link_stats *stats = rpl_neighbor_get_link_stats(nbr);
266  return link_stats_is_fresh(stats);
267 }
268 /*---------------------------------------------------------------------------*/
269 int
271  if(nbr == NULL) {
272  return 0;
273  } else {
274 #if UIP_ND6_SEND_NS
275  uip_ds6_nbr_t *ds6_nbr = rpl_get_ds6_nbr(nbr);
276  /* Exclude links to a neighbor that is not reachable at a NUD level */
277  if(ds6_nbr == NULL || ds6_nbr->state != NBR_REACHABLE) {
278  return 0;
279  }
280 #endif /* UIP_ND6_SEND_NS */
281  /* If we don't have fresh link information, assume the nbr is reachable. */
282  return !rpl_neighbor_is_fresh(nbr) || curr_instance.of->nbr_has_usable_link(nbr);
283  }
284 }
285 /*---------------------------------------------------------------------------*/
286 int
288 {
289  return nbr != NULL && nbr->rank < curr_instance.dag.rank;
290 }
291 /*---------------------------------------------------------------------------*/
292 void
294 {
295  if(curr_instance.dag.preferred_parent != nbr) {
296  LOG_INFO("parent switch: ");
297  LOG_INFO_6ADDR(rpl_neighbor_get_ipaddr(curr_instance.dag.preferred_parent));
298  LOG_INFO_(" -> ");
299  LOG_INFO_6ADDR(rpl_neighbor_get_ipaddr(nbr));
300  LOG_INFO_("\n");
301 
302 #ifdef RPL_CALLBACK_PARENT_SWITCH
303  RPL_CALLBACK_PARENT_SWITCH(curr_instance.dag.preferred_parent, nbr);
304 #endif /* RPL_CALLBACK_PARENT_SWITCH */
305 
306  /* Always keep the preferred parent locked, so it remains in the
307  * neighbor table. */
308  nbr_table_unlock(rpl_neighbors, curr_instance.dag.preferred_parent);
309  nbr_table_lock(rpl_neighbors, nbr);
310 
311  /* Update DS6 default route. Use an infinite lifetime */
312  uip_ds6_defrt_rm(uip_ds6_defrt_lookup(
313  rpl_neighbor_get_ipaddr(curr_instance.dag.preferred_parent)));
314  uip_ds6_defrt_add(rpl_neighbor_get_ipaddr(nbr), 0);
315 
316  curr_instance.dag.preferred_parent = nbr;
317  }
318 }
319 /*---------------------------------------------------------------------------*/
320 /* Remove DAG neighbors with a rank that is at least the same as minimum_rank. */
321 void
323 {
324  rpl_nbr_t *nbr;
325 
326  LOG_INFO("removing all neighbors\n");
327 
328  nbr = nbr_table_head(rpl_neighbors);
329  while(nbr != NULL) {
330  remove_neighbor(nbr);
331  nbr = nbr_table_next(rpl_neighbors, nbr);
332  }
333 
334  /* Update needed immediately so as to ensure preferred_parent becomes NULL,
335  * and no longer points to a de-allocated neighbor. */
337 }
338 /*---------------------------------------------------------------------------*/
339 rpl_nbr_t *
341 {
342  uip_ds6_nbr_t *ds6_nbr = uip_ds6_nbr_lookup(addr);
343  const uip_lladdr_t *lladdr = uip_ds6_nbr_get_ll(ds6_nbr);
344  return nbr_table_get_from_lladdr(rpl_neighbors, (linkaddr_t *)lladdr);
345 }
346 /*---------------------------------------------------------------------------*/
347 static rpl_nbr_t *
348 best_parent(int fresh_only)
349 {
350  rpl_nbr_t *nbr;
351  rpl_nbr_t *best = NULL;
352 
353  if(curr_instance.used == 0) {
354  return NULL;
355  }
356 
357  /* Search for the best parent according to the OF */
358  for(nbr = nbr_table_head(rpl_neighbors); nbr != NULL; nbr = nbr_table_next(rpl_neighbors, nbr)) {
359 
360  if(!acceptable_rank(nbr->rank) || !curr_instance.of->nbr_is_acceptable_parent(nbr)) {
361  /* Exclude neighbors with a rank that is not acceptable) */
362  continue;
363  }
364 
365  if(fresh_only && !rpl_neighbor_is_fresh(nbr)) {
366  /* Filter out non-fresh nerighbors if fresh_only is set */
367  continue;
368  }
369 
370 #if UIP_ND6_SEND_NS
371  {
372  uip_ds6_nbr_t *ds6_nbr = rpl_get_ds6_nbr(nbr);
373  /* Exclude links to a neighbor that is not reachable at a NUD level */
374  if(ds6_nbr == NULL || ds6_nbr->state != NBR_REACHABLE) {
375  continue;
376  }
377  }
378 #endif /* UIP_ND6_SEND_NS */
379 
380  /* Now we have an acceptable parent, check if it is the new best */
381  best = curr_instance.of->best_parent(best, nbr);
382  }
383 
384  return best;
385 }
386 /*---------------------------------------------------------------------------*/
387 rpl_nbr_t *
389 {
390  rpl_nbr_t *best;
391 
392  if(rpl_dag_root_is_root()) {
393  return NULL; /* The root has no parent */
394  }
395 
396  /* Look for best parent (regardless of freshness) */
397  best = best_parent(0);
398 
399 #if RPL_WITH_PROBING
400  if(best != NULL) {
401  if(rpl_neighbor_is_fresh(best)) {
402  /* Unschedule any already scheduled urgent probing */
403  curr_instance.dag.urgent_probing_target = NULL;
404  /* Return best if it is fresh */
405  return best;
406  } else {
407  rpl_nbr_t *best_fresh;
408 
409  /* The best is not fresh. Probe it (unless there is already an urgent
410  probing target). We will be called back after the probing anyway. */
411  if(curr_instance.dag.urgent_probing_target == NULL) {
412  LOG_INFO("best parent is not fresh, schedule urgent probing to ");
413  LOG_INFO_6ADDR(rpl_neighbor_get_ipaddr(best));
414  LOG_INFO_("\n");
415  curr_instance.dag.urgent_probing_target = best;
417  }
418 
419  /* The best is our preferred parent. It is not fresh but used to be,
420  else we would not have selected it in the first place. Stick to it
421  for a little while and rely on urgent probing to make a call. */
422  if(best == curr_instance.dag.preferred_parent) {
423  return best;
424  }
425 
426  /* Look for the best fresh parent. */
427  best_fresh = best_parent(1);
428  if(best_fresh == NULL) {
429  if(curr_instance.dag.preferred_parent == NULL) {
430  /* We will wait to find a fresh node before selecting our first parent */
431  return NULL;
432  } else {
433  /* We already have a parent, now stick to the best and count on
434  urgent probing to get a fresh parent soon */
435  return best;
436  }
437  } else {
438  /* Select best fresh */
439  return best_fresh;
440  }
441  }
442  } else {
443  /* No acceptable parent */
444  return NULL;
445  }
446 #else /* RPL_WITH_PROBING */
447  return best;
448 #endif /* RPL_WITH_PROBING */
449 }
450 /*---------------------------------------------------------------------------*/
451 void
453 {
454  nbr_table_register(rpl_neighbors, (nbr_table_callback *)remove_neighbor);
455 }
456 /** @} */
const linkaddr_t * rpl_neighbor_get_lladdr(rpl_nbr_t *nbr)
Returns a neighbors&#39;s link-layer address.
Definition: rpl-neighbor.c:243
uip_ipaddr_t * rpl_neighbor_get_ipaddr(rpl_nbr_t *nbr)
Returns a neighbor&#39;s (link-local) IPv6 address.
Definition: rpl-neighbor.c:249
static uip_ds6_nbr_t * nbr
Pointer to llao option in uip_buf.
Definition: uip-nd6.c:115
#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:116
void rpl_neighbor_remove_all(void)
Empty the RPL neighbor table.
Definition: rpl-neighbor.c:322
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:340
int rpl_neighbor_is_fresh(rpl_nbr_t *nbr)
Tells wether we have fresh link information towards a given neighbor.
Definition: rpl-neighbor.c:263
uint16_t rpl_neighbor_get_link_metric(rpl_nbr_t *nbr)
Returns a neighbor&#39;s link metric.
Definition: rpl-neighbor.c:225
int rpl_neighbor_is_parent(rpl_nbr_t *nbr)
Tells whether a neighbor is in the parent set.
Definition: rpl-neighbor.c:287
int rpl_dag_root_is_root(void)
Tells whether we are DAG root or not.
Definition: rpl-dag-root.c:147
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:556
void rpl_neighbor_init(void)
Initialize rpl-dag-neighbor module.
Definition: rpl-neighbor.c:452
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82
Header file for the IP address manipulation library.
const struct link_stats * rpl_neighbor_get_link_stats(rpl_nbr_t *nbr)
Returns a neighbor&#39;s link statistics.
Definition: rpl-neighbor.c:256
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:264
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:216
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:95
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:234
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:293
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:388
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:210
An entry in the nbr cache.
Definition: uip-ds6-nbr.h:69
int rpl_neighbor_is_reachable(rpl_nbr_t *nbr)
Tells wether we a given neighbor is reachable.
Definition: rpl-neighbor.c:270
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:168