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: Stig Bjørlykke <stig.bjorlykke@nordicsemi.no>
This commit is contained in:
Stig Bjørlykke 2023-02-20 08:26:15 +01:00 committed by Fabio Baltieri
parent c69731a281
commit cb50d49f33
4 changed files with 93 additions and 5 deletions

View file

@ -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.

View file

@ -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;

View file

@ -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:

View file

@ -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,19 +86,19 @@ 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,
/* MTU */
0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc,
/* Prefix info*/
/* Prefix info */
0x03, 0x04, 0x40, 0xc0, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x3f, 0xfe, 0x05, 0x07, 0x00, 0x00, 0x00, 0x01,
@ -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)