From f840f009dbfb023517fae1948ba10ff7c07faa17 Mon Sep 17 00:00:00 2001 From: Mihai Renea Date: Mon, 26 Aug 2024 09:21:52 +0200 Subject: [PATCH 1/8] gnrc/ipv6/nib: don't queue packets on 6lo neighbors and drop/flush if UNREACHABLE --- sys/include/net/gnrc/ipv6/nib/conf.h | 11 +++ .../gnrc/network_layer/ipv6/nib/_nib-arsm.c | 15 ++-- .../gnrc/network_layer/ipv6/nib/_nib-arsm.h | 12 +++- .../network_layer/ipv6/nib/_nib-internal.c | 11 +-- .../network_layer/ipv6/nib/_nib-internal.h | 33 +++++++++ sys/net/gnrc/network_layer/ipv6/nib/nib.c | 70 +++++++++++++++++-- 6 files changed, 131 insertions(+), 21 deletions(-) diff --git a/sys/include/net/gnrc/ipv6/nib/conf.h b/sys/include/net/gnrc/ipv6/nib/conf.h index fddbc03e8005..7d41d827242f 100644 --- a/sys/include/net/gnrc/ipv6/nib/conf.h +++ b/sys/include/net/gnrc/ipv6/nib/conf.h @@ -176,6 +176,17 @@ extern "C" { #define CONFIG_GNRC_IPV6_NIB_QUEUE_PKT 1 #endif +#if CONFIG_GNRC_IPV6_NIB_QUEUE_PKT +/** + * @brief queue capacity for the packets waiting for address resolution, + * per neighbor. SHOULD always be smaller than @ref CONFIG_GNRC_IPV6_NIB_NUMOF + */ +#ifndef CONFIG_GNRC_IPV6_NIB_QUEUE_PKT_CAP +#define CONFIG_GNRC_IPV6_NIB_QUEUE_PKT_CAP (CONFIG_GNRC_IPV6_NIB_NUMOF > 16 ? 16 : 1) +#endif + +#endif /* CONFIG_GNRC_IPV6_NIB_QUEUE_PKT */ + /** * @brief handle NDP messages according for stateless address * auto-configuration (if activated on interface) diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c b/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c index 0709f6b71412..f449a221da9d 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c @@ -228,9 +228,7 @@ void _handle_snd_ns(_nib_onl_entry_t *nbr) case GNRC_IPV6_NIB_NC_INFO_NUD_STATE_PROBE: if (nbr->ns_sent >= NDP_MAX_UC_SOL_NUMOF) { gnrc_netif_t *netif = gnrc_netif_get_by_pid(_nib_onl_get_if(nbr)); - - _set_nud_state(netif, nbr, - GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE); + _set_unreachable(netif, nbr); } /* intentionally falls through */ case GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE: @@ -391,7 +389,7 @@ void _handle_adv_l2(gnrc_netif_t *netif, _nib_onl_entry_t *nce, /* send queued packets */ gnrc_pktqueue_t *ptr; DEBUG("nib: Sending queued packets\n"); - while ((ptr = gnrc_pktqueue_remove_head(&nce->pktqueue)) != NULL) { + while ((ptr = _nbr_pop_pkt(nce)) != NULL) { if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_IPV6, GNRC_NETREG_DEMUX_CTX_ALL, ptr->pkt)) { @@ -439,6 +437,15 @@ void _set_reachable(gnrc_netif_t *netif, _nib_onl_entry_t *nce) netif->ipv6.reach_time); } +void _set_unreachable(gnrc_netif_t *netif, _nib_onl_entry_t *nce) +{ + DEBUG("nib: set %s to UNREACHABLE\n", + ipv6_addr_to_str(addr_str, &nce->ipv6, sizeof(addr_str))); + + _nbr_flush_pktqueue(nce); + _set_nud_state(netif, nce, GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE); +} + void _set_nud_state(gnrc_netif_t *netif, _nib_onl_entry_t *nce, uint16_t state) { diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.h b/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.h index ab69e40d4f6e..a3d37960e3d3 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.h +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.h @@ -174,14 +174,22 @@ void _handle_adv_l2(gnrc_netif_t *netif, _nib_onl_entry_t *nce, void _recalc_reach_time(gnrc_netif_ipv6_t *netif); /** - * @brief Sets a neighbor cache entry reachable and starts the required + * @brief Sets a neighbor cache entry REACHABLE and starts the required * event timers * * @param[in] netif Interface to the NCE - * @param[in] nce The neighbor cache entry to set reachable + * @param[in] nce The neighbor cache entry to set REACHABLE */ void _set_reachable(gnrc_netif_t *netif, _nib_onl_entry_t *nce); +/** + * @brief Sets a neighbor cache entry UNREACHABLE and flushes its packet queue + * + * @param[in] netif Interface to the NCE + * @param[in] nce The neighbor cache entry to set UNREACHABLE + */ +void _set_unreachable(gnrc_netif_t *netif, _nib_onl_entry_t *nce); + /** * @brief Initializes interface for address registration state machine * 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 0d4269533620..a66d9130d881 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c @@ -277,16 +277,7 @@ void _nib_nc_remove(_nib_onl_entry_t *node) evtimer_del((evtimer_t *)&_nib_evtimer, &node->addr_reg_timeout.event); #endif /* CONFIG_GNRC_IPV6_NIB_6LR */ #if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_QUEUE_PKT) - gnrc_pktqueue_t *tmp; - for (gnrc_pktqueue_t *ptr = node->pktqueue; - (ptr != NULL) && (tmp = (ptr->next), 1); - ptr = tmp) { - gnrc_pktqueue_t *entry = gnrc_pktqueue_remove(&node->pktqueue, ptr); - gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_ADDR, - entry->pkt); - gnrc_pktbuf_release_error(entry->pkt, EHOSTUNREACH); - entry->pkt = NULL; - } + _nbr_flush_pktqueue(node); #endif /* CONFIG_GNRC_IPV6_NIB_QUEUE_PKT */ /* remove from cache-out procedure */ clist_remove(&_next_removable, (clist_node_t *)node); 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 59d48591855c..336c60b2c478 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h @@ -100,6 +100,7 @@ typedef struct _nib_onl_entry { * @note Only available if @ref CONFIG_GNRC_IPV6_NIB_QUEUE_PKT != 0. */ gnrc_pktqueue_t *pktqueue; + size_t pktqueue_len; /**< Number of queued packets */ #endif /** * @brief Neighbors IPv6 address @@ -872,6 +873,38 @@ void _nib_ft_get(const _nib_offl_entry_t *dst, gnrc_ipv6_nib_ft_t *fte); int _nib_get_route(const ipv6_addr_t *dst, gnrc_pktsnip_t *ctx, gnrc_ipv6_nib_ft_t *entry); +#if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_QUEUE_PKT) +/** + * @brief Flush the packet queue of a on-link neighbor. + * + * @param node neighbor entry to be flushed + */ +void _nbr_flush_pktqueue(_nib_onl_entry_t *node); + +/** + * @brief Remove oldest packet from a on-link neighbor's packet queue. + * + * @param node neighbor entry + * + * @retval pointer to the packet entry or NULL if the queue is empty + */ +gnrc_pktqueue_t *_nbr_pop_pkt(_nib_onl_entry_t *node); + +/** + * @brief Push packet to a on-link neighbor's packet queue. + * + * @note If the neighbor is UNREACHABLE, the packet will be dropped. + * + * @note If the queue size is @ref CONFIG_GNRC_IPV6_NIB_QUEUE_PKT_CAP, + * this will @ref _nbr_pop_pkt() the oldest packet and release it. + * + * @param node neighbor entry + * @param pkt packet to be pushed + */ +void _nbr_push_pkt(_nib_onl_entry_t *node, gnrc_pktqueue_t *pkt); + +#endif + #ifdef __cplusplus } #endif diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib.c b/sys/net/gnrc/network_layer/ipv6/nib/nib.c index ebeca2b23afe..a1bfad304abf 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/nib.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/nib.c @@ -1338,7 +1338,7 @@ static bool _enqueue_for_resolve(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt, } #if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_QUEUE_PKT) - gnrc_pktqueue_add(&entry->pktqueue, queue_entry); + _nbr_push_pkt(entry, queue_entry); #else (void)entry; #endif @@ -1399,15 +1399,25 @@ static bool _resolve_addr(const ipv6_addr_t *dst, gnrc_netif_t *netif, } #endif + /* don't do multicast address resolution on 6lo */ + if (gnrc_netif_is_6ln(netif)) { + if (pkt != NULL) { + /* https://www.rfc-editor.org/rfc/rfc6775.html#section-5.6 + * A LoWPAN node is not required to maintain a minimum of one buffer + * per neighbor as specified in [RFC4861], since packets are never + * queued while waiting for address resolution. */ + gnrc_pktbuf_release(pkt); + } + + return false; + } + /* queue packet as we have to do address resolution first */ if (pkt != NULL && !_enqueue_for_resolve(netif, pkt, entry)) { return false; } - /* don't do multicast address resolution on 6lo */ - if (!gnrc_netif_is_6ln(netif)) { - _probe_nbr(entry, reset); - } + _probe_nbr(entry, reset); return false; } @@ -1733,4 +1743,54 @@ static uint32_t _handle_rio(gnrc_netif_t *netif, const ipv6_hdr_t *ipv6, return route_ltime; } + +#if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_QUEUE_PKT) +gnrc_pktqueue_t *_nbr_pop_pkt(_nib_onl_entry_t *node) +{ + if (node->pktqueue_len == 0) { + assert(node->pktqueue == NULL); + return NULL; + } + assert(node->pktqueue != NULL); + + node->pktqueue_len--; + + return gnrc_pktqueue_remove_head(&node->pktqueue); +} + +void _nbr_push_pkt(_nib_onl_entry_t *node, gnrc_pktqueue_t *pkt) +{ + if (_get_nud_state(node) == GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE) { + assert(node->pktqueue_len == 0); + + gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_ADDR, pkt->pkt); + gnrc_pktbuf_release_error(pkt->pkt, EHOSTUNREACH); + pkt->pkt = NULL; + + return; + } + + assert(node->pktqueue_len <= CONFIG_GNRC_IPV6_NIB_QUEUE_PKT_CAP); + if (node->pktqueue_len == CONFIG_GNRC_IPV6_NIB_QUEUE_PKT_CAP) { + gnrc_pktqueue_t *oldest = _nbr_pop_pkt(node); + assert(oldest != NULL); + gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_ADDR, oldest->pkt); + gnrc_pktbuf_release_error(oldest->pkt, ENOBUFS); + oldest->pkt = NULL; + } + + gnrc_pktqueue_add(&node->pktqueue, pkt); + node->pktqueue_len++; +} + +void _nbr_flush_pktqueue(_nib_onl_entry_t *node) +{ + gnrc_pktqueue_t *entry; + while ((entry = _nbr_pop_pkt(node))) { + gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_ADDR, entry->pkt); + gnrc_pktbuf_release_error(entry->pkt, EHOSTUNREACH); + entry->pkt = NULL; + } +} +#endif /* CONFIG_GNRC_IPV6_NIB_QUEUE_PKT */ /** @} */ From 83aa82981a65e80649eaa9d2d60f796c7d967fe8 Mon Sep 17 00:00:00 2001 From: Mihai Renea Date: Mon, 26 Aug 2024 13:21:50 +0200 Subject: [PATCH 2/8] fixup! gnrc/ipv6/nib: don't queue packets on 6lo neighbors and drop/flush if UNREACHABLE --- sys/net/gnrc/network_layer/ipv6/nib/nib.c | 26 +++++++++++------------ 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib.c b/sys/net/gnrc/network_layer/ipv6/nib/nib.c index a1bfad304abf..9e280d1ff563 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/nib.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/nib.c @@ -1377,6 +1377,15 @@ static bool _resolve_addr(const ipv6_addr_t *dst, gnrc_netif_t *netif, DEBUG("nib: resolve address %s by probing neighbors\n", ipv6_addr_to_str(addr_str, dst, sizeof(addr_str))); if (entry == NULL) { + /* don't do multicast address resolution on 6lo */ + if (gnrc_netif_is_6ln(netif)) { + /* https://www.rfc-editor.org/rfc/rfc6775.html#section-5.6 + * A LoWPAN node is not required to maintain a minimum of one buffer + * per neighbor as specified in [RFC4861], since packets are never + * queued while waiting for address resolution. */ + gnrc_pktbuf_release(pkt); + return false; + } entry = _nib_nc_add(dst, netif ? netif->pid : 0, GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE); if (entry == NULL) { @@ -1399,25 +1408,14 @@ static bool _resolve_addr(const ipv6_addr_t *dst, gnrc_netif_t *netif, } #endif - /* don't do multicast address resolution on 6lo */ - if (gnrc_netif_is_6ln(netif)) { - if (pkt != NULL) { - /* https://www.rfc-editor.org/rfc/rfc6775.html#section-5.6 - * A LoWPAN node is not required to maintain a minimum of one buffer - * per neighbor as specified in [RFC4861], since packets are never - * queued while waiting for address resolution. */ - gnrc_pktbuf_release(pkt); - } - - return false; - } - /* queue packet as we have to do address resolution first */ if (pkt != NULL && !_enqueue_for_resolve(netif, pkt, entry)) { return false; } - _probe_nbr(entry, reset); + if (!gnrc_netif_is_6ln(netif)) { + _probe_nbr(entry, reset); + } return false; } From c9719f641c2e818e2caed719732d0b472333367b Mon Sep 17 00:00:00 2001 From: Mihai Renea Date: Mon, 26 Aug 2024 13:43:13 +0200 Subject: [PATCH 3/8] fixup! fixup! gnrc/ipv6/nib: don't queue packets on 6lo neighbors and drop/flush if UNREACHABLE --- sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c | 2 ++ sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h | 2 +- sys/net/gnrc/network_layer/ipv6/nib/nib.c | 12 +++--------- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c b/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c index f449a221da9d..842b02aa734b 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c @@ -442,7 +442,9 @@ void _set_unreachable(gnrc_netif_t *netif, _nib_onl_entry_t *nce) DEBUG("nib: set %s to UNREACHABLE\n", ipv6_addr_to_str(addr_str, &nce->ipv6, sizeof(addr_str))); +#if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_QUEUE_PKT) _nbr_flush_pktqueue(nce); +#endif _set_nud_state(netif, nce, GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE); } 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 336c60b2c478..9c337854b07f 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h @@ -893,7 +893,7 @@ gnrc_pktqueue_t *_nbr_pop_pkt(_nib_onl_entry_t *node); /** * @brief Push packet to a on-link neighbor's packet queue. * - * @note If the neighbor is UNREACHABLE, the packet will be dropped. + * @pre Neighbor is INCOMPLETE. * * @note If the queue size is @ref CONFIG_GNRC_IPV6_NIB_QUEUE_PKT_CAP, * this will @ref _nbr_pop_pkt() the oldest packet and release it. diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib.c b/sys/net/gnrc/network_layer/ipv6/nib/nib.c index 9e280d1ff563..e13e21d636c8 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/nib.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/nib.c @@ -1758,20 +1758,14 @@ gnrc_pktqueue_t *_nbr_pop_pkt(_nib_onl_entry_t *node) void _nbr_push_pkt(_nib_onl_entry_t *node, gnrc_pktqueue_t *pkt) { - if (_get_nud_state(node) == GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE) { - assert(node->pktqueue_len == 0); - - gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_ADDR, pkt->pkt); - gnrc_pktbuf_release_error(pkt->pkt, EHOSTUNREACH); - pkt->pkt = NULL; - - return; - } + assert(_get_nud_state(node) == GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE); assert(node->pktqueue_len <= CONFIG_GNRC_IPV6_NIB_QUEUE_PKT_CAP); if (node->pktqueue_len == CONFIG_GNRC_IPV6_NIB_QUEUE_PKT_CAP) { gnrc_pktqueue_t *oldest = _nbr_pop_pkt(node); + assert(oldest != NULL); + gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_ADDR, oldest->pkt); gnrc_pktbuf_release_error(oldest->pkt, ENOBUFS); oldest->pkt = NULL; From aceabf16cb7da2d28e959604d2065f1ba125c040 Mon Sep 17 00:00:00 2001 From: Mihai Renea Date: Mon, 26 Aug 2024 14:09:18 +0200 Subject: [PATCH 4/8] fixup! fixup! fixup! gnrc/ipv6/nib: don't queue packets on 6lo neighbors and drop/flush if UNREACHABLE --- sys/net/gnrc/network_layer/ipv6/nib/nib.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib.c b/sys/net/gnrc/network_layer/ipv6/nib/nib.c index e13e21d636c8..3b7fc05eff1a 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/nib.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/nib.c @@ -1373,19 +1373,20 @@ static bool _resolve_addr(const ipv6_addr_t *dst, gnrc_netif_t *netif, return true; } + /* don't do multicast address resolution on 6lo */ + if (gnrc_netif_is_6ln(netif)) { + /* https://www.rfc-editor.org/rfc/rfc6775.html#section-5.6 + * A LoWPAN node is not required to maintain a minimum of one buffer + * per neighbor as specified in [RFC4861], since packets are never + * queued while waiting for address resolution. */ + gnrc_pktbuf_release(pkt); + return false; + } + bool reset = false; DEBUG("nib: resolve address %s by probing neighbors\n", ipv6_addr_to_str(addr_str, dst, sizeof(addr_str))); if (entry == NULL) { - /* don't do multicast address resolution on 6lo */ - if (gnrc_netif_is_6ln(netif)) { - /* https://www.rfc-editor.org/rfc/rfc6775.html#section-5.6 - * A LoWPAN node is not required to maintain a minimum of one buffer - * per neighbor as specified in [RFC4861], since packets are never - * queued while waiting for address resolution. */ - gnrc_pktbuf_release(pkt); - return false; - } entry = _nib_nc_add(dst, netif ? netif->pid : 0, GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE); if (entry == NULL) { @@ -1413,9 +1414,7 @@ static bool _resolve_addr(const ipv6_addr_t *dst, gnrc_netif_t *netif, return false; } - if (!gnrc_netif_is_6ln(netif)) { - _probe_nbr(entry, reset); - } + _probe_nbr(entry, reset); return false; } From 9c95f7ee7eb5e3610a27284a7934048ba82e5593 Mon Sep 17 00:00:00 2001 From: Mihai Renea Date: Mon, 26 Aug 2024 15:08:43 +0200 Subject: [PATCH 5/8] fixup! fixup! fixup! fixup! gnrc/ipv6/nib: don't queue packets on 6lo neighbors and drop/flush if UNREACHABLE --- sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c | 6 ++---- sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c | 2 -- sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h | 5 ++++- sys/net/gnrc/network_layer/ipv6/nib/nib.c | 4 ---- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c b/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c index 842b02aa734b..437b97b5a1d6 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c @@ -385,7 +385,7 @@ void _handle_adv_l2(gnrc_netif_t *netif, _nib_onl_entry_t *nce, nce->info &= ~GNRC_IPV6_NIB_NC_INFO_IS_ROUTER; } } -#if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_QUEUE_PKT) && MODULE_GNRC_IPV6 +#if MODULE_GNRC_IPV6 /* send queued packets */ gnrc_pktqueue_t *ptr; DEBUG("nib: Sending queued packets\n"); @@ -398,7 +398,7 @@ void _handle_adv_l2(gnrc_netif_t *netif, _nib_onl_entry_t *nce, } ptr->pkt = NULL; } -#endif /* CONFIG_GNRC_IPV6_NIB_QUEUE_PKT */ +#endif /* MODULE_GNRC_IPV6 */ if ((icmpv6->type == ICMPV6_NBR_ADV) && !_sflag_set((ndp_nbr_adv_t *)icmpv6) && (_get_nud_state(nce) == GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE) && @@ -442,9 +442,7 @@ void _set_unreachable(gnrc_netif_t *netif, _nib_onl_entry_t *nce) DEBUG("nib: set %s to UNREACHABLE\n", ipv6_addr_to_str(addr_str, &nce->ipv6, sizeof(addr_str))); -#if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_QUEUE_PKT) _nbr_flush_pktqueue(nce); -#endif _set_nud_state(netif, nce, GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE); } 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 a66d9130d881..9bee421842cb 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c @@ -276,9 +276,7 @@ void _nib_nc_remove(_nib_onl_entry_t *node) #if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_6LR) evtimer_del((evtimer_t *)&_nib_evtimer, &node->addr_reg_timeout.event); #endif /* CONFIG_GNRC_IPV6_NIB_6LR */ -#if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_QUEUE_PKT) _nbr_flush_pktqueue(node); -#endif /* CONFIG_GNRC_IPV6_NIB_QUEUE_PKT */ /* remove from cache-out procedure */ clist_remove(&_next_removable, (clist_node_t *)node); _nib_onl_clear(node); 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 9c337854b07f..9a1b55c25459 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h @@ -902,7 +902,10 @@ gnrc_pktqueue_t *_nbr_pop_pkt(_nib_onl_entry_t *node); * @param pkt packet to be pushed */ void _nbr_push_pkt(_nib_onl_entry_t *node, gnrc_pktqueue_t *pkt); - +#else +#define _nbr_flush_pktqueue(node) ((void)node) +#define _nbr_pop_pkt(node) ((void)node, NULL) +#define _nbr_push_pkt(node, pkt) ((void)node, (void)pkt) #endif #ifdef __cplusplus diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib.c b/sys/net/gnrc/network_layer/ipv6/nib/nib.c index 3b7fc05eff1a..be05b05c45a9 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/nib.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/nib.c @@ -1337,11 +1337,7 @@ static bool _enqueue_for_resolve(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt, queue_entry->pkt = gnrc_pkt_prepend(queue_entry->pkt, netif_hdr); } -#if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_QUEUE_PKT) _nbr_push_pkt(entry, queue_entry); -#else - (void)entry; -#endif return true; } From 6b7f9320ded53dfb2ccd91c4d82ea1af0f66e656 Mon Sep 17 00:00:00 2001 From: Mihai Renea Date: Mon, 26 Aug 2024 15:20:23 +0200 Subject: [PATCH 6/8] fixup! fixup! fixup! fixup! fixup! gnrc/ipv6/nib: don't queue packets on 6lo neighbors and drop/flush if UNREACHABLE --- sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9a1b55c25459..55005bd1c909 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h @@ -873,7 +873,7 @@ void _nib_ft_get(const _nib_offl_entry_t *dst, gnrc_ipv6_nib_ft_t *fte); int _nib_get_route(const ipv6_addr_t *dst, gnrc_pktsnip_t *ctx, gnrc_ipv6_nib_ft_t *entry); -#if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_QUEUE_PKT) +#if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_QUEUE_PKT) || DOXYGEN /** * @brief Flush the packet queue of a on-link neighbor. * From 2d73fe0ca9dcdd1500734f1764cc0e921d107e18 Mon Sep 17 00:00:00 2001 From: Mihai Renea Date: Thu, 29 Aug 2024 16:45:13 +0200 Subject: [PATCH 7/8] dropped per-neighbor packet queue --- sys/include/net/gnrc/ipv6/nib/conf.h | 11 --- sys/net/gnrc/network_layer/ipv6/nib/nib.c | 85 +++++++++++++++++------ 2 files changed, 64 insertions(+), 32 deletions(-) diff --git a/sys/include/net/gnrc/ipv6/nib/conf.h b/sys/include/net/gnrc/ipv6/nib/conf.h index 7d41d827242f..fddbc03e8005 100644 --- a/sys/include/net/gnrc/ipv6/nib/conf.h +++ b/sys/include/net/gnrc/ipv6/nib/conf.h @@ -176,17 +176,6 @@ extern "C" { #define CONFIG_GNRC_IPV6_NIB_QUEUE_PKT 1 #endif -#if CONFIG_GNRC_IPV6_NIB_QUEUE_PKT -/** - * @brief queue capacity for the packets waiting for address resolution, - * per neighbor. SHOULD always be smaller than @ref CONFIG_GNRC_IPV6_NIB_NUMOF - */ -#ifndef CONFIG_GNRC_IPV6_NIB_QUEUE_PKT_CAP -#define CONFIG_GNRC_IPV6_NIB_QUEUE_PKT_CAP (CONFIG_GNRC_IPV6_NIB_NUMOF > 16 ? 16 : 1) -#endif - -#endif /* CONFIG_GNRC_IPV6_NIB_QUEUE_PKT */ - /** * @brief handle NDP messages according for stateless address * auto-configuration (if activated on interface) diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib.c b/sys/net/gnrc/network_layer/ipv6/nib/nib.c index be05b05c45a9..900a54aa94f3 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/nib.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/nib.c @@ -54,7 +54,20 @@ static char addr_str[IPV6_ADDR_MAX_STR_LEN]; #if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_QUEUE_PKT) -static gnrc_pktqueue_t _queue_pool[CONFIG_GNRC_IPV6_NIB_NUMOF]; +/* +1 ensures that whenever the pool is empty, there is at least one neighbor + * with 2 or more packets, thus we can always pop a packet from that neighbor + * without leaving it's queue empty, as required by + * + * https://www.rfc-editor.org/rfc/rfc4861#section-7.2.2 + * + * While waiting for address resolution to complete, the sender MUST, + * for each neighbor, retain a small queue of packets waiting for + * address resolution to complete. The queue MUST hold at least one + * packet, and MAY contain more. However, the number of queued packets + * per neighbor SHOULD be limited to some small value. When a queue + * overflows, the new arrival SHOULD replace the oldest entry. Once + * address resolution completes, the node transmits any queued packets. */ +static gnrc_pktqueue_t _queue_pool[CONFIG_GNRC_IPV6_NIB_NUMOF + 1]; #endif /* CONFIG_GNRC_IPV6_NIB_QUEUE_PKT */ #if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_DNS) @@ -1267,19 +1280,67 @@ static void _handle_nbr_adv(gnrc_netif_t *netif, const ipv6_hdr_t *ipv6, } } +static _nib_onl_entry_t *_iter_nc_nbr(_nib_onl_entry_t const *last) +{ + while ((last = _nib_onl_iter(last))) { + if (last->mode & _NC) { + break; + } + } + + return (_nib_onl_entry_t *)last; +} + +/* This function nevers fail as doing so would force us to drop newer packets + * instead of older, thus leaving stale packets in the neighbor queues + * + * https://www.rfc-editor.org/rfc/rfc4861#section-7.2.2 + * + * While waiting for address resolution to complete, the sender MUST, + * for each neighbor, retain a small queue of packets waiting for + * address resolution to complete. The queue MUST hold at least one + * packet, and MAY contain more. However, the number of queued packets + * per neighbor SHOULD be limited to some small value. When a queue + * overflows, the new arrival SHOULD replace the oldest entry. Once + * address resolution completes, the node transmits any queued packets. */ static gnrc_pktqueue_t *_alloc_queue_entry(gnrc_pktsnip_t *pkt) { #if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_QUEUE_PKT) - for (int i = 0; i < CONFIG_GNRC_IPV6_NIB_NUMOF; i++) { + for (size_t i = 0; i < ARRAY_SIZE(_queue_pool); i++) { if (_queue_pool[i].pkt == NULL) { _queue_pool[i].pkt = pkt; return &_queue_pool[i]; } } + + /* We run out of free queue entries. Pop from the nbr with the longest queue */ + _nib_onl_entry_t *nbr = _iter_nc_nbr(NULL); + _nib_onl_entry_t *hoarder = nbr; + /* There MUST be at least a neighbor in the NC */ + assert(hoarder); + while ((nbr = _iter_nc_nbr(nbr))) { + if (nbr->pktqueue_len > hoarder->pktqueue_len) { + hoarder = nbr; + } + } + + DEBUG("nib: no free pktqueue entries, popping from %s hogging %"PRIuSIZE"\n", + ipv6_addr_to_str(addr_str, &hoarder->ipv6, sizeof(addr_str)), + hoarder->pktqueue_len); + + /* We have one more pktqueue entries than neighbors in the NC, therefore + * there must be a neighbor with two or more packets in its queue */ + assert(hoarder->pktqueue_len >= 2); + + gnrc_pktqueue_t *qentry = _nbr_pop_pkt(hoarder); + gnrc_pktbuf_release(qentry->pkt); + + qentry->pkt = pkt; + return qentry; #else (void)pkt; -#endif /* CONFIG_GNRC_IPV6_NIB_QUEUE_PKT */ return NULL; +#endif /* CONFIG_GNRC_IPV6_NIB_QUEUE_PKT */ } static bool _resolve_addr_from_nc(_nib_onl_entry_t *entry, gnrc_netif_t *netif, @@ -1317,13 +1378,6 @@ static bool _enqueue_for_resolve(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt, gnrc_pktqueue_t *queue_entry = _alloc_queue_entry(pkt); - if (queue_entry == NULL) { - DEBUG("nib: can't allocate entry for packet queue " - "dropping packet\n"); - gnrc_pktbuf_release(pkt); - return false; - } - if (netif != NULL) { gnrc_pktsnip_t *netif_hdr = gnrc_netif_hdr_build(NULL, 0, NULL, 0); @@ -1755,17 +1809,6 @@ void _nbr_push_pkt(_nib_onl_entry_t *node, gnrc_pktqueue_t *pkt) { assert(_get_nud_state(node) == GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE); - assert(node->pktqueue_len <= CONFIG_GNRC_IPV6_NIB_QUEUE_PKT_CAP); - if (node->pktqueue_len == CONFIG_GNRC_IPV6_NIB_QUEUE_PKT_CAP) { - gnrc_pktqueue_t *oldest = _nbr_pop_pkt(node); - - assert(oldest != NULL); - - gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_ADDR, oldest->pkt); - gnrc_pktbuf_release_error(oldest->pkt, ENOBUFS); - oldest->pkt = NULL; - } - gnrc_pktqueue_add(&node->pktqueue, pkt); node->pktqueue_len++; } From 37332097b8f86ab277e1bf8ad601a11e08a18799 Mon Sep 17 00:00:00 2001 From: Mihai Renea Date: Thu, 29 Aug 2024 16:54:04 +0200 Subject: [PATCH 8/8] fixup! dropped per-neighbor packet queue --- sys/net/gnrc/network_layer/ipv6/nib/nib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib.c b/sys/net/gnrc/network_layer/ipv6/nib/nib.c index 900a54aa94f3..52489a557b20 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/nib.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/nib.c @@ -1291,8 +1291,8 @@ static _nib_onl_entry_t *_iter_nc_nbr(_nib_onl_entry_t const *last) return (_nib_onl_entry_t *)last; } -/* This function nevers fail as doing so would force us to drop newer packets - * instead of older, thus leaving stale packets in the neighbor queues +/* This function never fails as doing so would force us to drop newer packets + * instead of older, thus leaving stale packets in the neighbor queues. * * https://www.rfc-editor.org/rfc/rfc4861#section-7.2.2 *