diff --git a/Makefile.dep b/Makefile.dep index 71c21be140b4af5c29236ad4d7a34f6556b978dd..8674023cac2cc31457276b01aa242846bee1355d 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -78,12 +78,16 @@ ifneq (,$(filter ng_ipv6_default,$(USEMODULE))) USEMODULE += ng_ipv6 USEMODULE += ng_icmpv6 USEMODULE += ng_ndp + USEMODULE += ng_ndp_internal + USEMODULE += ng_ndp_node endif ifneq (,$(filter ng_ipv6_router_default,$(USEMODULE))) USEMODULE += ng_ipv6_router USEMODULE += ng_icmpv6 USEMODULE += ng_ndp + USEMODULE += ng_ndp_internal + USEMODULE += ng_ndp_node endif ifneq (,$(filter ng_ndp,$(USEMODULE))) diff --git a/Makefile.include b/Makefile.include index 1b72b3137729c834f7e28ea86d7708df2cde2709..013c80be18ad69b0909a99fd1916de24ad67bec2 100644 --- a/Makefile.include +++ b/Makefile.include @@ -130,6 +130,14 @@ endif include $(RIOTBASE)/Makefile.modules include $(RIOTBOARD)/$(BOARD)/Makefile.include include $(RIOTCPU)/$(CPU)/Makefile.include + +# get number of interfaces straight before resolving dependencies +NG_NETIF_NUMOF ?= 1 + +ifneq ($(NG_NETIF_NUMOF),1) + CFLAGS += -DNG_NETIF_NUMOF=$(NG_NETIF_NUMOF) +endif + include $(RIOTBASE)/Makefile.dep USEMODULE += $(filter-out $(DISABLE_MODULE), $(DEFAULT_MODULE)) diff --git a/sys/Makefile b/sys/Makefile index 2e616b9f49962e04cde78dc74c6bb4a640e17233..d8c6383f203f16341d0a2130bcc3e3108f245423 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -49,6 +49,12 @@ endif ifneq (,$(filter ng_ndp,$(USEMODULE))) DIRS += net/network_layer/ng_ndp endif +ifneq (,$(filter ng_ndp_internal,$(USEMODULE))) + DIRS += net/network_layer/ng_ndp/internal +endif +ifneq (,$(filter ng_ndp_node,$(USEMODULE))) + DIRS += net/network_layer/ng_ndp/node +endif ifneq (,$(filter ng_netapi,$(USEMODULE))) DIRS += net/crosslayer/ng_netapi endif diff --git a/sys/include/net/fib.h b/sys/include/net/fib.h index 4bd2b0901b66177aba68144c75489344735373f2..4e2ed4a9d419658c4a9b058e68dc9cc9072e3a22 100644 --- a/sys/include/net/fib.h +++ b/sys/include/net/fib.h @@ -22,6 +22,7 @@ #ifndef FIB_H_ #define FIB_H_ +#include "kernel_types.h" #include "timex.h" #ifdef __cplusplus diff --git a/sys/include/net/ng_ipv6/netif.h b/sys/include/net/ng_ipv6/netif.h index fd9de92699346217e0491e7cd56ce751cf1d4b0c..720f9dcc419410158b244790b5f3211fd106fc50 100644 --- a/sys/include/net/ng_ipv6/netif.h +++ b/sys/include/net/ng_ipv6/netif.h @@ -253,6 +253,52 @@ void ng_ipv6_netif_remove(kernel_pid_t pid); */ ng_ipv6_netif_t *ng_ipv6_netif_get(kernel_pid_t pid); +/** + * @brief Set interface to router mode. + * + * @details This sets/unsets the NG_IPV6_NETIF_FLAGS_ROUTER and initializes + * or ceases router behavior for neighbor discovery. + * + * @param[in] The interface. + * @param[in] Status for the NG_IPV6_NETIF_FLAGS_ROUTER flag. + */ +static inline void ng_ipv6_netif_set_rtr(ng_ipv6_netif_t *netif, bool enable) +{ + (void)netif; /* Don't do anything for non-routers */ + (void)enable; +} + +/** + * @brief Set interface to router advertisement mode. + * + * @details If NG_IPV6_NETIF_FLAGS_ROUTER is set this sets/unsets the + * NG_IPV6_NETIF_FLAGS_RTR_ADV and initializes or ceases router + * advertising behavior for neighbor discovery. + * + * @param[in] The interface. + * @param[in] Status for the NG_IPV6_NETIF_FLAGS_RTR flag. + */ +static inline void ng_ipv6_netif_set_rtr_adv(ng_ipv6_netif_t *netif, bool enable) +{ + (void)netif; /* Don't do anything for non-routers */ + (void)enable; +} + +/** + * @brief Solicitates an advertisement of a neighboring router on this + * interface. + * + * @param[in] netif The interface. + * @param[in] dst The address of the neighboring router. + * May be NULL for @ref NG_IPV6_ADDR_ALL_ROUTERS_LINK_LOCAL. + */ +static inline void ng_ipv6_netif_sol_router(ng_ipv6_netif_t *netif, + ng_ipv6_addr_t *dst) +{ + (void)netif; /* TODO */ + (void)dst; +} + /** * @brief Adds an address to an interface. * diff --git a/sys/include/net/ng_ndp.h b/sys/include/net/ng_ndp.h index 75e5eadd522921132ddaa0d01b5cf05704af6cf9..5d3a976cc66aeff4cfd7da40df3008bd48a4bbb2 100644 --- a/sys/include/net/ng_ndp.h +++ b/sys/include/net/ng_ndp.h @@ -27,6 +27,7 @@ #include "net/ng_ipv6/nc.h" #include "net/ng_ipv6/netif.h" +#include "net/ng_ndp/node.h" #include "net/ng_ndp/types.h" #ifndef NG_NDP_H_ @@ -167,27 +168,6 @@ void ng_ndp_netif_add(ng_ipv6_netif_t *iface); */ void ng_ndp_netif_remove(ng_ipv6_netif_t *iface); -/** - * @brief Get link-layer address and interface for next hop to destination - * IPv6 address. - * - * @param[out] l2addr The link-layer for the next hop to @p dst. - * @param[out] l2addr_len Length of @p l2addr. - * @param[in] iface The interface to search the next hop on. - * May be @ref KERNEL_PID_UNDEF if not specified. - * @param[in] dst An IPv6 address to search the next hop for. - * @param[in] pkt Packet to send to @p dst. Leave NULL if you - * just want to get the addresses. - * - * @return The PID of the interface, on success. - * @return -EHOSTUNREACH, if @p dst is not reachable. - * @return -ENOBUFS, if @p l2addr_len was smaller than the resulting @p l2addr - * would be long. - */ -kernel_pid_t ng_ndp_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len, - kernel_pid_t iface, ng_ipv6_addr_t *dst, - ng_pktsnip_t *pkt); - /** * @brief Builds a neighbor solicitation message for sending. * diff --git a/sys/include/net/ng_ndp/internal.h b/sys/include/net/ng_ndp/internal.h new file mode 100644 index 0000000000000000000000000000000000000000..165e9151b6aead75627dd26d7e5864168ff9767d --- /dev/null +++ b/sys/include/net/ng_ndp/internal.h @@ -0,0 +1,124 @@ +/* + * 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_ng_ndp_internal Internal functions for neighbor discovery. + * @ingroup net_ng_ndp + * @brief Internal functions for neighbor discovery. + * @internal + * @note Only for use with a neighbor discovery implementations. + * @{ + * + * @file + * @brief Internal neighbor discovery functions for neighbor discovery. + * + * @author Martine Lenders <mlenders@inf.fu-berlin.de> + */ +#ifndef INTERNAL_H_ +#define INTERNAL_H_ + +#include "net/ng_ipv6/addr.h" +#include "net/ng_ndp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Get best match from default router list. + * + * @internal + * + * @see <a href="https://tools.ietf.org/html/rfc4861#section-6.3.6"> + * RFC 4861, section 6.3.6 + * </a> + * + * @return Address to a default router. + * @return NULL, if the default router list is empty. + */ +ng_ipv6_addr_t *ng_ndp_internal_default_router(void); + +/** + * @brief Sets state of a neighbor cache entry and triggers required actions. + * + * @internal + * + * @param[in] nc_entry A neighbor cache entry. + * @param[in] state The new state for the neighbor cache entry. + */ +void ng_ndp_internal_set_state(ng_ipv6_nc_t *nc_entry, uint8_t state); + +/** + * @brief Send precompiled neighbor solicitation. + * + * @internal + * + * @param[in] iface Interface to send over. May not be KERNEL_PID_UNDEF. + * @param[in] tgt Target address for the neighbor solicitation. May not be + * NULL. + * @param[in] dst Destination address for neighbor solicitation. May not be + * NULL. + */ +void ng_ndp_internal_send_nbr_sol(kernel_pid_t iface, ng_ipv6_addr_t *tgt, + ng_ipv6_addr_t *dst); + +/** + * @brief Send precompiled neighbor advertisement. + * + * @internal + * + * @param[in] iface Interface to send over. May not be KERNEL_PID_UNDEF. + * @param[in] tgt Target address for the neighbor solicitation. May + * not be NULL. + * @param[in] dst Destination address for neighbor solicitation. May + * not be NULL. + * @param[in] supply_tl2a Add target link-layer address option to neighbor + * advertisement if link-layer has addresses. + */ +void ng_ndp_internal_send_nbr_adv(kernel_pid_t iface, ng_ipv6_addr_t *tgt, + ng_ipv6_addr_t *dst, bool supply_tl2a); + +/** + * @brief Handles a SL2A option. + * + * @param[in] iface Interface the option was received on. + * @param[in] pkt Packet the option was received in. + * @param[in] ipv6 IPv6 header of @p pkt + * @param[in] icmpv6_type ICMPv6 type of the message carrying the option. + * @param[in] sl2a_opt The SL2A option. + * + * @return true, on success. + * @return false, if SL2A was not valid. + */ +bool ng_ndp_internal_sl2a_opt_handle(kernel_pid_t iface, ng_pktsnip_t *pkt, + ng_ipv6_hdr_t *ipv6, uint8_t icmpv6_type, + ng_ndp_opt_t *sl2a_opt); + +/** + * @brief Handles a TL2A option. + * + * @param[in] iface Interface the option was received on. + * @param[in] pkt Packet the option was received in. + * @param[in] ipv6 IPv6 header of @p pkt + * @param[in] icmpv6_type ICMPv6 type of the message carrying the option. + * @param[in] tl2a_opt The TL2A option. + * @param[out] l2addr The L2 address carried in the TL2A option. + * + * @return length of the L2 address, on success. + * @return -EINVAL, if TL2A was not valid. + */ +int ng_ndp_internal_tl2a_opt_handle(ng_pktsnip_t *pkt, ng_ipv6_hdr_t *ipv6, + uint8_t icmpv6_type, ng_ndp_opt_t *tl2a_opt, + uint8_t *l2addr); + +#ifdef __cplusplus +} +#endif + +#endif /* INTERNAL_H_ */ +/** @} */ diff --git a/sys/include/net/ng_ndp/node.h b/sys/include/net/ng_ndp/node.h new file mode 100644 index 0000000000000000000000000000000000000000..d17c3a6e35a3a1c621ce50aeeabcb91e089242e6 --- /dev/null +++ b/sys/include/net/ng_ndp/node.h @@ -0,0 +1,53 @@ +/* + * 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_ng_ndp_node Neighbor discovery for pure IPv6 nodes + * @ingroup net_ng_ndp + * @brief Used for pure IPv6 nodes (without 6LoWPAN). + * @{ + * + * @file + * @brief IPv6-node neighbor discovery definitions + * + * @author Martine Lenders <mlenders@inf.fu-berlin.de> + */ +#ifndef NG_NDP_NODE_H_ +#define NG_NDP_NODE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Get link-layer address and interface for next hop to destination + * IPv6 address. + * + * @param[out] l2addr The link-layer for the next hop to @p dst. + * @param[out] l2addr_len Length of @p l2addr. + * @param[in] iface The interface to search the next hop on. + * May be @ref KERNEL_PID_UNDEF if not specified. + * @param[in] dst An IPv6 address to search the next hop for. + * @param[in] pkt Packet to send to @p dst. Leave NULL if you + * just want to get the addresses. + * + * @return The PID of the interface, on success. + * @return -EHOSTUNREACH, if @p dst is not reachable. + * @return -ENOBUFS, if @p l2addr_len was smaller than the resulting @p l2addr + * would be long. + */ +kernel_pid_t ng_ndp_node_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len, + kernel_pid_t iface, ng_ipv6_addr_t *dst, + ng_pktsnip_t *pkt); + +#ifdef __cplusplus +} +#endif + +#endif /* NG_NDP_NODE_H_ */ +/** @} */ diff --git a/sys/net/network_layer/ng_ipv6/ng_ipv6.c b/sys/net/network_layer/ng_ipv6/ng_ipv6.c index 9f25fefc86c058d25d4bcb408240f5c4ba4cddb3..e7cd878b587cd503bb3df4a865bd12343328f9bd 100644 --- a/sys/net/network_layer/ng_ipv6/ng_ipv6.c +++ b/sys/net/network_layer/ng_ipv6/ng_ipv6.c @@ -450,6 +450,22 @@ static void _send_multicast(kernel_pid_t iface, ng_pktsnip_t *pkt, #endif /* NG_NETIF_NUMOF */ } +static inline kernel_pid_t _next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len, + kernel_pid_t iface, ng_ipv6_addr_t *dst, + ng_pktsnip_t *pkt) +{ +#ifdef MODULE_NG_NDP_NODE + return ng_ndp_node_next_hop_l2addr(l2addr, l2addr_len, iface, dst, pkt); +#else + (void)l2addr; + (void)iface; + (void)dst; + (void)pkt; + *l2addr_len = 0; + return KERNEL_PID_UNDEF; +#endif +} + static void _send(ng_pktsnip_t *pkt, bool prep_hdr) { kernel_pid_t iface = KERNEL_PID_UNDEF; @@ -537,8 +553,7 @@ static void _send(ng_pktsnip_t *pkt, bool prep_hdr) uint8_t l2addr_len = NG_IPV6_NC_L2_ADDR_MAX; uint8_t l2addr[l2addr_len]; - iface = ng_ndp_next_hop_l2addr(l2addr, &l2addr_len, iface, &hdr->dst, - pkt); + iface = _next_hop_l2addr(l2addr, &l2addr_len, iface, &hdr->dst, pkt); if (iface == KERNEL_PID_UNDEF) { DEBUG("ipv6: error determining next hop's link layer address\n"); diff --git a/sys/net/network_layer/ng_ndp/internal/Makefile b/sys/net/network_layer/ng_ndp/internal/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..7c18d557335705844cceb871845e9124f73e7fd4 --- /dev/null +++ b/sys/net/network_layer/ng_ndp/internal/Makefile @@ -0,0 +1,3 @@ +MODULE = ng_ndp_internal + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/network_layer/ng_ndp/internal/ng_ndp_internal.c b/sys/net/network_layer/ng_ndp/internal/ng_ndp_internal.c new file mode 100644 index 0000000000000000000000000000000000000000..1b9349bd9952602934a40092815f313b350cf8af --- /dev/null +++ b/sys/net/network_layer/ng_ndp/internal/ng_ndp_internal.c @@ -0,0 +1,449 @@ +/* + * 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 <stdlib.h> + +#include "net/ng_ipv6.h" +#include "net/ng_ndp.h" +#include "random.h" +#include "timex.h" +#include "vtimer.h" + +#include "net/ng_ndp/internal.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#if ENABLE_DEBUG +static char addr_str[NG_IPV6_ADDR_MAX_STR_LEN]; +#endif + +static ng_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); + +/** + * @brief Sends @ref NG_NETAPI_MSG_TYPE_SND delayed. + * + * @param[in] t Timer for the delay. + * @param[in] interval Delay interval. + * @param[in] pkt Packet to send delayed. + */ +static inline void _send_delayed(vtimer_t *t, timex_t interval, ng_pktsnip_t *pkt) +{ + vtimer_remove(t); + vtimer_set_msg(t, interval, ng_ipv6_pid, NG_NETAPI_MSG_TYPE_SND, pkt); +} + + +ng_ipv6_addr_t *ng_ndp_internal_default_router(void) +{ + ng_ipv6_nc_t *router = ng_ipv6_nc_get_next_router(NULL); + + /* first look if there is any reachable router */ + while (router != NULL) { + if ((ng_ipv6_nc_get_state(router) != NG_IPV6_NC_STATE_INCOMPLETE) && + (ng_ipv6_nc_get_state(router) != NG_IPV6_NC_STATE_UNREACHABLE)) { + _last_router = NULL; + + return &router->ipv6_addr; + } + + router = ng_ipv6_nc_get_next_router(router); + } + + /* else take the first one, but keep round-robin in further selections */ + router = ng_ipv6_nc_get_next_router(_last_router); + + if (router == NULL) { /* end of router list or there is none => wrap around */ + router = ng_ipv6_nc_get_next_router(router); + + if (router == NULL) { /* still nothing found => no router in list */ + return NULL; + } + } + + _last_router = router; + + return &router->ipv6_addr; +} + +void ng_ndp_internal_set_state(ng_ipv6_nc_t *nc_entry, uint8_t state) +{ + ng_ipv6_netif_t *ipv6_iface; + timex_t t = { NG_NDP_FIRST_PROBE_DELAY, 0 }; + + nc_entry->flags &= ~NG_IPV6_NC_STATE_MASK; + nc_entry->flags |= state; + + DEBUG("ndp internal: set %s state to ", + ng_ipv6_addr_to_str(addr_str, &nc_entry->ipv6_addr, sizeof(addr_str))); + + switch (state) { + case NG_IPV6_NC_STATE_REACHABLE: + ipv6_iface = ng_ipv6_netif_get(nc_entry->iface); + DEBUG("REACHABLE (reachable time = %" PRIu32 ".%06" PRIu32 ")\n", + ipv6_iface->reach_time.seconds, + ipv6_iface->reach_time.microseconds); + t = ipv6_iface->reach_time; + + /* we intentionally fall through here to set the desired timeout t */ + case NG_IPV6_NC_STATE_DELAY: +#if ENABLE_DEBUG + if (state == NG_IPV6_NC_STATE_DELAY) { + DEBUG("DELAY (probe with unicast NS in %u seconds)\n", + NG_NDP_FIRST_PROBE_DELAY); + } +#endif + vtimer_remove(&nc_entry->nbr_sol_timer); + vtimer_set_msg(&nc_entry->nbr_sol_timer, t, ng_ipv6_pid, + NG_NDP_MSG_NC_STATE_TIMEOUT, nc_entry); + break; + + case NG_IPV6_NC_STATE_PROBE: + ipv6_iface = ng_ipv6_netif_get(nc_entry->iface); + + nc_entry->probes_remaining = NG_NDP_MAX_UC_NBR_SOL_NUMOF; + DEBUG("PROBE (probe with %" PRIu8 " unicast NS every %" PRIu32 + ".%06" PRIu32 " seconds)\n", nc_entry->probes_remaining, + ipv6_iface->retrans_timer.seconds, + ipv6_iface->retrans_timer.microseconds); + + ng_ndp_internal_send_nbr_sol(nc_entry->iface, &nc_entry->ipv6_addr, + &nc_entry->ipv6_addr); + + mutex_lock(&ipv6_iface->mutex); + vtimer_remove(&nc_entry->nbr_sol_timer); + vtimer_set_msg(&nc_entry->nbr_sol_timer, + ipv6_iface->retrans_timer, ng_ipv6_pid, + NG_NDP_MSG_NBR_SOL_RETRANS, nc_entry); + mutex_unlock(&ipv6_iface->mutex); + break; + +#ifdef ENABLE_DEBUG + case NG_IPV6_NC_STATE_STALE: + DEBUG("STALE (go into DELAY on next packet)\n"); + break; +#endif + + default: + DEBUG("errorneous or unknown\n"); + break; + } +} + +void ng_ndp_internal_send_nbr_adv(kernel_pid_t iface, ng_ipv6_addr_t *tgt, + ng_ipv6_addr_t *dst, bool supply_tl2a) +{ + ng_pktsnip_t *hdr, *pkt = NULL; + uint8_t adv_flags = 0; + + DEBUG("ndp internal: send neighbor advertisement (iface: %" PRIkernel_pid ", tgt: %s, ", + iface, ng_ipv6_addr_to_str(addr_str, tgt, sizeof(addr_str))); + DEBUG("dst: %s, supply_tl2a: %d)\n", + ng_ipv6_addr_to_str(addr_str, dst, sizeof(addr_str)), supply_tl2a); + + if (ng_ipv6_netif_get(iface)->flags & NG_IPV6_NETIF_FLAGS_ROUTER) { + adv_flags |= NG_NDP_NBR_ADV_FLAGS_R; + } + + if (ng_ipv6_addr_is_unspecified(dst)) { + ng_ipv6_addr_set_all_nodes_multicast(dst, + NG_IPV6_ADDR_MCAST_SCP_LINK_LOCAL); + } + else { + adv_flags |= NG_NDP_NBR_ADV_FLAGS_S; + } + + if (supply_tl2a) { + uint8_t l2src[8]; + uint16_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); + + if (l2src_len > 0) { + /* add target address link-layer address option */ + pkt = ng_ndp_opt_tl2a_build(l2src, l2src_len, NULL); + + if (pkt == NULL) { + DEBUG("ndp internal: error allocating Target Link-layer address option.\n"); + ng_pktbuf_release(pkt); + return; + } + } + } + + /* TODO: also check if the node provides proxy servies for tgt */ + if ((pkt != NULL) && !ng_ipv6_netif_addr_is_non_unicast(tgt)) { + /* TL2A is not supplied and tgt is not anycast */ + adv_flags |= NG_NDP_NBR_ADV_FLAGS_O; + } + + hdr = ng_ndp_nbr_adv_build(adv_flags, tgt, pkt); + + if (hdr == NULL) { + DEBUG("ndp internal: error allocating Neighbor advertisement.\n"); + ng_pktbuf_release(pkt); + return; + } + + pkt = hdr; + hdr = ng_ipv6_hdr_build(pkt, NULL, 0, (uint8_t *)dst, + sizeof(ng_ipv6_addr_t)); + + if (hdr == NULL) { + DEBUG("ndp internal: error allocating IPv6 header.\n"); + ng_pktbuf_release(pkt); + return; + } + + ((ng_ipv6_hdr_t *)hdr->data)->hl = 255; + + pkt = hdr; + /* add netif header for send interface specification */ + hdr = ng_netif_hdr_build(NULL, 0, NULL, 0); + + if (hdr == NULL) { + DEBUG("ndp internal: error allocating netif header.\n"); + return; + } + + ((ng_netif_hdr_t *)hdr->data)->if_pid = iface; + + LL_PREPEND(pkt, hdr); + + if (ng_ipv6_netif_addr_is_non_unicast(tgt)) { + /* avoid collision for anycast addresses + * (see https://tools.ietf.org/html/rfc4861#section-7.2.7) */ + timex_t delay = { 0, genrand_uint32_range(0, NG_NDP_MAX_AC_TGT_DELAY * SEC_IN_USEC) }; + timex_normalize(&delay); + ng_ipv6_nc_t *nc_entry = ng_ipv6_nc_get(iface, tgt); + DEBUG("ndp internal: delay neighbor advertisement for %" PRIu32 " sec.", + delay.seconds); + + /* nc_entry must be set so no need to check it */ + _send_delayed(&nc_entry->nbr_adv_timer, delay, pkt); + } + else { + ng_netapi_send(ng_ipv6_pid, pkt); + } +} + +void ng_ndp_internal_send_nbr_sol(kernel_pid_t iface, ng_ipv6_addr_t *tgt, + ng_ipv6_addr_t *dst) +{ + ng_pktsnip_t *hdr, *pkt = NULL; + ng_ipv6_addr_t *src = NULL; + size_t src_len = 0; + + DEBUG("ndp internal: send neighbor solicitation (iface: %" PRIkernel_pid ", tgt: %s, ", + iface, ng_ipv6_addr_to_str(addr_str, tgt, sizeof(addr_str))); + DEBUG("dst: %s)\n", ng_ipv6_addr_to_str(addr_str, dst, sizeof(addr_str))); + + /* check if there is a fitting source address to target */ + if ((src = ng_ipv6_netif_find_best_src_addr(iface, tgt)) != NULL) { + uint8_t l2src[8]; + uint16_t l2src_len; + src_len = sizeof(ng_ipv6_addr_t); + l2src_len = _get_l2src(l2src, sizeof(l2src), iface); + + if (l2src_len > 0) { + /* add source address link-layer address option */ + pkt = ng_ndp_opt_sl2a_build(l2src, l2src_len, NULL); + + if (pkt == NULL) { + DEBUG("ndp internal: error allocating Source Link-layer address option.\n"); + ng_pktbuf_release(pkt); + return; + } + } + } + + hdr = ng_ndp_nbr_sol_build(tgt, pkt); + + if (hdr == NULL) { + DEBUG("ndp internal: error allocating Neighbor solicitation.\n"); + ng_pktbuf_release(pkt); + return; + } + + pkt = hdr; + hdr = ng_ipv6_hdr_build(pkt, (uint8_t *)src, src_len, (uint8_t *)dst, + sizeof(ng_ipv6_addr_t)); + + if (hdr == NULL) { + DEBUG("ndp internal: error allocating IPv6 header.\n"); + ng_pktbuf_release(pkt); + return; + } + + ((ng_ipv6_hdr_t *)hdr->data)->hl = 255; + + pkt = hdr; + /* add netif header for send interface specification */ + hdr = ng_netif_hdr_build(NULL, 0, NULL, 0); + + if (hdr == NULL) { + DEBUG("ndp internal: error allocating netif header.\n"); + return; + } + + ((ng_netif_hdr_t *)hdr->data)->if_pid = iface; + + LL_PREPEND(pkt, hdr); + + ng_netapi_send(ng_ipv6_pid, pkt); +} + +bool ng_ndp_internal_sl2a_opt_handle(kernel_pid_t iface, ng_pktsnip_t *pkt, + ng_ipv6_hdr_t *ipv6, uint8_t icmpv6_type, + ng_ndp_opt_t *sl2a_opt) +{ + ng_ipv6_nc_t *nc_entry = NULL; + uint8_t sl2a_len = 0; + uint8_t *sl2a = (uint8_t *)(sl2a_opt + 1); + + if ((sl2a_opt->len == 0) || ng_ipv6_addr_is_unspecified(&ipv6->src)) { + DEBUG("ndp: invalid source link-layer address option received\n"); + return false; + } + + while (pkt) { + if (pkt->type == NG_NETTYPE_NETIF) { + ng_netif_hdr_t *hdr = pkt->data; + sl2a_len = hdr->src_l2addr_len; + break; + } + pkt = pkt->next; + } + + if (sl2a_len == 0) { /* in case there was no source address in l2 */ + sl2a_len = (sl2a_opt->len / 8) - sizeof(ng_ndp_opt_t); + + /* ignore all zeroes at the end for length */ + for (; sl2a[sl2a_len - 1] == 0x00; sl2a_len--); + } + + DEBUG("ndp: received SL2A (link-layer address: %s)\n", + ng_netif_addr_to_str(addr_str, sizeof(addr_str), sl2a, sl2a_len)); + + switch (icmpv6_type) { + case NG_ICMPV6_NBR_SOL: + nc_entry = ng_ipv6_nc_get(iface, &ipv6->src); + + if (nc_entry != NULL) { + if ((sl2a_len != nc_entry->l2_addr_len) || + (memcmp(sl2a, nc_entry->l2_addr, sl2a_len) != 0)) { + /* if entry exists but l2 address differs: set */ + nc_entry->l2_addr_len = sl2a_len; + memcpy(nc_entry->l2_addr, sl2a, sl2a_len); + + ng_ndp_internal_set_state(nc_entry, NG_IPV6_NC_STATE_STALE); + } + } + else { + ng_ipv6_nc_add(iface, &ipv6->src, sl2a, sl2a_len, + NG_IPV6_NC_STATE_STALE); + } + + return true; + + default: /* wrong encapsulating message: silently discard */ + DEBUG("ndp: silently discard sl2a_opt for ICMPv6 message type %" + PRIu8 "\n", icmpv6_type); + return true; + } +} + +int ng_ndp_internal_tl2a_opt_handle(ng_pktsnip_t *pkt, ng_ipv6_hdr_t *ipv6, + uint8_t icmpv6_type, ng_ndp_opt_t *tl2a_opt, + uint8_t *l2addr) +{ + uint8_t tl2a_len = 0; + uint8_t *tl2a = (uint8_t *)(tl2a_opt + 1); + + if ((tl2a_opt->len == 0) || ng_ipv6_addr_is_unspecified(&ipv6->src)) { + DEBUG("ndp: invalid target link-layer address option received\n"); + return -EINVAL; + } + + switch (icmpv6_type) { + case NG_ICMPV6_NBR_ADV: + while (pkt) { + if (pkt->type == NG_NETTYPE_NETIF) { + ng_netif_hdr_t *hdr = pkt->data; + tl2a_len = hdr->src_l2addr_len; + break; + } + pkt = pkt->next; + } + + if (tl2a_len == 0) { /* in case there was no source address in l2 */ + tl2a_len = (tl2a_opt->len / 8) - sizeof(ng_ndp_opt_t); + + /* ignore all zeroes at the end for length */ + for (; tl2a[tl2a_len - 1] == 0x00; tl2a_len--); + } + + DEBUG("ndp: received TL2A (link-layer address: %s)\n", + ng_netif_addr_to_str(addr_str, sizeof(addr_str), tl2a, tl2a_len)); + + memcpy(l2addr, tl2a, tl2a_len); + + return (int)tl2a_len; + + default: /* wrong encapsulating message: silently discard */ + DEBUG("ndp: silently discard tl2a_opt for ICMPv6 message type %" + PRIu8 "\n", icmpv6_type); + return 0; + } +} +static uint16_t _get_l2src(uint8_t *l2src, size_t l2src_size, kernel_pid_t iface) +{ + bool try_long = false; + int res; + uint16_t l2src_len; + /* maximum address length that fits into a minimum length (8) S/TL2A option */ + const uint16_t max_short_len = 6; + + /* try getting source address */ + if ((ng_netapi_get(iface, NETOPT_SRC_LEN, 0, &l2src_len, + sizeof(l2src_len)) >= 0) && + (l2src_len > max_short_len)) { + try_long = true; + } + + if (try_long && ((res = ng_netapi_get(iface, NETOPT_ADDRESS_LONG, 0, + l2src, l2src_size)) > max_short_len)) { + l2src_len = (uint16_t)res; + } + else if ((res = ng_netapi_get(iface, NETOPT_ADDRESS, 0, l2src, + l2src_size)) >= 0) { + l2src_len = (uint16_t)res; + } + else { + DEBUG("ndp internal: no link-layer address found.\n"); + l2src_len = 0; + } + + return l2src_len; +} + +/** @} */ diff --git a/sys/net/network_layer/ng_ndp/ng_ndp.c b/sys/net/network_layer/ng_ndp/ng_ndp.c index b50c31756b47dc8b03c40bc279252ac3a7684b6e..a722b3eb7d6f02319d38514296b293652e086763 100644 --- a/sys/net/network_layer/ng_ndp/ng_ndp.c +++ b/sys/net/network_layer/ng_ndp/ng_ndp.c @@ -19,6 +19,7 @@ #include <string.h> #include "byteorder.h" +#include "net/fib.h" #include "net/ng_icmpv6.h" #include "net/ng_ipv6.h" #include "net/ng_ipv6/ext/rh.h" @@ -28,8 +29,9 @@ #include "thread.h" #include "vtimer.h" +#include "net/ng_ndp/internal.h" + #include "net/ng_ndp.h" -#include "net/fib.h" #define ENABLE_DEBUG (0) #include "debug.h" @@ -41,43 +43,7 @@ static char addr_str[NG_IPV6_ADDR_MAX_STR_LEN]; #endif -static ng_pktqueue_t _pkt_nodes[NG_IPV6_NC_SIZE * 2]; -static ng_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) */ - /* random helper function */ -static inline uint32_t _rand(uint32_t min, uint32_t max) -{ - return (genrand_uint32() % (max - min)) + min; -} - -static bool _handle_sl2a_opt(kernel_pid_t iface, ng_pktsnip_t *pkt, - ng_ipv6_hdr_t *ipv6, uint8_t icmpv6_type, - ng_ndp_opt_t *sl2a_opt); -static int _handle_tl2a_opt(ng_pktsnip_t *pkt, ng_ipv6_hdr_t *ipv6, - uint8_t icmpv6_type, ng_ndp_opt_t *tl2a_opt, - uint8_t *l2addr); - -/* send address resolution messages */ -static void _send_nbr_sol(kernel_pid_t iface, ng_ipv6_addr_t *tgt, - ng_ipv6_addr_t *dst); -static void _send_nbr_adv(kernel_pid_t iface, ng_ipv6_addr_t *tgt, - ng_ipv6_addr_t *dst, bool supply_tl2a); - -static void _set_state(ng_ipv6_nc_t *nc_entry, uint8_t state); - -/* special netapi helper */ -static inline void _send_delayed(vtimer_t *t, timex_t interval, ng_pktsnip_t *pkt) -{ - vtimer_remove(t); - vtimer_set_msg(t, interval, ng_ipv6_pid, NG_NETAPI_MSG_TYPE_SND, pkt); -} - -/* packet queue node allocation */ -static ng_pktqueue_t *_alloc_pkt_node(ng_pktsnip_t *pkt); - void ng_ndp_nbr_sol_handle(kernel_pid_t iface, ng_pktsnip_t *pkt, ng_ipv6_hdr_t *ipv6, ng_ndp_nbr_sol_t *nbr_sol, size_t icmpv6_size) @@ -119,7 +85,7 @@ void ng_ndp_nbr_sol_handle(kernel_pid_t iface, ng_pktsnip_t *pkt, switch (opt->type) { case NG_NDP_OPT_SL2A: - if (!_handle_sl2a_opt(iface, pkt, ipv6, nbr_sol->type, opt)) { + if (!ng_ndp_internal_sl2a_opt_handle(iface, pkt, ipv6, nbr_sol->type, opt)) { /* invalid source link-layer address option */ return; } @@ -135,8 +101,8 @@ void ng_ndp_nbr_sol_handle(kernel_pid_t iface, ng_pktsnip_t *pkt, sicmpv6_size -= (opt->len * 8); } - _send_nbr_adv(iface, tgt, &ipv6->src, - ng_ipv6_addr_is_multicast(&ipv6->dst)); + ng_ndp_internal_send_nbr_adv(iface, tgt, &ipv6->src, + ng_ipv6_addr_is_multicast(&ipv6->dst)); return; } @@ -190,8 +156,7 @@ void ng_ndp_nbr_adv_handle(kernel_pid_t iface, ng_pktsnip_t *pkt, switch (opt->type) { case NG_NDP_OPT_TL2A: - if ((l2tgt_len = _handle_tl2a_opt(pkt, ipv6, nbr_adv->type, opt, - l2tgt)) < 0) { + if ((l2tgt_len = ng_ndp_internal_tl2a_opt_handle(pkt, ipv6, nbr_adv->type, opt, l2tgt)) < 0) { /* invalid target link-layer address option */ return; } @@ -227,10 +192,10 @@ void ng_ndp_nbr_adv_handle(kernel_pid_t iface, ng_pktsnip_t *pkt, memcpy(nc_entry->l2_addr, l2tgt, l2tgt_len); if (nbr_adv->flags & NG_NDP_NBR_ADV_FLAGS_S) { - _set_state(nc_entry, NG_IPV6_NC_STATE_REACHABLE); + ng_ndp_internal_set_state(nc_entry, NG_IPV6_NC_STATE_REACHABLE); } else { - _set_state(nc_entry, NG_IPV6_NC_STATE_STALE); + ng_ndp_internal_set_state(nc_entry, NG_IPV6_NC_STATE_STALE); } if (nbr_adv->flags & NG_NDP_NBR_ADV_FLAGS_R) { @@ -270,10 +235,10 @@ void ng_ndp_nbr_adv_handle(kernel_pid_t iface, ng_pktsnip_t *pkt, } if (nbr_adv->flags & NG_NDP_NBR_ADV_FLAGS_S) { - _set_state(nc_entry, NG_IPV6_NC_STATE_REACHABLE); + ng_ndp_internal_set_state(nc_entry, NG_IPV6_NC_STATE_REACHABLE); } else if (l2tgt_changed && (l2tgt_len != 0)) { - _set_state(nc_entry, NG_IPV6_NC_STATE_STALE); + ng_ndp_internal_set_state(nc_entry, NG_IPV6_NC_STATE_STALE); } if (nbr_adv->flags & NG_NDP_NBR_ADV_FLAGS_R) { @@ -286,7 +251,7 @@ void ng_ndp_nbr_adv_handle(kernel_pid_t iface, ng_pktsnip_t *pkt, } else if (l2tgt_changed) { if (ng_ipv6_nc_get_state(nc_entry) == NG_IPV6_NC_STATE_REACHABLE) { - _set_state(nc_entry, NG_IPV6_NC_STATE_STALE); + ng_ndp_internal_set_state(nc_entry, NG_IPV6_NC_STATE_STALE); } } @@ -322,7 +287,7 @@ void ng_ndp_retrans_nbr_sol(ng_ipv6_nc_t *nc_entry) size_t ifnum = ng_netif_get(ifs); for (size_t i = 0; i < ifnum; i++) { - _send_nbr_sol(ifs[i], &nc_entry->ipv6_addr, &dst); + ng_ndp_internal_send_nbr_sol(ifs[i], &nc_entry->ipv6_addr, &dst); } vtimer_remove(&nc_entry->nbr_sol_timer); @@ -332,7 +297,7 @@ void ng_ndp_retrans_nbr_sol(ng_ipv6_nc_t *nc_entry) else { ng_ipv6_netif_t *ipv6_iface = ng_ipv6_netif_get(nc_entry->iface); - _send_nbr_sol(nc_entry->iface, &nc_entry->ipv6_addr, &dst); + ng_ndp_internal_send_nbr_sol(nc_entry->iface, &nc_entry->ipv6_addr, &dst); mutex_lock(&ipv6_iface->mutex); vtimer_remove(&nc_entry->nbr_sol_timer); @@ -359,11 +324,11 @@ void ng_ndp_state_timeout(ng_ipv6_nc_t *nc_entry) { switch (ng_ipv6_nc_get_state(nc_entry)) { case NG_IPV6_NC_STATE_REACHABLE: - _set_state(nc_entry, NG_IPV6_NC_STATE_STALE); + ng_ndp_internal_set_state(nc_entry, NG_IPV6_NC_STATE_STALE); break; case NG_IPV6_NC_STATE_DELAY: - _set_state(nc_entry, NG_IPV6_NC_STATE_PROBE); + ng_ndp_internal_set_state(nc_entry, NG_IPV6_NC_STATE_PROBE); break; default: @@ -373,7 +338,7 @@ void ng_ndp_state_timeout(ng_ipv6_nc_t *nc_entry) void ng_ndp_netif_add(ng_ipv6_netif_t *iface) { - uint32_t reach_time = _rand(NG_NDP_MIN_RAND, NG_NDP_MAX_RAND); + uint32_t reach_time = genrand_uint32_range(NG_NDP_MIN_RAND, NG_NDP_MAX_RAND); /* set default values */ mutex_lock(&iface->mutex); @@ -392,173 +357,6 @@ void ng_ndp_netif_remove(ng_ipv6_netif_t *iface) (void) iface; } -static ng_ipv6_addr_t *_default_router(void) -{ - ng_ipv6_nc_t *router = ng_ipv6_nc_get_next_router(NULL); - - /* first look if there is any reachable router */ - while (router != NULL) { - if ((ng_ipv6_nc_get_state(router) != NG_IPV6_NC_STATE_INCOMPLETE) && - (ng_ipv6_nc_get_state(router) != NG_IPV6_NC_STATE_UNREACHABLE)) { - _last_router = NULL; - - return &router->ipv6_addr; - } - - router = ng_ipv6_nc_get_next_router(router); - } - - /* else take the first one, but keep round-robin in further selections */ - router = ng_ipv6_nc_get_next_router(_last_router); - - if (router == NULL) { /* end of router list or there is none => wrap around */ - router = ng_ipv6_nc_get_next_router(router); - - if (router == NULL) { /* still nothing found => no router in list */ - return NULL; - } - } - - _last_router = router; - - return &router->ipv6_addr; -} - -kernel_pid_t ng_ndp_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len, - kernel_pid_t iface, ng_ipv6_addr_t *dst, - ng_pktsnip_t *pkt) -{ - ng_ipv6_addr_t *next_hop_ip = NULL, *prefix = NULL; - -#ifdef MODULE_NG_IPV6_EXT_RH - next_hop_ip = ng_ipv6_ext_rh_next_hop(hdr); -#endif -#ifdef MODULE_FIB - size_t next_hop_size = sizeof(ng_ipv6_addr_t); - uint32_t next_hop_flags = 0; - ng_ipv6_addr_t next_hop_actual; /* FIB copies address into this variable */ - - if ((next_hop_ip == NULL) && - (fib_get_next_hop(&iface, next_hop_actual.u8, &next_hop_size, - &next_hop_flags, (uint8_t *)dst, - sizeof(ng_ipv6_addr_t), 0) >= 0) && - (next_hop_size == sizeof(ng_ipv6_addr_t))) { - next_hop_ip = &next_hop_actual; - } - -#endif - - if (next_hop_ip == NULL) { /* no route to host */ - if (iface == KERNEL_PID_UNDEF) { - /* ng_ipv6_netif_t doubles as prefix list */ - iface = ng_ipv6_netif_find_by_prefix(&prefix, dst); - } - else { - /* ng_ipv6_netif_t doubles as prefix list */ - prefix = ng_ipv6_netif_match_prefix(iface, dst); - } - - if ((prefix != NULL) && /* prefix is on-link */ - (ng_ipv6_netif_addr_get(prefix)->flags & - NG_IPV6_NETIF_ADDR_FLAGS_NDP_ON_LINK)) { - next_hop_ip = dst; -#ifdef MODULE_FIB - /* We don't care if FIB is full, this is just for efficiency - * for later sends */ - fib_add_entry(iface, (uint8_t *)dst, sizeof(ng_ipv6_addr_t), 0, - (uint8_t *)next_hop_ip, sizeof(ng_ipv6_addr_t), 0, - FIB_LIFETIME_NO_EXPIRE); -#endif - } - } - - if (next_hop_ip == NULL) { - next_hop_ip = _default_router(); -#ifdef MODULE_FIB - /* We don't care if FIB is full, this is just for efficiency for later - * sends */ - fib_add_entry(iface, (uint8_t *)dst, sizeof(ng_ipv6_addr_t), 0, - (uint8_t *)next_hop_ip, sizeof(ng_ipv6_addr_t), 0, - FIB_LIFETIME_NO_EXPIRE); -#endif - } - - if (next_hop_ip != NULL) { - ng_ipv6_nc_t *nc_entry = ng_ipv6_nc_get(iface, next_hop_ip); - - if ((nc_entry != NULL) && ng_ipv6_nc_is_reachable(nc_entry)) { - DEBUG("ndp: found reachable neighbor (%s => ", - ng_ipv6_addr_to_str(addr_str, &nc_entry->ipv6_addr, sizeof(addr_str))); - DEBUG("%s)\n", - ng_netif_addr_to_str(addr_str, sizeof(addr_str), - nc_entry->l2_addr, nc_entry->l2_addr_len)); - - if (ng_ipv6_nc_get_state(nc_entry) == NG_IPV6_NC_STATE_STALE) { - _set_state(nc_entry, NG_IPV6_NC_STATE_DELAY); - } - - memcpy(l2addr, nc_entry->l2_addr, nc_entry->l2_addr_len); - *l2addr_len = nc_entry->l2_addr_len; - /* TODO: unreachability check */ - return nc_entry->iface; - } - else if (nc_entry == NULL) { - ng_pktqueue_t *pkt_node; - ng_ipv6_addr_t dst_sol; - - nc_entry = ng_ipv6_nc_add(iface, next_hop_ip, NULL, 0, - NG_IPV6_NC_STATE_INCOMPLETE << NG_IPV6_NC_STATE_POS); - - if (nc_entry == NULL) { - DEBUG("ndp: could not create neighbor cache entry\n"); - return KERNEL_PID_UNDEF; - } - - pkt_node = _alloc_pkt_node(pkt); - - if (pkt_node == NULL) { - DEBUG("ndp: could not add packet to packet queue\n"); - } - else { - /* prevent packet from being released by IPv6 */ - ng_pktbuf_hold(pkt_node->pkt, 1); - ng_pktqueue_add(&nc_entry->pkts, pkt_node); - } - - /* address resolution */ - ng_ipv6_addr_set_solicited_nodes(&dst_sol, next_hop_ip); - - if (iface == KERNEL_PID_UNDEF) { - timex_t t = { 0, NG_NDP_RETRANS_TIMER }; - kernel_pid_t ifs[NG_NETIF_NUMOF]; - size_t ifnum = ng_netif_get(ifs); - - for (size_t i = 0; i < ifnum; i++) { - _send_nbr_sol(ifs[i], next_hop_ip, &dst_sol); - } - - vtimer_remove(&nc_entry->nbr_sol_timer); - vtimer_set_msg(&nc_entry->nbr_sol_timer, t, ng_ipv6_pid, - NG_NDP_MSG_NBR_SOL_RETRANS, nc_entry); - } - else { - ng_ipv6_netif_t *ipv6_iface = ng_ipv6_netif_get(iface); - - _send_nbr_sol(iface, next_hop_ip, &dst_sol); - - mutex_lock(&ipv6_iface->mutex); - vtimer_remove(&nc_entry->nbr_sol_timer); - vtimer_set_msg(&nc_entry->nbr_sol_timer, - ipv6_iface->retrans_timer, ng_ipv6_pid, - NG_NDP_MSG_NBR_SOL_RETRANS, nc_entry); - mutex_unlock(&ipv6_iface->mutex); - } - } - } - - return KERNEL_PID_UNDEF; -} - ng_pktsnip_t *ng_ndp_nbr_sol_build(ng_ipv6_addr_t *tgt, ng_pktsnip_t *options) { ng_pktsnip_t *pkt; @@ -631,199 +429,6 @@ ng_pktsnip_t *ng_ndp_opt_build(uint8_t type, size_t size, ng_pktsnip_t *next) return pkt; } -static uint16_t _get_l2src(uint8_t *l2src, size_t l2src_size, kernel_pid_t iface) -{ - bool try_long = false; - int res; - uint16_t l2src_len; - /* maximum address length that fits into a minimum length (8) S/TL2A option */ - const uint16_t max_short_len = 6; - - /* try getting source address */ - if ((ng_netapi_get(iface, NETOPT_SRC_LEN, 0, &l2src_len, - sizeof(l2src_len)) >= 0) && - (l2src_len > max_short_len)) { - try_long = true; - } - - if (try_long && ((res = ng_netapi_get(iface, NETOPT_ADDRESS_LONG, 0, - l2src, l2src_size)) > max_short_len)) { - l2src_len = (uint16_t)res; - } - else if ((res = ng_netapi_get(iface, NETOPT_ADDRESS, 0, l2src, - l2src_size)) >= 0) { - l2src_len = (uint16_t)res; - } - else { - DEBUG("ndp: no link-layer address found.\n"); - l2src_len = 0; - } - - return l2src_len; -} - -static void _send_nbr_sol(kernel_pid_t iface, ng_ipv6_addr_t *tgt, - ng_ipv6_addr_t *dst) -{ - ng_pktsnip_t *hdr, *pkt = NULL; - ng_ipv6_addr_t *src = NULL; - size_t src_len = 0; - - DEBUG("ndp: send neighbor solicitation (iface: %" PRIkernel_pid ", tgt: %s, ", - iface, ng_ipv6_addr_to_str(addr_str, tgt, sizeof(addr_str))); - DEBUG("dst: %s)\n", ng_ipv6_addr_to_str(addr_str, dst, sizeof(addr_str))); - - /* check if there is a fitting source address to target */ - if ((src = ng_ipv6_netif_find_best_src_addr(iface, tgt)) != NULL) { - uint8_t l2src[8]; - uint16_t l2src_len; - src_len = sizeof(ng_ipv6_addr_t); - l2src_len = _get_l2src(l2src, sizeof(l2src), iface); - - if (l2src_len > 0) { - /* add source address link-layer address option */ - pkt = ng_ndp_opt_sl2a_build(l2src, l2src_len, NULL); - - if (pkt == NULL) { - DEBUG("ndp: error allocating Source Link-layer address option.\n"); - ng_pktbuf_release(pkt); - return; - } - } - } - - hdr = ng_ndp_nbr_sol_build(tgt, pkt); - - if (hdr == NULL) { - DEBUG("ndp: error allocating Neighbor solicitation.\n"); - ng_pktbuf_release(pkt); - return; - } - - pkt = hdr; - hdr = ng_ipv6_hdr_build(pkt, (uint8_t *)src, src_len, (uint8_t *)dst, - sizeof(ng_ipv6_addr_t)); - - if (hdr == NULL) { - DEBUG("ndp: error allocating IPv6 header.\n"); - ng_pktbuf_release(pkt); - return; - } - - ((ng_ipv6_hdr_t *)hdr->data)->hl = 255; - - pkt = hdr; - /* add netif header for send interface specification */ - hdr = ng_netif_hdr_build(NULL, 0, NULL, 0); - - if (hdr == NULL) { - DEBUG("ndp: error allocating netif header.\n"); - return; - } - - ((ng_netif_hdr_t *)hdr->data)->if_pid = iface; - - LL_PREPEND(pkt, hdr); - - ng_netapi_send(ng_ipv6_pid, pkt); -} - -static void _send_nbr_adv(kernel_pid_t iface, ng_ipv6_addr_t *tgt, - ng_ipv6_addr_t *dst, bool supply_tl2a) -{ - ng_pktsnip_t *hdr, *pkt = NULL; - uint8_t adv_flags = 0; - - DEBUG("ndp: send neighbor advertisement (iface: %" PRIkernel_pid ", tgt: %s, ", - iface, ng_ipv6_addr_to_str(addr_str, tgt, sizeof(addr_str))); - DEBUG("dst: %s, supply_tl2a: %d)\n", - ng_ipv6_addr_to_str(addr_str, dst, sizeof(addr_str)), supply_tl2a); - - if (ng_ipv6_netif_get(iface)->flags & NG_IPV6_NETIF_FLAGS_ROUTER) { - adv_flags |= NG_NDP_NBR_ADV_FLAGS_R; - } - - if (ng_ipv6_addr_is_unspecified(dst)) { - ng_ipv6_addr_set_all_nodes_multicast(dst, - NG_IPV6_ADDR_MCAST_SCP_LINK_LOCAL); - } - else { - adv_flags |= NG_NDP_NBR_ADV_FLAGS_S; - } - - if (supply_tl2a) { - uint8_t l2src[8]; - uint16_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); - - if (l2src_len > 0) { - /* add target address link-layer address option */ - pkt = ng_ndp_opt_tl2a_build(l2src, l2src_len, NULL); - - if (pkt == NULL) { - DEBUG("ndp: error allocating Target Link-layer address option.\n"); - ng_pktbuf_release(pkt); - return; - } - } - } - - /* TODO: also check if the node provides proxy servies for tgt */ - if ((pkt != NULL) && !ng_ipv6_netif_addr_is_non_unicast(tgt)) { - /* TL2A is not supplied and tgt is not anycast */ - adv_flags |= NG_NDP_NBR_ADV_FLAGS_O; - } - - hdr = ng_ndp_nbr_adv_build(adv_flags, tgt, pkt); - - if (hdr == NULL) { - DEBUG("ndp: error allocating Neighbor advertisement.\n"); - ng_pktbuf_release(pkt); - return; - } - - pkt = hdr; - hdr = ng_ipv6_hdr_build(pkt, NULL, 0, (uint8_t *)dst, - sizeof(ng_ipv6_addr_t)); - - if (hdr == NULL) { - DEBUG("ndp: error allocating IPv6 header.\n"); - ng_pktbuf_release(pkt); - return; - } - - ((ng_ipv6_hdr_t *)hdr->data)->hl = 255; - - pkt = hdr; - /* add netif header for send interface specification */ - hdr = ng_netif_hdr_build(NULL, 0, NULL, 0); - - if (hdr == NULL) { - DEBUG("ndp: error allocating netif header.\n"); - return; - } - - ((ng_netif_hdr_t *)hdr->data)->if_pid = iface; - - LL_PREPEND(pkt, hdr); - - if (ng_ipv6_netif_addr_is_non_unicast(tgt)) { - /* avoid collision for anycast addresses - * (see https://tools.ietf.org/html/rfc4861#section-7.2.7) */ - timex_t delay = { _rand(0, NG_NDP_MAX_AC_TGT_DELAY), 0 }; - ng_ipv6_nc_t *nc_entry = ng_ipv6_nc_get(iface, tgt); - DEBUG("ndp: delay neighbor advertisement for %" PRIu32 " sec.", - delay.seconds); - - /* nc_entry must be set so no need to check it */ - _send_delayed(&nc_entry->nbr_adv_timer, delay, pkt); - } - else { - ng_netapi_send(ng_ipv6_pid, pkt); - } -} - static inline ng_pktsnip_t *_opt_l2a_build(uint8_t type, const uint8_t *l2addr, uint8_t l2addr_len, ng_pktsnip_t *next) { @@ -858,188 +463,6 @@ ng_pktsnip_t *ng_ndp_opt_tl2a_build(const uint8_t *l2addr, uint8_t l2addr_len, return _opt_l2a_build(NG_NDP_OPT_TL2A, l2addr, l2addr_len, next); } -/* internal functions */ -/* packet queue node allocation */ -static ng_pktqueue_t *_alloc_pkt_node(ng_pktsnip_t *pkt) -{ - for (size_t i = 0; i < sizeof(_pkt_nodes) / sizeof(ng_pktqueue_t); i++) { - if ((_pkt_nodes[i].pkt == NULL) && (_pkt_nodes[i].next == NULL)) { - _pkt_nodes[i].pkt = pkt; - - return &(_pkt_nodes[i]); - } - } - - return NULL; -} - -static bool _handle_sl2a_opt(kernel_pid_t iface, ng_pktsnip_t *pkt, - ng_ipv6_hdr_t *ipv6, uint8_t icmpv6_type, - ng_ndp_opt_t *sl2a_opt) -{ - ng_ipv6_nc_t *nc_entry = NULL; - uint8_t sl2a_len = 0; - uint8_t *sl2a = (uint8_t *)(sl2a_opt + 1); - - if ((sl2a_opt->len == 0) || ng_ipv6_addr_is_unspecified(&ipv6->src)) { - DEBUG("ndp: invalid source link-layer address option received\n"); - return false; - } - - while (pkt) { - if (pkt->type == NG_NETTYPE_NETIF) { - ng_netif_hdr_t *hdr = pkt->data; - sl2a_len = hdr->src_l2addr_len; - break; - } - pkt = pkt->next; - } - - if (sl2a_len == 0) { /* in case there was no source address in l2 */ - sl2a_len = (sl2a_opt->len / 8) - sizeof(ng_ndp_opt_t); - - /* ignore all zeroes at the end for length */ - for (; sl2a[sl2a_len - 1] == 0x00; sl2a_len--); - } - - DEBUG("ndp: received SL2A (link-layer address: %s)\n", - ng_netif_addr_to_str(addr_str, sizeof(addr_str), sl2a, sl2a_len)); - - switch (icmpv6_type) { - case NG_ICMPV6_NBR_SOL: - nc_entry = ng_ipv6_nc_get(iface, &ipv6->src); - - if (nc_entry != NULL) { - if ((sl2a_len != nc_entry->l2_addr_len) || - (memcmp(sl2a, nc_entry->l2_addr, sl2a_len) != 0)) { - /* if entry exists but l2 address differs: set */ - nc_entry->l2_addr_len = sl2a_len; - memcpy(nc_entry->l2_addr, sl2a, sl2a_len); - - _set_state(nc_entry, NG_IPV6_NC_STATE_STALE); - } - } - else { - ng_ipv6_nc_add(iface, &ipv6->src, sl2a, sl2a_len, - NG_IPV6_NC_STATE_STALE); - } - - return true; - - default: /* wrong encapsulating message: silently discard */ - DEBUG("ndp: silently discard sl2a_opt for ICMPv6 message type %" - PRIu8 "\n", icmpv6_type); - return true; - } -} - -static int _handle_tl2a_opt(ng_pktsnip_t *pkt, ng_ipv6_hdr_t *ipv6, - uint8_t icmpv6_type, ng_ndp_opt_t *tl2a_opt, - uint8_t *l2addr) -{ - uint8_t tl2a_len = 0; - uint8_t *tl2a = (uint8_t *)(tl2a_opt + 1); - - if ((tl2a_opt->len == 0) || ng_ipv6_addr_is_unspecified(&ipv6->src)) { - DEBUG("ndp: invalid target link-layer address option received\n"); - return -EINVAL; - } - - switch (icmpv6_type) { - case NG_ICMPV6_NBR_ADV: - while (pkt) { - if (pkt->type == NG_NETTYPE_NETIF) { - ng_netif_hdr_t *hdr = pkt->data; - tl2a_len = hdr->src_l2addr_len; - break; - } - pkt = pkt->next; - } - - if (tl2a_len == 0) { /* in case there was no source address in l2 */ - tl2a_len = (tl2a_opt->len / 8) - sizeof(ng_ndp_opt_t); - - /* ignore all zeroes at the end for length */ - for (; tl2a[tl2a_len - 1] == 0x00; tl2a_len--); - } - - DEBUG("ndp: received TL2A (link-layer address: %s)\n", - ng_netif_addr_to_str(addr_str, sizeof(addr_str), tl2a, tl2a_len)); - - memcpy(l2addr, tl2a, tl2a_len); - - return (int)tl2a_len; - - default: /* wrong encapsulating message: silently discard */ - DEBUG("ndp: silently discard tl2a_opt for ICMPv6 message type %" - PRIu8 "\n", icmpv6_type); - return 0; - } -} - -static void _set_state(ng_ipv6_nc_t *nc_entry, uint8_t state) -{ - ng_ipv6_netif_t *ipv6_iface; - timex_t t = { NG_NDP_FIRST_PROBE_DELAY, 0 }; - - nc_entry->flags &= ~NG_IPV6_NC_STATE_MASK; - nc_entry->flags |= state; - - DEBUG("ndp: set %s state to ", - ng_ipv6_addr_to_str(addr_str, &nc_entry->ipv6_addr, sizeof(addr_str))); - - switch (state) { - case NG_IPV6_NC_STATE_REACHABLE: - ipv6_iface = ng_ipv6_netif_get(nc_entry->iface); - DEBUG("REACHABLE (reachable time = %" PRIu32 ".%06" PRIu32 ")\n", - ipv6_iface->reach_time.seconds, - ipv6_iface->reach_time.microseconds); - t = ipv6_iface->reach_time; - /* we intentionally fall through here to set the desired timeout t */ - case NG_IPV6_NC_STATE_DELAY: -#if ENABLE_DEBUG - if (state == NG_IPV6_NC_STATE_DELAY) { - DEBUG("DELAY (probe with unicast NS in %u seconds)\n", - NG_NDP_FIRST_PROBE_DELAY); - } -#endif - vtimer_remove(&nc_entry->nbr_sol_timer); - vtimer_set_msg(&nc_entry->nbr_sol_timer, t, ng_ipv6_pid, - NG_NDP_MSG_NC_STATE_TIMEOUT, nc_entry); - break; - - case NG_IPV6_NC_STATE_PROBE: - ipv6_iface = ng_ipv6_netif_get(nc_entry->iface); - - nc_entry->probes_remaining = NG_NDP_MAX_UC_NBR_SOL_NUMOF; - DEBUG("PROBE (probe with %" PRIu8 " unicast NS every %" PRIu32 - ".%06" PRIu32 " seconds)\n", nc_entry->probes_remaining, - ipv6_iface->retrans_timer.seconds, - ipv6_iface->retrans_timer.microseconds); - - _send_nbr_sol(nc_entry->iface, &nc_entry->ipv6_addr, - &nc_entry->ipv6_addr); - - mutex_lock(&ipv6_iface->mutex); - vtimer_remove(&nc_entry->nbr_sol_timer); - vtimer_set_msg(&nc_entry->nbr_sol_timer, - ipv6_iface->retrans_timer, ng_ipv6_pid, - NG_NDP_MSG_NBR_SOL_RETRANS, nc_entry); - mutex_unlock(&ipv6_iface->mutex); - break; - -#ifdef ENABLE_DEBUG - case NG_IPV6_NC_STATE_STALE: - DEBUG("STALE (go into DELAY on next packet)\n"); - break; -#endif - - default: - DEBUG("errorneous or unknown\n"); - break; - } -} - /** * @} */ diff --git a/sys/net/network_layer/ng_ndp/node/Makefile b/sys/net/network_layer/ng_ndp/node/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..abdb17d251c8b6e7f36c9914eb6e47913cbef50d --- /dev/null +++ b/sys/net/network_layer/ng_ndp/node/Makefile @@ -0,0 +1,3 @@ +MODULE = ng_ndp_node + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/network_layer/ng_ndp/node/ng_ndp_node.c b/sys/net/network_layer/ng_ndp/node/ng_ndp_node.c new file mode 100644 index 0000000000000000000000000000000000000000..1de413b19a1a9b24ded496ce45fa6d61f53ad5ea --- /dev/null +++ b/sys/net/network_layer/ng_ndp/node/ng_ndp_node.c @@ -0,0 +1,194 @@ +/* + * 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 <stdint.h> +#include <stdlib.h> + +#include "kernel_types.h" +#include "net/fib.h" +#include "net/ng_ipv6.h" +#include "net/ng_ndp.h" +#include "net/ng_pkt.h" +#include "net/ng_pktqueue.h" + +#include "net/ng_ndp/internal.h" + +#include "net/ng_ndp/node.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#if ENABLE_DEBUG +static char addr_str[NG_IPV6_ADDR_MAX_STR_LEN]; +#endif + +static ng_pktqueue_t _pkt_nodes[NG_IPV6_NC_SIZE * 2]; + +/** + * @brief Allocates a node for the packet queue. + * + * @param[in] pkt Packet to add to the packet queue. + * + * @return A packet queue node. + */ +static ng_pktqueue_t *_alloc_pkt_node(ng_pktsnip_t *pkt) +{ + for (size_t i = 0; i < sizeof(_pkt_nodes) / sizeof(ng_pktqueue_t); i++) { + if ((_pkt_nodes[i].pkt == NULL) && (_pkt_nodes[i].next == NULL)) { + _pkt_nodes[i].pkt = pkt; + + return &(_pkt_nodes[i]); + } + } + + return NULL; +} + +kernel_pid_t ng_ndp_node_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len, + kernel_pid_t iface, ng_ipv6_addr_t *dst, + ng_pktsnip_t *pkt) +{ + ng_ipv6_addr_t *next_hop_ip = NULL, *prefix = NULL; + +#ifdef MODULE_NG_IPV6_EXT_RH + next_hop_ip = ng_ipv6_ext_rh_next_hop(hdr); +#endif +#ifdef MODULE_FIB + size_t next_hop_size; + uint32_t next_hop_flags = 0; + ng_ipv6_addr_t next_hop_actual; /* FIB copies address into this variable */ + + if ((next_hop_ip == NULL) && + (fib_get_next_hop(&iface, next_hop_actual.u8, &next_hop_size, + &next_hop_flags, (uint8_t *)dst, + sizeof(ng_ipv6_addr_t), 0) >= 0) && + (next_hop_size == sizeof(ng_ipv6_addr_t))) { + next_hop_ip = &next_hop_actual; + } + +#endif + + if ((next_hop_ip == NULL)) { /* no route to host */ + if (iface == KERNEL_PID_UNDEF) { + /* ng_ipv6_netif_t doubles as prefix list */ + iface = ng_ipv6_netif_find_by_prefix(&prefix, dst); + } + else { + /* ng_ipv6_netif_t doubles as prefix list */ + prefix = ng_ipv6_netif_match_prefix(iface, dst); + } + + if ((prefix != NULL) && /* prefix is on-link */ + (ng_ipv6_netif_addr_get(prefix)->flags & + NG_IPV6_NETIF_ADDR_FLAGS_NDP_ON_LINK)) { + next_hop_ip = dst; +#ifdef MODULE_FIB + /* We don't care if FIB is full, this is just for efficiency + * for later sends */ + fib_add_entry(iface, (uint8_t *)dst, sizeof(ng_ipv6_addr_t), 0, + (uint8_t *)next_hop_ip, sizeof(ng_ipv6_addr_t), 0, + FIB_LIFETIME_NO_EXPIRE); +#endif + } + } + + if (next_hop_ip == NULL) { + next_hop_ip = ng_ndp_internal_default_router(); +#ifdef MODULE_FIB + /* We don't care if FIB is full, this is just for efficiency for later + * sends */ + fib_add_entry(iface, (uint8_t *)dst, sizeof(ng_ipv6_addr_t), 0, + (uint8_t *)next_hop_ip, sizeof(ng_ipv6_addr_t), 0, + FIB_LIFETIME_NO_EXPIRE); +#endif + } + + if (next_hop_ip != NULL) { + ng_ipv6_nc_t *nc_entry = ng_ipv6_nc_get(iface, next_hop_ip); + + if ((nc_entry != NULL) && ng_ipv6_nc_is_reachable(nc_entry)) { + DEBUG("ndp node: found reachable neighbor (%s => ", + ng_ipv6_addr_to_str(addr_str, &nc_entry->ipv6_addr, sizeof(addr_str))); + DEBUG("%s)\n", + ng_netif_addr_to_str(addr_str, sizeof(addr_str), + nc_entry->l2_addr, nc_entry->l2_addr_len)); + + if (ng_ipv6_nc_get_state(nc_entry) == NG_IPV6_NC_STATE_STALE) { + ng_ndp_internal_set_state(nc_entry, NG_IPV6_NC_STATE_DELAY); + } + + memcpy(l2addr, nc_entry->l2_addr, nc_entry->l2_addr_len); + *l2addr_len = nc_entry->l2_addr_len; + /* TODO: unreachability check */ + return nc_entry->iface; + } + else if (nc_entry == NULL) { + ng_pktqueue_t *pkt_node; + ng_ipv6_addr_t dst_sol; + + nc_entry = ng_ipv6_nc_add(iface, next_hop_ip, NULL, 0, + NG_IPV6_NC_STATE_INCOMPLETE << NG_IPV6_NC_STATE_POS); + + if (nc_entry == NULL) { + DEBUG("ndp node: could not create neighbor cache entry\n"); + return KERNEL_PID_UNDEF; + } + + pkt_node = _alloc_pkt_node(pkt); + + if (pkt_node == NULL) { + DEBUG("ndp node: could not add packet to packet queue\n"); + } + else { + /* prevent packet from being released by IPv6 */ + ng_pktbuf_hold(pkt_node->pkt, 1); + ng_pktqueue_add(&nc_entry->pkts, pkt_node); + } + + /* address resolution */ + ng_ipv6_addr_set_solicited_nodes(&dst_sol, next_hop_ip); + + if (iface == KERNEL_PID_UNDEF) { + timex_t t = { 0, NG_NDP_RETRANS_TIMER }; + kernel_pid_t ifs[NG_NETIF_NUMOF]; + size_t ifnum = ng_netif_get(ifs); + + for (size_t i = 0; i < ifnum; i++) { + ng_ndp_internal_send_nbr_sol(ifs[i], next_hop_ip, &dst_sol); + } + + vtimer_remove(&nc_entry->nbr_sol_timer); + vtimer_set_msg(&nc_entry->nbr_sol_timer, t, ng_ipv6_pid, + NG_NDP_MSG_NBR_SOL_RETRANS, nc_entry); + } + else { + ng_ipv6_netif_t *ipv6_iface = ng_ipv6_netif_get(iface); + + ng_ndp_internal_send_nbr_sol(iface, next_hop_ip, &dst_sol); + + mutex_lock(&ipv6_iface->mutex); + vtimer_remove(&nc_entry->nbr_sol_timer); + vtimer_set_msg(&nc_entry->nbr_sol_timer, + ipv6_iface->retrans_timer, ng_ipv6_pid, + NG_NDP_MSG_NBR_SOL_RETRANS, nc_entry); + mutex_unlock(&ipv6_iface->mutex); + } + } + } + + return KERNEL_PID_UNDEF; +} + + +/** @} */