Skip to content

Commit

Permalink
gnrc/ipv6/nib: don't queue packets on 6lo neighbors and drop/flush if…
Browse files Browse the repository at this point in the history
… UNREACHABLE
  • Loading branch information
derMihai committed Aug 26, 2024
1 parent 0e7636a commit f840f00
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 21 deletions.
11 changes: 11 additions & 0 deletions sys/include/net/gnrc/ipv6/nib/conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
15 changes: 11 additions & 4 deletions sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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)
{
Expand Down
12 changes: 10 additions & 2 deletions sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
11 changes: 1 addition & 10 deletions sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
33 changes: 33 additions & 0 deletions sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
70 changes: 65 additions & 5 deletions sys/net/gnrc/network_layer/ipv6/nib/nib.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 */
/** @} */

0 comments on commit f840f00

Please sign in to comment.