diff --git a/Makefile.dep b/Makefile.dep
index 0f1bdb16cb..b533885381 100644
--- a/Makefile.dep
+++ b/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
diff --git a/sys/include/net/gnrc/ndp.h b/sys/include/net/gnrc/ndp.h
index 87fbd39bc2..5315a2c4c5 100644
--- a/sys/include/net/gnrc/ndp.h
+++ b/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
+ * RFC 4861, section 10
+ *
*/
+/**
+ * @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
+ * RFC 4861, section 6.2.6
+ *
+ *
+ * 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
+ * RFC 4861, section 4.1
+ *
+ *
+ * @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
+ * RFC 4861, section 4.2
+ *
+ *
+ * @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
+ * RFC 4861, section 6.3.4
+ *
+ *
+ * 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
+ * RFC 4861, section 4.6.2
+ *
+ *
+ * @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
+ * RFC 4861, section 4.6.4
+ *
+ *
+ * @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
+ * RFC 4861, section 6.3.4
+ *
+ *
+ * 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
+ * RFC 4861, section 6.3.4
+ *
+ *
+ * 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
diff --git a/sys/include/net/gnrc/ndp/internal.h b/sys/include/net/gnrc/ndp/internal.h
index 6765fcea89..90f8f7900a 100644
--- a/sys/include/net/gnrc/ndp/internal.h
+++ b/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.
*
diff --git a/sys/include/net/gnrc/ndp/router.h b/sys/include/net/gnrc/ndp/router.h
new file mode 100644
index 0000000000..d82c9705fb
--- /dev/null
+++ b/sys/include/net/gnrc/ndp/router.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 Martine Lenders
+ *
+ * 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
+ */
+#ifndef GNRC_NDP_ROUTER_H_
+#define GNRC_NDP_ROUTER_H_
+
+#include
+
+#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
+ * RFC 4861, section 6.2.2
+ *
+ * @see
+ * RFC 4861, section 6.2.5
+ *
+ *
+ * @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_ */
+/** @} */
diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile
index bee9e79773..69439a9aa9 100644
--- a/sys/net/gnrc/Makefile
+++ b/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
diff --git a/sys/net/gnrc/network_layer/ndp/gnrc_ndp.c b/sys/net/gnrc/network_layer/ndp/gnrc_ndp.c
index 1d7fa505e8..9aab1182d5 100644
--- a/sys/net/gnrc/network_layer/ndp/gnrc_ndp.c
+++ b/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
+
/**
* @}
*/
diff --git a/sys/net/gnrc/network_layer/ndp/internal/gnrc_ndp_internal.c b/sys/net/gnrc/network_layer/ndp/internal/gnrc_ndp_internal.c
index e1af1a8f7f..9b68742da0 100644
--- a/sys/net/gnrc/network_layer/ndp/internal/gnrc_ndp_internal.c
+++ b/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;
diff --git a/sys/net/gnrc/network_layer/ndp/router/Makefile b/sys/net/gnrc/network_layer/ndp/router/Makefile
new file mode 100644
index 0000000000..541dc34369
--- /dev/null
+++ b/sys/net/gnrc/network_layer/ndp/router/Makefile
@@ -0,0 +1,3 @@
+MODULE = gnrc_ndp_router
+
+include $(RIOTBASE)/Makefile.base
diff --git a/sys/net/gnrc/network_layer/ndp/router/gnrc_ndp_router.c b/sys/net/gnrc/network_layer/ndp/router/gnrc_ndp_router.c
new file mode 100644
index 0000000000..ac51e3d48e
--- /dev/null
+++ b/sys/net/gnrc/network_layer/ndp/router/gnrc_ndp_router.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2015 Martine Lenders
+ *
+ * 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);
+ }
+ }
+}
+
+/** @} */