Commit cb50d49f authored by Stig Bjørlykke's avatar Stig Bjørlykke Committed by Fabio Baltieri
Browse files

net: icmpv6: Implement IPv6 RA Recursive DNS Server option



Handle RA RDNSS and use the first DNS server fetched. This is needed
when building IPv6 only without static DNS server IP addresses.

This implementation does not handle the lifetime, because the current
resolve logic does not have support for a DNS server lifetime.

Signed-off-by: default avatarStig Bjørlykke <stig.bjorlykke@nordicsemi.no>
parent c69731a2
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -132,6 +132,7 @@ config NET_IPV6_DAD
config NET_IPV6_RA_RDNSS
	bool "Support RA RDNSS option"
	depends on NET_IPV6_ND
	select DNS_RESOLVER
	default y
	help
	  Support Router Advertisement Recursive DNS Server option.
+8 −0
Original line number Diff line number Diff line
@@ -89,6 +89,14 @@ struct net_icmpv6_nd_opt_route_info {
	 */
} __packed;

struct net_icmpv6_nd_opt_rdnss {
	uint16_t reserved;
	uint32_t lifetime;
	/* Variable-length DNS server address follows,
	 * depending on the option length.
	 */
} __packed;

struct net_icmpv6_echo_req {
	uint16_t identifier;
	uint16_t sequence;
+61 −2
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ LOG_MODULE_DECLARE(net_ipv6, CONFIG_NET_IPV6_LOG_LEVEL);
#include <zephyr/net/net_stats.h>
#include <zephyr/net/net_context.h>
#include <zephyr/net/net_mgmt.h>
#include <zephyr/net/dns_resolve.h>
#include "net_private.h"
#include "connection.h"
#include "icmpv6.h"
@@ -2327,6 +2328,62 @@ static inline bool handle_ra_route_info(struct net_pkt *pkt, uint8_t len)
	return true;
}

#if defined(CONFIG_NET_IPV6_RA_RDNSS)
static inline bool handle_ra_rdnss(struct net_pkt *pkt, uint8_t len)
{
	NET_PKT_DATA_ACCESS_DEFINE(rdnss_access, struct net_icmpv6_nd_opt_rdnss);
	struct net_icmpv6_nd_opt_rdnss *rdnss;
	struct dns_resolve_context *ctx;
	struct sockaddr_in6 dns;
	const struct sockaddr *dns_servers[] = {
		(struct sockaddr *)&dns, NULL
	};
	size_t rdnss_size;
	int ret;

	rdnss = (struct net_icmpv6_nd_opt_rdnss *) net_pkt_get_data(pkt, &rdnss_access);
	if (!rdnss) {
		return false;
	}

	ret = net_pkt_acknowledge_data(pkt, &rdnss_access);
	if (ret < 0) {
		return false;
	}

	rdnss_size = len * 8U - 2 - sizeof(struct net_icmpv6_nd_opt_rdnss);
	if ((rdnss_size % NET_IPV6_ADDR_SIZE) != 0) {
		return false;
	}

	/* Recursive DNS servers option may present 1 or more addresses,
	 * each 16 bytes in length. DNS servers should be listed in order
	 * of preference, choose the first and skip the rest.
	 */
	ret = net_pkt_read(pkt, dns.sin6_addr.s6_addr, NET_IPV6_ADDR_SIZE);
	if (ret < 0) {
		NET_ERR("Failed to read RDNSS address, %d", ret);
		return false;
	}

	/* Skip the rest of the DNS servers. */
	if (net_pkt_skip(pkt, rdnss_size - NET_IPV6_ADDR_SIZE)) {
		NET_ERR("Failed to skip RDNSS address, %d", ret);
		return false;
	}

	/* TODO: Handle lifetime. */
	ctx = dns_resolve_get_default();
	dns.sin6_family = AF_INET6;
	ret = dns_resolve_reconfigure(ctx, NULL, dns_servers);
	if (ret < 0) {
		NET_DBG("Failed to set RDNSS resolve address: %d", ret);
	}

	return true;
}
#endif

static enum net_verdict handle_ra_input(struct net_pkt *pkt,
					struct net_ipv6_hdr *ip_hdr,
					struct net_icmp_hdr *icmp_hdr)
@@ -2477,8 +2534,10 @@ static enum net_verdict handle_ra_input(struct net_pkt *pkt,
			break;
#if defined(CONFIG_NET_IPV6_RA_RDNSS)
		case NET_ICMPV6_ND_OPT_RDNSS:
			NET_DBG("RDNSS option skipped");
			goto skip;
			if (!handle_ra_rdnss(pkt, nd_opt_hdr->len)) {
				goto drop;
			}
			break;
#endif

		case NET_ICMPV6_ND_OPT_DNSSL:
+23 −3
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ LOG_MODULE_REGISTER(net_test, CONFIG_NET_IPV6_LOG_LEVEL);
#include <zephyr/net/ethernet.h>
#include <zephyr/net/dummy.h>
#include <zephyr/net/udp.h>
#include <zephyr/net/dns_resolve.h>

#include "icmpv6.h"
#include "ipv6.h"
@@ -85,13 +86,13 @@ static const unsigned char icmpv6_ns_no_sllao[] = {
/* */
static const unsigned char icmpv6_ra[] = {
/* IPv6 header starts here */
	0x60, 0x00, 0x00, 0x00, 0x00, 0x58, 0x3a, 0xff,
	0x60, 0x00, 0x00, 0x00, 0x00, 0x70, 0x3a, 0xff,
	0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x02, 0x60, 0x97, 0xff, 0xfe, 0x07, 0x69, 0xea,
	0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
/* ICMPv6 RA header starts here */
	0x86, 0x00, 0x05, 0xd7, 0x40, 0x00, 0x07, 0x08,
	0x86, 0x00, 0xbf, 0x01, 0x40, 0x00, 0x07, 0x08,
	0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
/* SLLAO */
	0x01, 0x01, 0x00, 0x60, 0x97, 0x07, 0x69, 0xea,
@@ -106,6 +107,10 @@ static const unsigned char icmpv6_ra[] = {
	0x18, 0x03, 0x30, 0x08, 0xff, 0xff, 0xff, 0xff,
	0x20, 0x01, 0x0d, 0xb0, 0x0f, 0xff, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* Recursive DNS Server */
	0x19, 0x03, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
	0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
};

/* IPv6 hop-by-hop option in the message */
@@ -697,7 +702,14 @@ static void ra_message(void)
	struct in6_addr prefix = { { { 0x3f, 0xfe, 0x05, 0x07, 0, 0, 0, 1,
				       0, 0, 0, 0, 0, 0, 0, 0 } } };
	struct in6_addr route_prefix = { { { 0x20, 0x01, 0x0d, 0xb0, 0x0f, 0xff } } };
	struct sockaddr_in6 dns_addr = {
		.sin6_family = AF_INET6,
		.sin6_addr = { { {  0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
				    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } },
	};
	struct net_route_entry *route;
	struct dns_resolve_context *ctx;
	struct sockaddr_in6 *dns_server;

	/* We received RA message earlier, make sure that the information
	 * in that message is placed to proper prefix and lookup info.
@@ -722,6 +734,14 @@ static void ra_message(void)
	zassert_true(route->is_infinite, "Wrong lifetime set");
	zassert_equal(route->preference, NET_ROUTE_PREFERENCE_HIGH,
		      "Wrong preference set");

	/* Check if RDNSS was added correctly. */
	ctx = dns_resolve_get_default();
	zassert_equal(ctx->state, DNS_RESOLVE_CONTEXT_ACTIVE);
	dns_server = (struct sockaddr_in6 *)&ctx->servers[0].dns_server;
	zassert_equal(dns_server->sin6_family, dns_addr.sin6_family);
	zassert_mem_equal(&dns_server->sin6_addr, &dns_addr.sin6_addr,
			  sizeof(dns_addr.sin6_addr), "Wrong DNS address set");
}

ZTEST(net_ipv6, test_rs_ra_message)