1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-26 15:03:53 +01:00

gnrc_ipv6_nib: add forwarding table component

This commit is contained in:
Martine Lenders 2017-06-23 19:33:25 +02:00
parent da7ef18a8d
commit d8acdc9d2d
5 changed files with 438 additions and 0 deletions

View File

@ -23,6 +23,7 @@
#define NET_GNRC_IPV6_NIB_H
#include "net/gnrc/ipv6/nib/abr.h"
#include "net/gnrc/ipv6/nib/ft.h"
#include "net/gnrc/ipv6/nib/nc.h"
#include "net/gnrc/ipv6/nib/pl.h"

View File

@ -0,0 +1,148 @@
/*
* Copyright (C) 2017 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup net_gnrc_ipv6_nib_ft Forwarding table
* @ingroup net_gnrc_ipv6_nib
* @brief
* @{
*
* @file
* @brief Forwarding table definitions
*
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/
#ifndef NET_GNRC_IPV6_NIB_FT_H
#define NET_GNRC_IPV6_NIB_FT_H
#include <stdint.h>
#include "net/gnrc/pkt.h"
#include "net/ipv6/addr.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Forwarding table entry view on NIB
*/
typedef struct {
ipv6_addr_t dst; /**< destination or prefix */
ipv6_addr_t next_hop; /**< next hop to gnrc_ipv6_nib_ft_t::dst */
uint8_t dst_len; /**< prefix-length in bits of
* gnrc_ipv6_nib_ft_t::dst */
uint8_t primary; /**< != 0 if gnrc_ipv6_nib_ft_t::dst is preferred
* default route */
uint16_t iface; /**< interface to gnrc_ipv6_nib_ft_t::next_hop */
} gnrc_ipv6_nib_ft_t;
/**
* @brief Gets the best matching forwarding table entry to a destination
*
* @pre `(dst != NULL) && (fte != NULL)`
*
* @param[in] dst The destination.
* @param[in] pkt Packet that is supposed to go to that destination
* (is handed over to a reactive routing protocol if one exists
* on the interface found and no route is found)
* @param[out] fte The resulting forwarding table entry.
*
* @return 0, on success.
* @return -ENETUNREACH, if no route was found.
*/
int gnrc_ipv6_nib_ft_get(const ipv6_addr_t *dst, gnrc_pktsnip_t *pkt,
gnrc_ipv6_nib_ft_t *fte);
/**
* @brief Adds a new route to the forwarding table
*
* @param[in] dst The destination to the route. May be NULL or `::` for
* default route.
* @param[in] dst_len The prefix length of @p dst in bits. May be 0 for
* default route.
* @param[in] next_hop The next hop to @p dst/@p dst_len. May be NULL, if
* @p dst/@p dst_len is no the default route.
* @param[in] iface The interface to @p next_hop. May not be 0.
*
* @return 0, on success.
* @return -EINVAL, if a parameter was of invalid value.
* @return -ENOMEM, if there was no space left in forwarding table.
*/
int gnrc_ipv6_nib_ft_add(const ipv6_addr_t *dst, unsigned dst_len,
const ipv6_addr_t *next_hop, unsigned iface);
/**
* @brief Deletes a route from forwarding table.
*
* @param[in] dst The destination of the route. May be NULL or `::` for
* default route.
* @param[in] dst_len The prefix length of @p dst in bits. May be 0 for
* default route.
*/
void gnrc_ipv6_nib_ft_del(const ipv6_addr_t *dst, unsigned dst_len);
/**
* @brief Iterates over all forwarding table entries in the NIB
*
* @pre `(state != NULL) && (fte != NULL)`
*
* @param[in] next_hop Restrict iteration to entries to this next hop. NULL
* for any next hop. Can be used to build a source
* routing tree.
* @param[in] iface Restrict iteration to entries on this interface.
* 0 for any interface.
* @param[in,out] state Iteration state of the forwarding table. Must point
* to a NULL pointer to start iteration.
* @param[out] fte The next forwarding table entry.
*
* The iteration over all forwarding table entries in the NIB includes all
* entries added via @p gnrc_ipv6_nib_ft_add() and entries that are currently
* in the Destination Cache, in the Prefix List, and in the Default Router List.
*
* Usage example:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
* #include "net/gnrc/ipv6/nib/ft.h"
*
* int main(void) {
* void *state = NULL;
* gnrc_ipv6_nib_ft_t fte;
*
* puts("My neighbors:");
* while (gnrc_ipv6_nib_ft_iter(NULL, 0, &state, &fte)) {
* gnrc_ipv6_nib_ft_print(&fte);
* }
* return 0;
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @note The list may change during iteration.
*
* @return true, if iteration can be continued.
* @return false, if @p fte is the last neighbor cache entry in the NIB.
*/
bool gnrc_ipv6_nib_ft_iter(const ipv6_addr_t *next_hop, unsigned iface,
void **state, gnrc_ipv6_nib_ft_t *fte);
/**
* @brief Prints a forwarding table entry
*
* @pre `fce != NULL`
*
* @param[in] fte A forwarding table entry.
*/
void gnrc_ipv6_nib_ft_print(const gnrc_ipv6_nib_ft_t *fte);
#ifdef __cplusplus
}
#endif
#endif /* NET_GNRC_IPV6_NIB_FT_H */
/** @} */

View File

@ -401,6 +401,18 @@ _nib_dr_entry_t *_nib_drl_get_dr(void)
return _prime_def_router;
}
void _nib_drl_ft_get(const _nib_dr_entry_t *drl, gnrc_ipv6_nib_ft_t *fte)
{
assert((drl != NULL) && (drl->next_hop != NULL) && (fte != NULL));
ipv6_addr_set_unspecified(&fte->dst);
fte->dst_len = 0;
fte->primary = ((drl == _prime_def_router) &&
!((_prime_def_router == NULL) ||
(_node_unreachable(_prime_def_router->next_hop))));
memcpy(&fte->next_hop, &drl->next_hop->ipv6, sizeof(fte->next_hop));
fte->iface = _nib_onl_get_if(drl->next_hop);
}
_nib_offl_entry_t *_nib_offl_alloc(const ipv6_addr_t *next_hop, unsigned iface,
const ipv6_addr_t *pfx, unsigned pfx_len)
{
@ -497,6 +509,87 @@ _nib_offl_entry_t *_nib_offl_iter(const _nib_offl_entry_t *last)
return NULL;
}
bool _nib_offl_is_entry(const _nib_offl_entry_t *entry)
{
return (entry >= _dsts) && _in_dsts(entry);
}
static _nib_offl_entry_t *_nib_offl_get_match(const ipv6_addr_t *dst)
{
_nib_offl_entry_t *res = NULL;
uint8_t best_match = 0;
DEBUG("nib: get match for destination %s from NIB\n",
ipv6_addr_to_str(addr_str, dst, sizeof(addr_str)));
for (_nib_offl_entry_t *entry = _dsts; _in_dsts(entry); entry++) {
if (entry->mode != _EMPTY) {
uint8_t match = ipv6_addr_match_prefix(&entry->pfx, dst);
DEBUG("nib: %s/%u => ",
ipv6_addr_to_str(addr_str, &entry->pfx, sizeof(addr_str)),
entry->pfx_len);
DEBUG("%s%%%u matches with %u bits\n",
(entry->mode == _PL) ? "(nil)" :
ipv6_addr_to_str(addr_str, &entry->next_hop->ipv6,
sizeof(addr_str)),
_nib_onl_get_if(entry->next_hop), match);
if ((match > best_match) && (match >= entry->pfx_len)) {
DEBUG("nib: best match (%u bits)\n", match);
res = entry;
best_match = match;
}
}
}
return res;
}
void _nib_ft_get(const _nib_offl_entry_t *dst, gnrc_ipv6_nib_ft_t *fte)
{
assert((dst != NULL) && (dst->next_hop != NULL) && (fte != NULL));
memcpy(&fte->dst, &dst->pfx, sizeof(dst->pfx));
fte->dst_len = dst->pfx_len;
fte->primary = 0;
fte->iface = _nib_onl_get_if(dst->next_hop);
if (dst->mode == _PL) { /* entry is only in prefix list */
ipv6_addr_set_unspecified(&fte->next_hop);
}
else {
memcpy(&fte->next_hop, &dst->next_hop->ipv6, sizeof(dst->next_hop->ipv6));
}
}
int _nib_get_route(const ipv6_addr_t *dst, gnrc_pktsnip_t *pkt,
gnrc_ipv6_nib_ft_t *fte)
{
assert((dst != NULL) && (fte != NULL));
DEBUG("nib: get route %s for packet %p\n",
ipv6_addr_to_str(addr_str, dst, sizeof(addr_str)),
(void *)pkt);
_nib_offl_entry_t *offl = _nib_offl_get_match(dst);
assert((dst != NULL) && (fte != NULL));
if ((offl == NULL) || (offl->mode == _PL)) {
/* give default router precedence over PLE */
_nib_dr_entry_t *router = _nib_drl_get_dr();
if ((router == NULL) && (offl == NULL)) {
(void)pkt;
/* TODO: ask RRP to search for route (using pkt) */
return -ENETUNREACH;
}
else if (router != NULL) {
DEBUG("nib: prefer default router %s%%%u over prefix list entry\n",
ipv6_addr_to_str(addr_str, &router->next_hop->ipv6,
sizeof(addr_str)),
_nib_onl_get_if(router->next_hop));
_nib_drl_ft_get(router, fte);
return 0;
}
}
_nib_ft_get(offl, fte);
return 0;
}
void _nib_pl_remove(_nib_offl_entry_t *nib_offl)
{
_nib_offl_remove(nib_offl, _PL);

View File

@ -32,6 +32,7 @@
#ifdef MODULE_GNRC_IPV6
#include "net/gnrc/ipv6.h"
#endif
#include "net/gnrc/ipv6/nib/ft.h"
#include "net/gnrc/ipv6/nib/nc.h"
#include "net/gnrc/ipv6/nib/conf.h"
#include "net/gnrc/pktqueue.h"
@ -463,6 +464,17 @@ _nib_dr_entry_t *_nib_drl_iter(const _nib_dr_entry_t *last);
*/
_nib_dr_entry_t *_nib_drl_get(const ipv6_addr_t *router_addr, unsigned iface);
/**
* @brief Gets external forwarding table entry representation from default
* router entry
*
* @pre `(drl != NULL) && (drl->next_hop != NULL) && (fte != NULL)`
*
* @param[in] drl Default router entry.
* @param[out] fte External representation of the forwarding table entry.
*/
void _nib_drl_ft_get(const _nib_dr_entry_t *drl, gnrc_ipv6_nib_ft_t *fte);
/**
* @brief Gets *the* default router
*
@ -509,6 +521,16 @@ void _nib_offl_clear(_nib_offl_entry_t *dst);
*/
_nib_offl_entry_t *_nib_offl_iter(const _nib_offl_entry_t *last);
/**
* @brief Checks if @p entry was allocated using _nib_offl_alloc()
*
* @param[in] entry An entry.
*
* @return true, if @p entry was allocated using @ref _nib_offl_alloc()
* @return false, if @p entry was not allocated using @ref _nib_offl_alloc()
*/
bool _nib_offl_is_entry(const _nib_offl_entry_t *entry);
/**
* @brief Helper function for view-level add-functions below
*
@ -726,6 +748,34 @@ _nib_offl_entry_t *_nib_abr_iter_pfx(const _nib_abr_entry_t *abr,
_nib_abr_entry_t *_nib_abr_iter(const _nib_abr_entry_t *last);
#endif
/**
* @brief Gets external forwarding table entry representation from off-link
* entry
*
* @pre `(dst != NULL) && (dst->next_hop != NULL) && (fte != NULL)`
*
* @param[in] dst Off-link entry.
* @param[out] fte External representation of the forwarding table entry.
*/
void _nib_ft_get(const _nib_offl_entry_t *dst, gnrc_ipv6_nib_ft_t *fte);
/**
* @brief Gets best match to @p dst from all off-link entries and default
* route.
*
* @pre `(dst != NULL) && (fte != NULL)`
*
* @param[in] dst Destination address to get the off-link entry for.
* @param[in] pkt Packet causing the route look-up (provided to allow reactive
* routing protocols to queue it if needed). May be NULL.
* @param[out] fte Resulting forwarding table entry.
*
* @return 0, on success.
* @return -ENETUNREACH, when no route was found.
*/
int _nib_get_route(const ipv6_addr_t *dst, gnrc_pktsnip_t *ctx,
gnrc_ipv6_nib_ft_t *entry);
/**
* @brief Gets (or creates if it not exists) interface information for
* neighbor discovery

View File

@ -0,0 +1,146 @@
/*
* Copyright (C) 2017 Freie Universität Berlin
*
* 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
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/
#include <errno.h>
#include <stdio.h>
#include "_nib-internal.h"
#include "net/gnrc/ipv6/nib/ft.h"
int gnrc_ipv6_nib_ft_get(const ipv6_addr_t *dst, gnrc_pktsnip_t *pkt,
gnrc_ipv6_nib_ft_t *fte)
{
int res;
assert((dst != NULL) && (fte != NULL));
mutex_lock(&_nib_mutex);
res = _nib_get_route(dst, pkt, fte);
mutex_unlock(&_nib_mutex);
return res;
}
int gnrc_ipv6_nib_ft_add(const ipv6_addr_t *dst, unsigned dst_len,
const ipv6_addr_t *next_hop, unsigned iface)
{
void *ptr = NULL;
int res = 0;
bool is_default_route = ((dst == NULL) || (dst_len == 0) ||
ipv6_addr_is_unspecified(dst));
if ((iface == 0) || ((is_default_route) && (next_hop == NULL))) {
return -EINVAL;
}
mutex_lock(&_nib_mutex);
if (is_default_route) {
ptr = _nib_drl_add(next_hop, iface);
}
#if GNRC_IPV6_NIB_CONF_ROUTER
else {
dst_len = (dst_len > 128) ? 128 : dst_len;
ptr = _nib_ft_add(next_hop, iface, dst, dst_len);
}
#else /* GNRC_IPV6_NIB_CONF_ROUTER */
else {
res = -ENOTSUP;
}
#endif
mutex_unlock(&_nib_mutex);
if (ptr == NULL) {
res = -ENOMEM;
}
return res;
}
void gnrc_ipv6_nib_ft_del(const ipv6_addr_t *dst, unsigned dst_len)
{
mutex_lock(&_nib_mutex);
if ((dst == NULL) || (dst_len == 0) || ipv6_addr_is_unspecified(dst)) {
_nib_dr_entry_t *entry = _nib_drl_get_dr();
if (entry != NULL) {
_nib_drl_remove(entry);
}
}
#if GNRC_IPV6_NIB_CONF_ROUTER
else {
_nib_offl_entry_t *entry = NULL;
while ((entry = _nib_offl_iter(entry))) {
if ((entry->pfx_len == dst_len) &&
(ipv6_addr_match_prefix(&entry->pfx, dst) >= dst_len)) {
_nib_ft_remove(entry);
break;
}
}
}
#endif
mutex_unlock(&_nib_mutex);
}
bool gnrc_ipv6_nib_ft_iter(const ipv6_addr_t *next_hop, unsigned iface,
void **state, gnrc_ipv6_nib_ft_t *fte)
{
_nib_dr_entry_t *entry;
assert((state != NULL) && (fte != NULL));
if ((*state == NULL) || _nib_offl_is_entry(*state)) {
_nib_offl_entry_t *offl = *state;
while ((offl = _nib_offl_iter(offl))) {
assert((offl->mode != 0) || (offl->next_hop != NULL));
if (((iface == 0) || (iface == _nib_onl_get_if(offl->next_hop))) &&
((next_hop == NULL) || ipv6_addr_equal(&offl->next_hop->ipv6,
next_hop))) {
_nib_ft_get(offl, fte);
*state = offl;
return true;
}
}
*state = NULL;
}
entry = *state;
while ((entry = _nib_drl_iter(entry))) {
assert((entry->next_hop != NULL));
if (((iface == 0) || (iface == _nib_onl_get_if(entry->next_hop))) &&
((next_hop == NULL) || ipv6_addr_equal(&entry->next_hop->ipv6,
next_hop))) {
_nib_drl_ft_get(entry, fte);
break;
}
}
*state = entry;
return (*state != NULL);
}
void gnrc_ipv6_nib_ft_print(const gnrc_ipv6_nib_ft_t *fte)
{
char addr_str[IPV6_ADDR_MAX_STR_LEN];
if ((fte->dst_len == 0) || ipv6_addr_is_unspecified(&fte->dst)) {
printf("default%s ", (fte->primary ? "*" : ""));
}
else {
printf("%s/%u ", ipv6_addr_to_str(addr_str, &fte->dst, sizeof(addr_str)),
fte->dst_len);
}
if (!ipv6_addr_is_unspecified(&fte->next_hop)) {
printf("via %s ", ipv6_addr_to_str(addr_str, &fte->next_hop,
sizeof(addr_str)));
}
printf("dev #%u\n", fte->iface);
}
/**i @} */