diff --git a/sys/include/net/gnrc/ipv6/nib.h b/sys/include/net/gnrc/ipv6/nib.h index 56a34b1b46..00796e7fd4 100644 --- a/sys/include/net/gnrc/ipv6/nib.h +++ b/sys/include/net/gnrc/ipv6/nib.h @@ -23,6 +23,7 @@ #define NET_GNRC_IPV6_NIB_H #include "net/gnrc/ipv6/nib/nc.h" +#include "net/gnrc/ipv6/nib/pl.h" #ifdef __cplusplus extern "C" { diff --git a/sys/include/net/gnrc/ipv6/nib/pl.h b/sys/include/net/gnrc/ipv6/nib/pl.h new file mode 100644 index 0000000000..ca84fe2e3f --- /dev/null +++ b/sys/include/net/gnrc/ipv6/nib/pl.h @@ -0,0 +1,137 @@ +/* + * 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_pl Prefix list + * @ingroup net_gnrc_ipv6_nib + * @brief Prefix list component of neighbor information base + * @{ + * + * @file + * @brief Prefix list defintions + * + * @author Martine Lenders + */ +#ifndef NET_GNRC_IPV6_NIB_PL_H +#define NET_GNRC_IPV6_NIB_PL_H + +#include + +#include "net/ipv6/addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Prefix list entry view on NIB + */ +typedef struct { + ipv6_addr_t pfx; /**< prefix */ + uint8_t pfx_len; /**< length of gnrc_ipv6_nib_pl_t::pfx in bits */ + uint16_t iface; /**< interface gnrc_ipv6_nib_pl_t::pfx is assigned + * to */ + uint32_t valid_until; /**< timestamp (in ms) until which the prefix is + valid */ + uint32_t pref_until; /**< timestamp (in ms) until which the prefix is + preferred */ +} gnrc_ipv6_nib_pl_t; + +/** + * @brief Adds (or updates) prefix to NIB + * + * @pre `(pfx != NULL)` + * + * @param[in] iface Interface @p pfx is valid on. + * @param[in] pfx The prefix. May not be a link-local prefix or a + * multicast address and its first @p pfx_len bits + * may not be 0. + * @param[in] pfx_len Length of @p pfx in bits. + * Condition @p pfx_len > 0 must hold. + * @param[in] valid_ltime Lifetime (in ms) until prefix expires from now. + * UINT32_MAX for infinite lifetime. Addresses with + * expired prefixes are removed from @p iface. + * @param[in] pref_ltime Lifetime (in ms) until prefix deprecates from now. + * UINT32_MAX for infinite lifetime. Addresses with + * deprecated prefixes should not be used for new + * communication. Only applications with difficulty + * changing to another address without service + * disruption should use deprecated addresses. May not + * be greater then @p valid_ltime. + * + * @return 0, on success. + * @return -EINVAL, if @p pfx was fe80::` or multicast, + * @p pfx_len was == 0, the first @p pfx_len bits of @ pfx were 0, + * or if pref_ltime > valid_ltime. + * @return -ENOMEM, if no space was left in the prefix list. + */ +int gnrc_ipv6_nib_pl_set(unsigned iface, + const ipv6_addr_t *pfx, unsigned pfx_len, + uint32_t valid_ltime, uint32_t pref_ltime); + +/** + * @brief Deletes prefix from NIB + * + * @pre `pfx != NULL` + * + * @param[in] iface The interface @p pfx is expected to be on (0 for any). + * @param[in] pfx The prefix to be removed. + * @param[in] pfx_len Length of @p pfx in bits. + */ +void gnrc_ipv6_nib_pl_del(unsigned iface, + const ipv6_addr_t *pfx, unsigned pfx_len); + +/** + * @brief Iterates over all prefix list entries in the NIB. + * + * @pre `(state != NULL) && (ple != NULL)` + * + * @param[in] iface Restrict iteration to entries on this interface. + * 0 for any interface. + * @param[in,out] state Iteration state of the prefix list. Must point to NULL + * pointer to start iteration + * @param[out] ple The next prefix list entry. + * + * Usage example: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * #include "net/gnrc/ipv6/nib/pl.h" + * + * int main(void) { + * void *state = NULL; + * gnrc_ipv6_nib_pl_t ple; + * + * puts("My prefixes:"); + * while (gnrc_ipv6_nib_pl_iter(0, &state, &ple)) { + * gnrc_ipv6_nib_pl_print(&ple); + * } + * return 0; + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @return true, if iteration can be continued. + * @return false, if @p ple is the last prefix list ple in the NIB. + */ +bool gnrc_ipv6_nib_pl_iter(unsigned iface, void **state, + gnrc_ipv6_nib_pl_t *ple); + +/** + * @brief Prints a prefix list entry + * + * @pre `ple != NULL` + * + * @param[in] ple A prefix list entry + */ +void gnrc_ipv6_nib_pl_print(gnrc_ipv6_nib_pl_t *ple); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_IPV6_NIB_PL_H */ +/** @} */ diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c index 383fddfcfb..1593f94057 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c @@ -483,6 +483,41 @@ _nib_offl_entry_t *_nib_offl_iter(const _nib_offl_entry_t *last) return NULL; } +_nib_offl_entry_t *_nib_pl_add(unsigned iface, + const ipv6_addr_t *pfx, + unsigned pfx_len, + uint32_t valid_ltime, + uint32_t pref_ltime) +{ + _nib_offl_entry_t *dst = _nib_offl_add(NULL, iface, pfx, pfx_len, _PL); + + if (dst == NULL) { + return NULL; + } + assert(valid_ltime >= pref_ltime); + if ((valid_ltime != UINT32_MAX) || (pref_ltime != UINT32_MAX)) { + uint32_t now = (xtimer_now_usec64() / US_PER_MS) & UINT32_MAX; + if (pref_ltime != UINT32_MAX) { + _evtimer_add(dst, GNRC_IPV6_NIB_PFX_TIMEOUT, &dst->pfx_timeout, + pref_ltime); + if (((pref_ltime + now) == UINT32_MAX) && (now != 0)) { + pref_ltime++; + } + pref_ltime += now; + } + if (valid_ltime != UINT32_MAX) { + /* prevent valid_ltime from becoming UINT32_MAX */ + if ((valid_ltime + now) == UINT32_MAX) { + valid_ltime++; + } + valid_ltime += now; + } + } + dst->valid_until = valid_ltime; + dst->pref_until = pref_ltime; + return dst; +} + _nib_iface_t *_nib_iface_get(unsigned iface) { _nib_iface_t *ni = NULL; diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h index 491633b769..0b96040b14 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h @@ -165,8 +165,16 @@ typedef struct { ipv6_addr_t pfx; /**< prefix to the destination */ unsigned pfx_len; /**< prefix-length in bits of * _nib_onl_entry_t::pfx */ + /** + * @brief Event for @ref GNRC_IPV6_NIB_PFX_TIMEOUT + */ + evtimer_msg_event_t pfx_timeout; uint8_t mode; /**< [mode](@ref net_gnrc_ipv6_nib_mode) of the * off-link entry */ + uint32_t valid_until; /**< timestamp (in ms) until which the prefix + valid (UINT32_MAX means forever) */ + uint32_t pref_until; /**< timestamp (in ms) until which the prefix + preferred (UINT32_MAX means forever) */ } _nib_offl_entry_t; /** @@ -565,6 +573,7 @@ static inline void _nib_dc_remove(_nib_offl_entry_t *nib_offl) * * @pre `(next_hop != NULL)` * @pre `(pfx != NULL) && (pfx != "::") && (pfx_len != 0) && (pfx_len <= 128)` + * @pre `(pref_ltime <= valid_ltime)` * * @param[in] iface The interface to the prefix is added to. * @param[in] pfx The IPv6 prefix or address of the destination. @@ -576,12 +585,11 @@ static inline void _nib_dc_remove(_nib_offl_entry_t *nib_offl) * @p pfx. * @return NULL, if no space is left. */ -static inline _nib_offl_entry_t *_nib_pl_add(unsigned iface, - const ipv6_addr_t *pfx, - unsigned pfx_len) -{ - return _nib_offl_add(NULL, iface, pfx, pfx_len, _PL); -} +_nib_offl_entry_t *_nib_pl_add(unsigned iface, + const ipv6_addr_t *pfx, + unsigned pfx_len, + uint32_t valid_ltime, + uint32_t pref_ltime); /** * @brief Removes a prefix list entry @@ -592,6 +600,7 @@ static inline _nib_offl_entry_t *_nib_pl_add(unsigned iface, */ static inline void _nib_pl_remove(_nib_offl_entry_t *nib_offl) { + evtimer_del(&_nib_evtimer, &nib_offl->pfx_timeout.event); _nib_offl_remove(nib_offl, _PL); } diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib_pl.c b/sys/net/gnrc/network_layer/ipv6/nib/nib_pl.c new file mode 100644 index 0000000000..635d9dec0d --- /dev/null +++ b/sys/net/gnrc/network_layer/ipv6/nib/nib_pl.c @@ -0,0 +1,120 @@ +/* + * 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 + */ + +#include +#include +#include + +#include "net/gnrc/ipv6/nib/pl.h" +#include "timex.h" +#include "xtimer.h" + +#include "_nib-internal.h" + +int gnrc_ipv6_nib_pl_set(unsigned iface, + const ipv6_addr_t *pfx, unsigned pfx_len, + uint32_t valid_ltime, uint32_t pref_ltime) +{ + int res = 0; + _nib_offl_entry_t *dst; + ipv6_addr_t tmp = IPV6_ADDR_UNSPECIFIED; + + assert((pfx != NULL)); + if (pfx_len > IPV6_ADDR_BIT_LEN) { + pfx_len = IPV6_ADDR_BIT_LEN; + } + ipv6_addr_init_prefix(&tmp, pfx, pfx_len); + /* pfx_len == 0 implicitly checked, since this leaves tmp unspecified */ + if (ipv6_addr_is_unspecified(&tmp) || ipv6_addr_is_link_local(pfx) || + ipv6_addr_is_multicast(pfx) || (pref_ltime > valid_ltime)) { + return -EINVAL; + } + mutex_lock(&_nib_mutex); + dst = _nib_pl_add(iface, pfx, pfx_len, valid_ltime, + pref_ltime); + if (dst == NULL) { + res = -ENOMEM; + } + mutex_unlock(&_nib_mutex); + /* TODO: send RA with PIO, if iface is allowed to send RAs */ + return res; +} + +void gnrc_ipv6_nib_pl_del(unsigned iface, + const ipv6_addr_t *pfx, unsigned pfx_len) +{ + _nib_offl_entry_t *dst = NULL; + + assert(pfx != NULL); + mutex_lock(&_nib_mutex); + while ((dst = _nib_offl_iter(dst)) != NULL) { + assert(dst->next_hop != NULL); + if ((pfx_len == dst->pfx_len) && + ((iface == 0) || (iface == _nib_onl_get_if(dst->next_hop))) && + (ipv6_addr_match_prefix(pfx, &dst->pfx) >= pfx_len)) { + _nib_pl_remove(dst); + mutex_unlock(&_nib_mutex); + /* TODO: send RA with PIO, if iface is allowed to send RAs */ + return; + } + } + mutex_unlock(&_nib_mutex); +} + +bool gnrc_ipv6_nib_pl_iter(unsigned iface, void **state, + gnrc_ipv6_nib_pl_t *entry) +{ + _nib_offl_entry_t *dst = *state; + + mutex_lock(&_nib_mutex); + while ((dst = _nib_offl_iter(dst)) != NULL) { + const _nib_onl_entry_t *node = dst->next_hop; + assert(node != NULL); + if ((dst->mode & _PL) && + ((iface == 0) || (_nib_onl_get_if(node) == iface))) { + entry->pfx_len = dst->pfx_len; + ipv6_addr_set_unspecified(&entry->pfx); + ipv6_addr_init_prefix(&entry->pfx, &dst->pfx, dst->pfx_len); + entry->iface = _nib_onl_get_if(node); + entry->valid_until = dst->valid_until; + entry->pref_until = dst->pref_until; + break; + } + } + mutex_unlock(&_nib_mutex); + *state = dst; + return (*state != NULL); +} + +void gnrc_ipv6_nib_pl_print(gnrc_ipv6_nib_pl_t *entry) +{ + char addr_str[IPV6_ADDR_MAX_STR_LEN]; + ipv6_addr_t pfx = IPV6_ADDR_UNSPECIFIED; + uint32_t now = ((xtimer_now_usec64() / US_PER_MS)) & UINT32_MAX; + + ipv6_addr_init_prefix(&pfx, &entry->pfx, entry->pfx_len); + printf("%s/%u ", ipv6_addr_to_str(addr_str, &pfx, sizeof(addr_str)), + entry->pfx_len); + printf("dev #%u ", entry->iface); + if (entry->valid_until < UINT32_MAX) { + printf(" expires %" PRIu32 "sec", (entry->valid_until - now) / MS_PER_SEC); + } + if (entry->pref_until < UINT32_MAX) { + printf(" deprecates %" PRIu32 "sec", (entry->pref_until - now) / MS_PER_SEC); + } + puts(""); +} + +/** @} */