1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/sys/net/gnrc/network_layer/ndp/gnrc_ndp.c
Oleg Hahm cc0101472e 6lowpan: do garbage collect unreachable NCEs
RFC6775 says "if NUD on the router determines that the host is UNREACHABLE (based on the logic in [RFC4861]), the NCE SHOULD NOT be deleted but rather retained until the Registration Lifetime expires." Since this is a "SHOULD NOT" and not a "MUST NOT", we can still remove these NCEs to circumvent NCEs going into UNREACHABLE and never leave this state again. Actually removing these entries from the cache seems also be the better choice for memory constraint device.
2015-09-24 17:33:38 +02:00

905 lines
34 KiB
C

/*
* Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de>
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for
* more details.
*/
/**
* @ingroup net_gnrc_ndp
* @{
*
* @file
*
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
*/
#include <errno.h>
#include <string.h>
#include "byteorder.h"
#include "net/fib.h"
#include "net/ieee802154.h"
#include "net/ipv6/ext/rh.h"
#include "net/gnrc/icmpv6.h"
#include "net/gnrc/ipv6.h"
#include "net/gnrc/sixlowpan/nd.h"
#include "net/gnrc.h"
#include "net/sixlowpan/nd.h"
#include "random.h"
#include "utlist.h"
#include "thread.h"
#include "vtimer.h"
#include "net/gnrc/ndp/internal.h"
#include "net/gnrc/ndp.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#if ENABLE_DEBUG
/* For PRIu8 etc. */
#include <inttypes.h>
static char addr_str[IPV6_ADDR_MAX_STR_LEN];
#endif
/* sets an entry to stale if its l2addr differs from the given one or creates it stale if it
* does not exist */
static void _stale_nc(kernel_pid_t iface, ipv6_addr_t *ipaddr, uint8_t *l2addr,
int l2addr_len)
{
if (l2addr_len != -ENOTSUP) {
gnrc_ipv6_nc_t *nc_entry = gnrc_ipv6_nc_get(iface, ipaddr);
if (nc_entry == NULL) {
#ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER
/* tentative type see https://tools.ietf.org/html/rfc6775#section-6.3 */
gnrc_ipv6_netif_t *ipv6_iface = gnrc_ipv6_netif_get(iface);
if ((ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) &&
(ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER)) {
timex_t t = { GNRC_SIXLOWPAN_ND_TENTATIVE_NCE_LIFETIME, 0 };
if ((nc_entry = gnrc_ipv6_nc_add(iface, ipaddr, l2addr,
(uint16_t)l2addr_len,
GNRC_IPV6_NC_STATE_STALE |
GNRC_IPV6_NC_TYPE_TENTATIVE)) != NULL) {
vtimer_remove(&nc_entry->type_timeout);
vtimer_set_msg(&nc_entry->type_timeout, t, gnrc_ipv6_pid,
GNRC_SIXLOWPAN_ND_MSG_AR_TIMEOUT, nc_entry);
}
return;
}
#endif
gnrc_ipv6_nc_add(iface, ipaddr, l2addr, (uint16_t)l2addr_len,
GNRC_IPV6_NC_STATE_STALE);
}
#ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER
/* unreachable set in gnrc_ndp_retrans_nbr_sol() will just be staled */
else if (gnrc_ipv6_nc_get_state(nc_entry) == GNRC_IPV6_NC_STATE_UNREACHABLE) {
gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_STALE);
}
#endif
else if (((uint16_t)l2addr_len != nc_entry->l2_addr_len) ||
(memcmp(l2addr, nc_entry->l2_addr, l2addr_len) != 0)) {
/* if entry exists but l2 address differs: set */
nc_entry->l2_addr_len = (uint16_t)l2addr_len;
memcpy(nc_entry->l2_addr, l2addr, l2addr_len);
gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_STALE);
}
}
}
void gnrc_ndp_nbr_sol_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt,
ipv6_hdr_t *ipv6, ndp_nbr_sol_t *nbr_sol,
size_t icmpv6_size)
{
uint16_t opt_offset = 0;
uint8_t l2src[GNRC_IPV6_NC_L2_ADDR_MAX];
uint8_t *buf = ((uint8_t *)nbr_sol) + sizeof(ndp_nbr_sol_t);
ipv6_addr_t *tgt, nbr_adv_dst;
gnrc_pktsnip_t *nbr_adv_opts = NULL;
#ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER
ndp_opt_t *sl2a_opt = NULL;
sixlowpan_nd_opt_ar_t *ar_opt = NULL;
#endif
int sicmpv6_size = (int)icmpv6_size, l2src_len = 0;
DEBUG("ndp: received neighbor solicitation (src: %s, ",
ipv6_addr_to_str(addr_str, &ipv6->src, sizeof(addr_str)));
DEBUG("dst: %s, ",
ipv6_addr_to_str(addr_str, &ipv6->dst, sizeof(addr_str)));
DEBUG("tgt: %s)\n",
ipv6_addr_to_str(addr_str, &nbr_sol->tgt, sizeof(addr_str)));
/* check validity */
if ((ipv6->hl != 255) || (nbr_sol->code != 0) ||
(icmpv6_size < sizeof(ndp_nbr_sol_t)) ||
ipv6_addr_is_multicast(&nbr_sol->tgt) ||
(ipv6_addr_is_unspecified(&ipv6->src) &&
ipv6_addr_is_solicited_node(&ipv6->dst))) {
DEBUG("ndp: neighbor solicitation was invalid.\n");
/* ipv6 releases */
return;
}
if ((tgt = gnrc_ipv6_netif_find_addr(iface, &nbr_sol->tgt)) == NULL) {
DEBUG("ndp: Target address is not to interface %" PRIkernel_pid "\n",
iface);
/* ipv6 releases */
return;
}
sicmpv6_size -= sizeof(ndp_nbr_sol_t);
while (sicmpv6_size > 0) {
ndp_opt_t *opt = (ndp_opt_t *)(buf + opt_offset);
switch (opt->type) {
case NDP_OPT_SL2A:
if ((l2src_len = gnrc_ndp_internal_sl2a_opt_handle(pkt, ipv6, nbr_sol->type, opt,
l2src)) < 0) {
/* -ENOTSUP can not happen, since the function only returns this for invalid
* message types containing the SL2A. Neighbor solicitations are not an
* invalid message type for SL2A. According to that, we don't need to watch
* out for that here, but regardless, the source link-layer address option
* is invalid. */
return;
}
#ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER
sl2a_opt = opt;
break;
case NDP_OPT_AR:
/* actually handling at the end of the function (see below) */
ar_opt = (sixlowpan_nd_opt_ar_t *)opt;
#endif
break;
default:
/* silently discard all other options */
break;
}
opt_offset += (opt->len * 8);
sicmpv6_size -= (opt->len * 8);
#if ENABLE_DEBUG
if (sicmpv6_size < 0) {
DEBUG("ndp: Option parsing out of sync.\n");
}
#endif
}
#ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER
gnrc_ipv6_netif_t *ipv6_iface = gnrc_ipv6_netif_get(iface);
assert(ipv6_iface != NULL);
if ((sl2a_opt != NULL) && (ar_opt != NULL) &&
(ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) &&
(ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER)) {
uint8_t status = gnrc_sixlowpan_nd_opt_ar_handle(iface, ipv6,
nbr_sol->type,
&ipv6->src, ar_opt,
l2src, l2src_len);
/* check for multihop DAD return */
nbr_adv_opts = gnrc_sixlowpan_nd_opt_ar_build(status, GNRC_SIXLOWPAN_ND_AR_LTIME,
&ar_opt->eui64, NULL);
if (status == 0) {
memcpy(&nbr_adv_dst, &ipv6->src, sizeof(ipv6_addr_t));
}
else {
/* see https://tools.ietf.org/html/rfc6775#section-6.5.2 */
eui64_t iid;
ieee802154_get_iid(&iid, ar_opt->eui64.uint8, sizeof(eui64_t));
ipv6_addr_set_iid(&nbr_adv_dst, iid.uint64.u64);
ipv6_addr_set_link_local_prefix(&nbr_adv_dst);
}
}
else { /* gnrc_sixlowpan_nd_opt_ar_handle updates neighbor cache */
_stale_nc(iface, &ipv6->src, l2src, l2src_len);
memcpy(&nbr_adv_dst, &ipv6->src, sizeof(ipv6_addr_t));
}
#else
_stale_nc(iface, &ipv6->src, l2src, l2src_len);
memcpy(&nbr_adv_dst, &ipv6->src, sizeof(ipv6_addr_t));
#endif
gnrc_ndp_internal_send_nbr_adv(iface, tgt, &nbr_adv_dst, ipv6_addr_is_multicast(&ipv6->dst),
nbr_adv_opts);
}
static inline bool _pkt_has_l2addr(gnrc_netif_hdr_t *netif_hdr)
{
return (netif_hdr != NULL) && (netif_hdr->src_l2addr_len != 0) &&
(netif_hdr->dst_l2addr_len != 0);
}
void gnrc_ndp_nbr_adv_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt,
ipv6_hdr_t *ipv6, ndp_nbr_adv_t *nbr_adv,
size_t icmpv6_size)
{
uint16_t opt_offset = 0;
uint8_t *buf = ((uint8_t *)nbr_adv) + sizeof(ndp_nbr_adv_t);
int l2tgt_len = 0;
uint8_t l2tgt[GNRC_IPV6_NC_L2_ADDR_MAX];
int sicmpv6_size = (int)icmpv6_size;
gnrc_ipv6_nc_t *nc_entry = gnrc_ipv6_nc_get(iface, &nbr_adv->tgt);
gnrc_pktsnip_t *netif;
gnrc_netif_hdr_t *netif_hdr = NULL;
DEBUG("ndp: received neighbor advertisement (src: %s, ",
ipv6_addr_to_str(addr_str, &ipv6->src, sizeof(addr_str)));
DEBUG("dst: %s, ",
ipv6_addr_to_str(addr_str, &ipv6->dst, sizeof(addr_str)));
DEBUG("tgt: %s)\n",
ipv6_addr_to_str(addr_str, &nbr_adv->tgt, sizeof(addr_str)));
/* check validity */
if ((ipv6->hl != 255) || (nbr_adv->code != 0) ||
(icmpv6_size < sizeof(ndp_nbr_adv_t)) ||
ipv6_addr_is_multicast(&nbr_adv->tgt)) {
DEBUG("ndp: neighbor advertisement was invalid.\n");
/* ipv6 releases */
return;
}
if (nc_entry == NULL) {
/* see https://tools.ietf.org/html/rfc4861#section-7.2.5 */
DEBUG("ndp: no neighbor cache entry found for advertisement's target\n");
/* ipv6 releases */
return;
}
sicmpv6_size -= sizeof(ndp_nbr_adv_t);
while (sicmpv6_size > 0) {
ndp_opt_t *opt = (ndp_opt_t *)(buf + opt_offset);
switch (opt->type) {
case NDP_OPT_TL2A:
if ((l2tgt_len = gnrc_ndp_internal_tl2a_opt_handle(pkt, ipv6, nbr_adv->type, opt, l2tgt)) < 0) {
/* invalid target link-layer address option */
return;
}
break;
#ifdef MODULE_GNRC_SIXLOWPAN_ND
case NDP_OPT_AR:
/* address registration option is always ignored when invalid */
gnrc_sixlowpan_nd_opt_ar_handle(iface, ipv6, nbr_adv->type,
&nbr_adv->tgt,
(sixlowpan_nd_opt_ar_t *)opt,
NULL, 0);
break;
#endif
default:
/* silently discard all other options */
break;
}
opt_offset += (opt->len * 8);
sicmpv6_size -= (opt->len * 8);
#if ENABLE_DEBUG
if (sicmpv6_size < 0) {
DEBUG("ndp: Option parsing out of sync.\n");
}
#endif
}
LL_SEARCH_SCALAR(pkt, netif, type, GNRC_NETTYPE_NETIF);
if (netif != NULL) {
netif_hdr = netif->data;
}
if (l2tgt_len != -ENOTSUP) {
#ifdef MODULE_GNRC_SIXLOWPAN_ND
/* check if entry wasn't removed by ARO, ideally there should not be any TL2A in here */
nc_entry = gnrc_ipv6_nc_get(iface, &nbr_adv->tgt);
if (nc_entry == NULL) {
return;
}
#endif
if (gnrc_ipv6_nc_get_state(nc_entry) == GNRC_IPV6_NC_STATE_INCOMPLETE) {
if (_pkt_has_l2addr(netif_hdr) && (l2tgt_len == 0)) {
/* link-layer has addresses, but no TLLAO supplied: discard silently
* (see https://tools.ietf.org/html/rfc4861#section-7.2.5) */
return;
}
nc_entry->iface = iface;
nc_entry->l2_addr_len = l2tgt_len;
memcpy(nc_entry->l2_addr, l2tgt, l2tgt_len);
if (nbr_adv->flags & NDP_NBR_ADV_FLAGS_S) {
gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_REACHABLE);
}
else {
gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_STALE);
}
if (nbr_adv->flags & NDP_NBR_ADV_FLAGS_R) {
nc_entry->flags |= GNRC_IPV6_NC_IS_ROUTER;
}
else {
nc_entry->flags &= ~GNRC_IPV6_NC_IS_ROUTER;
/* TODO: update state of neighbor as router in FIB? */
}
#ifdef MODULE_GNRC_NDP_NODE
gnrc_pktqueue_t *queued_pkt;
while ((queued_pkt = gnrc_pktqueue_remove_head(&nc_entry->pkts)) != NULL) {
gnrc_netapi_send(gnrc_ipv6_pid, queued_pkt->pkt);
queued_pkt->pkt = NULL;
}
#endif
}
else {
/* first or-term: no link-layer, but nc_entry has l2addr,
* second or-term: different l2addr cached */
bool l2tgt_changed = false;
if ((!_pkt_has_l2addr(netif_hdr)) && (l2tgt_len == 0)) {
/* there was previously a L2 address registered */
l2tgt_changed = (nc_entry->l2_addr_len != 0);
}
/* link-layer has addresses and TLLAO with different address */
else if (_pkt_has_l2addr(netif_hdr) && (l2tgt_len != 0)) {
l2tgt_changed = (!(l2tgt_len == nc_entry->l2_addr_len)) &&
(memcmp(nc_entry->l2_addr, l2tgt, l2tgt_len) == 0);
}
if ((nbr_adv->flags & NDP_NBR_ADV_FLAGS_O) || !l2tgt_changed ||
(l2tgt_len == 0)) {
if (l2tgt_len != 0) {
nc_entry->iface = iface;
nc_entry->l2_addr_len = l2tgt_len;
memcpy(nc_entry->l2_addr, l2tgt, l2tgt_len);
}
if (nbr_adv->flags & NDP_NBR_ADV_FLAGS_S) {
gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_REACHABLE);
}
else if (l2tgt_changed) {
gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_STALE);
}
if (nbr_adv->flags & NDP_NBR_ADV_FLAGS_R) {
nc_entry->flags |= GNRC_IPV6_NC_IS_ROUTER;
}
else {
nc_entry->flags &= ~GNRC_IPV6_NC_IS_ROUTER;
/* TODO: update state of neighbor as router in FIB? */
}
}
else if (l2tgt_changed &&
gnrc_ipv6_nc_get_state(nc_entry) == GNRC_IPV6_NC_STATE_REACHABLE) {
gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_STALE);
}
}
}
return;
}
#if (defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_ROUTER))
void gnrc_ndp_rtr_sol_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt,
ipv6_hdr_t *ipv6, ndp_rtr_sol_t *rtr_sol,
size_t icmpv6_size)
{
gnrc_ipv6_netif_t *if_entry = gnrc_ipv6_netif_get(iface);
if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER) {
int sicmpv6_size = (int)icmpv6_size, l2src_len = 0;
uint8_t l2src[GNRC_IPV6_NC_L2_ADDR_MAX];
uint16_t opt_offset = 0;
uint8_t *buf = (uint8_t *)(rtr_sol + 1);
/* check validity */
if ((ipv6->hl != 255) || (rtr_sol->code != 0) ||
(icmpv6_size < sizeof(ndp_rtr_sol_t))) {
DEBUG("ndp: router solicitation was invalid\n");
return;
}
sicmpv6_size -= sizeof(ndp_rtr_sol_t);
while (sicmpv6_size > 0) {
ndp_opt_t *opt = (ndp_opt_t *)(buf + opt_offset);
switch (opt->type) {
case NDP_OPT_SL2A:
if ((l2src_len = gnrc_ndp_internal_sl2a_opt_handle(pkt, ipv6, rtr_sol->type, opt,
l2src)) < 0) {
/* -ENOTSUP can not happen */
/* invalid source link-layer address option */
return;
}
break;
default:
/* silently discard all other options */
break;
}
opt_offset += (opt->len * 8);
sicmpv6_size -= (opt->len * 8);
#if ENABLE_DEBUG
if (sicmpv6_size < 0) {
DEBUG("ndp: Option parsing out of sync.\n");
}
#endif
}
_stale_nc(iface, &ipv6->src, l2src, l2src_len);
/* send delayed */
if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_RTR_ADV) {
timex_t delay;
uint32_t ms = GNRC_NDP_MAX_RTR_ADV_DELAY;
#ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER
if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) {
ms = GNRC_SIXLOWPAN_ND_MAX_RTR_ADV_DELAY;
}
#endif
delay = timex_set(0, genrand_uint32_range(0, ms));
vtimer_remove(&if_entry->rtr_adv_timer);
#ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER
/* in case of a 6LBR we have to check if the interface is actually
* the 6lo interface */
if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) {
gnrc_ipv6_nc_t *nc_entry = gnrc_ipv6_nc_get(iface, &ipv6->src);
if (nc_entry != NULL) {
vtimer_set_msg(&if_entry->rtr_adv_timer, delay, gnrc_ipv6_pid,
GNRC_NDP_MSG_RTR_ADV_SIXLOWPAN_DELAY, nc_entry);
}
}
#elif defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_BORDER_ROUTER)
if (ipv6_addr_is_unspecified(&ipv6->src)) {
/* either multicast, if source unspecified */
vtimer_set_msg(&if_entry->rtr_adv_timer, delay, gnrc_ipv6_pid,
GNRC_NDP_MSG_RTR_ADV_RETRANS, if_entry);
}
else {
/* or unicast, if source is known */
/* XXX: can't just use GNRC_NETAPI_MSG_TYPE_SND, since the next retransmission
* must also be set. */
gnrc_ipv6_nc_t *nc_entry = gnrc_ipv6_nc_get(iface, &ipv6->src);
vtimer_set_msg(&if_entry->rtr_adv_timer, delay, gnrc_ipv6_pid,
GNRC_NDP_MSG_RTR_ADV_DELAY, nc_entry);
}
#endif
}
}
/* otherwise ignore silently */
}
#endif
static inline void _set_reach_time(gnrc_ipv6_netif_t *if_entry, uint32_t mean)
{
uint32_t reach_time = genrand_uint32_range(GNRC_NDP_MIN_RAND, GNRC_NDP_MAX_RAND);
if_entry->reach_time_base = mean;
/* to avoid floating point number computation and have higher value entropy, the
* boundaries for the random value are multiplied by 10 and we need to account for that */
reach_time = (reach_time * if_entry->reach_time_base) / 10;
if_entry->reach_time = timex_set(0, reach_time);
timex_normalize(&if_entry->reach_time);
}
void gnrc_ndp_rtr_adv_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6,
ndp_rtr_adv_t *rtr_adv, size_t icmpv6_size)
{
uint8_t *buf = (uint8_t *)(rtr_adv + 1);
gnrc_ipv6_nc_t *nc_entry = NULL;
gnrc_ipv6_netif_t *if_entry = gnrc_ipv6_netif_get(iface);
uint8_t l2src[GNRC_IPV6_NC_L2_ADDR_MAX];
#ifdef MODULE_GNRC_SIXLOWPAN_ND
uint32_t next_rtr_sol = 0;
#endif
int sicmpv6_size = (int)icmpv6_size, l2src_len = 0;
uint16_t opt_offset = 0;
if (!ipv6_addr_is_link_local(&ipv6->src) ||
ipv6_addr_is_multicast(&ipv6->src) ||
(ipv6->hl != 255) || (rtr_adv->code != 0) ||
(icmpv6_size < sizeof(ndp_rtr_adv_t))) {
DEBUG("ndp: router advertisement was invalid\n");
/* ipv6 releases */
return;
}
/* get source from default router list */
nc_entry = gnrc_ipv6_nc_get(iface, &ipv6->src);
if (nc_entry == NULL) { /* not in default router list */
/* create default router list entry */
nc_entry = gnrc_ipv6_nc_add(iface, &ipv6->src, NULL, 0,
GNRC_IPV6_NC_IS_ROUTER);
if (nc_entry == NULL) {
DEBUG("ndp: error on default router list entry creation\n");
return;
}
}
else if ((nc_entry->flags & GNRC_IPV6_NC_IS_ROUTER) && (byteorder_ntohs(rtr_adv->ltime) == 0)) {
nc_entry->flags &= ~GNRC_IPV6_NC_IS_ROUTER;
}
else {
nc_entry->flags |= GNRC_IPV6_NC_IS_ROUTER;
}
/* set router life timer */
if (rtr_adv->ltime.u16 != 0) {
uint16_t ltime = byteorder_ntohs(rtr_adv->ltime);
#ifdef MODULE_GNRC_SIXLOWPAN_ND
next_rtr_sol = ltime;
#endif
vtimer_remove(&nc_entry->rtr_timeout);
vtimer_set_msg(&nc_entry->rtr_timeout, timex_set(ltime, 0),
thread_getpid(), GNRC_NDP_MSG_RTR_TIMEOUT, nc_entry);
}
/* set current hop limit from message if available */
if (rtr_adv->cur_hl != 0) {
if_entry->cur_hl = rtr_adv->cur_hl;
}
/* set flags from message */
if_entry->flags &= ~GNRC_IPV6_NETIF_FLAGS_RTR_ADV_MASK;
if_entry->flags |= (rtr_adv->flags << GNRC_IPV6_NETIF_FLAGS_RTR_ADV_POS) &
GNRC_IPV6_NETIF_FLAGS_RTR_ADV_MASK;
/* set reachable time from message if it is not the same as the random base
* value */
if ((rtr_adv->reach_time.u32 != 0) &&
(if_entry->reach_time_base != byteorder_ntohl(rtr_adv->reach_time))) {
_set_reach_time(if_entry, byteorder_ntohl(rtr_adv->reach_time));
}
/* set retransmission timer from message */
if (rtr_adv->retrans_timer.u32 != 0) {
if_entry->retrans_timer = timex_set(0, byteorder_ntohl(rtr_adv->retrans_timer));
timex_normalize(&if_entry->retrans_timer);
}
mutex_unlock(&if_entry->mutex);
sicmpv6_size -= sizeof(ndp_rtr_adv_t);
/* parse options */
while (sicmpv6_size > 0) {
ndp_opt_t *opt = (ndp_opt_t *)(buf + opt_offset);
switch (opt->type) {
case NDP_OPT_SL2A:
if ((l2src_len = gnrc_ndp_internal_sl2a_opt_handle(pkt, ipv6, rtr_adv->type, opt,
l2src)) < 0) {
/* -ENOTSUP can not happen */
/* invalid source link-layer address option */
return;
}
break;
case NDP_OPT_MTU:
if (!gnrc_ndp_internal_mtu_opt_handle(iface, rtr_adv->type, (ndp_opt_mtu_t *)opt)) {
/* invalid MTU option */
return;
}
break;
case NDP_OPT_PI:
if (!gnrc_ndp_internal_pi_opt_handle(iface, rtr_adv->type, (ndp_opt_pi_t *)opt)) {
/* invalid prefix information option */
return;
}
#ifdef MODULE_GNRC_SIXLOWPAN_ND
if (byteorder_ntohl(((ndp_opt_pi_t *)opt)->valid_ltime) <
next_rtr_sol) {
next_rtr_sol = byteorder_ntohl(((ndp_opt_pi_t *)opt)->valid_ltime);
}
#endif
break;
#ifdef MODULE_GNRC_SIXLOWPAN_ND
case NDP_OPT_6CTX:
if (!gnrc_sixlowpan_nd_opt_6ctx_handle(rtr_adv->type,
(sixlowpan_nd_opt_6ctx_t *)opt)) {
/* invalid 6LoWPAN context option */
return;
}
if (byteorder_ntohs(((sixlowpan_nd_opt_6ctx_t *)opt)->ltime) <
(next_rtr_sol / 60)) {
next_rtr_sol = byteorder_ntohs(((sixlowpan_nd_opt_6ctx_t *)opt)->ltime) * 60;
}
break;
#endif
#ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER
case NDP_OPT_ABR:
gnrc_sixlowpan_nd_opt_abr_handle(iface, rtr_adv, icmpv6_size,
(sixlowpan_nd_opt_abr_t *)opt);
break;
#endif
}
opt_offset += (opt->len * 8);
sicmpv6_size -= (opt->len * 8);
#if ENABLE_DEBUG
if (sicmpv6_size < 0) {
DEBUG("ndp: Option parsing out of sync.\n");
}
#endif
}
#if ENABLE_DEBUG && defined(MODULE_GNRC_SIXLOWPAN_ND)
if ((if_entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) && (l2src_len <= 0)) {
DEBUG("ndp: Router advertisement did not contain any source address information\n");
}
#endif
_stale_nc(iface, &ipv6->src, l2src, l2src_len);
#ifdef MODULE_GNRC_SIXLOWPAN_ND
if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) {
/* stop multicast router solicitation retransmission timer */
vtimer_remove(&if_entry->rtr_sol_timer);
/* 3/4 of the time should be "well before" enough the respective timeout
* not to run out; see https://tools.ietf.org/html/rfc6775#section-5.4.3 */
next_rtr_sol *= 3;
next_rtr_sol >>= 2;
/* according to https://tools.ietf.org/html/rfc6775#section-5.3:
* "In all cases, the RS retransmissions are terminated when an RA is
* received."
* Hence, reset router solicitation counter and reset timer. */
if_entry->rtr_sol_count = 0;
gnrc_sixlowpan_nd_rtr_sol_reschedule(nc_entry, next_rtr_sol);
gnrc_ndp_internal_send_nbr_sol(nc_entry->iface, NULL, &nc_entry->ipv6_addr,
&nc_entry->ipv6_addr);
if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER) {
gnrc_ipv6_netif_set_rtr_adv(if_entry, true);
}
}
#endif
}
void gnrc_ndp_retrans_nbr_sol(gnrc_ipv6_nc_t *nc_entry)
{
if ((gnrc_ipv6_nc_get_state(nc_entry) == GNRC_IPV6_NC_STATE_INCOMPLETE) ||
(gnrc_ipv6_nc_get_state(nc_entry) == GNRC_IPV6_NC_STATE_PROBE)) {
if (nc_entry->probes_remaining > 1) {
ipv6_addr_t dst;
DEBUG("ndp: Retransmit neighbor solicitation for %s\n",
ipv6_addr_to_str(addr_str, &nc_entry->ipv6_addr, sizeof(addr_str)));
/* retransmit neighbor solicatation */
if (gnrc_ipv6_nc_get_state(nc_entry) == GNRC_IPV6_NC_STATE_INCOMPLETE) {
ipv6_addr_set_solicited_nodes(&dst, &nc_entry->ipv6_addr);
}
else {
dst.u64[0] = nc_entry->ipv6_addr.u64[0];
dst.u64[1] = nc_entry->ipv6_addr.u64[1];
}
nc_entry->probes_remaining--;
if (nc_entry->iface == KERNEL_PID_UNDEF) {
timex_t t = { 0, GNRC_NDP_RETRANS_TIMER };
kernel_pid_t ifs[GNRC_NETIF_NUMOF];
size_t ifnum = gnrc_netif_get(ifs);
for (size_t i = 0; i < ifnum; i++) {
gnrc_ndp_internal_send_nbr_sol(ifs[i], NULL, &nc_entry->ipv6_addr, &dst);
}
vtimer_remove(&nc_entry->nbr_sol_timer);
vtimer_set_msg(&nc_entry->nbr_sol_timer, t, gnrc_ipv6_pid,
GNRC_NDP_MSG_NBR_SOL_RETRANS, nc_entry);
}
else {
gnrc_ipv6_netif_t *ipv6_iface = gnrc_ipv6_netif_get(nc_entry->iface);
gnrc_ndp_internal_send_nbr_sol(nc_entry->iface, NULL, &nc_entry->ipv6_addr, &dst);
mutex_lock(&ipv6_iface->mutex);
vtimer_remove(&nc_entry->nbr_sol_timer);
vtimer_set_msg(&nc_entry->nbr_sol_timer,
ipv6_iface->retrans_timer, gnrc_ipv6_pid,
GNRC_NDP_MSG_NBR_SOL_RETRANS, nc_entry);
mutex_unlock(&ipv6_iface->mutex);
}
}
else if (nc_entry->probes_remaining <= 1) {
/* For a 6LoWPAN router entries may be set to UNREACHABLE instead
* of removing them, since RFC6775, section 6
* (https://tools.ietf.org/html/rfc6775#section-6) says: "if NUD on
* the router determines that the host is UNREACHABLE (based on the
* logic in [RFC4861]), the NCE SHOULD NOT be deleted but rather
* retained until the Registration Lifetime expires." However, this
* "SHOULD NOT" is not implemented to circumvent NCEs going into
* UNREACHABLE forever and in order to save some memory in the
* neighbor cache. */
DEBUG("ndp: Remove nc entry %s for interface %" PRIkernel_pid "\n",
ipv6_addr_to_str(addr_str, &nc_entry->ipv6_addr, sizeof(addr_str)),
nc_entry->iface);
gnrc_ipv6_nc_remove(nc_entry->iface, &nc_entry->ipv6_addr);
}
}
}
void gnrc_ndp_state_timeout(gnrc_ipv6_nc_t *nc_entry)
{
switch (gnrc_ipv6_nc_get_state(nc_entry)) {
case GNRC_IPV6_NC_STATE_REACHABLE:
gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_STALE);
break;
case GNRC_IPV6_NC_STATE_DELAY:
gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_PROBE);
break;
default:
break;
}
}
void gnrc_ndp_netif_add(gnrc_ipv6_netif_t *iface)
{
/* set default values */
mutex_lock(&iface->mutex);
_set_reach_time(iface, GNRC_NDP_REACH_TIME);
iface->retrans_timer = timex_set(0, GNRC_NDP_RETRANS_TIMER);
timex_normalize(&iface->retrans_timer);
mutex_unlock(&iface->mutex);
}
void gnrc_ndp_netif_remove(gnrc_ipv6_netif_t *iface)
{
/* TODO */
(void) iface;
}
gnrc_pktsnip_t *gnrc_ndp_nbr_sol_build(ipv6_addr_t *tgt, gnrc_pktsnip_t *options)
{
gnrc_pktsnip_t *pkt;
DEBUG("ndp: building neighbor solicitation message\n");
if (ipv6_addr_is_multicast(tgt)) {
DEBUG("ndp: tgt must not be multicast\n");
return NULL;
}
pkt = gnrc_icmpv6_build(options, ICMPV6_NBR_SOL, 0, sizeof(ndp_nbr_sol_t));
if (pkt != NULL) {
ndp_nbr_sol_t *nbr_sol = pkt->data;
nbr_sol->resv.u32 = 0;
nbr_sol->tgt.u64[0].u64 = tgt->u64[0].u64;
nbr_sol->tgt.u64[1].u64 = tgt->u64[1].u64;
}
return pkt;
}
gnrc_pktsnip_t *gnrc_ndp_nbr_adv_build(uint8_t flags, ipv6_addr_t *tgt,
gnrc_pktsnip_t *options)
{
gnrc_pktsnip_t *pkt;
DEBUG("ndp: building neighbor advertisement message\n");
if (ipv6_addr_is_multicast(tgt)) {
DEBUG("ndp: tgt must not be multicast\n");
return NULL;
}
pkt = gnrc_icmpv6_build(options, ICMPV6_NBR_ADV, 0, sizeof(ndp_nbr_adv_t));
if (pkt != NULL) {
ndp_nbr_adv_t *nbr_adv = pkt->data;
nbr_adv->flags = (flags & NDP_NBR_ADV_FLAGS_MASK);
nbr_adv->resv[0] = nbr_adv->resv[1] = nbr_adv->resv[2] = 0;
nbr_adv->tgt.u64[0].u64 = tgt->u64[0].u64;
nbr_adv->tgt.u64[1].u64 = tgt->u64[1].u64;
}
return pkt;
}
gnrc_pktsnip_t *gnrc_ndp_rtr_sol_build(gnrc_pktsnip_t *options)
{
gnrc_pktsnip_t *pkt;
DEBUG("ndp: building router solicitation message\n");
pkt = gnrc_icmpv6_build(options, ICMPV6_RTR_SOL, 0, sizeof(ndp_rtr_sol_t));
if (pkt != NULL) {
ndp_rtr_sol_t *rtr_sol = pkt->data;
rtr_sol->resv.u32 = 0;
}
return pkt;
}
static inline size_t _ceil8(uint8_t length)
{
/* NDP options use units of 8 byte for there length field, so round up */
return (length + 7U) & 0xf8U;
}
gnrc_pktsnip_t *gnrc_ndp_opt_build(uint8_t type, size_t size, gnrc_pktsnip_t *next)
{
ndp_opt_t *opt;
gnrc_pktsnip_t *pkt = gnrc_pktbuf_add(next, NULL, _ceil8(size), GNRC_NETTYPE_UNDEF);
if (pkt == NULL) {
DEBUG("ndp: no space left in packet buffer\n");
return NULL;
}
opt = pkt->data;
opt->type = type;
opt->len = (uint8_t)(pkt->size / 8);
return pkt;
}
static inline gnrc_pktsnip_t *_opt_l2a_build(uint8_t type, const uint8_t *l2addr,
uint8_t l2addr_len, gnrc_pktsnip_t *next)
{
gnrc_pktsnip_t *pkt = gnrc_ndp_opt_build(type, sizeof(ndp_opt_t) + l2addr_len, next);
if (pkt != NULL) {
ndp_opt_t *l2a_opt = pkt->data;
memset(l2a_opt + 1, 0, pkt->size - sizeof(ndp_opt_t));
memcpy(l2a_opt + 1, l2addr, l2addr_len);
}
return pkt;
}
gnrc_pktsnip_t *gnrc_ndp_opt_sl2a_build(const uint8_t *l2addr, uint8_t l2addr_len,
gnrc_pktsnip_t *next)
{
DEBUG("ndp: building source link-layer address option (l2addr: %s)\n",
gnrc_netif_addr_to_str(addr_str, sizeof(addr_str), l2addr, l2addr_len));
return _opt_l2a_build(NDP_OPT_SL2A, l2addr, l2addr_len, next);
}
gnrc_pktsnip_t *gnrc_ndp_opt_tl2a_build(const uint8_t *l2addr, uint8_t l2addr_len,
gnrc_pktsnip_t *next)
{
DEBUG("ndp: building target link-layer address option (l2addr: %s)\n",
gnrc_netif_addr_to_str(addr_str, sizeof(addr_str), l2addr, l2addr_len));
return _opt_l2a_build(NDP_OPT_TL2A, l2addr, l2addr_len, next);
}
#if (defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_ROUTER))
gnrc_pktsnip_t *gnrc_ndp_rtr_adv_build(uint8_t cur_hl, uint8_t flags,
uint16_t ltime, uint32_t reach_time,
uint32_t retrans_timer, gnrc_pktsnip_t *options)
{
gnrc_pktsnip_t *pkt;
DEBUG("ndp rtr: building router advertisement message\n");
pkt = gnrc_icmpv6_build(options, ICMPV6_RTR_ADV, 0, sizeof(ndp_rtr_adv_t));
if (pkt != NULL) {
ndp_rtr_adv_t *rtr_adv = pkt->data;
rtr_adv->cur_hl = cur_hl;
rtr_adv->flags = (flags & NDP_RTR_ADV_FLAGS_MASK);
rtr_adv->ltime = byteorder_htons(ltime);
rtr_adv->reach_time = byteorder_htonl(reach_time);
rtr_adv->retrans_timer = byteorder_htonl(retrans_timer);
}
return pkt;
}
gnrc_pktsnip_t *gnrc_ndp_opt_pi_build(uint8_t prefix_len, uint8_t flags,
uint32_t valid_ltime, uint32_t pref_ltime,
ipv6_addr_t *prefix, gnrc_pktsnip_t *next)
{
gnrc_pktsnip_t *pkt = gnrc_ndp_opt_build(NDP_OPT_PI, sizeof(ndp_opt_pi_t),
next);
if (pkt != NULL) {
ndp_opt_pi_t *pi_opt = pkt->data;
pi_opt->prefix_len = prefix_len;
pi_opt->flags = (flags & NDP_OPT_PI_FLAGS_MASK);
pi_opt->valid_ltime = byteorder_htonl(valid_ltime);
pi_opt->pref_ltime = byteorder_htonl(pref_ltime);
pi_opt->resv.u32 = 0;
/* Bits beyond prefix_len MUST be 0 */
ipv6_addr_set_unspecified(&pi_opt->prefix);
ipv6_addr_init_prefix(&pi_opt->prefix, prefix, prefix_len);
}
return pkt;
}
gnrc_pktsnip_t *gnrc_ndp_opt_mtu_build(uint32_t mtu, gnrc_pktsnip_t *next)
{
gnrc_pktsnip_t *pkt = gnrc_ndp_opt_build(NDP_OPT_MTU, sizeof(ndp_opt_mtu_t),
next);
if (pkt != NULL) {
ndp_opt_mtu_t *mtu_opt = pkt->data;
mtu_opt->resv.u16 = 0;
mtu_opt->mtu = byteorder_htonl(mtu);
}
return pkt;
}
#endif
/**
* @}
*/