
5 changed files with 415 additions and 0 deletions
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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_ipv6_nc IPv6 neighbor cache |
||||
* @ingroup net_ng_ipv6 |
||||
* @brief Translates IPv6 addresses to link layer addresses. |
||||
* @{ |
||||
* |
||||
* @file |
||||
* @brief Neighbor cache definitions. |
||||
* |
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de> |
||||
*/ |
||||
|
||||
#ifndef NG_IPV6_NC_H_ |
||||
#define NG_IPV6_NC_H_ |
||||
|
||||
#include <stdint.h> |
||||
|
||||
#include "kernel_types.h" |
||||
#include "net/ng_ipv6/addr.h" |
||||
#include "net/ng_netif.h" |
||||
#include "net/ng_pktqueue.h" |
||||
#include "timex.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#ifndef NG_IPV6_NC_SIZE |
||||
/**
|
||||
* @brief The size of the neighbor cache |
||||
*/ |
||||
#define NG_IPV6_NC_SIZE (NG_NETIF_NUMOF * 8) |
||||
#endif |
||||
|
||||
#ifndef NG_IPV6_NC_L2_ADDR_MAX |
||||
/**
|
||||
* @brief The maximum size of a link layer address |
||||
*/ |
||||
#define NG_IPV6_NC_L2_ADDR_MAX (8) |
||||
#endif |
||||
|
||||
/**
|
||||
* @{ |
||||
* @name Flag definitions for ng_ipv6_nc_t |
||||
*/ |
||||
/**
|
||||
* @{ |
||||
* @brief States of a neighbor cache entry. |
||||
* |
||||
* @see <a href="http://tools.ietf.org/html/rfc4861#section-7.3.2"> |
||||
* RFC 4861, section 7.3.2 |
||||
* </a> |
||||
*/ |
||||
#define NG_IPV6_NC_STATE_MASK (0x07) /**< Mask for neighbor cache state */ |
||||
#define NG_IPV6_NC_STATE_POS (0) /**< Shift of neighbor cache state */ |
||||
|
||||
#define NG_IPV6_NC_STATE_UNMANAGED (0x00) /**< The entry is not manage by NDP */ |
||||
|
||||
/**
|
||||
* @brief The entry is unreachable |
||||
* |
||||
* @see <a href="http://tools.ietf.org/html/rfc7048#section-3"> |
||||
* RFC 7048, section 3 |
||||
* </a> |
||||
*/ |
||||
#define NG_IPV6_NC_STATE_UNREACHABLE (0x01) |
||||
#define NG_IPV6_NC_STATE_INCOMPLETE (0x02) /**< Address resolution is performed */ |
||||
#define NG_IPV6_NC_STATE_STALE (0x03) /**< The entry is stale */ |
||||
#define NG_IPV6_NC_STATE_DELAY (0x04) /**< The entry was stale but packet was sent out */ |
||||
#define NG_IPV6_NC_STATE_PROBE (0x05) /**< Periodic reachabality confirmation */ |
||||
#define NG_IPV6_NC_STATE_REACHABLE (0x07) /**< The entry is reachable */ |
||||
/**
|
||||
* @} |
||||
*/ |
||||
|
||||
#define NG_IPV6_NC_IS_ROUTER (0x08) /**< The neighbor is a router */ |
||||
|
||||
#define NG_IPV6_NC_TYPE_MASK (0x30) /**< Mask for neighbor cache state */ |
||||
#define NG_IPV6_NC_TYPE_POS (4) /**< Shift of neighbor cache state */ |
||||
|
||||
/**
|
||||
* @{ |
||||
* @brief States of a neighbor cache entry. |
||||
* |
||||
* @see <a href="http://tools.ietf.org/html/rfc6775#section-3.5"> |
||||
* RFC 6775, section 3.5 |
||||
* </a> |
||||
*/ |
||||
/**
|
||||
* @brief The entry has no type |
||||
* |
||||
* @details The node sents multicast Neighbor Solicitations for hosts. |
||||
*/ |
||||
#define NG_IPV6_NC_TYPE_NONE (0x00) |
||||
#define NG_IPV6_NC_TYPE_GC (0x10) /**< The entry is marked for removal */ |
||||
#define NG_IPV6_NC_TYPE_TENTATIVE (0x20) /**< The entry is temporary */ |
||||
#define NG_IPV6_NC_TYPE_REGISTERED (0x30) /**< The entry is registered */ |
||||
/**
|
||||
* @} |
||||
*/ |
||||
/**
|
||||
* @} |
||||
*/ |
||||
|
||||
/**
|
||||
* @brief Neighbor cache entry as defined in |
||||
* <a href="http://tools.ietf.org/html/rfc4861#section-5.1"> |
||||
* RFC 4861, section 5.1 |
||||
* </a>. |
||||
*/ |
||||
typedef struct { |
||||
ng_pktqueue_t pkts; /**< Packets waiting for address resolution */ |
||||
ng_ipv6_addr_t ipv6_addr; /**< IPv6 address of the neighbor */ |
||||
uint8_t l2_addr[NG_IPV6_NC_L2_ADDR_MAX];/**< Link layer address of the neighbor */ |
||||
uint8_t l2_addr_len; /**< Length of ng_ipv6_nc_t::l2_addr */ |
||||
uint8_t flags; /**< Flags as defined above */ |
||||
kernel_pid_t iface; /**< PID to the interface where the neighbor is */ |
||||
} ng_ipv6_nc_t; |
||||
|
||||
/**
|
||||
* @brief Initializes neighbor cache |
||||
*/ |
||||
void ng_ipv6_nc_init(void); |
||||
|
||||
/**
|
||||
* @brief Adds a neighbor to the neighbor cache |
||||
* |
||||
* @param[in] iface PID to the interface where the neighbor is. |
||||
* Must not be KERNEL_PID_UNDEF. |
||||
* @param[in] ipv6_addr IPv6 address of the neighbor. Must not be NULL. |
||||
* @param[in] l2_addr Link layer address of the neighbor. NULL if unknown. |
||||
* @param[in] l2_addr_len Length of @p l2_addr, must be lesser than or equal |
||||
* to NG_IPV6_L2_ADDR_MAX. 0 if unknown. |
||||
* @param[in] flags Flags for the entry |
||||
* |
||||
* @return 0 on success |
||||
* @return -EADDRINUSE, if @p ipv6_addr is already registered to the neighbor |
||||
* cache. |
||||
* @return -EFAULT, if @p ipv6_addr was NULL. |
||||
* @return -EINVAL, if @p l2_addr_len is greater then @ref NG_IPV6_NC_L2_ADDR_MAX, |
||||
* @p ipv6_addr is unspecified or @p iface is KERNEL_PID_UNDEF. |
||||
* @return -ENOMEM, if no space is left to store entry. |
||||
*/ |
||||
int ng_ipv6_nc_add(kernel_pid_t iface, const ng_ipv6_addr_t *ipv6_addr, |
||||
const void *l2_addr, size_t l2_addr_len, uint8_t flags); |
||||
|
||||
/**
|
||||
* @brief Removes a neighbor from the neighbor cache |
||||
* |
||||
* @param[in] iface PID to the interface where the neighbor is. If it |
||||
* is KERNEL_PID_UNDEF it will be removed for all |
||||
* interfaces. |
||||
* @param[in] ipv6_addr IPv6 address of the neighbor |
||||
*/ |
||||
void ng_ipv6_nc_remove(kernel_pid_t iface, const ng_ipv6_addr_t *ipv6_addr); |
||||
|
||||
/**
|
||||
* @brief Searches for any neighbor cache entry fitting the @p ipv6_addr. |
||||
* |
||||
* @param[in] iface PID to the interface where the neighbor is. If it |
||||
* is KERNEL_PID_UNDEF it will be searched on all |
||||
* interfaces. |
||||
* @param[in] ipv6_addr An IPv6 address |
||||
* |
||||
* @return The neighbor cache entry, if one is found. |
||||
* @return NULL, if none is found. |
||||
*/ |
||||
ng_ipv6_nc_t *ng_ipv6_nc_get(kernel_pid_t iface, const ng_ipv6_addr_t *ipv6_addr); |
||||
|
||||
/**
|
||||
* @brief Searches for any neighbor cache entry fitting the @p ipv6_addr, |
||||
* where you currently can send a packet to (do not confuse with |
||||
* NG_IPV6_NC_STATE_REACHABLE). |
||||
* |
||||
* @param[in] iface PID to the interface where the neighbor is. If it |
||||
* is KERNEL_PID_UNDEF it will be searched on all |
||||
* interfaces. |
||||
* @param[in] ipv6_addr An IPv6 address |
||||
* |
||||
* @return The neighbor cache entry, if one is found. |
||||
* @return NULL, if none is found. |
||||
*/ |
||||
ng_ipv6_nc_t *ng_ipv6_nc_get_reachable(kernel_pid_t iface, |
||||
const ng_ipv6_addr_t *ipv6_addr); |
||||
|
||||
/**
|
||||
* @brief Marks an entry as still reachable, if one with a fitting @p ipv6_addr |
||||
* can be found. |
||||
* |
||||
* @details This function can be used by upper layer protocols for neighbor |
||||
* discovery optimization to confirm that there was a reachability |
||||
* confirmation (e. g. an ACK in TCP) from the neighbor. |
||||
* |
||||
* @see <a href="http://tools.ietf.org/html/rfc4861#section-7.3.1"> |
||||
* RFC 4861, section 7.3.1 |
||||
* </a> |
||||
* |
||||
* @param[in] ipv6_addr An IPv6 address |
||||
* |
||||
* @return The neighbor cache entry, if one is found. |
||||
* @return NULL, if none is found. |
||||
*/ |
||||
ng_ipv6_nc_t *ng_ipv6_nc_still_reachable(const ng_ipv6_addr_t *ipv6_addr); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif /* NG_IPV6_NC_H_ */ |
||||
/**
|
||||
* @} |
||||
*/ |
@ -0,0 +1,3 @@
|
||||
MODULE = ng_ipv6_nc
|
||||
|
||||
include $(RIOTBASE)/Makefile.base |
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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 <errno.h> |
||||
#include <string.h> |
||||
|
||||
#include "net/ng_ipv6/addr.h" |
||||
#include "net/ng_ipv6/nc.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 ncache[NG_IPV6_NC_SIZE]; |
||||
|
||||
void ng_ipv6_nc_init(void) |
||||
{ |
||||
memset(ncache, 0, sizeof(ncache)); |
||||
} |
||||
|
||||
ng_ipv6_nc_t *_find_free_entry(void) |
||||
{ |
||||
for (int i = 0; i < NG_IPV6_NC_SIZE; i++) { |
||||
if (ng_ipv6_addr_is_unspecified(&(ncache[i].ipv6_addr))) { |
||||
return ncache + i; |
||||
} |
||||
} |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
int ng_ipv6_nc_add(kernel_pid_t iface, const ng_ipv6_addr_t *ipv6_addr, |
||||
const void *l2_addr, size_t l2_addr_len, uint8_t flags) |
||||
{ |
||||
if (ipv6_addr == NULL) { |
||||
DEBUG("ipv6_nc: address was NULL\n"); |
||||
return -EFAULT; |
||||
} |
||||
|
||||
if ((l2_addr_len > NG_IPV6_NC_L2_ADDR_MAX) || (iface == KERNEL_PID_UNDEF) || |
||||
ng_ipv6_addr_is_unspecified(ipv6_addr)) { |
||||
return -EINVAL; |
||||
} |
||||
|
||||
for (int i = 0; i < NG_IPV6_NC_SIZE; i++) { |
||||
if (ng_ipv6_addr_equal(&(ncache[i].ipv6_addr), ipv6_addr)) { |
||||
DEBUG("ipv6_nc: Address %s already registered\n", |
||||
ng_ipv6_addr_to_str(addr_str, ipv6_addr, sizeof(addr_str))); |
||||
return -EADDRINUSE; |
||||
} |
||||
|
||||
if (ncache[i].iface == KERNEL_PID_UNDEF) { |
||||
ncache[i].iface = iface; |
||||
|
||||
ng_pktqueue_init(&(ncache[i].pkts)); |
||||
memcpy(&(ncache[i].ipv6_addr), ipv6_addr, sizeof(ng_ipv6_addr_t)); |
||||
DEBUG("ipv6_nc: Register %s for interface %" PRIkernel_pid, |
||||
ng_ipv6_addr_to_str(addr_str, ipv6_addr, sizeof(addr_str)), |
||||
iface); |
||||
|
||||
if ((l2_addr != NULL) && (l2_addr_len > 0)) { |
||||
#if ENABLE_DEBUG |
||||
DEBUG(" to L2 address "); |
||||
|
||||
for (size_t i = 0; i < l2_addr_len; i++) { |
||||
if (i > 0) { |
||||
putchar(':'); |
||||
} |
||||
|
||||
DEBUG("%02x", ((uint8_t *)l2_addr)[i]); |
||||
} |
||||
|
||||
#endif |
||||
memcpy(&(ncache[i].l2_addr), l2_addr, l2_addr_len); |
||||
ncache[i].l2_addr_len = l2_addr_len; |
||||
} |
||||
|
||||
ncache[i].flags = flags; |
||||
DEBUG(" with flags = 0x%0x\n", flags); |
||||
|
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
return -ENOMEM; |
||||
} |
||||
|
||||
void ng_ipv6_nc_remove(kernel_pid_t iface, const ng_ipv6_addr_t *ipv6_addr) |
||||
{ |
||||
ng_ipv6_nc_t *entry = ng_ipv6_nc_get(iface, ipv6_addr); |
||||
|
||||
if (entry != NULL) { |
||||
DEBUG("ipv6_nc: Remove %s for interface %" PRIkernel_pid "\n", |
||||
ng_ipv6_addr_to_str(addr_str, ipv6_addr, sizeof(addr_str)), |
||||
iface); |
||||
|
||||
ng_ipv6_addr_set_unspecified(&(entry->ipv6_addr)); |
||||
entry->iface = KERNEL_PID_UNDEF; |
||||
entry->flags = 0; |
||||
} |
||||
} |
||||
|
||||
ng_ipv6_nc_t *ng_ipv6_nc_get(kernel_pid_t iface, const ng_ipv6_addr_t *ipv6_addr) |
||||
{ |
||||
if (ipv6_addr == NULL) { |
||||
DEBUG("ipv6_nc: address was NULL\n"); |
||||
return NULL; |
||||
} |
||||
|
||||
for (int i = 0; i < NG_IPV6_NC_SIZE; i++) { |
||||
if (((iface == KERNEL_PID_UNDEF) || (iface == ncache[i].iface)) && |
||||
ng_ipv6_addr_equal(&(ncache[i].ipv6_addr), ipv6_addr)) { |
||||
DEBUG("ipv6_nc: Found entry for %s on interface %" PRIkernel_pid |
||||
" (0 = all interfaces) [%p]\n", |
||||
ng_ipv6_addr_to_str(addr_str, ipv6_addr, sizeof(addr_str)), |
||||
iface, (void *)(ncache + i)); |
||||
|
||||
return ncache + i; |
||||
} |
||||
} |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
ng_ipv6_nc_t *ng_ipv6_nc_get_reachable(kernel_pid_t iface, |
||||
const ng_ipv6_addr_t *ipv6_addr) |
||||
{ |
||||
ng_ipv6_nc_t *entry = ng_ipv6_nc_get(iface, ipv6_addr); |
||||
|
||||
if (entry == NULL) { |
||||
DEBUG("ipv6_nc: No entry found for %s on interface %" PRIkernel_pid "\n", |
||||
ng_ipv6_addr_to_str(addr_str, ipv6_addr, sizeof(addr_str)), |
||||
iface); |
||||
return NULL; |
||||
} |
||||
|
||||
switch ((entry->flags & NG_IPV6_NC_STATE_MASK) >> NG_IPV6_NC_STATE_POS) { |
||||
case NG_IPV6_NC_STATE_UNREACHABLE: |
||||
case NG_IPV6_NC_STATE_INCOMPLETE: |
||||
DEBUG("ipv6_nc: Entry %s is unreachable (flags = 0x%02x)\n", |
||||
ng_ipv6_addr_to_str(addr_str, ipv6_addr, sizeof(addr_str)), |
||||
entry->flags); |
||||
return NULL; |
||||
|
||||
default: |
||||
return entry; |
||||
} |
||||
} |
||||
|
||||
ng_ipv6_nc_t *ng_ipv6_nc_still_reachable(const ng_ipv6_addr_t *ipv6_addr) |
||||
{ |
||||
ng_ipv6_nc_t *entry = ng_ipv6_nc_get(KERNEL_PID_UNDEF, ipv6_addr); |
||||
|
||||
if (entry == NULL) { |
||||
DEBUG("ipv6_nc: No entry found for %s\n", |
||||
ng_ipv6_addr_to_str(addr_str, ipv6_addr, sizeof(addr_str))); |
||||
return NULL; |
||||
} |
||||
|
||||
if (((entry->flags & NG_IPV6_NC_STATE_MASK) >> NG_IPV6_NC_STATE_POS) != |
||||
NG_IPV6_NC_STATE_INCOMPLETE) { |
||||
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); |
||||
entry->flags |= (NG_IPV6_NC_STATE_REACHABLE >> NG_IPV6_NC_STATE_POS); |
||||
} |
||||
|
||||
return entry; |
||||
} |
||||
|
||||
/** @} */ |
Loading…
Reference in new issue