1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

gnrc_ndp_host: initial import

This commit is contained in:
Martine Lenders 2015-08-31 14:25:17 +02:00 committed by Martine Lenders
parent df21f1b59d
commit b033ff590b
10 changed files with 514 additions and 71 deletions

View File

@ -80,19 +80,29 @@ endif
ifneq (,$(filter gnrc_ipv6_default,$(USEMODULE)))
USEMODULE += gnrc_ipv6
USEMODULE += gnrc_icmpv6
USEMODULE += gnrc_ndp
USEMODULE += gnrc_ndp_internal
USEMODULE += gnrc_ndp_node
USEMODULE += gnrc_ndp_host
endif
ifneq (,$(filter gnrc_ipv6_router_default,$(USEMODULE)))
USEMODULE += gnrc_ipv6_router
USEMODULE += gnrc_icmpv6
USEMODULE += gnrc_ndp
USEMODULE += gnrc_ndp_internal
USEMODULE += gnrc_ndp_node
endif
ifneq (,$(filter gnrc_ndp_host,$(USEMODULE)))
USEMODULE += gnrc_ndp_node
USEMODULE += random
USEMODULE += vtimer
endif
ifneq (,$(filter gnrc_ndp_node,$(USEMODULE)))
USEMODULE += gnrc_ndp_internal
endif
ifneq (,$(filter gnrc_ndp_%,$(USEMODULE)))
USEMODULE += gnrc_ndp
endif
ifneq (,$(filter gnrc_ndp,$(USEMODULE)))
USEMODULE += gnrc_icmpv6
USEMODULE += random

View File

@ -194,6 +194,18 @@ extern "C" {
*/
#define GNRC_IPV6_NETIF_FLAGS_IS_WIRED (0x0080)
/**
* @brief Offset of the router advertisement flags compared to the position in router
* advertisements.
*/
#define GNRC_IPV6_NETIF_FLAGS_RTR_ADV_POS (8U)
/**
* @brief Mask for flags intended for router advertisements.
* @note Please expand if more router advertisement flags are introduced.
*/
#define GNRC_IPV6_NETIF_FLAGS_RTR_ADV_MASK (0xc000)
/**
* @brief Flag to indicate that the interface has other address
* configuration.

View File

@ -22,6 +22,7 @@
#define GNRC_NDP_H_
#include <inttypes.h>
#include <stdlib.h>
#include "byteorder.h"
#include "net/ndp.h"
@ -31,21 +32,49 @@
#include "net/gnrc/ipv6/nc.h"
#include "net/gnrc/ipv6/netif.h"
#include "net/gnrc/ndp/host.h"
#include "net/gnrc/ndp/internal.h"
#include "net/gnrc/ndp/node.h"
#ifdef __cplusplus
extern "C" {
#endif
#define GNRC_NDP_MSG_RTR_TIMEOUT (0x0211) /**< Message type for router timeouts */
#define GNRC_NDP_MSG_ADDR_TIMEOUT (0x0212) /**< Message type for address timeouts */
#define GNRC_NDP_MSG_NBR_SOL_RETRANS (0x0213) /**< Message type for multicast
#define GNRC_NDP_MSG_RTR_TIMEOUT (0x0210) /**< Message type for router timeouts */
#define GNRC_NDP_MSG_ADDR_TIMEOUT (0x0211) /**< Message type for address timeouts */
#define GNRC_NDP_MSG_NBR_SOL_RETRANS (0x0212) /**< Message type for multicast
* neighbor solicitation retransmissions */
#define GNRC_NDP_MSG_NC_STATE_TIMEOUT (0x0214) /**< Message type for neighbor cache state timeouts */
#define GNRC_NDP_MSG_RTR_SOL_RETRANS (0x0215) /**< Message type for periodic router solicitations */
#define GNRC_NDP_MSG_NC_STATE_TIMEOUT (0x0216) /**< Message type for neighbor cache state timeouts */
/**
* @name Host constants
* @{
* @see <a href="https://tools.ietf.org/html/rfc4861#section-10">
* RFC 4861, section 10
* </a>
*/
/**
* @brief Upper bound for randomised delay in seconds for initial
* router solicitation transmissions
*/
#define GNRC_NDP_MAX_RTR_SOL_DELAY (1U)
/**
* @brief Interval in seconds between initial router solicitation
* transmissions
*/
#define GNRC_NDP_MAX_RTR_SOL_INT (4U)
/**
* @brief Maximum number of initial router solicitation transmissions
*/
#define GNRC_NDP_MAX_RTR_SOL_NUMOF (3U)
/** @} */
/**
* @name Node constants
* @{
* @see <a href="https://tools.ietf.org/html/rfc4861#section-10">
* RFC 4861, section 10
* </a>
@ -132,6 +161,21 @@ 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);
/**
* @brief Handles received router advertisements
*
* @todo As router check consistency as described in RFC 4861, section 6.2.3
*
* @param[in] iface The receiving interface.
* @param[in] pkt The received packet.
* @param[in] ipv6 The IPv6 header in @p pkt.
* @param[in] rtr_adv The router advertisement in @p pkt.
* @param[in] icmpv6_size The overall size of the router advertisement.
*/
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);
/**
* @brief Retransmits a multicast neighbor solicitation for an incomplete or
* probing neighbor cache entry @p nc_entry,
@ -233,6 +277,20 @@ gnrc_pktsnip_t *gnrc_ndp_nbr_sol_build(ipv6_addr_t *tgt, gnrc_pktsnip_t *options
gnrc_pktsnip_t *gnrc_ndp_nbr_adv_build(uint8_t flags, ipv6_addr_t *tgt,
gnrc_pktsnip_t *options);
/**
* @brief Builds a router solicitation message for sending.
*
* @see <a href="https://tools.ietf.org/html/rfc4861#section-4.1">
* RFC 4861, section 4.1
* </a>
*
* @param[in] options Options to append to the router solicitation.
*
* @return The resulting ICMPv6 packet on success.
* @return NULL, on failure.
*/
gnrc_pktsnip_t *gnrc_ndp_rtr_sol_build(gnrc_pktsnip_t *options);
/**
* @brief Builds a generic NDP option.
*

View File

@ -0,0 +1,54 @@
/*
* 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.
*/
/**
* @defgroup net_gnrc_ndp_host Host-specific part of router discovery.
* @ingroup net_gnrc_ndp
* @brief Host-specific part for the router discovery in IPv6
* neighbor discovery.
* @{
*
* @file
* @brief Host-specific router discovery definitions
*
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
*/
#ifndef GNRC_NDP_HOST_H_
#define GNRC_NDP_HOST_H_
#include "net/gnrc/ipv6/netif.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Initializes interface @p iface as host.
*
* @pre iface != NULL
*
* @param[in] iface An IPv6 interface
*/
void gnrc_ndp_host_init(gnrc_ipv6_netif_t *iface);
/**
* @brief Sends a router solicitation over interface @p iface
* and reset the timer for the next one.
*
* @pre iface != NULL
*
* @param[in] iface An IPv6 interface
*/
void gnrc_ndp_host_retrans_rtr_sol(gnrc_ipv6_netif_t *iface);
#ifdef __cplusplus
}
#endif
#endif /* GNRC_NDP_HOST_H_ */
/** @} */

View File

@ -22,6 +22,7 @@
#ifndef GNRC_NDP_INTERNAL_H_
#define GNRC_NDP_INTERNAL_H_
#include "kernel_types.h"
#include "net/ipv6/addr.h"
#include "net/ipv6/hdr.h"
#include "net/ndp.h"
@ -88,6 +89,16 @@ void gnrc_ndp_internal_send_nbr_sol(kernel_pid_t iface, ipv6_addr_t *tgt,
void gnrc_ndp_internal_send_nbr_adv(kernel_pid_t iface, ipv6_addr_t *tgt, ipv6_addr_t *dst,
bool supply_tl2a, gnrc_pktsnip_t *ext_opts);
/**
* @brief Send precompiled router solicitation to @p dst.
*
* @internal
*
* @param[in] iface Interface to send over. May not be KERNEL_PID_UNDEF.
* @param[in] dst Destination for the router solicitation. ff02::2 if NULL.
*/
void gnrc_ndp_internal_send_rtr_sol(kernel_pid_t iface, ipv6_addr_t *dst);
/**
* @brief Handles a SL2A option.
*
@ -121,6 +132,36 @@ int gnrc_ndp_internal_tl2a_opt_handle(gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6,
uint8_t icmpv6_type, ndp_opt_t *tl2a_opt,
uint8_t *l2addr);
/**
* @brief Handles a MTU option.
*
* @internal
*
* @param[in] iface Interface the MTU option was received on.
* @param[in] icmpv6_type ICMPv6 type of the message carrying the option.
* @param[in] mtu_opt A MTU option.
*
* @return true, on success (or if the node should silently ignore the option).
* @return false, if MTU option was not valid.
*/
bool gnrc_ndp_internal_mtu_opt_handle(kernel_pid_t iface, uint8_t icmpv6_type,
ndp_opt_mtu_t *mtu_opt);
/**
* @brief Handles a PI option.
*
* @internal
*
* @param[in] iface Interface the PI option was received on.
* @param[in] icmpv6_type ICMPv6 type of the message carrying the option.
* @param[in] pi_opt A PI option.
*
* @return true, on success (or if the node should silently ignore the option).
* @return false, if PIO was not valid.
*/
bool gnrc_ndp_internal_pi_opt_handle(kernel_pid_t iface, uint8_t icmpv6_type,
ndp_opt_pi_t *pi_opt);
#ifdef __cplusplus
}
#endif

View File

@ -25,6 +25,9 @@ endif
ifneq (,$(filter gnrc_ndp_internal,$(USEMODULE)))
DIRS += network_layer/ndp/internal
endif
ifneq (,$(filter gnrc_ndp_host,$(USEMODULE)))
DIRS += network_layer/ndp/host
endif
ifneq (,$(filter gnrc_ndp_node,$(USEMODULE)))
DIRS += network_layer/ndp/node
endif

View File

@ -275,6 +275,111 @@ void gnrc_ndp_nbr_adv_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt,
return;
}
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];
int sicmpv6_size = (int)icmpv6_size, l2src_len = 0;
uint16_t opt_offset = 0;
assert(if_entry != NULL);
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) {
vtimer_remove(&nc_entry->rtr_timeout);
vtimer_set_msg(&nc_entry->rtr_timeout,
timex_set(byteorder_ntohs(rtr_adv->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;
}
break;
}
}
_stale_nc(iface, &ipv6->src, l2src, l2src_len);
}
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) ||
@ -350,14 +455,9 @@ void gnrc_ndp_state_timeout(gnrc_ipv6_nc_t *nc_entry)
void gnrc_ndp_netif_add(gnrc_ipv6_netif_t *iface)
{
uint32_t reach_time = genrand_uint32_range(GNRC_NDP_MIN_RAND, GNRC_NDP_MAX_RAND);
/* set default values */
mutex_lock(&iface->mutex);
iface->reach_time_base = GNRC_NDP_REACH_TIME;
reach_time = (reach_time * iface->reach_time_base) / 10;
iface->reach_time = timex_set(0, reach_time);
timex_normalize(&iface->reach_time);
_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);
@ -417,6 +517,18 @@ gnrc_pktsnip_t *gnrc_ndp_nbr_adv_build(uint8_t flags, ipv6_addr_t *tgt,
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 */

View File

@ -0,0 +1,3 @@
MODULE = gnrc_ndp_host
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,56 @@
/*
* 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.
*/
/**
* @{
*
* @file
*/
#include <inttypes.h>
#include "random.h"
#include "net/gnrc/ipv6.h"
#include "net/gnrc/ndp.h"
#include "net/gnrc/ndp/internal.h"
#include "vtimer.h"
#include "net/gnrc/ndp/host.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static inline void _reschedule_rtr_sol(gnrc_ipv6_netif_t *iface, timex_t delay)
{
vtimer_remove(&iface->rtr_sol_timer);
vtimer_set_msg(&iface->rtr_sol_timer, delay, gnrc_ipv6_pid, GNRC_NDP_MSG_RTR_SOL_RETRANS,
iface);
}
void gnrc_ndp_host_init(gnrc_ipv6_netif_t *iface)
{
uint32_t interval = genrand_uint32_range(0, GNRC_NDP_MAX_RTR_SOL_DELAY * SEC_IN_USEC);
mutex_lock(&iface->mutex);
iface->rtr_sol_count = GNRC_NDP_MAX_RTR_SOL_NUMOF;
DEBUG("ndp host: delayed initial router solicitation by %" PRIu32 " usec.\n", interval);
_reschedule_rtr_sol(iface, timex_set(0, interval));
mutex_unlock(&iface->mutex);
}
void gnrc_ndp_host_retrans_rtr_sol(gnrc_ipv6_netif_t *iface)
{
mutex_lock(&iface->mutex);
if (iface->rtr_sol_count > 1) { /* regard off-by-one error */
DEBUG("ndp hst: retransmit rtr sol in %d sec\n", GNRC_NDP_MAX_RTR_SOL_INT);
iface->rtr_sol_count--;
_reschedule_rtr_sol(iface, timex_set(GNRC_NDP_MAX_RTR_SOL_INT, 0));
}
mutex_unlock(&iface->mutex);
gnrc_ndp_internal_send_rtr_sol(iface->pid, NULL);
}
/** @} */

View File

@ -33,11 +33,9 @@ static gnrc_ipv6_nc_t *_last_router = NULL; /* last router chosen as default
* router. Only used if reachability
* is suspect (i. e. incomplete or
* not at all) */
/**
* @brief Get L2 address from interface
*/
static uint16_t _get_l2src(uint8_t *l2src, size_t l2src_size, kernel_pid_t iface);
static gnrc_pktsnip_t *_build_headers(kernel_pid_t iface, gnrc_pktsnip_t *payload,
ipv6_addr_t *dst, ipv6_addr_t *src);
static size_t _get_l2src(kernel_pid_t iface, uint8_t *l2src, size_t l2src_maxlen);
/**
* @brief Sends @ref GNRC_NETAPI_MSG_TYPE_SND delayed.
@ -160,7 +158,8 @@ void gnrc_ndp_internal_send_nbr_adv(kernel_pid_t iface, ipv6_addr_t *tgt, ipv6_a
DEBUG("dst: %s, supply_tl2a: %d)\n",
ipv6_addr_to_str(addr_str, dst, sizeof(addr_str)), supply_tl2a);
if (gnrc_ipv6_netif_get(iface)->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER) {
if ((gnrc_ipv6_netif_get(iface)->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER) &&
(gnrc_ipv6_netif_get(iface)->flags & GNRC_IPV6_NETIF_FLAGS_RTR_ADV)) {
adv_flags |= NDP_NBR_ADV_FLAGS_R;
}
@ -173,9 +172,9 @@ void gnrc_ndp_internal_send_nbr_adv(kernel_pid_t iface, ipv6_addr_t *tgt, ipv6_a
if (supply_tl2a) {
uint8_t l2src[8];
uint16_t l2src_len;
size_t l2src_len;
/* we previously checked if we are the target, so we can take our L2src */
l2src_len = _get_l2src(l2src, sizeof(l2src), iface);
l2src_len = _get_l2src(iface, l2src, sizeof(l2src));
if (l2src_len > 0) {
/* add target address link-layer address option */
@ -202,32 +201,13 @@ void gnrc_ndp_internal_send_nbr_adv(kernel_pid_t iface, ipv6_addr_t *tgt, ipv6_a
gnrc_pktbuf_release(pkt);
return;
}
pkt = hdr;
hdr = gnrc_ipv6_hdr_build(pkt, NULL, 0, (uint8_t *)dst,
sizeof(ipv6_addr_t));
hdr = _build_headers(iface, pkt, dst, NULL);
if (hdr == NULL) {
DEBUG("ndp internal: error allocating IPv6 header.\n");
DEBUG("ndp internal: error adding lower-layer headers.\n");
gnrc_pktbuf_release(pkt);
return;
}
((ipv6_hdr_t *)hdr->data)->hl = 255;
pkt = hdr;
/* add netif header for send interface specification */
hdr = gnrc_netif_hdr_build(NULL, 0, NULL, 0);
if (hdr == NULL) {
DEBUG("ndp internal: error allocating netif header.\n");
return;
}
((gnrc_netif_hdr_t *)hdr->data)->if_pid = iface;
LL_PREPEND(pkt, hdr);
if (gnrc_ipv6_netif_addr_is_non_unicast(tgt)) {
/* avoid collision for anycast addresses
* (see https://tools.ietf.org/html/rfc4861#section-7.2.7) */
@ -238,10 +218,10 @@ void gnrc_ndp_internal_send_nbr_adv(kernel_pid_t iface, ipv6_addr_t *tgt, ipv6_a
delay.seconds);
/* nc_entry must be set so no need to check it */
_send_delayed(&nc_entry->nbr_adv_timer, delay, pkt);
_send_delayed(&nc_entry->nbr_adv_timer, delay, hdr);
}
else {
gnrc_netapi_send(gnrc_ipv6_pid, pkt);
gnrc_netapi_send(gnrc_ipv6_pid, hdr);
}
}
@ -250,7 +230,6 @@ void gnrc_ndp_internal_send_nbr_sol(kernel_pid_t iface, ipv6_addr_t *tgt,
{
gnrc_pktsnip_t *hdr, *pkt = NULL;
ipv6_addr_t *src = NULL;
size_t src_len = 0;
DEBUG("ndp internal: send neighbor solicitation (iface: %" PRIkernel_pid ", tgt: %s, ",
iface, ipv6_addr_to_str(addr_str, tgt, sizeof(addr_str)));
@ -259,9 +238,8 @@ void gnrc_ndp_internal_send_nbr_sol(kernel_pid_t iface, ipv6_addr_t *tgt,
/* check if there is a fitting source address to target */
if ((src = gnrc_ipv6_netif_find_best_src_addr(iface, tgt)) != NULL) {
uint8_t l2src[8];
uint16_t l2src_len;
src_len = sizeof(ipv6_addr_t);
l2src_len = _get_l2src(l2src, sizeof(l2src), iface);
size_t l2src_len;
l2src_len = _get_l2src(iface, l2src, sizeof(l2src));
if (l2src_len > 0) {
/* add source address link-layer address option */
@ -282,33 +260,55 @@ void gnrc_ndp_internal_send_nbr_sol(kernel_pid_t iface, ipv6_addr_t *tgt,
gnrc_pktbuf_release(pkt);
return;
}
pkt = hdr;
hdr = gnrc_ipv6_hdr_build(pkt, (uint8_t *)src, src_len, (uint8_t *)dst,
sizeof(ipv6_addr_t));
hdr = _build_headers(iface, pkt, dst, src);
if (hdr == NULL) {
DEBUG("ndp internal: error allocating IPv6 header.\n");
DEBUG("ndp internal: error adding lower-layer headers.\n");
gnrc_pktbuf_release(pkt);
return;
}
gnrc_netapi_send(gnrc_ipv6_pid, hdr);
}
((ipv6_hdr_t *)hdr->data)->hl = 255;
pkt = hdr;
/* add netif header for send interface specification */
hdr = gnrc_netif_hdr_build(NULL, 0, NULL, 0);
void gnrc_ndp_internal_send_rtr_sol(kernel_pid_t iface, ipv6_addr_t *dst)
{
gnrc_pktsnip_t *hdr, *pkt = NULL;
ipv6_addr_t *src = NULL, all_routers = IPV6_ADDR_ALL_ROUTERS_LINK_LOCAL;
DEBUG("ndp internal: send router solicitation (iface: %" PRIkernel_pid ", dst: ff02::2)\n",
iface);
if (dst == NULL) {
dst = &all_routers;
}
/* check if there is a fitting source address to target */
if ((src = gnrc_ipv6_netif_find_best_src_addr(iface, dst)) != NULL) {
uint8_t l2src[8];
size_t l2src_len;
l2src_len = _get_l2src(iface, l2src, sizeof(l2src));
if (l2src_len > 0) {
/* add source address link-layer address option */
pkt = gnrc_ndp_opt_sl2a_build(l2src, l2src_len, NULL);
if (pkt == NULL) {
DEBUG("ndp internal: error allocating Source Link-layer address option.\n");
gnrc_pktbuf_release(pkt);
return;
}
}
}
hdr = gnrc_ndp_rtr_sol_build(pkt);
if (hdr == NULL) {
DEBUG("ndp internal: error allocating netif header.\n");
DEBUG("ndp internal: error allocating router solicitation.\n");
gnrc_pktbuf_release(pkt);
return;
}
((gnrc_netif_hdr_t *)hdr->data)->if_pid = iface;
LL_PREPEND(pkt, hdr);
gnrc_netapi_send(gnrc_ipv6_pid, pkt);
pkt = hdr;
hdr = _build_headers(iface, pkt, dst, src);
if (hdr == NULL) {
DEBUG("ndp internal: error adding lower-layer headers.\n");
gnrc_pktbuf_release(pkt);
return;
}
gnrc_netapi_send(gnrc_ipv6_pid, hdr);
}
int gnrc_ndp_internal_sl2a_opt_handle(gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6, uint8_t icmpv6_type,
@ -335,6 +335,7 @@ int gnrc_ndp_internal_sl2a_opt_handle(gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6, uin
gnrc_netif_addr_to_str(addr_str, sizeof(addr_str), sl2a, sl2a_len));
switch (icmpv6_type) {
case ICMPV6_RTR_ADV:
case ICMPV6_NBR_SOL:
if (sl2a_len == 0) { /* in case there was no source address in l2 */
sl2a_len = (sl2a_opt->len / 8) - sizeof(ndp_opt_t);
@ -396,11 +397,81 @@ int gnrc_ndp_internal_tl2a_opt_handle(gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6,
return 0;
}
}
static uint16_t _get_l2src(uint8_t *l2src, size_t l2src_size, kernel_pid_t iface)
bool gnrc_ndp_internal_mtu_opt_handle(kernel_pid_t iface, uint8_t icmpv6_type,
ndp_opt_mtu_t *mtu_opt)
{
gnrc_ipv6_netif_t *if_entry = gnrc_ipv6_netif_get(iface);
assert(if_entry != NULL);
if ((mtu_opt->len != NDP_OPT_MTU_LEN)) {
DEBUG("ndp: invalid MTU option received\n");
return false;
}
if (icmpv6_type != ICMPV6_RTR_ADV) {
/* else discard silently */
return true;
}
mutex_lock(&if_entry->mutex);
if_entry->mtu = byteorder_ntohl(mtu_opt->mtu);
mutex_unlock(&if_entry->mutex);
return true;
}
bool gnrc_ndp_internal_pi_opt_handle(kernel_pid_t iface, uint8_t icmpv6_type,
ndp_opt_pi_t *pi_opt)
{
ipv6_addr_t *prefix;
gnrc_ipv6_netif_addr_t *netif_addr;
if ((pi_opt->len != NDP_OPT_MTU_LEN)) {
DEBUG("ndp: invalid MTU option received\n");
return false;
}
if (icmpv6_type != ICMPV6_RTR_ADV || ipv6_addr_is_link_local(&pi_opt->prefix)) {
/* else discard silently */
return true;
}
prefix = gnrc_ipv6_netif_find_addr(iface, &pi_opt->prefix);
if (((prefix == NULL) ||
(gnrc_ipv6_netif_addr_get(prefix)->prefix_len != pi_opt->prefix_len)) &&
(pi_opt->valid_ltime.u32 != 0)) {
prefix = gnrc_ipv6_netif_add_addr(iface, &pi_opt->prefix,
pi_opt->prefix_len,
pi_opt->flags & NDP_OPT_PI_FLAGS_MASK);
if (prefix == NULL) {
DEBUG("ndp: could not add prefix to interface %d\n", iface);
return false;
}
}
netif_addr = gnrc_ipv6_netif_addr_get(prefix);
if (pi_opt->valid_ltime.u32 == 0) {
if (prefix != NULL) {
gnrc_ipv6_netif_remove_addr(iface, &netif_addr->addr);
}
return true;
}
netif_addr->valid = byteorder_ntohl(pi_opt->valid_ltime);
netif_addr->preferred = byteorder_ntohl(pi_opt->pref_ltime);
vtimer_remove(&netif_addr->valid_timeout);
if (netif_addr->valid != UINT32_MAX) {
vtimer_set_msg(&netif_addr->valid_timeout,
timex_set(byteorder_ntohl(pi_opt->valid_ltime), 0),
thread_getpid(), GNRC_NDP_MSG_ADDR_TIMEOUT, &netif_addr->addr);
}
/* TODO: preferred lifetime for address auto configuration */
/* on-link flag MUST stay set if it was */
netif_addr->flags &= ~NDP_OPT_PI_FLAGS_A;
netif_addr->flags |= (pi_opt->flags & NDP_OPT_PI_FLAGS_MASK);
return true;
}
static size_t _get_l2src(kernel_pid_t iface, uint8_t *l2src, size_t l2src_maxlen)
{
bool try_long = false;
int res;
uint16_t l2src_len;
size_t l2src_len;
/* maximum address length that fits into a minimum length (8) S/TL2A option */
const uint16_t max_short_len = 6;
@ -412,11 +483,11 @@ static uint16_t _get_l2src(uint8_t *l2src, size_t l2src_size, kernel_pid_t iface
}
if (try_long && ((res = gnrc_netapi_get(iface, NETOPT_ADDRESS_LONG, 0,
l2src, l2src_size)) > max_short_len)) {
l2src, l2src_maxlen)) > max_short_len)) {
l2src_len = (uint16_t)res;
}
else if ((res = gnrc_netapi_get(iface, NETOPT_ADDRESS, 0, l2src,
l2src_size)) >= 0) {
l2src_maxlen)) >= 0) {
l2src_len = (uint16_t)res;
}
else {
@ -427,4 +498,27 @@ static uint16_t _get_l2src(uint8_t *l2src, size_t l2src_size, kernel_pid_t iface
return l2src_len;
}
static gnrc_pktsnip_t *_build_headers(kernel_pid_t iface, gnrc_pktsnip_t *payload,
ipv6_addr_t *dst, ipv6_addr_t *src)
{
gnrc_pktsnip_t *l2hdr;
gnrc_pktsnip_t *iphdr = gnrc_ipv6_hdr_build(payload, (uint8_t *)src, sizeof(ipv6_addr_t),
(uint8_t *)dst, sizeof(ipv6_addr_t));
if (iphdr == NULL) {
DEBUG("ndp internal: error allocating IPv6 header.\n");
return NULL;
}
((ipv6_hdr_t *)iphdr->data)->hl = 255;
/* add netif header for send interface specification */
l2hdr = gnrc_netif_hdr_build(NULL, 0, NULL, 0);
if (l2hdr == NULL) {
DEBUG("ndp internal: error allocating netif header.\n");
gnrc_pktbuf_remove_snip(iphdr, iphdr);
return NULL;
}
((gnrc_netif_hdr_t *)l2hdr->data)->if_pid = iface;
LL_PREPEND(iphdr, l2hdr);
return l2hdr;
}
/** @} */