
13 changed files with 1617 additions and 28 deletions
@ -0,0 +1,297 @@
|
||||
/*
|
||||
* 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 IPv6 Neighbor discovery |
||||
* @ingroup net_ng_icmpv6 |
||||
* @brief IPv6 Neighbor Discovery Implementation |
||||
* @{ |
||||
* |
||||
* @file |
||||
* @brief Neighbor Discovery definitions |
||||
* |
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de> |
||||
*/ |
||||
|
||||
#include <inttypes.h> |
||||
|
||||
#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 <a href="https://tools.ietf.org/html/rfc4861#section-10"> |
||||
* RFC 4861, section 10 |
||||
* </a> |
||||
*/ |
||||
/**
|
||||
* @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 <a href="https://tools.ietf.org/html/rfc4861#section-4.3"> |
||||
* RFC 4861, section 4.3 |
||||
* </a> |
||||
* |
||||
* @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 <a href="https://tools.ietf.org/html/rfc4861#section-4.4"> |
||||
* RFC 4861, section 4.4 |
||||
* </a> |
||||
* |
||||
* @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 <a href="https://tools.ietf.org/html/rfc4861#section-4.6.1"> |
||||
* RFC 4861, section 4.6.1 |
||||
* </a> |
||||
* |
||||
* @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 <a href="https://tools.ietf.org/html/rfc4861#section-4.6.1"> |
||||
* RFC 4861, section 4.6.1 |
||||
* </a> |
||||
* |
||||
* @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_ */ |
||||
/**
|
||||
* @} |
||||
*/ |
@ -0,0 +1,250 @@
|
||||
/*
|
||||
* 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_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 <mlenders@inf.fu-berlin.de> |
||||
*/ |
||||
#ifndef NG_NDP_TYPES_H_ |
||||
#define NG_NDP_TYPES_H_ |
||||
|
||||
#include <stdint.h> |
||||
|
||||
#include "byteorder.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/**
|
||||
* @{ |
||||
* @name Flags for router advertisement messages |
||||
* @see <a href="https://tools.ietf.org/html/rfc4861#section-4.2"> |
||||
* RFC 4861, section 4.2 |
||||
* </a> |
||||
*/ |
||||
#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 <a href="https://tools.ietf.org/html/rfc4861#section-4.4"> |
||||
* RFC 4861, section 4.2 |
||||
* </a> |
||||
*/ |
||||
#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 <a href="http://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-5"> |
||||
* IANA, IPv6 Neighbor Discovery Option Formats |
||||
* </a> |
||||
*/ |
||||
#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 <a href="https://tools.ietf.org/html/rfc4861#section-4.1"> |
||||
* RFC 4861, section 4.1 |
||||
* </a> |
||||
*/ |
||||
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 <a href="https://tools.ietf.org/html/rfc4861#section-4.2"> |
||||
* RFC 4861, section 4.2 |
||||
* </a> |
||||
*/ |
||||
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 <a href="https://tools.ietf.org/html/rfc4861#section-4.3"> |
||||
* RFC 4861, section 4.3 |
||||
* </a> |
||||
*/ |
||||
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 <a href="https://tools.ietf.org/html/rfc4861#section-4.4"> |
||||
* RFC 4861, section 4.4 |
||||
* </a> |
||||
*/ |
||||
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 <a href="https://tools.ietf.org/html/rfc4861#section-4.5"> |
||||
* RFC 4861, section 4.5 |
||||
* </a> |
||||
*/ |
||||
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 <a href="https://tools.ietf.org/html/rfc4861#section-4.6"> |
||||
* RFC 4861, section 4.6 |
||||
* </a> |
||||
*/ |
||||
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 <a href="https://tools.ietf.org/html/rfc4861#section-4.6.2"> |
||||
* RFC 4861, section 4.6.2 |
||||
* </a> |
||||
*/ |
||||
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 <a href="https://tools.ietf.org/html/rfc4861#section-4.6.3"> |
||||
* RFC 4861, section 4.6.3 |
||||
* </a> |
||||
*/ |
||||
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 <a href="https://tools.ietf.org/html/rfc4861#section-4.6.4"> |
||||
* RFC 4861, section 4.6.4 |
||||
* </a> |
||||
*/ |
||||
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_ */ |
||||
/** @} */ |
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base |
@ -0,0 +1,938 @@
|
||||
/*
|
||||
* 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. |
||||
*/ |
||||
|
||||
/**
|
||||
* @ingroup net_ng_ndp |
||||
* @{ |
||||
* |
||||
* @file |
||||
* |
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de> |
||||
*/ |
||||
|
||||
#include <string.h> |
||||
|
||||
#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; |
||||
} |
||||
|
||||
|