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); + } + } +} + +/** @} */