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
58void RPL_CALLBACK_PARENT_SWITCH(rpl_nbr_t *old, rpl_nbr_t *new);
59#endif /* RPL_CALLBACK_PARENT_SWITCH */
60
61static rpl_nbr_t * best_parent(int fresh_only);
62
63/*---------------------------------------------------------------------------*/
64/* Per-neighbor RPL information */
65NBR_TABLE_GLOBAL(rpl_nbr_t, rpl_neighbors);
66
67/*---------------------------------------------------------------------------*/
68static int
69max_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 */
81static int
82acceptable_rank(rpl_rank_t rank)
83{
84 return rank != RPL_INFINITE_RANK
85 && rank >= ROOT_RANK
86 && rank <= max_acceptable_rank();
87}
88/*---------------------------------------------------------------------------*/
89int
90rpl_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 != NULL && 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/*---------------------------------------------------------------------------*/
142void
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/*---------------------------------------------------------------------------*/
167int
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
181static uip_ds6_nbr_t *
182rpl_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/*---------------------------------------------------------------------------*/
193static void
194remove_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/*---------------------------------------------------------------------------*/
212rpl_nbr_t *
214{
215 return nbr_table_get_from_lladdr(rpl_neighbors, (linkaddr_t *)addr);
216}
217/*---------------------------------------------------------------------------*/
218int
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/*---------------------------------------------------------------------------*/
227uint16_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/*---------------------------------------------------------------------------*/
236rpl_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/*---------------------------------------------------------------------------*/
245const linkaddr_t *
247{
248 return nbr_table_get_lladdr(rpl_neighbors, nbr);
249}
250/*---------------------------------------------------------------------------*/
251uip_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/*---------------------------------------------------------------------------*/
258const struct link_stats *
260{
261 const linkaddr_t *lladdr = rpl_neighbor_get_lladdr(nbr);
262 return link_stats_from_lladdr(lladdr);
263}
264/*---------------------------------------------------------------------------*/
265int
267{
268 const struct link_stats *stats = rpl_neighbor_get_link_stats(nbr);
269 return link_stats_is_fresh(stats);
270}
271/*---------------------------------------------------------------------------*/
272int
274{
275 return nbr != NULL && nbr->rank < curr_instance.dag.rank;
276}
277/*---------------------------------------------------------------------------*/
278void
280{
281 if(curr_instance.dag.preferred_parent != nbr) {
282 LOG_INFO("parent switch: ");
283 LOG_INFO_6ADDR(rpl_neighbor_get_ipaddr(curr_instance.dag.preferred_parent));
284 LOG_INFO_(" -> ");
285 LOG_INFO_6ADDR(rpl_neighbor_get_ipaddr(nbr));
286 LOG_INFO_("\n");
287
288#ifdef RPL_CALLBACK_PARENT_SWITCH
289 RPL_CALLBACK_PARENT_SWITCH(curr_instance.dag.preferred_parent, nbr);
290#endif /* RPL_CALLBACK_PARENT_SWITCH */
291
292 /* Always keep the preferred parent locked, so it remains in the
293 * neighbor table. */
294 nbr_table_unlock(rpl_neighbors, curr_instance.dag.preferred_parent);
295 nbr_table_lock(rpl_neighbors, nbr);
296
297 /* Update DS6 default route. Use an infinite lifetime */
298 uip_ds6_defrt_rm(uip_ds6_defrt_lookup(
299 rpl_neighbor_get_ipaddr(curr_instance.dag.preferred_parent)));
300 uip_ds6_defrt_add(rpl_neighbor_get_ipaddr(nbr), 0);
301
302 curr_instance.dag.preferred_parent = nbr;
303 curr_instance.dag.unprocessed_parent_switch = true;
304 }
305}
306/*---------------------------------------------------------------------------*/
307/* Remove all DAG neighbors */
308void
310{
311 rpl_nbr_t *nbr;
312
313 LOG_INFO("removing all neighbors\n");
314
315 /* Unset preferred parent before we de-allocate it. This will set
316 * unprocessed_parent_switch which will make sure rpl_dag_update_state takes
317 * all actions necessary after losing the preferred parent */
319
320 nbr = nbr_table_head(rpl_neighbors);
321 while(nbr != NULL) {
322 remove_neighbor(nbr);
323 nbr = nbr_table_next(rpl_neighbors, nbr);
324 }
325
326 /* Update needed immediately. As we have lost the preferred parent this will
327 * enter poisoining and set timers accordingly. */
329}
330/*---------------------------------------------------------------------------*/
331rpl_nbr_t *
333{
335 const uip_lladdr_t *lladdr = uip_ds6_nbr_get_ll(ds6_nbr);
336 return nbr_table_get_from_lladdr(rpl_neighbors, (linkaddr_t *)lladdr);
337}
338/*---------------------------------------------------------------------------*/
339static rpl_nbr_t *
340best_parent(int fresh_only)
341{
342 rpl_nbr_t *nbr;
343 rpl_nbr_t *best = NULL;
344
345 if(curr_instance.used == 0) {
346 return NULL;
347 }
348
349 /* Search for the best parent according to the OF */
350 for(nbr = nbr_table_head(rpl_neighbors); nbr != NULL; nbr = nbr_table_next(rpl_neighbors, nbr)) {
351
352 if(!acceptable_rank(rpl_neighbor_rank_via_nbr(nbr))
353 || !curr_instance.of->nbr_is_acceptable_parent(nbr)) {
354 /* Exclude neighbors with a rank that is not acceptable */
355 continue;
356 }
357
358 if(fresh_only && !rpl_neighbor_is_fresh(nbr)) {
359 /* Filter out non-fresh nerighbors if fresh_only is set */
360 continue;
361 }
362
363#if UIP_ND6_SEND_NS
364 /* Exclude links to a neighbor that is not reachable at a NUD level */
365 if(rpl_get_ds6_nbr(nbr) == NULL) {
366 continue;
367 }
368#endif /* UIP_ND6_SEND_NS */
369
370 /* Now we have an acceptable parent, check if it is the new best */
371 best = curr_instance.of->best_parent(best, nbr);
372 }
373
374 return best;
375}
376/*---------------------------------------------------------------------------*/
377rpl_nbr_t *
379{
380 rpl_nbr_t *best;
381
383 return NULL; /* The root has no parent */
384 }
385
386 /* Look for best parent (regardless of freshness) */
387 best = best_parent(0);
388
389#if RPL_WITH_PROBING
390 if(best != NULL) {
391 if(rpl_neighbor_is_fresh(best)) {
392 /* Unschedule any already scheduled urgent probing */
393 curr_instance.dag.urgent_probing_target = NULL;
394 /* Return best if it is fresh */
395 return best;
396 } else {
397 rpl_nbr_t *best_fresh;
398
399 /* The best is not fresh. Probe it (unless there is already an urgent
400 probing target). We will be called back after the probing anyway. */
401 if(curr_instance.dag.urgent_probing_target == NULL) {
402 LOG_INFO("best parent is not fresh, schedule urgent probing to ");
403 LOG_INFO_6ADDR(rpl_neighbor_get_ipaddr(best));
404 LOG_INFO_("\n");
405 curr_instance.dag.urgent_probing_target = best;
407 }
408
409 /* The best is our preferred parent. It is not fresh but used to be,
410 else we would not have selected it in the first place. Stick to it
411 for a little while and rely on urgent probing to make a call. */
412 if(best == curr_instance.dag.preferred_parent) {
413 return best;
414 }
415
416 /* Look for the best fresh parent. */
417 best_fresh = best_parent(1);
418 if(best_fresh == NULL) {
419 if(curr_instance.dag.preferred_parent == NULL) {
420 /* We will wait to find a fresh node before selecting our first parent */
421 return NULL;
422 } else {
423 /* We already have a parent, now stick to the best and count on
424 urgent probing to get a fresh parent soon */
425 return best;
426 }
427 } else {
428 /* Select best fresh */
429 return best_fresh;
430 }
431 }
432 } else {
433 /* No acceptable parent */
434 return NULL;
435 }
436#else /* RPL_WITH_PROBING */
437 return best;
438#endif /* RPL_WITH_PROBING */
439}
440/*---------------------------------------------------------------------------*/
441void
443{
444 nbr_table_register(rpl_neighbors, (nbr_table_callback *)remove_neighbor);
445}
446/** @} */
clock_time_t clock_time(void)
Get the current clock time.
Definition: clock.c:118
static volatile uint64_t count
Num.
Definition: clock.c:50
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82
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
void rpl_neighbor_init(void)
Initialize rpl-dag-neighbor module.
Definition: rpl-neighbor.c:442
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
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_schedule_probing_now(void)
Schedule probing within a few seconds.
uint16_t rpl_neighbor_get_link_metric(rpl_nbr_t *nbr)
Returns a neighbor's link metric.
Definition: rpl-neighbor.c:228
void rpl_neighbor_remove_all(void)
Empty the RPL neighbor table.
Definition: rpl-neighbor.c:309
const linkaddr_t * rpl_neighbor_get_lladdr(rpl_nbr_t *nbr)
Returns a neighbors's link-layer address.
Definition: rpl-neighbor.c:246
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
void rpl_dag_update_state(void)
Updates RPL internal state: selects preferred parent, updates rank & metreic container,...
Definition: rpl-dag.c:266
int rpl_neighbor_is_parent(rpl_nbr_t *nbr)
Tells whether a neighbor is in the parent set.
Definition: rpl-neighbor.c:273
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
const struct link_stats * rpl_neighbor_get_link_stats(rpl_nbr_t *nbr)
Returns a neighbor's link statistics.
Definition: rpl-neighbor.c:259
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:332
const uip_ipaddr_t * rpl_get_global_address(void)
Get one of the node's global addresses.
Definition: rpl.c:71
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:279
uip_ipaddr_t * rpl_neighbor_get_ipaddr(rpl_nbr_t *nbr)
Returns a neighbor's (link-local) IPv6 address.
Definition: rpl-neighbor.c:252
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
#define ROOT_RANK
Rank of a root node.
Definition: rpl-types.h:78
void rpl_neighbor_print_list(const char *str)
Prints a summary of all RPL neighbors and their properties.
Definition: rpl-neighbor.c:143
rpl_nbr_t * rpl_neighbor_select_best(void)
Returns the best candidate for preferred parent.
Definition: rpl-neighbor.c:378
int rpl_neighbor_is_acceptable_parent(rpl_nbr_t *nbr)
Tells whether a nbr is acceptable as per the OF's definition.
Definition: rpl-neighbor.c:219
int rpl_neighbor_count(void)
Returns the number of nodes in the RPL neighbor table.
Definition: rpl-neighbor.c:168
void rpl_timers_schedule_state_update(void)
Schedule a state update ASAP.
Definition: rpl-timers.c:563
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
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:422
int rpl_dag_root_is_root(void)
Tells whether we are DAG root or not.
Definition: rpl-dag-root.c:152
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:512
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:497
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:535
Header file for the logging system.
All information related to a RPL neighbor.
Definition: rpl-types.h:136
The default nbr_table entry (when UIP_DS6_NBR_MULTI_IPV6_ADDRS is disabled), that implements nbr cach...
Definition: uip-ds6-nbr.h:105
static uip_ds6_nbr_t * nbr
Pointer to llao option in uip_buf.
Definition: uip-nd6.c:106
static uip_ds6_addr_t * addr
Pointer to a nbr cache entry.
Definition: uip-nd6.c:107
Header file for the IP address manipulation library.