diff --git a/Makefile.dep b/Makefile.dep index c688738e4467..ed5013033022 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -72,6 +72,13 @@ ifneq (,$(filter ng_sixlowpan_ctx,$(USEMODULE))) USEMODULE += vtimer endif +ifneq (,$(filter ng_ndp,$(USEMODULE))) + USEMODULE += ng_icmpv6 + USEMODULE += random + USEMODULE += timex + USEMODULE += vtimer +endif + ifneq (,$(filter ng_icmpv6_echo,$(USEMODULE))) USEMODULE += ng_icmpv6 USEMODULE += ng_netbase @@ -97,7 +104,11 @@ ifneq (,$(filter ng_ipv6,$(USEMODULE))) USEMODULE += ng_ipv6_hdr USEMODULE += ng_ipv6_nc USEMODULE += ng_ipv6_netif + USEMODULE += ng_ndp USEMODULE += ng_netbase + USEMODULE += random + USEMODULE += timex + USEMODULE += vtimer endif ifneq (,$(filter ng_ipv6_nc,$(USEMODULE))) diff --git a/sys/Makefile b/sys/Makefile index 515cc1314803..b5ce3cd2bc03 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -86,6 +86,9 @@ endif ifneq (,$(filter ng_inet_csum,$(USEMODULE))) DIRS += net/crosslayer/ng_inet_csum endif +ifneq (,$(filter ng_ndp,$(USEMODULE))) + DIRS += net/network_layer/ng_ndp +endif ifneq (,$(filter ng_netapi,$(USEMODULE))) DIRS += net/crosslayer/ng_netapi endif diff --git a/sys/include/net/ng_ipv6.h b/sys/include/net/ng_ipv6.h index 15300e10d0bd..b7415de35c3b 100644 --- a/sys/include/net/ng_ipv6.h +++ b/sys/include/net/ng_ipv6.h @@ -62,6 +62,16 @@ extern "C" { #define NG_IPV6_MSG_QUEUE_SIZE (8U) #endif +/** + * @brief The PID to the IPv6 thread. + * + * @note Use @ref ng_ipv6_init() to initialize. **Do not set by hand**. + * + * @details This variable is preferred for IPv6 internal communication *only*. + * Please use @ref net_ng_netreg for external communication. + */ +extern kernel_pid_t ng_ipv6_pid; + /** * @brief Initialization of the IPv6 thread. * diff --git a/sys/include/net/ng_ipv6/nc.h b/sys/include/net/ng_ipv6/nc.h index 2821b014cc37..e256ae60df96 100644 --- a/sys/include/net/ng_ipv6/nc.h +++ b/sys/include/net/ng_ipv6/nc.h @@ -137,7 +137,7 @@ typedef struct { */ vtimer_t nbr_adv_timer; - uint8_t unanswered_probes; /**< number of unanswered probes */ + uint8_t probes_remaining; /**< remaining number of unanswered probes */ /** * @} */ diff --git a/sys/include/net/ng_ipv6/netif.h b/sys/include/net/ng_ipv6/netif.h index 5acbc6c55c92..6f24e90e1973 100644 --- a/sys/include/net/ng_ipv6/netif.h +++ b/sys/include/net/ng_ipv6/netif.h @@ -195,6 +195,29 @@ typedef struct { uint16_t mtu; /**< Maximum Transmission Unit (MTU) of the interface */ uint8_t cur_hl; /**< current hop limit for the interface */ uint16_t flags; /**< flags for 6LoWPAN and Neighbor Discovery */ + /** + * @brief Base value in microseconds for computing random + * ng_ipv6_netif_t::reach_time. + * The default value is @ref NG_NDP_REACH_TIME. + */ + uint32_t reach_time_base; + + /** + * @brief The time a neighbor is considered reachable after receiving + * a reachability confirmation. + * Should be uniformly distributed between @ref NG_NDP_MIN_RAND + * and NG_NDP_MAX_RAND multiplied with + * ng_ipv6_netif_t::reach_time_base microseconds devided by 10. + * Can't be greater than 1 hour. + */ + timex_t reach_time; + + /** + * @brief Time between retransmissions of neighbor solicitations to a + * neighbor. + * The default value is @ref NG_NDP_RETRANS_TIMER. + */ + timex_t retrans_timer; } ng_ipv6_netif_t; /** diff --git a/sys/include/net/ng_ndp.h b/sys/include/net/ng_ndp.h new file mode 100644 index 000000000000..d92339f90c2f --- /dev/null +++ b/sys/include/net/ng_ndp.h @@ -0,0 +1,297 @@ +/* + * 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_ng_ndp IPv6 Neighbor discovery + * @ingroup net_ng_icmpv6 + * @brief IPv6 Neighbor Discovery Implementation + * @{ + * + * @file + * @brief Neighbor Discovery definitions + * + * @author Martine Lenders + */ + +#include + +#include "byteorder.h" +#include "net/ng_pkt.h" +#include "net/ng_icmpv6.h" +#include "net/ng_ipv6/addr.h" +#include "net/ng_ipv6/nc.h" +#include "net/ng_ipv6/netif.h" + +#include "net/ng_ndp/types.h" + +#ifndef NG_NDP_H_ +#define NG_NDP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define NG_NDP_MSG_RTR_TIMEOUT (0x0211) /**< Message type for router timeouts */ +#define NG_NDP_MSG_ADDR_TIMEOUT (0x0212) /**< Message type for address timeouts */ +#define NG_NDP_MSG_NBR_SOL_RETRANS (0x0213) /**< Message type for multicast + * neighbor solicitation retransmissions */ +#define NG_NDP_MSG_NC_STATE_TIMEOUT (0x0214) /**< Message type for neighbor cache state timeouts */ + +/** + * @{ + * @name Node constants + * @see + * RFC 4861, section 10 + * + */ +/** + * @brief Maximum number of unanswered multicast neighbor solicitations + * before address resolution is considered failed. + */ +#define NG_NDP_MAX_MC_NBR_SOL_NUMOF (3U) + +/** + * @brief Maximum number of unanswered unicast neighbor solicitations before + * an address is considered unreachable. + */ +#define NG_NDP_MAX_UC_NBR_SOL_NUMOF (3U) + +/** + * @brief Upper bound of randomized delay in seconds for a solicited + * neighbor advertisement transmission for an anycast target. + */ +#define NG_NDP_MAX_AC_TGT_DELAY (1U) + +/** + * @brief Maximum number of unsolicited neighbor advertisements before on + * link-layer address change. + */ +#define NG_NDP_MAX_NBR_ADV_NUMOF (3U) + +/** + * @brief Base value in mircoseconds for computing randomised + * reachable time. + */ +#define NG_NDP_REACH_TIME (30U * SEC_IN_USEC) + +/** + * @brief Time in mircoseconds between retransmissions of neighbor + * solicitations to a neighbor. + */ +#define NG_NDP_RETRANS_TIMER (1U * SEC_IN_USEC) + +/** + * @brief Delay in seconds for neighbor cache entry between entering + * DELAY state and entering PROBE state if no reachability + * confirmation has been received. + */ +#define NG_NDP_FIRST_PROBE_DELAY (5U) + +/** + * @brief Lower bound for randomised reachable time calculation. + */ +#define NG_NDP_MIN_RAND (5U) + +/** + * @brief Upper bound for randomised reachable time calculation. + */ +#define NG_NDP_MAX_RAND (15U) +/** + * @} + */ + +/** + * @brief Handles received neighbor solicitations + * + * @param[in] iface The receiving interface. + * @param[in] pkt The received packet. + * @param[in] ipv6 The IPv6 header in @p pkt. + * @param[in] nbr_sol The neighbor solicitation in @p pkt. + * @param[in] icmpv6_size The overall size of the neighbor solicitation + */ +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); + +/** + * @brief Handles received neighbor solicitations + * + * @param[in] iface The receiving interface. + * @param[in] pkt The received packet. + * @param[in] ipv6 The IPv6 header in @p pkt. + * @param[in] nbr_adv The neighbor advertisement in @p pkt. + * @param[in] icmpv6_size The overall size of the neighbor solicitation + */ +void ng_ndp_nbr_adv_handle(kernel_pid_t iface, ng_pktsnip_t *pkt, + ng_ipv6_hdr_t *ipv6, ng_ndp_nbr_adv_t *nbr_adv, + size_t icmpv6_size); + +/** + * @brief Retransmits a multicast neighbor solicitation for an incomplete or + * probing neighbor cache entry @p nc_entry, + * if nc_entry::probes_remaining > 0. + * + * @details If nc_entry::probes_remaining > 0 it will be decremented. If it + * reaches 0 it the entry @p nc_entry will be removed from the + * neighbor cache. + * + * @param[in] nc_entry A neighbor cache entry. Will be ignored if its state + * is not @ref NG_IPV6_NC_STATE_INCOMPLETE or + * @ref NG_IPV6_NC_STATE_PROBE. + */ +void ng_ndp_retrans_nbr_sol(ng_ipv6_nc_t *nc_entry); + +/** + * @brief Event handler for a neighbor cache state timeout. + * + * @param[in] nc_entry A neighbor cache entry. + */ +void ng_ndp_state_timeout(ng_ipv6_nc_t *nc_entry); + +/** + * @brief NDP interface initialization. + * + * @param[in] iface An IPv6 interface descriptor. Must not be NULL. + */ +void ng_ndp_netif_add(ng_ipv6_netif_t *iface); + +/** + * @brief NDP interface removal. + * + * @param[in] iface An IPv6 interface descriptor. Must not be NULL. + */ +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. + * + * @see + * RFC 4861, section 4.3 + * + * + * @param[in] tgt The target address. + * @param[in] options Options to append to the router solicitation. + * + * @return The resulting ICMPv6 packet on success. + * @return NULL, on failure. + */ +ng_pktsnip_t *ng_ndp_nbr_sol_build(ng_ipv6_addr_t *tgt, ng_pktsnip_t *options); + +/** + * @brief Builds a neighbor advertisement message for sending. + * + * @see + * RFC 4861, section 4.4 + * + * + * @param[in] flags Flags as defined above. + * @ref NG_NDP_NBR_ADV_FLAGS_R == 1 indicates, that the + * sender is a router, + * @ref NG_NDP_NBR_ADV_FLAGS_S == 1 indicates that the + * advertisement was sent in response to a neighbor + * solicitation, + * @ref NG_NDP_NBR_ADV_FLAGS_O == 1 indicates that the + * advertisement should override an existing cache entry + * and update the cached link-layer address. + * @param[in] tgt For solicited advertisements, the Target Address field + * in the neighbor solicitaton. + * For and unsolicited advertisement, the address whose + * link-layer addres has changed. + * MUST NOT be multicast. + * @param[in] options Options to append to the neighbor advertisement. + * + * @return The resulting ICMPv6 packet on success. + * @return NULL, on failure. + */ +ng_pktsnip_t *ng_ndp_nbr_adv_build(uint8_t flags, ng_ipv6_addr_t *tgt, + ng_pktsnip_t *options); + +/** + * @brief Builds a generic NDP option. + * + * @param[in] type Type of the option. + * @param[in] size Size in byte of the option (will be rounded up to the next + * multiple of 8). + * @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 + */ +ng_pktsnip_t *ng_ndp_opt_build(uint8_t type, size_t size, ng_pktsnip_t *next); + +/** + * @brief Builds the source link-layer address option. + * + * @see + * RFC 4861, section 4.6.1 + * + * + * @note Must only be used with neighbor solicitations, router solicitations, + * and router advertisements. This is not checked however, since + * hosts should silently ignore it in other NDP messages. + * + * @param[in] l2addr A link-layer address of variable length. + * @param[in] l2addr_len Length of @p l2addr. + * @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 + */ +ng_pktsnip_t *ng_ndp_opt_sl2a_build(const uint8_t *l2addr, uint8_t l2addr_len, + ng_pktsnip_t *next); + +/** + * @brief Builds the target link-layer address option. + * + * @see + * RFC 4861, section 4.6.1 + * + * + * @note Must only be used with neighbor advertisemnents and redirect packets. + * This is not checked however, since hosts should silently ignore it + * in other NDP messages. + * + * @param[in] l2addr A link-layer address of variable length. + * @param[in] l2addr_len Length of @p l2addr. + * @param[in] next More options in the packet. NULL, if there are none. + * + * @return The pkt snip list of options, on success + * @return NULL, if packet buffer is full + */ +ng_pktsnip_t *ng_ndp_opt_tl2a_build(const uint8_t *l2addr, uint8_t l2addr_len, + ng_pktsnip_t *next); + +#ifdef __cplusplus +} +#endif + +#endif /* NG_NDP_H_ */ +/** + * @} + */ diff --git a/sys/include/net/ng_ndp/types.h b/sys/include/net/ng_ndp/types.h new file mode 100644 index 000000000000..2e331b5156bf --- /dev/null +++ b/sys/include/net/ng_ndp/types.h @@ -0,0 +1,250 @@ +/* + * 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_ng_ndp_types Types for IPv6 neighbor discovery + * @ingroup net_ng_ndp + * @brief IPv6 neighbor discovery message types + * @{ + * + * @file + * @brief IPv6 neighbor discovery message type definitions + * + * @author Martine Lenders + */ +#ifndef NG_NDP_TYPES_H_ +#define NG_NDP_TYPES_H_ + +#include + +#include "byteorder.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @{ + * @name Flags for router advertisement messages + * @see + * RFC 4861, section 4.2 + * + */ +#define NG_NDP_RTR_ADV_FLAGS_MASK (0xc0) +#define NG_NDP_RTR_ADV_FLAGS_M (0x80) /**< managed address configuration */ +#define NG_NDP_RTR_ADV_FLAGS_O (0x40) /**< other configuration */ +/** + * @} + */ + +/** + * @{ + * @name Flags for neighbor advertisement messages + * @see + * RFC 4861, section 4.2 + * + */ +#define NG_NDP_NBR_ADV_FLAGS_MASK (0xe0) +#define NG_NDP_NBR_ADV_FLAGS_R (0x80) /**< router */ +#define NG_NDP_NBR_ADV_FLAGS_S (0x40) /**< solicited */ +#define NG_NDP_NBR_ADV_FLAGS_O (0x20) /**< override */ +/** + * @} + */ + +/** + * @{ + * @name NDP option types + * @see + * IANA, IPv6 Neighbor Discovery Option Formats + * + */ +#define NG_NDP_OPT_SL2A (1) /**< source link-layer address option */ +#define NG_NDP_OPT_TL2A (2) /**< target link-layer address option */ +#define NG_NDP_OPT_PI (3) /**< prefix information option */ +#define NG_NDP_OPT_RH (4) /**< redirected option */ +#define NG_NDP_OPT_MTU (5) /**< MTU option */ +/** + * @} + */ + +/** + * @{ + * @name Flags for prefix information option + */ +#define NG_NDP_OPT_PI_FLAGS_MASK (0xc0) +#define NG_NDP_OPT_PI_FLAGS_L (0x80) /**< on-link */ +#define NG_NDP_OPT_PI_FLAGS_A (0x40) /**< autonomous address configuration */ +/** + * @} + */ + +/** + * @{ + * @name Lengths for fixed length options + * @brief Options don't use bytes as their length unit, but 8 bytes. + */ +#define NG_NDP_OPT_PI_LEN (4U) +#define NG_NDP_OPT_MTU_LEN (1U) +/** + * @} + */ + +/** + * @brief Router solicitation message format. + * @extends ng_icmpv6_hdr_t + * + * @see + * RFC 4861, section 4.1 + * + */ +typedef struct __attribute__((packed)) { + uint8_t type; /**< message type */ + uint8_t code; /**< message code */ + network_uint16_t csum; /**< checksum */ + network_uint32_t resv; /**< reserved field */ +} ng_ndp_rtr_sol_t; + +/** + * @brief Router advertisement message format. + * @extends ng_icmpv6_hdr_t + * + * @see + * RFC 4861, section 4.2 + * + */ +typedef struct __attribute__((packed)) { + uint8_t type; /**< message type */ + uint8_t code; /**< message code */ + network_uint16_t csum; /**< checksum */ + uint8_t cur_hl; /**< current hop limit */ + uint8_t flags; /**< flags */ + network_uint16_t ltime; /**< router lifetime */ + network_uint32_t reach_time; /**< reachable time */ + network_uint32_t retrans_timer; /**< retransmission timer */ +} ng_ndp_rtr_adv_t; + +/** + * @brief Neighbor solicitation message format. + * @extends ng_icmpv6_hdr_t + * + * @see + * RFC 4861, section 4.3 + * + */ +typedef struct __attribute__((packed)) { + uint8_t type; /**< message type */ + uint8_t code; /**< message code */ + network_uint16_t csum; /**< checksum */ + network_uint32_t resv; /**< reserved field */ + ng_ipv6_addr_t tgt; /**< target address */ +} ng_ndp_nbr_sol_t; + +/** + * @brief Neighbor advertisement message format. + * @extends ng_icmpv6_hdr_t + * + * @see + * RFC 4861, section 4.4 + * + */ +typedef struct __attribute__((packed)) { + uint8_t type; /**< message type */ + uint8_t code; /**< message code */ + network_uint16_t csum; /**< checksum */ + uint8_t flags; /**< flags */ + uint8_t resv[3]; /**< reserved fields */ + ng_ipv6_addr_t tgt; /**< target address */ +} ng_ndp_nbr_adv_t; + +/** + * @brief Neighbor advertisement message format. + * @extends ng_icmpv6_hdr_t + * + * @see + * RFC 4861, section 4.5 + * + */ +typedef struct __attribute__((packed)) { + uint8_t type; /**< message type */ + uint8_t code; /**< message code */ + network_uint16_t csum; /**< checksum */ + network_uint32_t resv; /**< reserved field */ + ng_ipv6_addr_t tgt; /**< target address */ + ng_ipv6_addr_t dst; /**< destination address */ +} ng_ndp_redirect_t; + +/** + * @brief General NDP option format + * @see + * RFC 4861, section 4.6 + * + */ +typedef struct __attribute__((packed)) { + uint8_t type; /**< option type */ + uint8_t len; /**< length in units of 8 octets */ +} ng_ndp_opt_t; + +/* XXX: slla and tlla are just ng_ndp_opt_t with variable link layer address + * appended */ + +/** + * @brief Prefix information option format + * @extends ng_ndp_opt_t + * + * @see + * RFC 4861, section 4.6.2 + * + */ +typedef struct __attribute__((packed)) { + uint8_t type; /**< option type */ + uint8_t len; /**< length in units of 8 octets */ + uint8_t prefix_len; /**< prefix length */ + uint8_t flags; /**< flags */ + network_uint32_t valid_ltime; /**< valid lifetime */ + network_uint32_t pref_ltime; /**< preferred lifetime */ + network_uint32_t resv; /**< reserved field */ + ng_ipv6_addr_t prefix; /**< prefix */ +} ng_ndp_opt_pi_t; + +/** + * @brief Redirected header option format + * @extends ng_ndp_opt_t + * + * @see + * RFC 4861, section 4.6.3 + * + */ +typedef struct __attribute__((packed)) { + uint8_t type; /**< option type */ + uint8_t len; /**< length in units of 8 octets */ + uint8_t resv[6]; /**< reserved field */ +} ng_ndp_opt_rh_t; + +/** + * @brief MTU option format + * @extends ng_ndp_opt_t + * + * @see + * RFC 4861, section 4.6.4 + * + */ +typedef struct __attribute__((packed)) { + uint8_t type; /**< option type */ + uint8_t len; /**< length in units of 8 octets */ + network_uint16_t resv; /**< reserved field */ + network_uint32_t mtu; /**< MTU */ +} ng_ndp_opt_mtu_t; + + +#ifdef __cplusplus +} +#endif + +#endif /* NG_NDP_TYPES_H_ */ +/** @} */ diff --git a/sys/net/network_layer/ng_icmpv6/ng_icmpv6.c b/sys/net/network_layer/ng_icmpv6/ng_icmpv6.c index 06eba4335efa..4750f05a6777 100644 --- a/sys/net/network_layer/ng_icmpv6/ng_icmpv6.c +++ b/sys/net/network_layer/ng_icmpv6/ng_icmpv6.c @@ -24,6 +24,7 @@ #include "net/ng_netbase.h" #include "net/ng_protnum.h" #include "net/ng_ipv6/hdr.h" +#include "net/ng_ndp.h" #include "od.h" #include "utlist.h" @@ -83,16 +84,32 @@ void ng_icmpv6_demux(kernel_pid_t iface, ng_pktsnip_t *pkt) break; #endif -#ifdef MODULE_NG_NDP case NG_ICMPV6_RTR_SOL: + DEBUG("icmpv6: router solicitation received\n"); + /* TODO */ + break; + case NG_ICMPV6_RTR_ADV: + DEBUG("icmpv6: router advertisement received\n"); + /* TODO */ + break; + case NG_ICMPV6_NBR_SOL: + DEBUG("icmpv6: neighbor solicitation received\n"); + ng_ndp_nbr_sol_handle(iface, pkt, ipv6->data, (ng_ndp_nbr_sol_t *)hdr, + icmpv6->size); + break; + case NG_ICMPV6_NBR_ADV: + DEBUG("icmpv6: neighbor advertisement received\n"); + ng_ndp_nbr_adv_handle(iface, pkt, ipv6->data, (ng_ndp_nbr_adv_t *)hdr, + icmpv6->size); + break; + case NG_ICMPV6_REDIRECT: - DEBUG("icmpv6: neighbor discovery message received\n"); + DEBUG("icmpv6: redirect message received\n"); /* TODO */ break; -#endif #ifdef MODULE_NG_RPL case NG_ICMPV6_RPL_CTRL: diff --git a/sys/net/network_layer/ng_ipv6/nc/ng_ipv6_nc.c b/sys/net/network_layer/ng_ipv6/nc/ng_ipv6_nc.c index 9f7cbe756d11..026b8fe919e4 100644 --- a/sys/net/network_layer/ng_ipv6/nc/ng_ipv6_nc.c +++ b/sys/net/network_layer/ng_ipv6/nc/ng_ipv6_nc.c @@ -15,8 +15,14 @@ #include #include +#include "net/ng_ipv6.h" #include "net/ng_ipv6/addr.h" #include "net/ng_ipv6/nc.h" +#include "net/ng_ipv6/netif.h" +#include "net/ng_ndp.h" +#include "thread.h" +#include "timex.h" +#include "vtimer.h" #define ENABLE_DEBUG (0) #include "debug.h" @@ -110,6 +116,11 @@ ng_ipv6_nc_t *ng_ipv6_nc_add(kernel_pid_t iface, const ng_ipv6_addr_t *ipv6_addr DEBUG(" with flags = 0x%0x\n", flags); + if (ng_ipv6_nc_get_state(free_entry) == NG_IPV6_NC_STATE_INCOMPLETE) { + DEBUG("ipv6_nc: Set remaining probes to %" PRIu8 "\n"); + free_entry->probes_remaining = NG_NDP_MAX_MC_NBR_SOL_NUMOF; + } + return free_entry; } @@ -192,8 +203,16 @@ ng_ipv6_nc_t *ng_ipv6_nc_still_reachable(const ng_ipv6_addr_t *ipv6_addr) return NULL; } - if (((entry->flags & NG_IPV6_NC_STATE_MASK) >> NG_IPV6_NC_STATE_POS) != - NG_IPV6_NC_STATE_INCOMPLETE) { + if (ng_ipv6_nc_get_state(entry) != NG_IPV6_NC_STATE_INCOMPLETE) { +#if defined(MODULE_NG_IPV6_NETIF) && defined(MODULE_VTIMER) && defined(MODULE_NG_IPV6) + ng_ipv6_netif_t *iface = ng_ipv6_netif_get(entry->iface); + timex_t t = iface->reach_time; + + vtimer_remove(&entry->nbr_sol_timer); + vtimer_set_msg(&entry->nbr_sol_timer, t, ng_ipv6_pid, + NG_NDP_MSG_NC_STATE_TIMEOUT, entry); +#endif + DEBUG("ipv6_nc: Marking entry %s as reachable\n", ng_ipv6_addr_to_str(addr_str, ipv6_addr, sizeof(addr_str))); entry->flags &= ~(NG_IPV6_NC_STATE_MASK >> NG_IPV6_NC_STATE_POS); diff --git a/sys/net/network_layer/ng_ipv6/netif/ng_ipv6_netif.c b/sys/net/network_layer/ng_ipv6/netif/ng_ipv6_netif.c index cebff338aef6..0b0c3103058f 100644 --- a/sys/net/network_layer/ng_ipv6/netif/ng_ipv6_netif.c +++ b/sys/net/network_layer/ng_ipv6/netif/ng_ipv6_netif.c @@ -21,6 +21,7 @@ #include "kernel_types.h" #include "mutex.h" #include "net/ng_ipv6/addr.h" +#include "net/ng_ndp.h" #include "net/ng_netif.h" #include "net/ng_ipv6/netif.h" @@ -119,6 +120,10 @@ void ng_ipv6_netif_add(kernel_pid_t pid) mutex_unlock(&ipv6_ifs[i].mutex); +#ifdef MODULE_NG_NDP + ng_ndp_netif_add(&ipv6_ifs[i]); +#endif + DEBUG(" * pid = %" PRIkernel_pid " ", ipv6_ifs[i].pid); DEBUG("cur_hl = %d ", ipv6_ifs[i].cur_hl); DEBUG("mtu = %d ", ipv6_ifs[i].mtu); @@ -138,6 +143,10 @@ void ng_ipv6_netif_remove(kernel_pid_t pid) return; } +#ifdef MODULE_NG_NDP + ng_ndp_netif_remove(entry); +#endif + mutex_lock(&entry->mutex); _reset_addr_from_entry(entry); diff --git a/sys/net/network_layer/ng_ipv6/ng_ipv6.c b/sys/net/network_layer/ng_ipv6/ng_ipv6.c index 5a26186aabb0..9389b60ceb6a 100644 --- a/sys/net/network_layer/ng_ipv6/ng_ipv6.c +++ b/sys/net/network_layer/ng_ipv6/ng_ipv6.c @@ -20,6 +20,7 @@ #include "kernel_types.h" #include "net/ng_icmpv6.h" #include "net/ng_netbase.h" +#include "net/ng_ndp.h" #include "net/ng_protnum.h" #include "thread.h" #include "utlist.h" @@ -35,12 +36,13 @@ #define _MAX_L2_ADDR_LEN (8U) static char _stack[NG_IPV6_STACK_SIZE]; -static kernel_pid_t _pid = KERNEL_PID_UNDEF; #if ENABLE_DEBUG static char addr_str[NG_IPV6_ADDR_MAX_STR_LEN]; #endif +kernel_pid_t ng_ipv6_pid = KERNEL_PID_UNDEF; + /* handles NG_NETAPI_MSG_TYPE_RCV commands */ static void _receive(ng_pktsnip_t *pkt); /* dispatches received IPv6 packet for upper layer */ @@ -58,12 +60,12 @@ static void _decapsulate(ng_pktsnip_t *pkt); kernel_pid_t ng_ipv6_init(void) { - if (_pid == KERNEL_PID_UNDEF) { - _pid = thread_create(_stack, sizeof(_stack), NG_IPV6_PRIO, + if (ng_ipv6_pid == KERNEL_PID_UNDEF) { + ng_ipv6_pid = thread_create(_stack, sizeof(_stack), NG_IPV6_PRIO, CREATE_STACKTEST, _event_loop, NULL, "ipv6"); } - return _pid; + return ng_ipv6_pid; } void ng_ipv6_demux(kernel_pid_t iface, ng_pktsnip_t *pkt, uint8_t nh) @@ -140,6 +142,27 @@ static void *_event_loop(void *args) msg_reply(&msg, &reply); break; + case NG_NDP_MSG_RTR_TIMEOUT: + DEBUG("ipv6: Router timeout received\n"); + ((ng_ipv6_nc_t *)msg.content.ptr)->flags &= ~NG_IPV6_NC_IS_ROUTER; + break; + + case NG_NDP_MSG_ADDR_TIMEOUT: + DEBUG("ipv6: Router advertisement timer event received\n"); + ng_ipv6_netif_remove_addr(KERNEL_PID_UNDEF, + (ng_ipv6_addr_t *)msg.content.ptr); + break; + + case NG_NDP_MSG_NBR_SOL_RETRANS: + DEBUG("ipv6: Neigbor solicitation retransmission timer event received\n"); + ng_ndp_retrans_nbr_sol((ng_ipv6_nc_t *)msg.content.ptr); + break; + + case NG_NDP_MSG_NC_STATE_TIMEOUT: + DEBUG("ipv6: Neigbor cace state timeout received\n"); + ng_ndp_state_timeout((ng_ipv6_nc_t *)msg.content.ptr); + break; + default: break; } @@ -411,8 +434,6 @@ static void _send(ng_pktsnip_t *pkt, bool prep_hdr) kernel_pid_t iface = KERNEL_PID_UNDEF; ng_pktsnip_t *ipv6, *payload; ng_ipv6_hdr_t *hdr; - ng_ipv6_nc_t *nc_entry; - /* seize payload as temporary variable */ payload = ng_pktbuf_start_write(pkt); @@ -442,24 +463,14 @@ static void _send(ng_pktsnip_t *pkt, bool prep_hdr) _send_multicast(iface, pkt, ipv6, payload, prep_hdr); } else { - ng_ipv6_addr_t *next_hop = NULL; + uint8_t l2addr_len = NG_IPV6_NC_L2_ADDR_MAX; + uint8_t l2addr[l2addr_len]; - next_hop = &hdr->dst; /* TODO: next hop determination */ - - if (((nc_entry = ng_ipv6_nc_get(iface, next_hop)) == NULL) || - !ng_ipv6_nc_is_reachable(nc_entry)) { - DEBUG("ipv6: No link layer address for next_hop %s found.\n", - ng_ipv6_addr_to_str(addr_str, next_hop, sizeof(addr_str))); - ng_pktbuf_release(pkt); - return; - } - else { - iface = nc_entry->iface; - } + iface = ng_ndp_next_hop_l2addr(l2addr, &l2addr_len, iface, &hdr->dst, + pkt); if (iface == KERNEL_PID_UNDEF) { - DEBUG("ipv6: no interface for %s registered, dropping packet\n", - ng_ipv6_addr_to_str(addr_str, next_hop, sizeof(addr_str))); + DEBUG("ipv6: error determining next hop's link layer address\n"); ng_pktbuf_release(pkt); return; } @@ -472,7 +483,7 @@ static void _send(ng_pktsnip_t *pkt, bool prep_hdr) } } - _send_unicast(iface, nc_entry->l2_addr, nc_entry->l2_addr_len, pkt); + _send_unicast(iface, l2addr, l2addr_len, pkt); } } diff --git a/sys/net/network_layer/ng_ndp/Makefile b/sys/net/network_layer/ng_ndp/Makefile new file mode 100644 index 000000000000..48422e909a47 --- /dev/null +++ b/sys/net/network_layer/ng_ndp/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/network_layer/ng_ndp/ng_ndp.c b/sys/net/network_layer/ng_ndp/ng_ndp.c new file mode 100644 index 000000000000..64f68a3ca5da --- /dev/null +++ b/sys/net/network_layer/ng_ndp/ng_ndp.c @@ -0,0 +1,938 @@ +/* + * 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. + */ + +/** + * @ingroup net_ng_ndp + * @{ + * + * @file + * + * @author Martine Lenders + */ + +#include + +#include "byteorder.h" +#include "net/ng_icmpv6.h" +#include "net/ng_ipv6.h" +#include "net/ng_netbase.h" +#include "random.h" +#include "utlist.h" +#include "thread.h" +#include "vtimer.h" + +#include "net/ng_ndp.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static ng_pktqueue_node_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 bool _handle_tl2a_opt(kernel_pid_t iface, ng_pktsnip_t *pkt, + ng_ipv6_hdr_t *ipv6, uint8_t icmpv6_type, + ng_ndp_opt_t *tl2a_opt, ng_ipv6_addr_t *tgt, + uint8_t adv_flags); + +/* 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_set_msg(t, interval, ng_ipv6_pid, NG_NETAPI_MSG_TYPE_SND, pkt); +} + +/* packet queue node allocation */ +static ng_pktqueue_node_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) +{ + uint16_t opt_offset = 0; + uint8_t *buf = ((uint8_t *)nbr_sol) + sizeof(ng_ndp_nbr_sol_t); + ng_ipv6_addr_t *tgt; + int sicmpv6_size = (int)icmpv6_size; + + /* check validity */ + if ((ipv6->hl != 255) || (nbr_sol->code != 0) || + (icmpv6_size < sizeof(ng_ndp_nbr_sol_t)) || + ng_ipv6_addr_is_multicast(&nbr_sol->tgt) || + (ng_ipv6_addr_is_unspecified(&ipv6->src) && + ng_ipv6_addr_is_solicited_node(&ipv6->dst))) { + DEBUG("ndp: neighbor solicitation was invalid.\n"); + /* ipv6 releases */ + return; + } + + if ((tgt = ng_ipv6_netif_find_addr(iface, &nbr_sol->tgt)) == NULL) { + DEBUG("ndp: Target address is not to interface %" PRIkernel_pid "\n", + iface); + /* ipv6 releases */ + return; + } + + sicmpv6_size -= sizeof(ng_ndp_nbr_sol_t); + + while (sicmpv6_size > 0) { + ng_ndp_opt_t *opt = (ng_ndp_opt_t *)(buf + opt_offset); + + switch (opt->type) { + case NG_NDP_OPT_SL2A: + if (!_handle_sl2a_opt(iface, pkt, ipv6, nbr_sol->type, opt)) { + /* 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); + } + + _send_nbr_adv(iface, tgt, &ipv6->src, + ng_ipv6_addr_is_multicast(&ipv6->dst)); + + return; +} + +void ng_ndp_nbr_adv_handle(kernel_pid_t iface, ng_pktsnip_t *pkt, + ng_ipv6_hdr_t *ipv6, ng_ndp_nbr_adv_t *nbr_adv, + size_t icmpv6_size) +{ + uint16_t opt_offset = 0; + uint8_t *buf = ((uint8_t *)nbr_adv) + sizeof(ng_ndp_nbr_adv_t); + bool tl2a_supplied = false; + int sicmpv6_size = (int)icmpv6_size; + + /* check validity */ + if ((ipv6->hl != 255) || (nbr_adv->code != 0) || + (icmpv6_size < sizeof(ng_ndp_nbr_adv_t)) || + ng_ipv6_addr_is_multicast(&nbr_adv->tgt)) { + DEBUG("ndp: neighbor advertisement was invalid.\n"); + /* ipv6 releases */ + return; + } + + if (ng_ipv6_nc_get(iface, &nbr_adv->tgt) == NULL) { + DEBUG("ndp: no neighbor cache entry found for advertisement's target\n"); + /* ipv6 releases */ + return; + } + + + sicmpv6_size -= sizeof(ng_ndp_nbr_adv_t); + + while (sicmpv6_size > 0) { + ng_ndp_opt_t *opt = (ng_ndp_opt_t *)(buf + opt_offset); + + switch (opt->type) { + case NG_NDP_OPT_TL2A: + if (!_handle_tl2a_opt(iface, pkt, ipv6, nbr_adv->type, opt, + &nbr_adv->tgt, nbr_adv->flags)) { + /* invalid target link-layer address option */ + return; + } + + tl2a_supplied = true; + + break; + + default: + /* silently discard all other options */ + break; + } + + opt_offset += (opt->len * 8); + sicmpv6_size -= (opt->len * 8); + } + + if (!tl2a_supplied) { + if (nbr_adv->flags & NG_NDP_NBR_ADV_FLAGS_O) { + ng_ipv6_nc_t *nc_entry = ng_ipv6_nc_get(iface, &nbr_adv->tgt); + + if (nc_entry != NULL) { + if (nbr_adv->flags & NG_NDP_NBR_ADV_FLAGS_S) { + _set_state(nc_entry, NG_IPV6_NC_STATE_REACHABLE); + } + else { + _set_state(nc_entry, NG_IPV6_NC_STATE_STALE); + } + } + } + } + + return; +} + +void ng_ndp_retrans_nbr_sol(ng_ipv6_nc_t *nc_entry) +{ + if ((nc_entry->probes_remaining > 1) && + ((ng_ipv6_nc_get_state(nc_entry) == NG_IPV6_NC_STATE_INCOMPLETE) || + (ng_ipv6_nc_get_state(nc_entry) == NG_IPV6_NC_STATE_PROBE))) { + ng_ipv6_addr_t dst; + + DEBUG("ndp: Retransmit neighbor solicitation for %s\n", + ng_ipv6_addr_to_str(addr_str, nc_entry->ipv6_addr, sizeof(addr_str))); + + /* retransmit neighbor solicatation */ + if (ng_ipv6_nc_get_state(nc_entry) == NG_IPV6_NC_STATE_INCOMPLETE) { + ng_ipv6_addr_set_solicited_nodes(&dst, &nc_entry->ipv6_addr); + } + else { + dst.u64[0] = nc_entry->ipv6_addr.u64[0]; + dst.u64[1] = nc_entry->ipv6_addr.u64[1]; + } + + nc_entry->probes_remaining--; + + if (nc_entry->iface == KERNEL_PID_UNDEF) { + timex_t t = { 0, NG_NDP_RETRANS_TIMER }; + kernel_pid_t *ifs; + size_t ifnum; + + ifs = ng_netif_get(&ifnum); + + for (size_t i = 0; i < ifnum; i++) { + _send_nbr_sol(ifs[i], &nc_entry->ipv6_addr, &dst); + } + + 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(nc_entry->iface); + + _send_nbr_sol(nc_entry->iface, &nc_entry->ipv6_addr, &dst); + + mutex_lock(&ipv6_iface->mutex); + 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); + } + } + else if (nc_entry->probes_remaining <= 1) { + ng_pktqueue_node_t *queue_node; + + /* No need to call ng_ipv6_nc_remove() we know already were the + * entry is */ + + DEBUG("ndp: Remove nc entry %s for interface %" PRIkernel_pid "\n", + ng_ipv6_addr_to_str(addr_str, nc_entry->ipv6_addr, sizeof(addr_str)), + nc_entry->iface); + + while ((queue_node = ng_pktqueue_remove_head(&nc_entry->pkts))) { + ng_pktbuf_release(queue_node->data); + queue_node->data = NULL; + } + + ng_ipv6_addr_set_unspecified(&(nc_entry->ipv6_addr)); + nc_entry->iface = KERNEL_PID_UNDEF; + nc_entry->flags = 0; + nc_entry->probes_remaining = 0; + } +} + +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); + break; + + case NG_IPV6_NC_STATE_DELAY: + _set_state(nc_entry, NG_IPV6_NC_STATE_PROBE); + break; + + default: + break; + } +} + +void ng_ndp_netif_add(ng_ipv6_netif_t *iface) +{ + uint32_t reach_time = _rand(NG_NDP_MIN_RAND, NG_NDP_MAX_RAND); + + /* set default values */ + mutex_lock(&iface->mutex); + iface->reach_time_base = NG_NDP_REACH_TIME; + reach_time = (reach_time * iface->reach_time_base) / 10; + iface->reach_time = timex_set(0, reach_time); + timex_normalize(&iface->reach_time); + iface->retrans_timer = timex_set(0, NG_NDP_RETRANS_TIMER); + timex_normalize(&iface->retrans_timer); + mutex_unlock(&iface->mutex); +} + +void ng_ndp_netif_remove(ng_ipv6_netif_t *iface) +{ + /* TODO */ +} + +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_FIB + size_t next_hop_size; + + if ((fib_get_next_hop(&iface, (uint8_t *)next_hop_ip, &next_hop_size, + (uint8_t *)dst, sizeof(ng_ipv6_addr_t), + 0) < 0) || (next_hop_ip != sizeof(ng_ipv6_addr_t))) { + next_hop_ip = NULL; + } +#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; + } + } + + if (next_hop_ip == NULL) { + next_hop_ip = _default_router(); + } + + 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 neigbor\n"); + + 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_node_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->data, 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; + size_t ifnum; + + ifs = ng_netif_get(&ifnum); + + for (size_t i = 0; i < ifnum; i++) { + _send_nbr_sol(ifs[i], next_hop_ip, &dst_sol); + } + + 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_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; + ng_ndp_nbr_sol_t *nbr_sol; + + DEBUG("ndp: building neighbor solicitation message\n"); + + if (ng_ipv6_addr_is_multicast(tgt)) { + DEBUG("ndp: tgt must not be multicast\n"); + return NULL; + } + + pkt = ng_icmpv6_build(options, NG_ICMPV6_NBR_SOL, 0, sizeof(ng_ndp_nbr_sol_t)); + + if (pkt != NULL) { + nbr_sol = pkt->data; + nbr_sol->resv.u32 = 0; + memcpy(&nbr_sol->tgt, tgt, sizeof(ng_ipv6_addr_t)); + } + + return pkt; +} + +ng_pktsnip_t *ng_ndp_nbr_adv_build(uint8_t flags, ng_ipv6_addr_t *tgt, + ng_pktsnip_t *options) +{ + ng_pktsnip_t *pkt; + ng_ndp_nbr_adv_t *nbr_adv; + + DEBUG("ndp: building neighbor advertisement message\n"); + + if (ng_ipv6_addr_is_multicast(tgt)) { + DEBUG("ndp: tgt must not be multicast\n"); + return NULL; + } + + pkt = ng_icmpv6_build(options, NG_ICMPV6_NBR_ADV, 0, sizeof(ng_ndp_nbr_adv_t)); + + if (pkt == NULL) { + return NULL; + } + + nbr_adv = pkt->data; + nbr_adv->flags = (flags & NG_NDP_NBR_ADV_FLAGS_MASK); + nbr_adv->resv[0] = nbr_adv->resv[1] = nbr_adv->resv[2] = 0; + memcpy(&nbr_adv->tgt, tgt, sizeof(ng_ipv6_addr_t)); + + return pkt; +} + +static inline size_t _ceil8(uint8_t length) +{ + /* NDP options use units of 8 byte for there length field, so round up */ + return (length + 7U) & 0xf8U; +} + +ng_pktsnip_t *ng_ndp_opt_build(uint8_t type, size_t size, ng_pktsnip_t *next) +{ + ng_ndp_opt_t *opt; + ng_pktsnip_t *pkt = ng_pktbuf_add(next, NULL, _ceil8(size), NG_NETTYPE_UNDEF); + + if (pkt == NULL) { + DEBUG("ndp: no space left in packet buffer\n"); + return NULL; + } + + opt = pkt->data; + + opt->type = type; + opt->len = (uint8_t)(pkt->size / 8); + + 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; + + /* try getting source address */ + if ((ng_netapi_get(iface, NETCONF_OPT_SRC_LEN, 0, &l2src_len, + sizeof(l2src_len)) >= 0) && + (l2src_len == 8)) { + try_long = true; + } + + if ((try_long && ((res = ng_netapi_get(iface, NETCONF_OPT_ADDRESS_LONG, 0, + l2src, l2src_size)) < 0)) || + ((res = ng_netapi_get(iface, NETCONF_OPT_ADDRESS, 0, l2src, + l2src_size)) < 0)) { + DEBUG("ndp: no link-layer address found.\n"); + l2src_len = 0; + } + else { + l2src_len = (uint16_t)res; + } + + 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; + uint8_t l2src[8]; + uint16_t l2src_len; + + /* check if there is a fitting source address to target */ + if ((src = ng_ipv6_netif_find_best_src_addr(iface, tgt)) != NULL) { + 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; + } + + LL_PREPEND(pkt, hdr); + + ((ng_netif_hdr_t *)hdr->data)->if_pid = iface; + + 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 l2src[8]; + uint16_t l2src_len; + uint8_t adv_flags = 0; + + 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) { + /* 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; + } + + LL_PREPEND(pkt, hdr); + + ((ng_netif_hdr_t *)hdr->data)->if_pid = iface; + + if (ng_ipv6_netif_addr_is_non_unicast(tgt)) { + /* avoid collision for anycast addresses */ + timex_t delay = { _rand(0, NG_NDP_MAX_AC_TGT_DELAY), 0 }; + ng_ipv6_nc_t *nc_entry = ng_ipv6_nc_get(iface, tgt); + + /* 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) +{ + ng_pktsnip_t *pkt = ng_ndp_opt_build(type, sizeof(ng_ndp_opt_t) + l2addr_len, + next); + + if (pkt != NULL) { + ng_ndp_opt_t *l2a_opt = pkt->data; + + memset(l2a_opt + 1, 0, pkt->size - sizeof(ng_ndp_opt_t)); + memcpy(l2a_opt + 1, l2addr, l2addr_len); + } + + return pkt; +} + +ng_pktsnip_t *ng_ndp_opt_sl2a_build(const uint8_t *l2addr, uint8_t l2addr_len, + ng_pktsnip_t *next) +{ + DEBUG("ndp: building source link-layer address option\n"); + + return _opt_l2a_build(NG_NDP_OPT_SL2A, l2addr, l2addr_len, next); +} + +ng_pktsnip_t *ng_ndp_opt_tl2a_build(const uint8_t *l2addr, uint8_t l2addr_len, + ng_pktsnip_t *next) +{ + DEBUG("ndp: building target link-layer address option\n"); + + return _opt_l2a_build(NG_NDP_OPT_TL2A, l2addr, l2addr_len, next); +} + +/* internal functions */ +/* packet queue node allocation */ +static ng_pktqueue_node_t *_alloc_pkt_node(ng_pktsnip_t *pkt) +{ + for (size_t i = 0; i < sizeof(_pkt_nodes); i++) { + if (_pkt_nodes[i].data == NULL) { + ng_pktqueue_node_init(_pkt_nodes + i); + _pkt_nodes[i].data = 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--); + } + + 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 bool _handle_tl2a_opt(kernel_pid_t iface, ng_pktsnip_t *pkt, + ng_ipv6_hdr_t *ipv6, uint8_t icmpv6_type, + ng_ndp_opt_t *tl2a_opt, ng_ipv6_addr_t *tgt, + uint8_t adv_flags) +{ + ng_ipv6_nc_t *nc_entry = NULL; + 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 false; + } + + 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--); + } + + switch (icmpv6_type) { + case NG_ICMPV6_NBR_ADV: + nc_entry = ng_ipv6_nc_get(iface, tgt); + + /* no need to create an entry in the negative case (see RFC 4861) */ + if (nc_entry != NULL) { + nc_entry->l2_addr_len = tl2a_len; + + if (tl2a_len > 0) { + memcpy(nc_entry->l2_addr, tl2a, tl2a_len); + } + + if (ng_ipv6_nc_get_state(nc_entry) == NG_IPV6_NC_STATE_INCOMPLETE) { + ng_pktqueue_node_t *queued_pkt; + + if (adv_flags & NG_NDP_NBR_ADV_FLAGS_S) { + _set_state(nc_entry, NG_IPV6_NC_STATE_REACHABLE); + } + else { + _set_state(nc_entry, NG_IPV6_NC_STATE_STALE); + } + + if (adv_flags & NG_NDP_NBR_ADV_FLAGS_R) { + nc_entry->flags |= NG_IPV6_NC_IS_ROUTER; + } + else { + nc_entry->flags &= ~NG_IPV6_NC_IS_ROUTER; + } + + while ((queued_pkt = ng_pktqueue_remove_head(&nc_entry->pkts)) != NULL) { + ng_netapi_send(ng_ipv6_pid, queued_pkt->data); + queued_pkt->data = NULL; + } + } + else { + if (memcmp(tl2a, nc_entry->l2_addr, tl2a_len) != 0) { + if ((adv_flags & NG_NDP_NBR_ADV_FLAGS_O)) { + memcpy(nc_entry->l2_addr, tl2a, tl2a_len); + } + else if (ng_ipv6_nc_get_state(nc_entry) == NG_IPV6_NC_STATE_REACHABLE) { + _set_state(nc_entry, NG_IPV6_NC_STATE_STALE); + } + } + + if ((adv_flags & NG_NDP_NBR_ADV_FLAGS_O)) { + if (adv_flags & NG_NDP_NBR_ADV_FLAGS_S) { + _set_state(nc_entry, NG_IPV6_NC_STATE_REACHABLE); + } + else { + _set_state(nc_entry, NG_IPV6_NC_STATE_STALE); + } + + if (adv_flags & NG_NDP_NBR_ADV_FLAGS_R) { + nc_entry->flags |= NG_IPV6_NC_IS_ROUTER; + } + else { + nc_entry->flags &= ~NG_IPV6_NC_IS_ROUTER; + } + } + } + } + + return true; + + default: /* wrong encapsulating message: silently discard */ + DEBUG("ndp: silently discard tl2a_opt for ICMPv6 message type %" + PRIu8 "\n", icmpv6_type); + return true; + } +} + +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; + + switch (state) { + case NG_IPV6_NC_STATE_REACHABLE: + ipv6_iface = ng_ipv6_netif_get(nc_entry->iface); + t = ipv6_iface->reach_time; + vtimer_remove(&nc_entry->nbr_sol_timer); + + case NG_IPV6_NC_STATE_DELAY: + 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; + _send_nbr_sol(nc_entry->iface, &nc_entry->ipv6_addr, + &nc_entry->ipv6_addr); + + mutex_lock(&ipv6_iface->mutex); + 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; + + default: + break; + } +} + +/** + * @} + */ diff --git a/sys/shell/commands/sc_icmpv6_echo.c b/sys/shell/commands/sc_icmpv6_echo.c index f9fb10257623..7b15e48075b4 100644 --- a/sys/shell/commands/sc_icmpv6_echo.c +++ b/sys/shell/commands/sc_icmpv6_echo.c @@ -23,6 +23,7 @@ #include "byteorder.h" #include "net/ng_icmpv6.h" #include "net/ng_ipv6/addr.h" +#include "net/ng_ipv6/nc.h" #include "net/ng_ipv6/hdr.h" #include "net/ng_netbase.h" #include "thread.h" @@ -83,6 +84,7 @@ int _handle_reply(ng_pktsnip_t *pkt, uint64_t time) ng_ipv6_addr_to_str(ipv6_str, &(ipv6_hdr->src), sizeof(ipv6_str)), byteorder_ntohs(icmpv6_hdr->id), byteorder_ntohs(icmpv6_hdr->seq), ipv6_hdr->hl, time / MS_IN_USEC, time % MS_IN_USEC); + ng_ipv6_nc_still_reachable(&ipv6_hdr->src); } else { puts("error: unexpected parameters");