Skip to content

Commit

Permalink
nib: implement public NIB functions up to link-local AR
Browse files Browse the repository at this point in the history
  • Loading branch information
miri64 committed Oct 10, 2017
1 parent d30216d commit 1b36cdf
Show file tree
Hide file tree
Showing 12 changed files with 2,015 additions and 18 deletions.
1 change: 1 addition & 0 deletions Makefile.dep
Expand Up @@ -330,6 +330,7 @@ endif

ifneq (,$(filter gnrc_ipv6_nib_6ln,$(USEMODULE)))
USEMODULE += gnrc_ipv6_nib
USEMODULE += gnrc_sixlowpan_nd
endif

ifneq (,$(filter gnrc_ipv6_nib_router,$(USEMODULE)))
Expand Down
109 changes: 109 additions & 0 deletions sys/include/net/gnrc/ipv6/nib.h
Expand Up @@ -12,6 +12,8 @@
* @brief Neighbor Information Base (NIB) for IPv6
*
* @todo Add detailed description
* @todo Implement multihop DAD
* @todo Implement classic SLAAC
* @{
*
* @file
Expand All @@ -27,6 +29,12 @@
#include "net/gnrc/ipv6/nib/nc.h"
#include "net/gnrc/ipv6/nib/pl.h"

#include "net/icmpv6.h"
#include "net/ipv6/addr.h"
#include "net/ipv6/hdr.h"
#include "net/gnrc/ipv6/nib/nc.h"
#include "net/gnrc/pkt.h"

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -181,8 +189,109 @@ extern "C" {
* context is a valid default router entry representing the router.
*/
#define GNRC_IPV6_NIB_RTR_TIMEOUT (0x4fcdU)

/**
* @brief Recalculate reachability timeout time.
*
* This message type is for the event of recalculating the reachability timeout
* time. The expected message context is a valid interface.
*
* @note Only handled with @ref GNRC_IPV6_NIB_CONF_ARSM != 0
*/
#define GNRC_IPV6_NIB_RECALC_REACH_TIME (0x4fceU)
/** @} */

/**
* @brief Initialize NIB
*/
void gnrc_ipv6_nib_init(void);

/**
* @brief Adds an interface to be managed by the NIB.
*
* @pre `(KERNEL_PID_UNDEF < iface)`
*
* @param[in] iface The interface to be managed by the NIB
*/
void gnrc_ipv6_nib_init_iface(kernel_pid_t iface);

/**
* @brief Gets link-layer address of next hop to a destination address
*
* @pre `(dst != NULL) && (nce != NULL)`
*
* @param[in] dst Destination address of a packet.
* @param[in] iface Restrict search to this interface. May be
* `KERNEL_PID_UNDEF` for any interface.
* @param[in] pkt The IPv6 packet in sending order for which the next hop
* is searched. Needed for queuing for with reactive
* routing or address resolution. May be `NULL`.
* Will be released properly on error.
* @param[out] nce The neighbor cache entry of the next hop to @p dst.
*
* @return 0, on success.
* @return -ENETUNREACH if there is no route to host.
* @return -EHOSTUNREACH if the next hop is not reachable or if @p dst was
* link-local, but @p iface was @ref KERNEL_PID_UNDEF (no neighbor
* cache entry will be created in this case and no neighbor
* solicitation sent).
*/
int gnrc_ipv6_nib_get_next_hop_l2addr(const ipv6_addr_t *dst,
kernel_pid_t iface, gnrc_pktsnip_t *pkt,
gnrc_ipv6_nib_nc_t *nce);

/**
* @brief Handles a received ICMPv6 packet
*
* @pre `iface != KERNEL_PID_UNDEF`
* @pre `ipv6 != NULL`
* @pre `icmpv6 != NULL`
* @pre `icmpv6_len > sizeof(icmpv6_hdr_t)`
*
* @attention The ICMPv6 checksum is supposed to be checked externally!
*
* @note @p ipv6 is just used for the addresses and hop limit. The next
* header field will not be checked for correctness (but should be
* @ref PROTNUM_ICMPV6)
*
* @see [RFC 4861, section 6.1](https://tools.ietf.org/html/rfc4861#section-6.1)
* @see [RFC 4861, section 6.2.6](https://tools.ietf.org/html/rfc4861#section-6.2.6)
* @see [RFC 4861, section 6.3.4](https://tools.ietf.org/html/rfc4861#section-6.3.4)
* @see [RFC 4861, section 7.1](https://tools.ietf.org/html/rfc4861#section-7.1)
* @see [RFC 4861, section 7.2.3](https://tools.ietf.org/html/rfc4861#section-7.2.3)
* @see [RFC 4861, section 7.2.5](https://tools.ietf.org/html/rfc4861#section-7.2.5)
* @see [RFC 4861, section 8.1](https://tools.ietf.org/html/rfc4861#section-8.1)
* @see [RFC 4861, section 8.3](https://tools.ietf.org/html/rfc4861#section-8.3)
* @see [RFC 4862, section 5.4.3](https://tools.ietf.org/html/rfc4862#section-5.4.3)
* @see [RFC 4862, section 5.4.4](https://tools.ietf.org/html/rfc4862#section-5.4.4)
* @see [RFC 4862, section 5.5.3](https://tools.ietf.org/html/rfc4862#section-5.5.3)
* @see [RFC 6775, section 5.5.2](https://tools.ietf.org/html/rfc6775#section-5.5.2)
* @see [RFC 6775, section 5.4](https://tools.ietf.org/html/rfc6775#section-5.4)
* @see [RFC 6775, section 6.3](https://tools.ietf.org/html/rfc6775#section-6.3)
* @see [RFC 6775, section 6.5](https://tools.ietf.org/html/rfc6775#section-6.5)
* @see [RFC 6775, section 8.1.3](https://tools.ietf.org/html/rfc6775#section-8.1.3)
* @see [RFC 6775, section 8.2.1](https://tools.ietf.org/html/rfc6775#section-8.2.1)
* @see [RFC 6775, section 8.2.4](https://tools.ietf.org/html/rfc6775#section-8.2.4)
* @see [RFC 6775, section 8.2.5](https://tools.ietf.org/html/rfc6775#section-8.2.5)
*
* @param[in] iface The interface the packet came over.
* @param[in] ipv6 The IPv6 header of the received packet.
* @param[in] icmpv6 The ICMPv6 header and payload of the received
* packet.
* @param[in] icmpv6_len The number of bytes at @p icmpv6.
*/
void gnrc_ipv6_nib_handle_pkt(kernel_pid_t iface, const ipv6_hdr_t *ipv6,
const icmpv6_hdr_t *icmpv6, size_t icmpv6_len);

/**
* @brief Handles a timer event
*
* @param[in] ctx Context of the timer event.
* @param[in] type Type of the timer event (see [timer event
* types](@ref net_gnrc_ipv6_nib_msg))
*/
void gnrc_ipv6_nib_handle_timer_event(void *ctx, uint16_t type);

#ifdef __cplusplus
}
#endif
Expand Down
151 changes: 151 additions & 0 deletions sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.c
@@ -0,0 +1,151 @@
/*
* Copyright (C) 2017 Freie Universität Berlin
*
* 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.
*/

/**
* @{
*
* @file
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/

#include "net/gnrc/ipv6/nib.h"

#include "_nib-6ln.h"
#include "_nib-6lr.h"

#define ENABLE_DEBUG (0)
#include "debug.h"

#if GNRC_IPV6_NIB_CONF_6LN
#if ENABLE_DEBUG
static char addr_str[IPV6_ADDR_MAX_STR_LEN];
#endif

static bool _is_iface_eui64(kernel_pid_t iface, const eui64_t *eui64)
{
eui64_t iface_eui64;

/* XXX: this *should* return successful so don't test it ;-) */
gnrc_netapi_get(iface, NETOPT_ADDRESS_LONG, 0,
&iface_eui64, sizeof(iface_eui64));
return (memcmp(&iface_eui64, eui64, sizeof(iface_eui64)) != 0);
}

bool _resolve_addr_from_ipv6(const ipv6_addr_t *dst, kernel_pid_t iface,
gnrc_ipv6_nib_nc_t *nce)
{
gnrc_ipv6_netif_t *netif = gnrc_ipv6_netif_get(iface);
bool res = (netif != NULL) && _is_6ln(netif) &&
ipv6_addr_is_link_local(dst);

if (res) {
memcpy(&nce->ipv6, dst, sizeof(nce->ipv6));
memcpy(&nce->l2addr, &dst->u64[1], sizeof(dst->u64[1]));
nce->l2addr[0] ^= 0x02;
nce->info = 0;
nce->info |= (iface << GNRC_IPV6_NIB_NC_INFO_IFACE_POS) &
GNRC_IPV6_NIB_NC_INFO_IFACE_MASK;
nce->info |= GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE;
nce->info |= GNRC_IPV6_NIB_NC_INFO_AR_STATE_REGISTERED;
nce->l2addr_len = sizeof(dst->u64[1]);
}
return res;
}

uint8_t _handle_aro(kernel_pid_t iface, const ipv6_hdr_t *ipv6,
const icmpv6_hdr_t *icmpv6,
const sixlowpan_nd_opt_ar_t *aro, const ndp_opt_t *sl2ao,
_nib_onl_entry_t *nce)
{
gnrc_ipv6_netif_t *netif = gnrc_ipv6_netif_get(iface);

#if !GNRC_IPV6_NIB_CONF_6LR
(void)sl2ao;
#endif
assert(netif != NULL);
if (_is_6ln(netif) && (aro->len == SIXLOWPAN_ND_OPT_AR_LEN)) {
DEBUG("nib: valid ARO received\n");
DEBUG(" - length: %u\n", aro->len);
DEBUG(" - status: %u\n", aro->status);
DEBUG(" - registration lifetime: %u\n", byteorder_ntohs(aro->ltime));
DEBUG(" - EUI-64: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
aro->eui64.uint8[0], aro->eui64.uint8[1], aro->eui64.uint8[2],
aro->eui64.uint8[3], aro->eui64.uint8[4], aro->eui64.uint8[5],
aro->eui64.uint8[6], aro->eui64.uint8[7]);
if (icmpv6->type == ICMPV6_NBR_ADV) {
if (!_is_iface_eui64(iface, &aro->eui64)) {
DEBUG("nib: ARO EUI-64 is not mine, ignoring ARO\n");
return _ADDR_REG_STATUS_IGNORE;
}
switch (aro->status) {
case SIXLOWPAN_ND_STATUS_SUCCESS: {
uint16_t ltime = byteorder_ntohs(aro->ltime);
uint32_t next_ns;
/* if ltime 1min, reschedule NS in 30sec, otherwise 1min
* before timeout */
next_ns = (ltime == 1U) ? (30 * MS_PER_SEC) :
(byteorder_ntohs(aro->ltime) - 1U) *
SEC_PER_MIN * MS_PER_SEC;
DEBUG("nib: Address registration successful. "
"Scheduling re-registration in %ums\n",
next_ns);
assert(nce != NULL);
_evtimer_add(nce, GNRC_IPV6_NIB_SND_UC_NS, &nce->nud_timeout,
next_ns);
break;
}
case SIXLOWPAN_ND_STATUS_DUP:
DEBUG("nib: Address registration reports duplicate. "
"Removing address %s%%%u\n",
ipv6_addr_to_str(addr_str,
&((ndp_nbr_adv_t *)icmpv6)->tgt,
sizeof(addr_str)),
iface);
gnrc_ipv6_netif_remove_addr(iface,
&((ndp_nbr_adv_t *)icmpv6)->tgt);
/* TODO: generate new address */
break;
case SIXLOWPAN_ND_STATUS_NC_FULL: {
DEBUG("nib: Router's neighbor cache is full. "
"Searching new router for DAD\n");
_nib_dr_entry_t *dr = _nib_drl_get(&ipv6->src, iface);
assert(dr != NULL); /* otherwise we wouldn't be here */
_nib_drl_remove(dr);
if (_nib_drl_iter(NULL) == NULL) { /* no DRL left */
_nib_iface_t *nib_iface = _nib_iface_get(iface);
nib_iface->rs_sent = 0;
/* TODO: search new router */
}
else {
assert(dr->next_hop != NULL);
_snd_uc_ns(dr->next_hop, true);
}
}
break;
}
return aro->status;
}
#if GNRC_IPV6_NIB_CONF_6LR
else if (_is_6lr(netif) && (icmpv6->type == ICMPV6_NBR_SOL)) {
return _reg_addr_upstream(iface, ipv6, icmpv6, aro, sl2ao);
}
#endif
}
#if ENABLE_DEBUG
else if (aro->len != SIXLOWPAN_ND_OPT_AR_LEN) {
DEBUG("nib: ARO of unexpected length %u, ignoring ARO\n", aro->len);
}
#endif
return _ADDR_REG_STATUS_IGNORE;
}
#else /* GNRC_IPV6_NIB_CONF_6LN */
typedef int dont_be_pedantic;
#endif /* GNRC_IPV6_NIB_CONF_6LN */


/** @} */
106 changes: 106 additions & 0 deletions sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.h
@@ -0,0 +1,106 @@
/*
* Copyright (C) 2017 Freie Universität Berlin
*
* 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_ipv6_nib
* @{
*
* @file
* @brief Definitions related to 6Lo node (6LN) functionality of the NIB
* @see @ref GNRC_IPV6_NIB_CONF_6LN
*
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/
#ifndef PRIV_NIB_6LN_H
#define PRIV_NIB_6LN_H

#include <stdint.h>

#include "net/gnrc/ipv6/nib/conf.h"
#include "net/sixlowpan/nd.h"

#include "_nib-arsm.h"
#include "_nib-internal.h"

#ifdef __cplusplus
extern "C" {
#endif

#if GNRC_IPV6_NIB_CONF_6LN || defined(DOXYGEN)
/**
* @brief Additional (local) status to ARO status values for tentative
* addresses
*/
#define _ADDR_REG_STATUS_TENTATIVE (3)

/**
* @brief Additional (local) status to ARO status values for return values
* to signify that the address was ignored
*/
#define _ADDR_REG_STATUS_IGNORE (4)

/**
* @brief Checks if interface represents a 6LN
*
* @todo Use corresponding function in `gnrc_netif2` instead.
*
* @param[in] netif A network interface.
*
* @return true, when the @p netif represents a 6LN.
* @return false, when the @p netif does not represent a 6LN.
*/
static inline bool _is_6ln(const gnrc_ipv6_netif_t *netif)
{
return (netif->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN);
}

/**
* @brief Resolves address statically from destination address using reverse
* translation of the IID
*
* @param[in] dst A destination address.
* @param[in] iface The interface to @p dst.
* @param[out] nce Neighbor cache entry to resolve into
*
* @return true when @p nce was set, false when not.
*/
bool _resolve_addr_from_ipv6(const ipv6_addr_t *dst, kernel_pid_t iface,
gnrc_ipv6_nib_nc_t *nce);

/**
* @brief Handles ARO
*
* @param[in] iface The interface the ARO-carrying message came over.
* @param[in] ipv6 The IPv6 header of the message carrying the ARO.
* @param[in] icmpv6 The message carrying the ARO.
* @param[in] aro ARO that carries the address registration information.
* @param[in] sl2ao SL2AO associated with the ARO.
* @param[in] nce Neighbor cache entry the ARO is supposed to change.
*
* @return registration status of the address (including
* @ref _ADDR_REG_STATUS_TENTATIVE and @ref _ADDR_REG_STATUS_IGNORE).
*/
uint8_t _handle_aro(kernel_pid_t iface, const ipv6_hdr_t *ipv6,
const icmpv6_hdr_t *icmpv6,
const sixlowpan_nd_opt_ar_t *aro, const ndp_opt_t *sl2ao,
_nib_onl_entry_t *nce);
#else /* GNRC_IPV6_NIB_CONF_6LN || defined(DOXYGEN) */
#define _is_6ln(netif) (false)
#define _resolve_addr_from_ipv6(dst, iface, nce) (false)
/* _handle_aro() doesn't make sense without 6LR so don't even use it
* => throw error in case it is compiled in => don't define it here as NOP macro
*/
#endif /* GNRC_IPV6_NIB_CONF_6LN || defined(DOXYGEN) */


#ifdef __cplusplus
}
#endif

#endif /* PRIV_NIB_6LN_H */
/** @} */

0 comments on commit 1b36cdf

Please sign in to comment.