Contiki-NG
Loading...
Searching...
No Matches
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 for(rpl_nbr_t *nbr = nbr_table_head(rpl_neighbors);
172 nbr != NULL;
173 nbr = nbr_table_next(rpl_neighbors, nbr)) {
174 count++;
175 }
176 return count;
177}
178/*---------------------------------------------------------------------------*/
179#if UIP_ND6_SEND_NS
180static uip_ds6_nbr_t *
181rpl_get_ds6_nbr(rpl_nbr_t *nbr)
182{
183 const uip_lladdr_t *lladdr = (const uip_lladdr_t *)rpl_neighbor_get_lladdr(nbr);
184 if(lladdr != NULL) {
185 return uip_ds6_nbr_ll_lookup(lladdr);
186 } else {
187 return NULL;
188 }
189}
190#endif /* UIP_ND6_SEND_NS */
191/*---------------------------------------------------------------------------*/
192static void
193remove_neighbor(rpl_nbr_t *nbr)
194{
195 /* Make sure we don't point to a removed neighbor. Note that we do not need
196 to worry about preferred_parent here, as it is locked in the the table
197 and will never be removed by external modules. */
198#if RPL_WITH_PROBING
199 if(nbr == curr_instance.dag.urgent_probing_target) {
200 curr_instance.dag.urgent_probing_target = NULL;
201 }
202#endif
203
204 if(nbr == curr_instance.dag.unicast_dio_target) {
205 curr_instance.dag.unicast_dio_target = NULL;
206 }
207 nbr_table_remove(rpl_neighbors, nbr);
208 rpl_timers_schedule_state_update(); /* Updating from here is unsafe; postpone */
209}
210/*---------------------------------------------------------------------------*/
211rpl_nbr_t *
213{
214 return nbr_table_get_from_lladdr(rpl_neighbors, (linkaddr_t *)addr);
215}
216/*---------------------------------------------------------------------------*/
217int
219{
220 if(nbr != NULL && curr_instance.of->nbr_is_acceptable_parent != NULL) {
221 return curr_instance.of->nbr_is_acceptable_parent(nbr);
222 }
223 return 0xffff;
224}
225/*---------------------------------------------------------------------------*/
226uint16_t
228{
229 if(nbr != NULL && curr_instance.of->nbr_link_metric != NULL) {
230 return curr_instance.of->nbr_link_metric(nbr);
231 }
232 return 0xffff;
233}
234/*---------------------------------------------------------------------------*/
235rpl_rank_t
237{
238 if(nbr != NULL && curr_instance.of->rank_via_nbr != NULL) {
239 return curr_instance.of->rank_via_nbr(nbr);
240 }
241 return RPL_INFINITE_RANK;
242}
243/*---------------------------------------------------------------------------*/
244const linkaddr_t *
246{
247 return nbr_table_get_lladdr(rpl_neighbors, nbr);
248}
249/*---------------------------------------------------------------------------*/
250uip_ipaddr_t *
252{
253 const linkaddr_t *lladdr = rpl_neighbor_get_lladdr(nbr);
254 return uip_ds6_nbr_ipaddr_from_lladdr((uip_lladdr_t *)lladdr);
255}
256/*---------------------------------------------------------------------------*/
257const struct link_stats *
259{
260 const linkaddr_t *lladdr = rpl_neighbor_get_lladdr(nbr);
261 return link_stats_from_lladdr(lladdr);
262}
263/*---------------------------------------------------------------------------*/
264int
266{
267 const struct link_stats *stats = rpl_neighbor_get_link_stats(nbr);
268 return link_stats_is_fresh(stats);
269}
270/*---------------------------------------------------------------------------*/
271int
273{
274 return nbr != NULL && nbr->rank < curr_instance.dag.rank;
275}
276/*---------------------------------------------------------------------------*/
277void
279{
280 if(curr_instance.dag.preferred_parent != nbr) {
281 LOG_INFO("parent switch: ");
282 LOG_INFO_6ADDR(rpl_neighbor_get_ipaddr(curr_instance.dag.preferred_parent));
283 LOG_INFO_(" -> ");
284 LOG_INFO_6ADDR(rpl_neighbor_get_ipaddr(nbr));
285 LOG_INFO_("\n");
286
287#ifdef RPL_CALLBACK_PARENT_SWITCH
288 RPL_CALLBACK_PARENT_SWITCH(curr_instance.dag.preferred_parent, nbr);
289#endif /* RPL_CALLBACK_PARENT_SWITCH */
290
291 /* Always keep the preferred parent locked, so it remains in the
292 * neighbor table. */
293 nbr_table_unlock(rpl_neighbors, curr_instance.dag.preferred_parent);
294 nbr_table_lock(rpl_neighbors, nbr);
295
296 /* Update DS6 default route. Use an infinite lifetime */
297 uip_ds6_defrt_rm(uip_ds6_defrt_lookup(
298 rpl_neighbor_get_ipaddr(curr_instance.dag.preferred_parent)));
299 uip_ds6_defrt_add(rpl_neighbor_get_ipaddr(nbr), 0);
300
301 curr_instance.dag.preferred_parent = nbr;
302 curr_instance.dag.unprocessed_parent_switch = true;
303 }
304}
305/*---------------------------------------------------------------------------*/
306/* Remove all DAG neighbors */
307void
309{
310 rpl_nbr_t *nbr;
311
312 LOG_INFO("removing all neighbors\n");
313
314 /* Unset preferred parent before we de-allocate it. This will set
315 * unprocessed_parent_switch which will make sure rpl_dag_update_state takes
316 * all actions necessary after losing the preferred parent */
318
319 nbr = nbr_table_head(rpl_neighbors);
320 while(nbr != NULL) {
321 remove_neighbor(nbr);
322 nbr = nbr_table_next(rpl_neighbors, nbr);
323 }
324
325 /* Update needed immediately. As we have lost the preferred parent this will
326 * enter poisoining and set timers accordingly. */
328}
329/*---------------------------------------------------------------------------*/
330rpl_nbr_t *
332{
334 const uip_lladdr_t *lladdr = uip_ds6_nbr_get_ll(ds6_nbr);
335 return nbr_table_get_from_lladdr(rpl_neighbors, (linkaddr_t *)lladdr);
336}
337/*---------------------------------------------------------------------------*/
338static rpl_nbr_t *
339best_parent(int fresh_only)
340{
341 rpl_nbr_t *nbr;
342 rpl_nbr_t *best = NULL;
343
344 if(curr_instance.used == 0) {
345 return NULL;
346 }
347
348 /* Search for the best parent according to the OF */
349 for(nbr = nbr_table_head(rpl_neighbors); nbr != NULL; nbr = nbr_table_next(rpl_neighbors, nbr)) {
350
351 if(!acceptable_rank(rpl_neighbor_rank_via_nbr(nbr))
352 || !curr_instance.of->nbr_is_acceptable_parent(nbr)) {
353 /* Exclude neighbors with a rank that is not acceptable */
354 continue;
355 }
356
357 if(fresh_only && !rpl_neighbor_is_fresh(nbr)) {
358 /* Filter out non-fresh nerighbors if fresh_only is set */
359 continue;
360 }
361
362#if UIP_ND6_SEND_NS
363 /* Exclude links to a neighbor that is not reachable at a NUD level */
364 if(rpl_get_ds6_nbr(nbr) == NULL) {
365 continue;
366 }
367#endif /* UIP_ND6_SEND_NS */
368
369 /* Now we have an acceptable parent, check if it is the new best */
370 best = curr_instance.of->best_parent(best, nbr);
371 }
372
373 return best;
374}
375/*---------------------------------------------------------------------------*/
376rpl_nbr_t *
378{
379 rpl_nbr_t *best;
380
381 if(rpl_dag_root_is_root()) {
382 return NULL; /* The root has no parent */
383 }
384
385 /* Look for best parent (regardless of freshness) */
386 best = best_parent(0);
387
388#if RPL_WITH_PROBING
389 if(best != NULL) {
390 if(rpl_neighbor_is_fresh(best)) {
391 /* Unschedule any already scheduled urgent probing */
392 curr_instance.dag.urgent_probing_target = NULL;
393 /* Return best if it is fresh */
394 return best;
395 } else {
396 rpl_nbr_t *best_fresh;
397
398 /* The best is not fresh. Probe it (unless there is already an urgent
399 probing target). We will be called back after the probing anyway. */
400 if(curr_instance.dag.urgent_probing_target == NULL) {
401 LOG_INFO("best parent is not fresh, schedule urgent probing to ");
402 LOG_INFO_6ADDR(rpl_neighbor_get_ipaddr(best));
403 LOG_INFO_("\n");
404 curr_instance.dag.urgent_probing_target = best;
406 }
407
408 /* The best is our preferred parent. It is not fresh but used to be,
409 else we would not have selected it in the first place. Stick to it
410 for a little while and rely on urgent probing to make a call. */
411 if(best == curr_instance.dag.preferred_parent) {
412 return best;
413 }
414
415 /* Look for the best fresh parent. */
416 best_fresh = best_parent(1);
417 if(best_fresh == NULL) {
418 if(curr_instance.dag.preferred_parent == NULL) {
419 /* We will wait to find a fresh node before selecting our first parent */
420 return NULL;
421 } else {
422 /* We already have a parent, now stick to the best and count on
423 urgent probing to get a fresh parent soon */
424 return best;
425 }
426 } else {
427 /* Select best fresh */
428 return best_fresh;
429 }
430 }
431 } else {
432 /* No acceptable parent */
433 return NULL;
434 }
435#else /* RPL_WITH_PROBING */
436 return best;
437#endif /* RPL_WITH_PROBING */
438}
439/*---------------------------------------------------------------------------*/
440void
442{
443 nbr_table_register(rpl_neighbors, (nbr_table_callback *)remove_neighbor);
444}
445/** @} */
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:103
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:99
void rpl_neighbor_init(void)
Initialize rpl-dag-neighbor module.
rpl_nbr_t * rpl_neighbor_get_from_lladdr(uip_lladdr_t *addr)
Returns a neighbor from its link-layer address.
rpl_rank_t rpl_neighbor_rank_via_nbr(rpl_nbr_t *nbr)
Returns our rank if selecting a given parent as preferred parent.
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.
void rpl_neighbor_remove_all(void)
Empty the RPL neighbor table.
const linkaddr_t * rpl_neighbor_get_lladdr(rpl_nbr_t *nbr)
Returns a neighbors's link-layer address.
int rpl_neighbor_is_fresh(rpl_nbr_t *nbr)
Tells wether we have fresh link information towards a given neighbor.
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.
int rpl_neighbor_snprint(char *buf, int buflen, rpl_nbr_t *nbr)
Print a textual description of RPL neighbor into a string.
const struct link_stats * rpl_neighbor_get_link_stats(rpl_nbr_t *nbr)
Returns a neighbor's link statistics.
rpl_nbr_t * rpl_neighbor_get_from_ipaddr(uip_ipaddr_t *addr)
Returns a neighbor from its link-local IPv6 address.
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.
uip_ipaddr_t * rpl_neighbor_get_ipaddr(rpl_nbr_t *nbr)
Returns a neighbor's (link-local) IPv6 address.
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_neighbor_print_list(const char *str)
Prints a summary of all RPL neighbors and their properties.
rpl_nbr_t * rpl_neighbor_select_best(void)
Returns the best candidate for preferred parent.
int rpl_neighbor_is_acceptable_parent(rpl_nbr_t *nbr)
Tells whether a nbr is acceptable as per the OF's definition.
int rpl_neighbor_count(void)
Returns the number of nodes in the RPL neighbor table.
void rpl_timers_schedule_state_update(void)
Schedule a state update ASAP.
Definition rpl-timers.c:555
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.
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.
uip_ds6_nbr_t * uip_ds6_nbr_lookup(const uip_ipaddr_t *ipaddr)
Get the neighbor cache associated with a specified IPv6 address.
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.
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...
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.