Browse Source

gnrc_ndp_router: initial import

dev/timer
Martine Lenders 8 years ago committed by Martine Lenders
parent
commit
abe63b3f71
  1. 8
      Makefile.dep
  2. 199
      sys/include/net/gnrc/ndp.h
  3. 24
      sys/include/net/gnrc/ndp/internal.h
  4. 88
      sys/include/net/gnrc/ndp/router.h
  5. 3
      sys/net/gnrc/Makefile
  6. 117
      sys/net/gnrc/network_layer/ndp/gnrc_ndp.c
  7. 141
      sys/net/gnrc/network_layer/ndp/internal/gnrc_ndp_internal.c
  8. 3
      sys/net/gnrc/network_layer/ndp/router/Makefile
  9. 123
      sys/net/gnrc/network_layer/ndp/router/gnrc_ndp_router.c

8
Makefile.dep

@ -86,7 +86,7 @@ endif
ifneq (,$(filter gnrc_ipv6_router_default,$(USEMODULE)))
USEMODULE += gnrc_ipv6_router
USEMODULE += gnrc_icmpv6
USEMODULE += gnrc_ndp_node
USEMODULE += gnrc_ndp_router
endif
ifneq (,$(filter gnrc_ndp_host,$(USEMODULE)))
@ -95,6 +95,12 @@ ifneq (,$(filter gnrc_ndp_host,$(USEMODULE)))
USEMODULE += vtimer
endif
ifneq (,$(filter gnrc_ndp_router,$(USEMODULE)))
USEMODULE += gnrc_ndp_node
USEMODULE += random
USEMODULE += vtimer
endif
ifneq (,$(filter gnrc_ndp_node,$(USEMODULE)))
USEMODULE += gnrc_ndp_internal
endif

199
sys/include/net/gnrc/ndp.h

@ -34,6 +34,7 @@
#include "net/gnrc/ndp/host.h"
#include "net/gnrc/ndp/internal.h"
#include "net/gnrc/ndp/router.h"
#include "net/gnrc/ndp/node.h"
#ifdef __cplusplus
@ -44,6 +45,8 @@ extern "C" {
#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_RTR_ADV_RETRANS (0x0213) /**< Message type for periodic router advertisements */
#define GNRC_NDP_MSG_RTR_ADV_DELAY (0x0214) /**< Message type for delayed router advertisements */
#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 */
@ -131,9 +134,43 @@ extern "C" {
* @brief Upper bound for randomised reachable time calculation.
*/
#define GNRC_NDP_MAX_RAND (15U)
/** @} */
/**
* @}
* @name Router constants
* @{
* @see <a href="https://tools.ietf.org/html/rfc4861#section-10">
* RFC 4861, section 10
* </a>
*/
/**
* @brief Initial router advertisement interval in seconds
*/
#define GNRC_NDP_MAX_INIT_RTR_ADV_INT (16U)
/**
* @brief Maximum number of initial router advertisement transmissions
*/
#define GNRC_NDP_MAX_INIT_RTR_ADV_NUMOF (3U)
/**
* @brief Maximum number of final router advertisement transmissions
*/
#define GNRC_NDP_MAX_FIN_RTR_ADV_NUMOF (3U)
/**
* @brief Minimum delay in seconds between router advertisement
* transmissions
*/
#define GNRC_NDP_MIN_RTR_ADV_DELAY (3U)
/**
* @brief Upper bound for randomised delay in microseconds between router
* solicitation reception and responding router advertisement
* transmission.
*/
#define GNRC_NDP_MAX_RTR_ADV_DELAY (500U * MS_IN_USEC)
/** @} */
/**
* @brief Handles received neighbor solicitations.
@ -161,6 +198,32 @@ 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);
#if (defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_ROUTER))
/**
* @brief Handles received router solicitations.
*
* @param[in] iface The receiving interface.
* @param[in] pkt The received packet.
* @param[in] ipv6 The IPv6 header in @p pkt.
* @param[in] rtr_sol The router solicitation in @p pkt.
* @param[in] icmpv6_size The overall size of the router solicitation.
*/
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);
#else
/**
* @brief A host *must* silently discard all received router solicitations.
* @see <a href="https://tools.ietf.org/html/rfc4861#section-6.2.6">
* RFC 4861, section 6.2.6
* </a>
*
* This macro is primarily an optimization to not go into the function defined
* above.
*/
#define gnrc_ndp_rtr_sol_handle(iface, pkt, ipv6, rtr_sol, size)
#endif
/**
* @brief Handles received router advertisements
*
@ -291,6 +354,65 @@ gnrc_pktsnip_t *gnrc_ndp_nbr_adv_build(uint8_t flags, ipv6_addr_t *tgt,
*/
gnrc_pktsnip_t *gnrc_ndp_rtr_sol_build(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);
#if (defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_ROUTER))
/**
* @brief Builds a router advertisement message for sending.
*
* @see <a href="https://tools.ietf.org/html/rfc4861#section-4.2">
* RFC 4861, section 4.2
* </a>
*
* @note The source address for the packet MUST be the link-local address
* of the interface.
*
* @param[in] cur_hl Default hop limit for outgoing IP packets, 0 if
* unspecified by this router.
* @param[in] flags Flags as defined above.
* @ref GNRC_NDP_RTR_ADV_FLAGS_M == 1 indicates, that the
* addresses are managed by DHCPv6,
* @ref GNRC_NDP_RTR_ADV_FLAGS_O == 1 indicates that other
* configuration information is available via DHCPv6.
* @param[in] ltime Lifetime of the default router in seconds.
* @param[in] reach_time Time in milliseconds a node should assume a neighbor
* reachable. 0 means unspecified by the router.
* @param[in] retrans_timer Time in milliseconds between retransmitted
* neighbor solicitations. 0 means unspecified by
* the router.
* @param[in] options Options to append to the router advertisement.
*
* @return The resulting ICMPv6 packet on success.
* @return NULL, on failure.
*/
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);
#else
/**
* @brief A host *must not* send router advertisements at any time (so why build them?)
* @see <a href="https://tools.ietf.org/html/rfc4861#section-6.3.4">
* RFC 4861, section 6.3.4
* </a>
*
* This macro is primarily an optimization to not go into the function defined
* above.
*/
#define gnrc_ndp_rtr_adv_build(cur_hl, flags, ltime, reach_time, retrans_timer, options) (NULL)
#endif
/**
* @brief Builds a generic NDP option.
*
@ -346,6 +468,81 @@ gnrc_pktsnip_t *gnrc_ndp_opt_sl2a_build(const uint8_t *l2addr, uint8_t l2addr_le
gnrc_pktsnip_t *gnrc_ndp_opt_tl2a_build(const uint8_t *l2addr, uint8_t l2addr_len,
gnrc_pktsnip_t *next);
#if (defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_ROUTER))
/**
* @brief Builds the prefix information option.
*
* @see <a href="https://tools.ietf.org/html/rfc4861#section-4.6.2">
* RFC 4861, section 4.6.2
* </a>
*
* @note Must only be used with router advertisemnents. This is not checked
* however, since nodes should silently ignore it in other NDP messages.
*
* @param[in] prefix_len The length of @p prefix in bits. Must be between
* 0 and 128.
* @param[in] flags Flags as defined above.
* @ref GNRC_NDP_OPT_PI_FLAGS_L == 1 indicates, that
* @p prefix can be used for on-link determination,
* @ref GNRC_NDP_OPT_PI_FLAGS_A == 1 indicates, that
* @p prefix can be used for stateless address
* configuration.
* @param[in] valid_ltime Length of time in seconds that @p prefix is valid.
* UINT32_MAX represents infinity.
* @param[in] pref_ltime Length of time in seconds that addresses using
* @p prefix remain prefered. UINT32_MAX represents
* infinity.
* @param[in] prefix An IPv6 address or a prefix of an IPv6 address.
* @param[in] next More options in the packet. NULL, if there are none.
*
* @return The packet snip list of options, on success
* @return NULL, if packet buffer is full
*/
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);
/**
* @brief Builds the MTU option.
*
* @see <a href="https://tools.ietf.org/html/rfc4861#section-4.6.4">
* RFC 4861, section 4.6.4
* </a>
*
* @note Must only be used with router advertisemnents. This is not checked
* however, since nodes should silently ignore it in other NDP messages.
*
* @param[in] mtu The recommended MTU for the link.
* @param[in] next More options in the packet. NULL, if there are none.
*
* @return The packet snip list of options, on success
* @return NULL, if packet buffer is full
*/
gnrc_pktsnip_t *gnrc_ndp_opt_mtu_build(uint32_t mtu, gnrc_pktsnip_t *next);
#else
/**
* @brief A host *must not* send router advertisements at any time (so why build their options?)
* @see <a href="https://tools.ietf.org/html/rfc4861#section-6.3.4">
* RFC 4861, section 6.3.4
* </a>
*
* This macro is primarily an optimization to not go into the function defined
* above.
*/
#define gnrc_ndp_opt_pi_build(prefix_len, flags, valid_ltime, pref_ltime, prefix, next) (NULL)
/**
* @brief A host *must not* send router advertisements at any time (so why build their options?)
* @see <a href="https://tools.ietf.org/html/rfc4861#section-6.3.4">
* RFC 4861, section 6.3.4
* </a>
*
* This macro is primarily an optimization to not go into the function defined
* above.
*/
#define gnrc_ndp_opt_mtu_build(mtu, next) (NULL)
#endif
#ifdef __cplusplus
}
#endif

24
sys/include/net/gnrc/ndp/internal.h

@ -99,6 +99,30 @@ void gnrc_ndp_internal_send_nbr_adv(kernel_pid_t iface, ipv6_addr_t *tgt, ipv6_a
*/
void gnrc_ndp_internal_send_rtr_sol(kernel_pid_t iface, ipv6_addr_t *dst);
#if (defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_ROUTER))
/**
* @brief Handles received router solicitations.
*
* @param[in] iface Interface to send over. May not be KERNEL_PID_UNDEF.
* @param[in] src Source address for the router advertisement. May be NULL to be determined
* by source address selection (:: if no @p iface has no address).
* @param[in] dst Destination address for router advertisement.
* @ref IPV6_ADDR_ALL_NODES_LINK_LOCAL if NULL.
* @param[in] fin This is part of the router's final batch of router advertisements
* before ceising to be a router (set's router lifetime field to 0).
*/
void gnrc_ndp_internal_send_rtr_adv(kernel_pid_t iface, ipv6_addr_t *src,
ipv6_addr_t *dst, bool fin);
#else
/**
* @brief A host *must not* send router advertisements at any time.
*
* This macro is primarily an optimization to not go into the function defined
* above.
*/
#define gnrc_ndp_internal_send_rtr_adv(iface, dst, fin)
#endif
/**
* @brief Handles a SL2A option.
*

88
sys/include/net/gnrc/ndp/router.h

@ -0,0 +1,88 @@
/*
* 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_router Router-specific part of router discovery.
* @ingroup net_gnrc_ndp
* @brief Router-specific part for the router discovery in IPv6
* neighbor discovery.
* @{
*
* @file
* @brief Router-specific router discovery definitions
*
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
*/
#ifndef GNRC_NDP_ROUTER_H_
#define GNRC_NDP_ROUTER_H_
#include <stdbool.h>
#include "kernel_types.h"
#include "net/ipv6/hdr.h"
#include "net/ndp.h"
#include "net/gnrc/ipv6/nc.h"
#include "timex.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Set @p iface to router mode.
*
* @details This sets/unsets the GNRC_IPV6_NETIF_FLAGS_ROUTER and
* GNRC_IPV6_NETIF_FLAGS_RTR_ADV and initializes or ceases router
* behavior for neighbor discovery.
*
* @param[in] iface An IPv6 interface. Must not be NULL.
* @param[in] enable Status for the GNRC_IPV6_NETIF_FLAGS_ROUTER and
* GNRC_IPV6_NETIF_FLAGS_RTR_ADV flags.
*/
void gnrc_ndp_router_set_router(gnrc_ipv6_netif_t *iface, bool enable);
/**
* @brief Set/Unset GNRC_IPV6_NETIF_FLAGS_RTR_ADV flag for @p iface.
*
* @see <a href="https://tools.ietf.org/html/rfc4861#section-6.2.2">
* RFC 4861, section 6.2.2
* </a>
* @see <a href="https://tools.ietf.org/html/rfc4861#section-6.2.5">
* RFC 4861, section 6.2.5
* </a>
*
* @details GNRC_IPV6_NETIF_FLAGS_RTR_ADV and initializes or ceases
* periodic router advertising behavior for neighbor discovery.
*
* @param[in] iface An IPv6 interface. Must not be NULL.
* @param[in] enable Status for the GNRC_IPV6_NETIF_FLAGS_RTR_ADV flags.
*/
void gnrc_ndp_router_set_rtr_adv(gnrc_ipv6_netif_t *iface, bool enable);
/**
* @brief Send an unsolicited router advertisement over @p iface
* and reset the timer for the next one if necessary.
*
* @param[in] iface An IPv6 interface.
*/
void gnrc_ndp_router_retrans_rtr_adv(gnrc_ipv6_netif_t *iface);
/**
* @brief Send an solicited router advertisement to IPv6 address of
* @p neighbor.
*
* @param[in] neighbor A neighbor cache entry.
*/
void gnrc_ndp_router_send_rtr_adv(gnrc_ipv6_nc_t *neighbor);
#ifdef __cplusplus
}
#endif
#endif /* GNRC_NDP_ROUTER_H_ */
/** @} */

3
sys/net/gnrc/Makefile

@ -31,6 +31,9 @@ endif
ifneq (,$(filter gnrc_ndp_node,$(USEMODULE)))
DIRS += network_layer/ndp/node
endif
ifneq (,$(filter gnrc_ndp_router,$(USEMODULE)))
DIRS += network_layer/ndp/router
endif
ifneq (,$(filter gnrc_netapi,$(USEMODULE)))
DIRS += netapi
endif

117
sys/net/gnrc/network_layer/ndp/gnrc_ndp.c

@ -275,6 +275,70 @@ void gnrc_ndp_nbr_adv_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt,
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);
}
_stale_nc(iface, &ipv6->src, l2src, l2src_len);
/* send delayed */
if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_RTR_ADV) {
timex_t delay = timex_set(0, genrand_uint32_range(0, GNRC_NDP_MAX_RTR_ADV_DELAY));
vtimer_remove(&if_entry->rtr_adv_timer);
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);
}
}
}
/* 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);
@ -297,7 +361,6 @@ void gnrc_ndp_rtr_adv_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, ipv6_hdr_t
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) ||
@ -586,6 +649,58 @@ gnrc_pktsnip_t *gnrc_ndp_opt_tl2a_build(const uint8_t *l2addr, uint8_t l2addr_le
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
/**
* @}
*/

141
sys/net/gnrc/network_layer/ndp/internal/gnrc_ndp_internal.c

@ -311,6 +311,145 @@ void gnrc_ndp_internal_send_rtr_sol(kernel_pid_t iface, ipv6_addr_t *dst)
gnrc_netapi_send(gnrc_ipv6_pid, hdr);
}
#if (defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_ROUTER))
static bool _pio_from_iface_addr(gnrc_pktsnip_t **res, gnrc_ipv6_netif_addr_t *addr,
gnrc_pktsnip_t *next)
{
if (((addr->prefix_len - 1U) > 127U) && /* 0 < prefix_len < 128 */
!ipv6_addr_is_unspecified(&addr->addr) &&
!ipv6_addr_is_link_local(&addr->addr) &&
!gnrc_ipv6_netif_addr_is_non_unicast(&addr->addr)) {
DEBUG(" - PIO for %s/%" PRIu8 "\n", ipv6_addr_to_str(addr_str, &addr->addr,
sizeof(addr_str)),
addr->prefix_len);
*res = gnrc_ndp_opt_pi_build(addr->prefix_len, (addr->flags &
(GNRC_IPV6_NETIF_ADDR_FLAGS_NDP_AUTO |
GNRC_IPV6_NETIF_ADDR_FLAGS_NDP_ON_LINK)),
addr->valid, addr->preferred, &addr->addr, next);
return true;
}
return false;
}
static gnrc_pktsnip_t *_add_pios(gnrc_ipv6_netif_t *ipv6_iface, gnrc_pktsnip_t *pkt)
{
gnrc_pktsnip_t *tmp;
for (int i = 0; i < GNRC_IPV6_NETIF_ADDR_NUMOF; i++) {
if (_pio_from_iface_addr(&tmp, &ipv6_iface->addrs[i], pkt)) {
if (tmp != NULL) {
pkt = tmp;
}
else {
DEBUG("ndp rtr: error allocating PIO\n");
gnrc_pktbuf_release(pkt);
return NULL;
}
}
}
return pkt;
}
void gnrc_ndp_internal_send_rtr_adv(kernel_pid_t iface, ipv6_addr_t *src, ipv6_addr_t *dst,
bool fin)
{
gnrc_pktsnip_t *hdr, *pkt = NULL;
ipv6_addr_t all_nodes = IPV6_ADDR_ALL_NODES_LINK_LOCAL;
gnrc_ipv6_netif_t *ipv6_iface = gnrc_ipv6_netif_get(iface);
uint32_t reach_time = 0, retrans_timer = 0;
uint16_t adv_ltime = 0;
uint8_t cur_hl = 0;
if (dst == NULL) {
dst = &all_nodes;
}
DEBUG("ndp internal: send router advertisement (iface: %" PRIkernel_pid ", dst: %s%s\n",
iface, ipv6_addr_to_str(addr_str, dst, sizeof(addr_str)), fin ? ", final" : "");
mutex_lock(&ipv6_iface->mutex);
hdr = _add_pios(ipv6_iface, pkt);
if (hdr == NULL) {
/* pkt already released in _add_pios */
mutex_unlock(&ipv6_iface->mutex);
return;
}
pkt = hdr;
if (ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_ADV_MTU) {
if ((hdr = gnrc_ndp_opt_mtu_build(ipv6_iface->mtu, pkt)) == NULL) {
DEBUG("ndp rtr: no space left in packet buffer\n");
mutex_unlock(&ipv6_iface->mutex);
gnrc_pktbuf_release(pkt);
return;
}
pkt = hdr;
}
if (src == NULL) {
/* get address from source selection algorithm */
src = gnrc_ipv6_netif_find_best_src_addr(iface, dst);
}
/* add SL2A for source address */
if (src != NULL) {
DEBUG(" - SL2A\n");
uint8_t l2src[8];
size_t l2src_len;
/* optimization note: MAY also be omitted to facilitate in-bound load balancing over
* replicated interfaces.
* source: https://tools.ietf.org/html/rfc4861#section-6.2.3 */
l2src_len = _get_l2src(iface, l2src, sizeof(l2src));
if (l2src_len > 0) {
/* add source address link-layer address option */
hdr = gnrc_ndp_opt_sl2a_build(l2src, l2src_len, NULL);
if (hdr == NULL) {
DEBUG("ndp internal: error allocating Source Link-layer address option.\n");
mutex_unlock(&ipv6_iface->mutex);
gnrc_pktbuf_release(pkt);
return;
}
pkt = hdr;
}
}
if (ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_ADV_CUR_HL) {
cur_hl = ipv6_iface->cur_hl;
}
if (ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_ADV_REACH_TIME) {
uint64_t tmp = timex_uint64(ipv6_iface->reach_time) / MS_IN_USEC;
if (tmp > (3600 * SEC_IN_MS)) { /* tmp > 1 hour */
tmp = (3600 * SEC_IN_MS);
}
reach_time = (uint32_t)tmp;
}
if (ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_ADV_RETRANS_TIMER) {
uint64_t tmp = timex_uint64(ipv6_iface->retrans_timer) / MS_IN_USEC;
if (tmp > UINT32_MAX) {
tmp = UINT32_MAX;
}
retrans_timer = (uint32_t)tmp;
}
if (!fin) {
adv_ltime = ipv6_iface->adv_ltime;
}
mutex_unlock(&ipv6_iface->mutex);
hdr = gnrc_ndp_rtr_adv_build(cur_hl,
(ipv6_iface->flags & (GNRC_IPV6_NETIF_FLAGS_OTHER_CONF |
GNRC_IPV6_NETIF_FLAGS_MANAGED)) >> 8,
adv_ltime, reach_time, retrans_timer, pkt);
if (hdr == NULL) {
DEBUG("ndp internal: error allocating router advertisement.\n");
gnrc_pktbuf_release(pkt);
return;
}
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);
}
#endif
int gnrc_ndp_internal_sl2a_opt_handle(gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6, uint8_t icmpv6_type,
ndp_opt_t *sl2a_opt, uint8_t *l2src)
{
@ -335,6 +474,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_SOL:
case ICMPV6_RTR_ADV:
case ICMPV6_NBR_SOL:
if (sl2a_len == 0) { /* in case there was no source address in l2 */
@ -403,7 +543,6 @@ bool gnrc_ndp_internal_mtu_opt_handle(kernel_pid_t iface, uint8_t icmpv6_type,
{
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;

3
sys/net/gnrc/network_layer/ndp/router/Makefile

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

123
sys/net/gnrc/network_layer/ndp/router/gnrc_ndp_router.c

@ -0,0 +1,123 @@
/*
* 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 "net/gnrc/ipv6.h"
#include "net/gnrc/ndp.h"
#include "net/gnrc/ndp/internal.h"
#include "random.h"
#include "timex.h"
#include "vtimer.h"
#include "net/gnrc/ndp/router.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static void _send_rtr_adv(gnrc_ipv6_netif_t *iface, ipv6_addr_t *dst);
void gnrc_ndp_router_set_router(gnrc_ipv6_netif_t *iface, bool enable)
{
ipv6_addr_t all_routers = IPV6_ADDR_ALL_ROUTERS_LINK_LOCAL;
if (enable && !(iface->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER)) {
gnrc_ipv6_netif_add_addr(iface->pid, &all_routers, 128,
GNRC_IPV6_NETIF_ADDR_FLAGS_NON_UNICAST);
mutex_lock(&iface->mutex);
iface->flags |= GNRC_IPV6_NETIF_FLAGS_ROUTER;
iface->max_adv_int = GNRC_IPV6_NETIF_DEFAULT_MAX_ADV_INT;
iface->min_adv_int = GNRC_IPV6_NETIF_DEFAULT_MIN_ADV_INT;
iface->adv_ltime = GNRC_IPV6_NETIF_DEFAULT_ROUTER_LTIME;
mutex_unlock(&iface->mutex);
gnrc_ndp_router_set_rtr_adv(iface, enable);
}
else if (!enable && (iface->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER)) {
gnrc_ipv6_netif_remove_addr(iface->pid, &all_routers);
gnrc_ndp_router_set_rtr_adv(iface, enable);
}
}
void gnrc_ndp_router_set_rtr_adv(gnrc_ipv6_netif_t *iface, bool enable)
{
if (enable && !(iface->flags & GNRC_IPV6_NETIF_FLAGS_RTR_ADV)) {
mutex_lock(&iface->mutex);
iface->flags |= GNRC_IPV6_NETIF_FLAGS_RTR_ADV;
iface->rtr_adv_count = GNRC_NDP_MAX_INIT_RTR_ADV_NUMOF;
mutex_unlock(&iface->mutex);
_send_rtr_adv(iface, NULL);
}
else if (!enable && (iface->flags & GNRC_IPV6_NETIF_FLAGS_RTR_ADV)) {
mutex_lock(&iface->mutex);
iface->rtr_adv_count = GNRC_NDP_MAX_FIN_RTR_ADV_NUMOF;
iface->flags &= ~GNRC_IPV6_NETIF_FLAGS_RTR_ADV;
iface->adv_ltime = 0;
#ifdef MODULE_GNRC_NDP_HOST
iface->rtr_sol_count = GNRC_NDP_MAX_RTR_SOL_NUMOF;
#endif
mutex_unlock(&iface->mutex);
_send_rtr_adv(iface, NULL);
#ifdef MODULE_GNRC_NDP_HOST
gnrc_ndp_host_retrans_rtr_sol(iface);
#endif
}
}
void gnrc_ndp_router_retrans_rtr_adv(gnrc_ipv6_netif_t *iface)
{
_send_rtr_adv(iface, NULL);
}
void gnrc_ndp_router_send_rtr_adv(gnrc_ipv6_nc_t *neighbor)
{
gnrc_ipv6_netif_t *iface = gnrc_ipv6_netif_get(neighbor->iface);
_send_rtr_adv(iface, &neighbor->ipv6_addr);
}
static void _send_rtr_adv(gnrc_ipv6_netif_t *iface, ipv6_addr_t *dst)
{
bool fin;
uint32_t interval;
mutex_lock(&iface->mutex);
fin = (iface->adv_ltime == 0);
interval = genrand_uint32_range(iface->min_adv_int, iface->max_adv_int);
if (!fin && !((iface->flags | GNRC_IPV6_NETIF_FLAGS_ROUTER) &&
(iface->flags | GNRC_IPV6_NETIF_FLAGS_RTR_ADV))) {
DEBUG("ndp rtr: interface %" PRIkernel_pid " is not an advertising interface\n",
iface->pid);
return;
}
if (iface->rtr_adv_count > 1) { /* regard for off-by-one error */
iface->rtr_adv_count--;
if (!fin && (interval > GNRC_NDP_MAX_INIT_RTR_ADV_INT)) {
interval = GNRC_NDP_MAX_INIT_RTR_ADV_INT;
}
}
if (!fin || (iface->rtr_adv_count > 1)) { /* regard for off-by-one-error */
/* reset timer for next router advertisement */
vtimer_remove(&iface->rtr_adv_timer);
vtimer_set_msg(&iface->rtr_adv_timer, timex_set(interval, 0),
gnrc_ipv6_pid, GNRC_NDP_MSG_RTR_ADV_RETRANS, iface);
}
mutex_unlock(&iface->mutex);
for (int i = 0; i < GNRC_IPV6_NETIF_ADDR_NUMOF; i++) {
ipv6_addr_t *src = &iface->addrs[i].addr;
if (!ipv6_addr_is_unspecified(src) && ipv6_addr_is_link_local(src) &&
!gnrc_ipv6_netif_addr_is_non_unicast(src)) {
/* send one for every link local address (ideally there is only one) */
gnrc_ndp_internal_send_rtr_adv(iface->pid, src, dst, fin);
}
}
}
/** @} */
Loading…
Cancel
Save