Commit 5273af6b authored by Łukasz Duda's avatar Łukasz Duda Committed by Carles Cufi
Browse files

net: ipv6: nbr: Add IPv6 reachability confirmation API



This commit introduces a new IPv6 API for positive reachability
confirmation, as specified in RFC 4861, Section 7.3.1. This feature aims
to enhance the effectiveness of the Neighbor Discovery mechanism, by
enabling upper-layer protocols to signal that the connection makes a
"forward progress".

The implementation within TCP serves as a reference. Compliance with
RFC 4861, especially Appendix E.1, was ensured by focusing on reliable
handshake and acknowledgment of new data transmissions.

Though initially integrated with TCP, the API is designed for broader
applicability. For example, it might be used by some UDP-based protocols
that can indicate two-way communication progress.

Signed-off-by: default avatarŁukasz Duda <lukasz.duda@nordicsemi.no>
parent e57ad265
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -244,4 +244,13 @@ config NET_TCP_REJECT_CONN_WITH_RST
	  If enabled, TCP stack will reject connection attempts on unbound ports
	  with TCP RST packet.

config NET_TCP_IPV6_ND_REACHABILITY_HINT
	bool "Provide a reachability hint for IPv6 Neighbor Discovery"
	depends on NET_TCP
	depends on NET_IPV6_ND
	help
	  If enabled, TCP stack will inform the IPv6 Neighbor Discovery process
	  about the active link to a specific neighbor by signaling recent
	  "forward progress" event as described in RFC 4861.

endif # NET_TCP
+23 −0
Original line number Diff line number Diff line
@@ -385,6 +385,29 @@ static inline void net_ipv6_nbr_foreach(net_nbr_cb_t cb, void *user_data)
}
#endif /* CONFIG_NET_IPV6_NBR_CACHE */

/**
 * @brief Provide a reachability hint for IPv6 Neighbor Discovery.
 *
 * This function is intended for upper-layer protocols to inform the IPv6
 * Neighbor Discovery process about the active link to a specific neighbor.
 * By signaling recent "forward progress" event, such as the reception of
 * an ACK, this function can help reducing unnecessary ND traffic as per the
 * guidelines in RFC 4861 (section 7.3).
 *
 * @param iface A pointer to the network interface.
 * @param ipv6_addr Pointer to the IPv6 address of the neighbor node.
 */
#if defined(CONFIG_NET_IPV6_ND) && defined(CONFIG_NET_NATIVE_IPV6)
void net_ipv6_nbr_reachability_hint(struct net_if *iface, const struct in6_addr *ipv6_addr);
#else
static inline void net_ipv6_nbr_reachability_hint(struct net_if *iface,
						  const struct in6_addr *ipv6_addr)
{
	ARG_UNUSED(iface);
	ARG_UNUSED(ipv6_addr);
}
#endif

/**
 * @brief Set the neighbor reachable timer.
 *
+21 −0
Original line number Diff line number Diff line
@@ -1536,6 +1536,27 @@ void net_ipv6_nbr_set_reachable_timer(struct net_if *iface,

	ipv6_nd_restart_reachable_timer(nbr, time);
}

void net_ipv6_nbr_reachability_hint(struct net_if *iface,
				    const struct in6_addr *ipv6_addr)
{
	struct net_nbr *nbr = NULL;

	nbr = nbr_lookup(&net_neighbor.table, iface, ipv6_addr);

	NET_DBG("nbr %p got rechability hint", nbr);

	if (nbr && net_ipv6_nbr_data(nbr)->state != NET_IPV6_NBR_STATE_INCOMPLETE &&
	    net_ipv6_nbr_data(nbr)->state != NET_IPV6_NBR_STATE_STATIC) {
		ipv6_nbr_set_state(nbr, NET_IPV6_NBR_STATE_REACHABLE);

		/* We might have active timer from PROBE */
		net_ipv6_nbr_data(nbr)->reachable = 0;
		net_ipv6_nbr_data(nbr)->reachable_timeout = 0;

		net_ipv6_nbr_set_reachable_timer(iface, nbr);
	}
}
#endif /* CONFIG_NET_IPV6_ND */

#if defined(CONFIG_NET_IPV6_NBR_CACHE)
+33 −0
Original line number Diff line number Diff line
@@ -956,6 +956,23 @@ static void tcp_send_timer_cancel(struct tcp *conn)
	}
}

#if defined(CONFIG_NET_TCP_IPV6_ND_REACHABILITY_HINT)

static void tcp_nbr_reachability_hint(struct tcp *conn)
{
	if (net_context_get_family(conn->context) == AF_INET6) {
		net_ipv6_nbr_reachability_hint(
			net_context_get_iface(conn->context),
			&conn->dst.sin6.sin6_addr);
	}
}

#else /* CONFIG_NET_TCP_IPV6_ND_REACHABILITY_HINT */

#define tcp_nbr_reachability_hint(...)

#endif /* CONFIG_NET_TCP_IPV6_ND_REACHABILITY_HINT */

static const char *tcp_state_to_str(enum tcp_state state, bool prefix)
{
	const char *s = NULL;
@@ -2921,6 +2938,11 @@ next_state:
			} else {
				verdict = NET_OK;
			}

			/* ACK for SYN | ACK has been received. This signilizes that
			 * the connection makes a "forward progress".
			 */
			tcp_nbr_reachability_hint(conn);
		}
		break;
	case TCP_SYN_SENT:
@@ -2957,6 +2979,11 @@ next_state:
			 * priority.
			 */
			connection_ok = true;

			/* ACK for SYN has been received. This signilizes that
			 * the connection makes a "forward progress".
			 */
			tcp_nbr_reachability_hint(conn);
		} else if (pkt) {
			net_tcp_reply_rst(pkt);
		}
@@ -3090,6 +3117,12 @@ next_state:
			conn_seq(conn, + len_acked);
			net_stats_update_tcp_seg_recv(conn->iface);

			/* Receipt of an acknowledgment that covers a sequence number
			 * not previously acknowledged indicates that the connection
			 * makes a "forward progress".
			 */
			tcp_nbr_reachability_hint(conn);

			conn_send_data_dump(conn);

			conn->send_data_retries = 0;
+25 −0
Original line number Diff line number Diff line
@@ -1657,4 +1657,29 @@ ZTEST(net_ipv6, test_no_nd_flag)
	net_if_flag_clear(iface, NET_IF_IPV6_NO_ND);
}

ZTEST(net_ipv6, test_nd_reachability_hint)
{
	struct net_nbr *nbr;

	nbr = net_ipv6_nbr_lookup(TEST_NET_IF, &peer_addr);
	zassert_not_null(nbr, "Neighbor %s not found in cache\n",
			 net_sprint_ipv6_addr(&peer_addr));

	/* Configure neighbor's state to STALE. */
	net_ipv6_nbr_data(nbr)->state = NET_IPV6_NBR_STATE_STALE;

	net_ipv6_nbr_reachability_hint(TEST_NET_IF, &peer_addr);
	zassert_equal(net_ipv6_nbr_data(nbr)->state, NET_IPV6_NBR_STATE_REACHABLE);

	/* Configure neighbor's state to PROBE. */
	net_ipv6_nbr_data(nbr)->state = NET_IPV6_NBR_STATE_PROBE;

	/* Additionally ensure that state is not changed for different interface ID. */
	net_ipv6_nbr_reachability_hint(TEST_NET_IF + 1, &peer_addr);
	zassert_equal(net_ipv6_nbr_data(nbr)->state, NET_IPV6_NBR_STATE_PROBE);

	net_ipv6_nbr_reachability_hint(TEST_NET_IF, &peer_addr);
	zassert_equal(net_ipv6_nbr_data(nbr)->state, NET_IPV6_NBR_STATE_REACHABLE);
}

ZTEST_SUITE(net_ipv6, NULL, ipv6_setup, NULL, NULL, ipv6_teardown);