uip-ds6-nbr.c 11.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
 * Copyright (c) 2013, Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *
 */

32
/**
Simon Duquennoy's avatar
Simon Duquennoy committed
33
 * \addtogroup uip
34
35
36
 * @{
 */

37
38
/**
 * \file
39
 *    IPv6 Neighbor cache (link-layer/IPv6 address mapping)
40
41
42
43
44
45
46
47
48
49
 * \author Mathilde Durvy <mdurvy@cisco.com>
 * \author Julien Abeille <jabeille@cisco.com>
 * \author Simon Duquennoy <simonduq@sics.se>
 *
 */

#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include "lib/list.h"
50
#include "net/link-stats.h"
51
#include "net/linkaddr.h"
52
#include "net/packetbuf.h"
Simon Duquennoy's avatar
Simon Duquennoy committed
53
#include "net/ipv6/uip-ds6.h"
54
#include "net/ipv6/uip-ds6-nbr.h"
Simon Duquennoy's avatar
Simon Duquennoy committed
55
#include "net/ipv6/uip-nd6.h"
56
#include "net/routing/routing.h"
57

Simon Duquennoy's avatar
Simon Duquennoy committed
58
59
/* Log configuration */
#include "sys/log.h"
60
#define LOG_MODULE "IPv6 Nbr"
61
#define LOG_LEVEL LOG_LEVEL_IPV6
62

63
NBR_TABLE_GLOBAL(uip_ds6_nbr_t, ds6_neighbors);
64
65
66
67
68

/*---------------------------------------------------------------------------*/
void
uip_ds6_neighbors_init(void)
{
69
  link_stats_init();
70
  nbr_table_register(ds6_neighbors, (nbr_table_callback *)uip_ds6_nbr_rm);
71
72
73
}
/*---------------------------------------------------------------------------*/
uip_ds6_nbr_t *
74
uip_ds6_nbr_add(const uip_ipaddr_t *ipaddr, const uip_lladdr_t *lladdr,
75
76
                uint8_t isrouter, uint8_t state, nbr_table_reason_t reason,
                void *data)
77
{
78
  uip_ds6_nbr_t *nbr = nbr_table_add_lladdr(ds6_neighbors, (linkaddr_t*)lladdr
79
                                            , reason, data);
80
81
  if(nbr) {
    uip_ipaddr_copy(&nbr->ipaddr, ipaddr);
82
#if UIP_ND6_SEND_RA || !UIP_CONF_ROUTER
83
    nbr->isrouter = isrouter;
84
#endif /* UIP_ND6_SEND_RA || !UIP_CONF_ROUTER */
85
    nbr->state = state;
86
#if UIP_CONF_IPV6_QUEUE_PKT
87
    uip_packetqueue_new(&nbr->packethandle);
88
#endif /* UIP_CONF_IPV6_QUEUE_PKT */
89
#if UIP_ND6_SEND_NS
90
91
92
93
94
95
    if(nbr->state == NBR_REACHABLE) {
      stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
    } else {
      /* We set the timer in expired state */
      stimer_set(&nbr->reachable, 0);
    }
96
97
    stimer_set(&nbr->sendns, 0);
    nbr->nscount = 0;
98
#endif /* UIP_ND6_SEND_NS */
Simon Duquennoy's avatar
Simon Duquennoy committed
99
100
    LOG_INFO("Adding neighbor with ip addr ");
    LOG_INFO_6ADDR(ipaddr);
101
    LOG_INFO_(" link addr ");
Simon Duquennoy's avatar
Simon Duquennoy committed
102
    LOG_INFO_LLADDR((linkaddr_t*)lladdr);
103
    LOG_INFO_(" state %u\n", state);
104
    NETSTACK_ROUTING.neighbor_state_changed(nbr);
105
106
    return nbr;
  } else {
Simon Duquennoy's avatar
Simon Duquennoy committed
107
108
    LOG_INFO("Add drop ip addr ");
    LOG_INFO_6ADDR(ipaddr);
109
    LOG_INFO_(" link addr (%p) ", lladdr);
Simon Duquennoy's avatar
Simon Duquennoy committed
110
    LOG_INFO_LLADDR((linkaddr_t*)lladdr);
111
    LOG_INFO_(" state %u\n", state);
112
113
114
115
116
    return NULL;
  }
}

/*---------------------------------------------------------------------------*/
117
int
118
119
120
121
122
123
uip_ds6_nbr_rm(uip_ds6_nbr_t *nbr)
{
  if(nbr != NULL) {
#if UIP_CONF_IPV6_QUEUE_PKT
    uip_packetqueue_free(&nbr->packethandle);
#endif /* UIP_CONF_IPV6_QUEUE_PKT */
124
    NETSTACK_ROUTING.neighbor_state_changed(nbr);
125
    return nbr_table_remove(ds6_neighbors, nbr);
126
  }
127
  return 0;
128
129
130
}

/*---------------------------------------------------------------------------*/
131
132
const uip_ipaddr_t *
uip_ds6_nbr_get_ipaddr(const uip_ds6_nbr_t *nbr)
133
134
135
136
137
{
  return (nbr != NULL) ? &nbr->ipaddr : NULL;
}

/*---------------------------------------------------------------------------*/
138
139
const uip_lladdr_t *
uip_ds6_nbr_get_ll(const uip_ds6_nbr_t *nbr)
140
{
141
  return (const uip_lladdr_t *)nbr_table_get_lladdr(ds6_neighbors, nbr);
142
}
143
144
145
146
147
148
/*---------------------------------------------------------------------------*/
int
uip_ds6_nbr_num(void)
{
  uip_ds6_nbr_t *nbr;
  int num;
149

150
151
152
153
154
155
156
157
  num = 0;
  for(nbr = nbr_table_head(ds6_neighbors);
      nbr != NULL;
      nbr = nbr_table_next(ds6_neighbors, nbr)) {
    num++;
  }
  return num;
}
158
159
/*---------------------------------------------------------------------------*/
uip_ds6_nbr_t *
160
161
162
163
164
165
166
167
168
169
170
171
uip_ds6_nbr_head(void)
{
  return nbr_table_head(ds6_neighbors);
}
/*---------------------------------------------------------------------------*/
uip_ds6_nbr_t *
uip_ds6_nbr_next(uip_ds6_nbr_t *nbr)
{
  return nbr_table_next(ds6_neighbors, nbr);
}
/*---------------------------------------------------------------------------*/
uip_ds6_nbr_t *
172
uip_ds6_nbr_lookup(const uip_ipaddr_t *ipaddr)
173
174
{
  uip_ds6_nbr_t *nbr = nbr_table_head(ds6_neighbors);
175
176
177
178
179
180
  if(ipaddr != NULL) {
    while(nbr != NULL) {
      if(uip_ipaddr_cmp(&nbr->ipaddr, ipaddr)) {
        return nbr;
      }
      nbr = nbr_table_next(ds6_neighbors, nbr);
181
182
183
184
185
186
    }
  }
  return NULL;
}
/*---------------------------------------------------------------------------*/
uip_ds6_nbr_t *
187
uip_ds6_nbr_ll_lookup(const uip_lladdr_t *lladdr)
188
{
189
  return nbr_table_get_from_lladdr(ds6_neighbors, (linkaddr_t*)lladdr);
190
191
192
193
}

/*---------------------------------------------------------------------------*/
uip_ipaddr_t *
194
uip_ds6_nbr_ipaddr_from_lladdr(const uip_lladdr_t *lladdr)
195
196
197
198
199
200
{
  uip_ds6_nbr_t *nbr = uip_ds6_nbr_ll_lookup(lladdr);
  return nbr ? &nbr->ipaddr : NULL;
}

/*---------------------------------------------------------------------------*/
201
202
const uip_lladdr_t *
uip_ds6_nbr_lladdr_from_ipaddr(const uip_ipaddr_t *ipaddr)
203
204
205
206
{
  uip_ds6_nbr_t *nbr = uip_ds6_nbr_lookup(ipaddr);
  return nbr ? uip_ds6_nbr_get_ll(nbr) : NULL;
}
207
208
/*---------------------------------------------------------------------------*/
void
209
uip_ds6_link_callback(int status, int numtx)
210
{
211
#if UIP_DS6_LL_NUD
212
213
  const linkaddr_t *dest = packetbuf_addr(PACKETBUF_ADDR_RECEIVER);
  if(linkaddr_cmp(dest, &linkaddr_null)) {
214
215
216
    return;
  }

217
218
  /* From RFC4861, page 72, last paragraph of section 7.3.3:
   *
219
220
221
222
223
224
225
   *         "In some cases, link-specific information may indicate that a path to
   *         a neighbor has failed (e.g., the resetting of a virtual circuit). In
   *         such cases, link-specific information may be used to purge Neighbor
   *         Cache entries before the Neighbor Unreachability Detection would do
   *         so. However, link-specific information MUST NOT be used to confirm
   *         the reachability of a neighbor; such information does not provide
   *         end-to-end confirmation between neighboring IP layers."
226
227
   *
   * However, we assume that receiving a link layer ack ensures the delivery
228
229
230
   * of the transmitted packed to the IP stack of the neighbour. This is a
   * fair assumption and allows battery powered nodes save some battery by
   * not re-testing the state of a neighbour periodically if it
231
   * acknowledges link packets. */
232
233
234
  if(status == MAC_TX_OK) {
    uip_ds6_nbr_t *nbr;
    nbr = uip_ds6_nbr_ll_lookup((uip_lladdr_t *)dest);
235
    if(nbr != NULL && nbr->state != NBR_INCOMPLETE) {
236
      nbr->state = NBR_REACHABLE;
237
      stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
Simon Duquennoy's avatar
Simon Duquennoy committed
238
239
      LOG_INFO("received a link layer ACK : ");
      LOG_INFO_LLADDR((uip_lladdr_t *)dest);
240
      LOG_INFO_(" is reachable.\n");
241
242
243
244
    }
  }
#endif /* UIP_DS6_LL_NUD */
}
245
#if UIP_ND6_SEND_NS
246
/*---------------------------------------------------------------------------*/
Tommy Sparber's avatar
Tommy Sparber committed
247
/** Periodic processing on neighbors */
248
void
249
uip_ds6_neighbor_periodic(void)
250
251
252
253
254
255
{
  uip_ds6_nbr_t *nbr = nbr_table_head(ds6_neighbors);
  while(nbr != NULL) {
    switch(nbr->state) {
    case NBR_REACHABLE:
      if(stimer_expired(&nbr->reachable)) {
Laurent Deru's avatar
Laurent Deru committed
256
#if UIP_CONF_IPV6_RPL
257
        /* when a neighbor leave its REACHABLE state and is a default router,
Laurent Deru's avatar
Laurent Deru committed
258
259
260
261
262
263
           instead of going to STALE state it enters DELAY state in order to
           force a NUD on it. Otherwise, if there is no upward traffic, the
           node never knows if the default router is still reachable. This
           mimics the 6LoWPAN-ND behavior.
         */
        if(uip_ds6_defrt_lookup(&nbr->ipaddr) != NULL) {
Simon Duquennoy's avatar
Simon Duquennoy committed
264
265
          LOG_INFO("REACHABLE: defrt moving to DELAY (");
          LOG_INFO_6ADDR(&nbr->ipaddr);
266
          LOG_INFO_(")\n");
Laurent Deru's avatar
Laurent Deru committed
267
268
269
270
          nbr->state = NBR_DELAY;
          stimer_set(&nbr->reachable, UIP_ND6_DELAY_FIRST_PROBE_TIME);
          nbr->nscount = 0;
        } else {
Simon Duquennoy's avatar
Simon Duquennoy committed
271
272
          LOG_INFO("REACHABLE: moving to STALE (");
          LOG_INFO_6ADDR(&nbr->ipaddr);
273
          LOG_INFO_(")\n");
Laurent Deru's avatar
Laurent Deru committed
274
275
276
          nbr->state = NBR_STALE;
        }
#else /* UIP_CONF_IPV6_RPL */
Simon Duquennoy's avatar
Simon Duquennoy committed
277
278
        LOG_INFO("REACHABLE: moving to STALE (");
        LOG_INFO_6ADDR(&nbr->ipaddr);
279
        LOG_INFO_(")\n");
280
        nbr->state = NBR_STALE;
Laurent Deru's avatar
Laurent Deru committed
281
#endif /* UIP_CONF_IPV6_RPL */
282
283
284
285
286
287
288
      }
      break;
    case NBR_INCOMPLETE:
      if(nbr->nscount >= UIP_ND6_MAX_MULTICAST_SOLICIT) {
        uip_ds6_nbr_rm(nbr);
      } else if(stimer_expired(&nbr->sendns) && (uip_len == 0)) {
        nbr->nscount++;
Simon Duquennoy's avatar
Simon Duquennoy committed
289
        LOG_INFO("NBR_INCOMPLETE: NS %u\n", nbr->nscount);
290
291
292
293
294
295
296
297
        uip_nd6_ns_output(NULL, NULL, &nbr->ipaddr);
        stimer_set(&nbr->sendns, uip_ds6_if.retrans_timer / 1000);
      }
      break;
    case NBR_DELAY:
      if(stimer_expired(&nbr->reachable)) {
        nbr->state = NBR_PROBE;
        nbr->nscount = 0;
Simon Duquennoy's avatar
Simon Duquennoy committed
298
        LOG_INFO("DELAY: moving to PROBE\n");
299
300
301
302
303
304
        stimer_set(&nbr->sendns, 0);
      }
      break;
    case NBR_PROBE:
      if(nbr->nscount >= UIP_ND6_MAX_UNICAST_SOLICIT) {
        uip_ds6_defrt_t *locdefrt;
Simon Duquennoy's avatar
Simon Duquennoy committed
305
        LOG_INFO("PROBE END\n");
306
307
308
309
310
311
312
313
        if((locdefrt = uip_ds6_defrt_lookup(&nbr->ipaddr)) != NULL) {
          if (!locdefrt->isinfinite) {
            uip_ds6_defrt_rm(locdefrt);
          }
        }
        uip_ds6_nbr_rm(nbr);
      } else if(stimer_expired(&nbr->sendns) && (uip_len == 0)) {
        nbr->nscount++;
Simon Duquennoy's avatar
Simon Duquennoy committed
314
        LOG_INFO("PROBE: NS %u\n", nbr->nscount);
315
316
317
318
319
320
321
322
323
324
325
        uip_nd6_ns_output(NULL, &nbr->ipaddr, &nbr->ipaddr);
        stimer_set(&nbr->sendns, uip_ds6_if.retrans_timer / 1000);
      }
      break;
    default:
      break;
    }
    nbr = nbr_table_next(ds6_neighbors, nbr);
  }
}
/*---------------------------------------------------------------------------*/
326
327
328
329
330
331
332
333
334
335
336
337
void
uip_ds6_nbr_refresh_reachable_state(const uip_ipaddr_t *ipaddr)
{
  uip_ds6_nbr_t *nbr;
  nbr = uip_ds6_nbr_lookup(ipaddr);
  if(nbr != NULL) {
    nbr->state = NBR_REACHABLE;
    nbr->nscount = 0;
    stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
  }
}
/*---------------------------------------------------------------------------*/
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
uip_ds6_nbr_t *
uip_ds6_get_least_lifetime_neighbor(void)
{
  uip_ds6_nbr_t *nbr = nbr_table_head(ds6_neighbors);
  uip_ds6_nbr_t *nbr_expiring = NULL;
  while(nbr != NULL) {
    if(nbr_expiring != NULL) {
      clock_time_t curr = stimer_remaining(&nbr->reachable);
      if(curr < stimer_remaining(&nbr->reachable)) {
        nbr_expiring = nbr;
      }
    } else {
      nbr_expiring = nbr;
    }
    nbr = nbr_table_next(ds6_neighbors, nbr);
  }
  return nbr_expiring;
}
356
#endif /* UIP_ND6_SEND_NS */
357
/*---------------------------------------------------------------------------*/
Rémy Léone's avatar
Rémy Léone committed
358
/** @} */