From d625fac562ba0fabfb81367ee00fbcaa647d6b36 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 11 Jun 2025 12:58:52 +0300 Subject: [PATCH 01/11] [nrf fromtree] net: dns: Allow reconfiguration with network interfaces Allow user to specify which network interfaces to bind the server to when reconfiguring the DNS system. Signed-off-by: Jukka Rissanen (cherry picked from commit 2af5dba9950c36a3ccd77e0f4b03b1af2a5057e6) Signed-off-by: Robert Lubos --- include/zephyr/net/dns_resolve.h | 26 ++++++++++++++++++++++++++ subsys/net/lib/dns/resolve.c | 20 ++++++++++++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/include/zephyr/net/dns_resolve.h b/include/zephyr/net/dns_resolve.h index 3413a008395..49043bd1733 100644 --- a/include/zephyr/net/dns_resolve.h +++ b/include/zephyr/net/dns_resolve.h @@ -524,6 +524,32 @@ int dns_resolve_reconfigure(struct dns_resolve_context *ctx, const char *servers_str[], const struct sockaddr *servers_sa[]); +/** + * @brief Reconfigure DNS resolving context with new server list and + * allowing servers to be specified to a specific network interface. + * + * @param ctx DNS context + * @param servers_str DNS server addresses using textual strings. The + * array is NULL terminated. The port number can be given in the string. + * Syntax for the server addresses with or without port numbers: + * IPv4 : 10.0.9.1 + * IPv4 + port : 10.0.9.1:5353 + * IPv6 : 2001:db8::22:42 + * IPv6 + port : [2001:db8::22:42]:5353 + * @param servers_sa DNS server addresses as struct sockaddr. The array + * is NULL terminated. Port numbers are optional in struct sockaddr, the + * default will be used if set to 0. + * @param interfaces Network interfaces to which the DNS servers are bound. + * This is an array of network interface indices. The array must be + * the same length as the servers_str and servers_sa arrays. + * + * @return 0 if ok, <0 if error. + */ +int dns_resolve_reconfigure_with_interfaces(struct dns_resolve_context *ctx, + const char *servers_str[], + const struct sockaddr *servers_sa[], + int interfaces[]); + /** * @brief Cancel a pending DNS query. * diff --git a/subsys/net/lib/dns/resolve.c b/subsys/net/lib/dns/resolve.c index 813b7cd8f6c..447d0a78322 100644 --- a/subsys/net/lib/dns/resolve.c +++ b/subsys/net/lib/dns/resolve.c @@ -1877,9 +1877,10 @@ static bool dns_servers_exists(struct dns_resolve_context *ctx, return true; } -int dns_resolve_reconfigure(struct dns_resolve_context *ctx, - const char *servers[], - const struct sockaddr *servers_sa[]) +int dns_resolve_reconfigure_with_interfaces(struct dns_resolve_context *ctx, + const char *servers[], + const struct sockaddr *servers_sa[], + int interfaces[]) { int err; @@ -1909,7 +1910,8 @@ int dns_resolve_reconfigure(struct dns_resolve_context *ctx, } } - err = dns_resolve_init_locked(ctx, servers, servers_sa, &resolve_svc, 0, NULL); + err = dns_resolve_init_locked(ctx, servers, servers_sa, + &resolve_svc, 0, interfaces); unlock: k_mutex_unlock(&ctx->lock); @@ -1917,6 +1919,16 @@ int dns_resolve_reconfigure(struct dns_resolve_context *ctx, return err; } +int dns_resolve_reconfigure(struct dns_resolve_context *ctx, + const char *servers[], + const struct sockaddr *servers_sa[]) +{ + return dns_resolve_reconfigure_with_interfaces(ctx, + servers, + servers_sa, + NULL); +} + struct dns_resolve_context *dns_resolve_get_default(void) { return &dns_default_ctx; From 6fe2ff8bfa7073e7bb941e5830587c245f92f861 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 11 Jun 2025 13:47:23 +0300 Subject: [PATCH 02/11] [nrf fromtree] net: dhcpv4: Honor network interface for DNS servers Bind the network interface to the network interface we have received the DNS servers from. This is now the default. The previous behavior can be restored by disabling the CONFIG_NET_DHCPV4_DNS_SERVER_VIA_INTERFACE option. Signed-off-by: Jukka Rissanen (cherry picked from commit 1224c5a43ad1025d5b2d2e5abbafa3e3258d2188) Signed-off-by: Robert Lubos --- subsys/net/lib/dhcpv4/Kconfig | 17 +++++++++++++++++ subsys/net/lib/dhcpv4/dhcpv4.c | 20 +++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/subsys/net/lib/dhcpv4/Kconfig b/subsys/net/lib/dhcpv4/Kconfig index 52dcfc099b5..cf7603ae65a 100644 --- a/subsys/net/lib/dhcpv4/Kconfig +++ b/subsys/net/lib/dhcpv4/Kconfig @@ -96,6 +96,23 @@ config NET_DHCPV4_OPTION_PRINT_IGNORED received and ignored. If this is not set, then we print these as unknown options. +config NET_DHCPV4_DNS_SERVER_VIA_INTERFACE + bool "Make DNS servers specific to the network interface" + depends on NET_DHCPV4_OPTION_DNS_ADDRESS + default y + help + If this is set, then if the system has multiple network interfaces + and each has DHCP enabled, then assign DNS servers received from that + network interface, to that specific interface. + If this option is not set, then any interface can be used for all + the configured DNS server addresses when doing DNS queries. + Example: We receive DNS server 192.0.2.53 DHCPv4 option from Wi-Fi + interface and DNS server 198.51.100.53 from Ethernet interface. + When this option is set, the DNS resolver will use DNS server + 192.0.2.53 when sending DNS query to the Wi-Fi interface and DNS + server 198.51.100.53 when sending DNS query to the Ethernet + interface. + endif # NET_DHCPV4 config NET_DHCPV4_SERVER diff --git a/subsys/net/lib/dhcpv4/dhcpv4.c b/subsys/net/lib/dhcpv4/dhcpv4.c index f7d7447b27d..6dedcab3cd9 100644 --- a/subsys/net/lib/dhcpv4/dhcpv4.c +++ b/subsys/net/lib/dhcpv4/dhcpv4.c @@ -1171,7 +1171,25 @@ static bool dhcpv4_parse_options(struct net_pkt *pkt, for (uint8_t i = 0; i < dns_servers_cnt; i++) { dnses[i].sin_family = AF_INET; } - status = dns_resolve_reconfigure(ctx, NULL, dns_servers); + + if (IS_ENABLED(CONFIG_NET_DHCPV4_DNS_SERVER_VIA_INTERFACE)) { + /* If we are using the interface to resolve DNS servers, + * we need to save the interface index. + */ + int ifindex = net_if_get_by_iface(iface); + int interfaces[MAX_DNS_SERVERS]; + + for (uint8_t i = 0; i < dns_servers_cnt; i++) { + interfaces[i] = ifindex; + } + + status = dns_resolve_reconfigure_with_interfaces(ctx, NULL, + dns_servers, + interfaces); + } else { + status = dns_resolve_reconfigure(ctx, NULL, dns_servers); + } + if (status < 0) { NET_DBG("options_dns, failed to set " "resolve address: %d", status); From 20c8cbeb4e0826c0fd72d7377ebe4bf77c30b57d Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 12 Jun 2025 12:40:43 +0300 Subject: [PATCH 03/11] [nrf fromtree] net: dhcpv6: Honor network interface for DNS servers Bind the network interface to the network interface we have received the DNS servers from. This is now the default. The previous behavior can be restored by disabling the CONFIG_NET_DHCPV6_DNS_SERVER_VIA_INTERFACE option. Signed-off-by: Jukka Rissanen (cherry picked from commit 248cb3d25165a87f0b72931e1e860ecefed864f0) Signed-off-by: Robert Lubos --- subsys/net/lib/dhcpv6/Kconfig | 17 +++++++++++++++++ subsys/net/lib/dhcpv6/dhcpv6.c | 20 +++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/subsys/net/lib/dhcpv6/Kconfig b/subsys/net/lib/dhcpv6/Kconfig index dad049dac77..7fda850ec5e 100644 --- a/subsys/net/lib/dhcpv6/Kconfig +++ b/subsys/net/lib/dhcpv6/Kconfig @@ -31,6 +31,23 @@ config NET_DHCPV6_OPTION_DNS_ADDRESS option from the server, and if available, use obtained information to configure DNS resolver. +config NET_DHCPV6_DNS_SERVER_VIA_INTERFACE + bool "Make DNS servers specific to the network interface" + depends on NET_DHCPV6_OPTION_DNS_ADDRESS + default y + help + If this is set, then if the system has multiple network interfaces + and each has DHCP enabled, then assign DNS servers received from that + network interface, to that specific interface. + If this option is not set, then any interface can be used for all + the configured DNS server addresses when doing DNS queries. + Example: We receive DNS server 2001:db8::1:53 DHCPv6 option from Wi-Fi + interface and DNS server 2001:db8::2:53 from Ethernet interface. + When this option is set, the DNS resolver will use DNS server + 2001:db8::1:53 when sending DNS query to the Wi-Fi interface and DNS + server 2001:db8::2:53 when sending DNS query to the Ethernet + interface. + if NET_DHCPV6 module = NET_DHCPV6 module-dep = NET_LOG diff --git a/subsys/net/lib/dhcpv6/dhcpv6.c b/subsys/net/lib/dhcpv6/dhcpv6.c index 29dd652d428..f5984273cc3 100644 --- a/subsys/net/lib/dhcpv6/dhcpv6.c +++ b/subsys/net/lib/dhcpv6/dhcpv6.c @@ -1412,7 +1412,25 @@ static int dhcpv6_handle_dns_server_option(struct net_pkt *pkt) } ctx = dns_resolve_get_default(); - status = dns_resolve_reconfigure(ctx, NULL, dns_servers); + + if (IS_ENABLED(CONFIG_NET_DHCPV6_DNS_SERVER_VIA_INTERFACE)) { + /* If we are using the interface to resolve DNS servers, + * we need to save the interface index. + */ + int ifindex = net_if_get_by_iface(net_pkt_iface(pkt)); + int interfaces[MAX_DNS_SERVERS]; + + for (uint8_t i = 0; i < server_count; i++) { + interfaces[i] = ifindex; + } + + status = dns_resolve_reconfigure_with_interfaces(ctx, NULL, + dns_servers, + interfaces); + } else { + status = dns_resolve_reconfigure(ctx, NULL, dns_servers); + } + if (status < 0) { NET_DBG("Failed to reconfigure DNS resolver from DHCPv6 " "option: %d", status); From 709c10100a99fcaa1e6ae60af6827c5efae68deb Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 12 Jun 2025 12:41:37 +0300 Subject: [PATCH 04/11] [nrf fromtree] net: dns: Properly cleanup DNS servers per network interface Make sure we cleanup only those DNS servers that belong to certain network interface when the interface goes down. Signed-off-by: Jukka Rissanen (cherry picked from commit cb9052459a5f021bdde92f2e6f1026f58e24e73f) Signed-off-by: Robert Lubos --- include/zephyr/net/dns_resolve.h | 10 + subsys/net/ip/Kconfig.stack | 1 + subsys/net/ip/ipv6_nbr.c | 6 +- subsys/net/lib/dhcpv4/dhcpv4.c | 10 + subsys/net/lib/dhcpv6/dhcpv6.c | 10 + subsys/net/lib/dns/Kconfig | 13 + subsys/net/lib/dns/resolve.c | 479 ++++++++++++++++----- tests/net/lib/dns_addremove/src/main.c | 20 +- tests/net/lib/dns_addremove/testcase.yaml | 3 + tests/net/lib/dns_dispatcher/src/main.c | 7 +- tests/net/lib/dns_dispatcher/testcase.yaml | 6 + 11 files changed, 435 insertions(+), 130 deletions(-) diff --git a/include/zephyr/net/dns_resolve.h b/include/zephyr/net/dns_resolve.h index 49043bd1733..05ee7154d1c 100644 --- a/include/zephyr/net/dns_resolve.h +++ b/include/zephyr/net/dns_resolve.h @@ -550,6 +550,16 @@ int dns_resolve_reconfigure_with_interfaces(struct dns_resolve_context *ctx, const struct sockaddr *servers_sa[], int interfaces[]); +/** + * @brief Remove servers from the DNS resolving context. + * + * @param ctx DNS context + * @param if_index Network interface from which the DNS servers are removed. + * + * @return 0 if ok, <0 if error. + */ +int dns_resolve_remove(struct dns_resolve_context *ctx, int if_index); + /** * @brief Cancel a pending DNS query. * diff --git a/subsys/net/ip/Kconfig.stack b/subsys/net/ip/Kconfig.stack index c02fe47bafc..ed8853d1394 100644 --- a/subsys/net/ip/Kconfig.stack +++ b/subsys/net/ip/Kconfig.stack @@ -17,6 +17,7 @@ config NET_TX_STACK_SIZE config NET_RX_STACK_SIZE int "RX thread stack size" + default 1792 if DNS_RESOLVER default 1500 help Set the RX thread stack size in bytes. The RX thread is waiting diff --git a/subsys/net/ip/ipv6_nbr.c b/subsys/net/ip/ipv6_nbr.c index b64a73ed537..19a0405f6ea 100644 --- a/subsys/net/ip/ipv6_nbr.c +++ b/subsys/net/ip/ipv6_nbr.c @@ -2469,6 +2469,9 @@ static inline bool handle_ra_rdnss(struct net_pkt *pkt, uint8_t len) const struct sockaddr *dns_servers[] = { (struct sockaddr *)&dns, NULL }; + int interfaces[] = { + net_if_get_by_iface(net_pkt_iface(pkt)) + }; size_t rdnss_size; int ret; @@ -2505,7 +2508,8 @@ static inline bool handle_ra_rdnss(struct net_pkt *pkt, uint8_t len) /* TODO: Handle lifetime. */ ctx = dns_resolve_get_default(); - ret = dns_resolve_reconfigure(ctx, NULL, dns_servers); + ret = dns_resolve_reconfigure_with_interfaces(ctx, NULL, dns_servers, + interfaces); if (ret < 0) { NET_DBG("Failed to set RDNSS resolve address: %d", ret); } diff --git a/subsys/net/lib/dhcpv4/dhcpv4.c b/subsys/net/lib/dhcpv4/dhcpv4.c index 6dedcab3cd9..c475b1fb4d2 100644 --- a/subsys/net/lib/dhcpv4/dhcpv4.c +++ b/subsys/net/lib/dhcpv4/dhcpv4.c @@ -1668,6 +1668,16 @@ static void dhcpv4_iface_event_handler(struct net_mgmt_event_callback *cb, if (!net_if_ipv4_addr_rm(iface, &iface->config.dhcpv4.requested_ip)) { NET_DBG("Failed to remove addr from iface"); } + + /* Remove DNS servers as interface is gone. We only need to + * do this for this interface. If using global setting, the + * DNS servers are removed automatically when the interface + * comes back up. + */ + if (IS_ENABLED(CONFIG_NET_DHCPV4_DNS_SERVER_VIA_INTERFACE)) { + dns_resolve_remove(dns_resolve_get_default(), + net_if_get_by_iface(iface)); + } } } else if (mgmt_event == NET_EVENT_IF_UP) { NET_DBG("Interface %p coming up", iface); diff --git a/subsys/net/lib/dhcpv6/dhcpv6.c b/subsys/net/lib/dhcpv6/dhcpv6.c index f5984273cc3..2e1441b0331 100644 --- a/subsys/net/lib/dhcpv6/dhcpv6.c +++ b/subsys/net/lib/dhcpv6/dhcpv6.c @@ -2217,6 +2217,16 @@ static void dhcpv6_iface_event_handler(struct net_mgmt_event_callback *cb, if (mgmt_event == NET_EVENT_IF_DOWN) { NET_DBG("Interface %p going down", iface); dhcpv6_set_timeout(iface, UINT64_MAX); + + /* Remove DNS servers as interface is gone. We only need to + * do this for this interface. If using global setting, the + * DNS servers are removed automatically when the interface + * comes back up. + */ + if (IS_ENABLED(CONFIG_NET_DHCPV6_DNS_SERVER_VIA_INTERFACE)) { + dns_resolve_remove(dns_resolve_get_default(), + net_if_get_by_iface(iface)); + } } else if (mgmt_event == NET_EVENT_IF_UP) { NET_DBG("Interface %p coming up", iface); dhcpv6_enter_state(iface, NET_DHCPV6_INIT); diff --git a/subsys/net/lib/dns/Kconfig b/subsys/net/lib/dns/Kconfig index c3f3ba6bc99..a413f04f656 100644 --- a/subsys/net/lib/dns/Kconfig +++ b/subsys/net/lib/dns/Kconfig @@ -121,6 +121,19 @@ config DNS_SERVER5 endif # DNS_SERVER_IP_ADDRESSES +config DNS_RECONFIGURE_CLEANUP + bool "Cleanup old DNS server entries when reconfiguring" + help + If calling dns_resolve_reconfigure() when new DNS servers + are being set, for example if receiving new ones from DHCP server, + remove the old entries before setting up the new ones. + If you have only one network interface, then this can be enabled. + If you have multiple network interfaces, then this should be disabled + because the later configuration update would remove the entries + set by the other network interface configuration. + The previous default in Zephyr 4.1 or earlier was to have this enabled. + The current default in Zephyr 4.2 is to disable this option. + config DNS_NUM_CONCUR_QUERIES int "Number of simultaneous DNS queries per one DNS context" default 1 diff --git a/subsys/net/lib/dns/resolve.c b/subsys/net/lib/dns/resolve.c index 447d0a78322..234d48ba843 100644 --- a/subsys/net/lib/dns/resolve.c +++ b/subsys/net/lib/dns/resolve.c @@ -81,6 +81,7 @@ NET_BUF_POOL_DEFINE(dns_qname_pool, DNS_RESOLVER_BUF_CTR, DNS_CACHE_DEFINE(dns_cache, CONFIG_DNS_RESOLVER_CACHE_MAX_ENTRIES); #endif /* CONFIG_DNS_RESOLVER_CACHE */ +static K_MUTEX_DEFINE(lock); static int init_called; static struct dns_resolve_context dns_default_ctx; @@ -414,12 +415,86 @@ static int bind_to_iface(int sock, const struct sockaddr *addr, int if_index) return ret; } +static bool is_server_name_found(struct dns_resolve_context *ctx, + const char *server, size_t server_len, + const char *iface_str) +{ + ARRAY_FOR_EACH(ctx->servers, i) { + if (ctx->servers[i].dns_server.sa_family == AF_INET || + ctx->servers[i].dns_server.sa_family == AF_INET6) { + char addr_str[INET6_ADDRSTRLEN]; + size_t addr_len; + + if (net_addr_ntop(ctx->servers[i].dns_server.sa_family, + &net_sin(&ctx->servers[i].dns_server)->sin_addr, + addr_str, sizeof(addr_str)) < 0) { + continue; + } + + addr_len = strlen(addr_str); + if (addr_len != server_len || + strncmp(addr_str, server, server_len) != 0) { + continue; + } + + if (iface_str != NULL && ctx->servers[i].if_index > 0) { + char iface_name[IFNAMSIZ]; + + net_if_get_name(net_if_get_by_index( + ctx->servers[i].if_index), + iface_name, sizeof(iface_name)); + + if (strcmp(iface_name, iface_str) != 0) { + continue; + } + } + + return true; + } + } + + return false; +} + +static bool is_server_addr_found(struct dns_resolve_context *ctx, + const struct sockaddr *addr, + int if_index) +{ + ARRAY_FOR_EACH(ctx->servers, i) { + if (ctx->servers[i].dns_server.sa_family == addr->sa_family && + memcmp(&ctx->servers[i].dns_server, addr, + sizeof(ctx->servers[i].dns_server)) == 0) { + if (if_index == 0 || + (if_index > 0 && + ctx->servers[i].if_index != if_index)) { + continue; + } + + return true; + } + } + + return false; +} + +static int get_free_slot(struct dns_resolve_context *ctx) +{ + ARRAY_FOR_EACH(ctx->servers, i) { + if (ctx->servers[i].dns_server.sa_family == 0) { + return i; + } + } + + return -ENOENT; +} + /* Must be invoked with context lock held */ static int dns_resolve_init_locked(struct dns_resolve_context *ctx, const char *servers[], const struct sockaddr *servers_sa[], const struct net_socket_service_desc *svc, - uint16_t port, int interfaces[]) + uint16_t port, int interfaces[], + bool do_cleanup) { #if defined(CONFIG_NET_IPV6) struct sockaddr_in6 local_addr6 = { @@ -445,17 +520,20 @@ static int dns_resolve_init_locked(struct dns_resolve_context *ctx, return -ENOENT; } - if (ctx->state != DNS_RESOLVE_CONTEXT_INACTIVE) { - ret = -ENOTEMPTY; - goto fail; - } + if (do_cleanup) { + if (ctx->state != DNS_RESOLVE_CONTEXT_INACTIVE) { + ret = -ENOTEMPTY; + NET_DBG("DNS resolver context is not inactive (%d)", ctx->state); + goto fail; + } - ARRAY_FOR_EACH(ctx->servers, j) { - ctx->servers[j].sock = -1; - } + ARRAY_FOR_EACH(ctx->servers, j) { + ctx->servers[j].sock = -1; + } - ARRAY_FOR_EACH(ctx->fds, j) { - ctx->fds[j].fd = -1; + ARRAY_FOR_EACH(ctx->fds, j) { + ctx->fds[j].fd = -1; + } } /* If user has provided a list of servers in string format, then @@ -464,78 +542,146 @@ static int dns_resolve_init_locked(struct dns_resolve_context *ctx, * The interfaces parameter should point to an array that is the * the same length as the servers_sa parameter array. */ - if (servers) { - for (i = 0; idx < SERVER_COUNT && servers[i]; i++) { - const char *iface_str; - size_t server_len; - - struct sockaddr *addr = &ctx->servers[idx].dns_server; - - iface_str = strstr(servers[i], "%"); - if (iface_str) { - server_len = iface_str - servers[i]; - iface_str++; + for (i = 0; servers != NULL && idx < SERVER_COUNT && servers[i] != NULL; i++) { + const char *iface_str; + size_t server_len; + struct sockaddr *addr; + bool found; + + iface_str = strstr(servers[i], "%"); + if (iface_str != NULL) { + server_len = iface_str - servers[i]; + iface_str++; + + if (server_len == 0) { + NET_DBG("Empty server name"); + continue; + } - if (server_len == 0) { - NET_DBG("Empty server name"); - continue; - } + found = is_server_name_found(ctx, servers[i], + server_len, iface_str); + if (found) { + NET_DBG("Server %.*s already exists", + (int)server_len, servers[i]); + continue; + } - /* Skip empty interface name */ - if (iface_str[0] == '\0') { - ctx->servers[idx].if_index = 0; - iface_str = NULL; - } else { - ctx->servers[idx].if_index = - net_if_get_by_name(iface_str); - } + /* Figure out if there are free slots where to add + * the server. + */ + idx = get_free_slot(ctx); + if (idx < 0) { + NET_DBG("No free slots for server %.*s", + (int)server_len, servers[i]); + break; + } - } else { - server_len = strlen(servers[i]); + /* Skip empty interface name */ + if (iface_str[0] == '\0') { ctx->servers[idx].if_index = 0; + iface_str = NULL; + } else { + ctx->servers[idx].if_index = + net_if_get_by_name(iface_str); } - (void)memset(addr, 0, sizeof(*addr)); - - ret = net_ipaddr_parse(servers[i], server_len, addr); - if (!ret) { - if (servers[i] != NULL && servers[i][0] != '\0') { - NET_DBG("Invalid server address %.*s", - (int)server_len, servers[i]); - } + } else { + server_len = strlen(servers[i]); + if (server_len == 0) { + NET_DBG("Empty server name"); + continue; + } + found = is_server_name_found(ctx, servers[i], + server_len, NULL); + if (found) { + NET_DBG("Server %.*s already exists", + (int)server_len, servers[i]); continue; } - dns_postprocess_server(ctx, idx); + idx = get_free_slot(ctx); + if (idx < 0) { + NET_DBG("No free slots for server %.*s", + (int)server_len, servers[i]); + break; + } + } + + addr = &ctx->servers[idx].dns_server; + + (void)memset(addr, 0, sizeof(*addr)); + + ret = net_ipaddr_parse(servers[i], server_len, addr); + if (!ret) { + if (servers[i][0] != '\0') { + NET_DBG("Invalid server address %.*s", + (int)server_len, servers[i]); + } - NET_DBG("[%d] %.*s%s%s%s%s", i, (int)server_len, servers[i], - IS_ENABLED(CONFIG_MDNS_RESOLVER) ? - (ctx->servers[i].is_mdns ? " mDNS" : "") : "", - IS_ENABLED(CONFIG_LLMNR_RESOLVER) ? - (ctx->servers[i].is_llmnr ? " LLMNR" : "") : "", - iface_str != NULL ? " via " : "", - iface_str != NULL ? iface_str : ""); - idx++; + continue; } + + dns_postprocess_server(ctx, idx); + + NET_DBG("[%d] %.*s%s%s%s%s", i, (int)server_len, servers[i], + IS_ENABLED(CONFIG_MDNS_RESOLVER) ? + (ctx->servers[i].is_mdns ? " mDNS" : "") : "", + IS_ENABLED(CONFIG_LLMNR_RESOLVER) ? + (ctx->servers[i].is_llmnr ? " LLMNR" : "") : "", + iface_str != NULL ? " via " : "", + iface_str != NULL ? iface_str : ""); + idx++; } - if (servers_sa) { - for (i = 0; idx < SERVER_COUNT && servers_sa[i]; i++) { - memcpy(&ctx->servers[idx].dns_server, servers_sa[i], - sizeof(ctx->servers[idx].dns_server)); + for (i = 0; servers_sa != NULL && idx < SERVER_COUNT && servers_sa[i] != NULL; i++) { + char iface_str[IFNAMSIZ] = { 0 }; + bool found; - if (interfaces != NULL) { - ctx->servers[idx].if_index = interfaces[idx]; - } + found = is_server_addr_found(ctx, servers_sa[i], interfaces[i]); + if (found) { + NET_DBG("Server %s already exists", + net_sprint_addr(ctx->servers[i].dns_server.sa_family, + &net_sin(&ctx->servers[i].dns_server)->sin_addr)); + continue; + } - dns_postprocess_server(ctx, idx); - idx++; + /* Figure out if there are free slots where to add the server. + */ + idx = get_free_slot(ctx); + if (idx < 0) { + NET_DBG("No free slots for server %s", + net_sprint_addr(ctx->servers[i].dns_server.sa_family, + &net_sin(&ctx->servers[i].dns_server)->sin_addr)); + break; + } + + memcpy(&ctx->servers[idx].dns_server, servers_sa[i], + sizeof(ctx->servers[idx].dns_server)); + + if (interfaces != NULL) { + ctx->servers[idx].if_index = interfaces[i]; + + net_if_get_name(net_if_get_by_index(ctx->servers[idx].if_index), + iface_str, sizeof(iface_str)); } + + dns_postprocess_server(ctx, idx); + + NET_DBG("[%d] %s%s%s%s%s", i, + net_sprint_addr(servers_sa[i]->sa_family, + &net_sin(servers_sa[i])->sin_addr), + IS_ENABLED(CONFIG_MDNS_RESOLVER) ? + (ctx->servers[i].is_mdns ? " mDNS" : "") : "", + IS_ENABLED(CONFIG_LLMNR_RESOLVER) ? + (ctx->servers[i].is_llmnr ? " LLMNR" : "") : "", + interfaces != NULL ? " via " : "", + interfaces != NULL ? iface_str : ""); + idx++; } for (i = 0, count = 0; - i < SERVER_COUNT && ctx->servers[i].dns_server.sa_family; i++) { + i < SERVER_COUNT && ctx->servers[i].dns_server.sa_family != 0; i++) { if (ctx->servers[i].dns_server.sa_family == AF_INET6) { #if defined(CONFIG_NET_IPV6) @@ -571,6 +717,16 @@ static int dns_resolve_init_locked(struct dns_resolve_context *ctx, goto fail; } + if (ctx->servers[i].sock >= 0) { + /* Socket already exists, so skip it */ + NET_DBG("Socket %d already exists for %s", + ctx->servers[i].sock, + net_sprint_addr(ctx->servers[i].dns_server.sa_family, + &net_sin(&ctx->servers[i].dns_server)->sin_addr)); + count++; + continue; + } + ret = zsock_socket(ctx->servers[i].dns_server.sa_family, SOCK_DGRAM, IPPROTO_UDP); if (ret < 0) { @@ -589,6 +745,7 @@ static int dns_resolve_init_locked(struct dns_resolve_context *ctx, if (ret < 0) { zsock_close(ctx->servers[i].sock); ctx->servers[i].sock = -1; + ctx->servers[i].dns_server.sa_family = 0; continue; } @@ -637,6 +794,8 @@ static int dns_resolve_init_locked(struct dns_resolve_context *ctx, if (ret < 0) { NET_DBG("Cannot set %s to socket (%d)", "polling", ret); zsock_close(ctx->servers[i].sock); + ctx->servers[i].sock = -1; + ctx->servers[i].dns_server.sa_family = 0; continue; } @@ -695,20 +854,28 @@ int dns_resolve_init_with_svc(struct dns_resolve_context *ctx, const char *serve const struct net_socket_service_desc *svc, uint16_t port, int interfaces[]) { + int ret; + if (!ctx) { return -ENOENT; } - (void)memset(ctx, 0, sizeof(*ctx)); + k_mutex_lock(&lock, K_FOREVER); - (void)k_mutex_init(&ctx->lock); - ctx->state = DNS_RESOLVE_CONTEXT_INACTIVE; + /* Do cleanup only if we are starting the context for the first time */ + if (init_called == 0) { + (void)memset(ctx, 0, sizeof(*ctx)); - /* As this function is called only once during system init, there is no - * reason to acquire lock. - */ - return dns_resolve_init_locked(ctx, servers, servers_sa, svc, port, - interfaces); + (void)k_mutex_init(&ctx->lock); + ctx->state = DNS_RESOLVE_CONTEXT_INACTIVE; + } + + ret = dns_resolve_init_locked(ctx, servers, servers_sa, svc, port, + interfaces, true); + + k_mutex_unlock(&lock); + + return ret; } int dns_resolve_init(struct dns_resolve_context *ctx, const char *servers[], @@ -1739,10 +1906,51 @@ int dns_resolve_name(struct dns_resolve_context *ctx, user_data, timeout, true); } +static int dns_server_close(struct dns_resolve_context *ctx, + int server_idx) +{ + struct net_if *iface; + + if (ctx->servers[server_idx].sock < 0) { + return -ENOENT; + } + + (void)dns_dispatcher_unregister(&ctx->servers[server_idx].dispatcher); + + if (ctx->servers[server_idx].dns_server.sa_family == AF_INET6) { + iface = net_if_ipv6_select_src_iface( + &net_sin6(&ctx->servers[server_idx].dns_server)->sin6_addr); + } else { + iface = net_if_ipv4_select_src_iface( + &net_sin(&ctx->servers[server_idx].dns_server)->sin_addr); + } + + if (IS_ENABLED(CONFIG_NET_MGMT_EVENT_INFO)) { + net_mgmt_event_notify_with_info( + NET_EVENT_DNS_SERVER_DEL, + iface, + (void *)&ctx->servers[server_idx].dns_server, + sizeof(struct sockaddr)); + } else { + net_mgmt_event_notify(NET_EVENT_DNS_SERVER_DEL, iface); + } + + zsock_close(ctx->servers[server_idx].sock); + + ctx->servers[server_idx].sock = -1; + ctx->servers[server_idx].dns_server.sa_family = 0; + + ARRAY_FOR_EACH(ctx->fds, j) { + ctx->fds[j].fd = -1; + } + + return 0; +} + /* Must be invoked with context lock held */ static int dns_resolve_close_locked(struct dns_resolve_context *ctx) { - int i; + int i, ret; if (ctx->state != DNS_RESOLVE_CONTEXT_ACTIVE) { return -ENOENT; @@ -1762,44 +1970,10 @@ static int dns_resolve_close_locked(struct dns_resolve_context *ctx) k_mutex_unlock(&ctx->lock); for (i = 0; i < SERVER_COUNT; i++) { - struct net_if *iface; - - if (ctx->servers[i].sock < 0) { - continue; - } - - (void)dns_dispatcher_unregister(&ctx->servers[i].dispatcher); - - if (ctx->servers[i].dns_server.sa_family == AF_INET6) { - iface = net_if_ipv6_select_src_iface( - &net_sin6(&ctx->servers[i].dns_server)->sin6_addr); - } else { - iface = net_if_ipv4_select_src_iface( - &net_sin(&ctx->servers[i].dns_server)->sin_addr); - } - - if (IS_ENABLED(CONFIG_NET_MGMT_EVENT_INFO)) { - net_mgmt_event_notify_with_info( - NET_EVENT_DNS_SERVER_DEL, - iface, - (void *)&ctx->servers[i].dns_server, - sizeof(struct sockaddr)); - } else { - net_mgmt_event_notify(NET_EVENT_DNS_SERVER_DEL, - iface); - } - - zsock_close(ctx->servers[i].sock); - - ARRAY_FOR_EACH(ctx->fds, j) { - if (ctx->fds[j].fd == ctx->servers[i].sock) { - ctx->fds[j].fd = -1; - } + ret = dns_server_close(ctx, i); + if (ret < 0) { + NET_DBG("Cannot close DNS server %d (%d)", i, ret); } - - (void)dns_dispatcher_unregister(&ctx->servers[i].dispatcher); - - ctx->servers[i].sock = -1; } if (--init_called <= 0) { @@ -1877,10 +2051,11 @@ static bool dns_servers_exists(struct dns_resolve_context *ctx, return true; } -int dns_resolve_reconfigure_with_interfaces(struct dns_resolve_context *ctx, - const char *servers[], - const struct sockaddr *servers_sa[], - int interfaces[]) +static int do_dns_resolve_reconfigure(struct dns_resolve_context *ctx, + const char *servers[], + const struct sockaddr *servers_sa[], + int interfaces[], + bool do_close) { int err; @@ -1888,6 +2063,7 @@ int dns_resolve_reconfigure_with_interfaces(struct dns_resolve_context *ctx, return -ENOENT; } + k_mutex_lock(&lock, K_FOREVER); k_mutex_lock(&ctx->lock, K_FOREVER); if (dns_servers_exists(ctx, servers, servers_sa)) { @@ -1901,32 +2077,99 @@ int dns_resolve_reconfigure_with_interfaces(struct dns_resolve_context *ctx, goto unlock; } - if (ctx->state == DNS_RESOLVE_CONTEXT_ACTIVE) { + if (ctx->state == DNS_RESOLVE_CONTEXT_ACTIVE && + (do_close || init_called == 0)) { dns_resolve_cancel_all(ctx); err = dns_resolve_close_locked(ctx); if (err) { goto unlock; } + + /* Make sure we do fresh start once */ + do_close = true; } err = dns_resolve_init_locked(ctx, servers, servers_sa, - &resolve_svc, 0, interfaces); + &resolve_svc, 0, interfaces, + do_close); unlock: k_mutex_unlock(&ctx->lock); + k_mutex_unlock(&lock); return err; } +int dns_resolve_reconfigure_with_interfaces(struct dns_resolve_context *ctx, + const char *servers[], + const struct sockaddr *servers_sa[], + int interfaces[]) +{ + return do_dns_resolve_reconfigure(ctx, + servers, + servers_sa, + interfaces, + IS_ENABLED(CONFIG_DNS_RECONFIGURE_CLEANUP) ? + true : false); +} + int dns_resolve_reconfigure(struct dns_resolve_context *ctx, const char *servers[], const struct sockaddr *servers_sa[]) { - return dns_resolve_reconfigure_with_interfaces(ctx, - servers, - servers_sa, - NULL); + return do_dns_resolve_reconfigure(ctx, + servers, + servers_sa, + NULL, + IS_ENABLED(CONFIG_DNS_RECONFIGURE_CLEANUP) ? + true : false); +} + +int dns_resolve_remove(struct dns_resolve_context *ctx, int if_index) +{ + int i; + int ret = -ENOENT; + int st = 0; + + if (!ctx) { + return -ENOENT; + } + + if (if_index <= 0) { + /* If network interface index is 0, then do nothing. + * If your want to remove all the servers, then just call + * dns_resolve_close() directly. + */ + return -EINVAL; + } + + k_mutex_lock(&ctx->lock, K_FOREVER); + + for (i = 0; i < SERVER_COUNT; i++) { + if (ctx->servers[i].if_index != if_index) { + continue; + } + + ctx->servers[i].if_index = 0; + + /* See comment in dns_resolve_close_locked() about + * releasing the lock before closing the server socket. + */ + k_mutex_unlock(&ctx->lock); + + ret = dns_server_close(ctx, i); + + k_mutex_lock(&ctx->lock, K_FOREVER); + + if (ret < 0) { + st = ret; + } + } + + k_mutex_unlock(&ctx->lock); + + return st; } struct dns_resolve_context *dns_resolve_get_default(void) diff --git a/tests/net/lib/dns_addremove/src/main.c b/tests/net/lib/dns_addremove/src/main.c index eb8d25a5fa5..3ff9bb2aabe 100644 --- a/tests/net/lib/dns_addremove/src/main.c +++ b/tests/net/lib/dns_addremove/src/main.c @@ -470,15 +470,17 @@ ZTEST(dns_addremove, test_dns_reconfigure_callback) zassert_equal(ret, 0, "Cannot reconfigure DNS server"); /* Wait for DNS removed callback after reconfiguring DNS */ - if (k_sem_take(&dns_removed, WAIT_TIME)) { - zassert_true(false, - "Timeout while waiting for DNS removed callback"); - } - - /* Wait for DNS added callback after reconfiguring DNS */ - if (k_sem_take(&dns_added, WAIT_TIME)) { - zassert_true(false, - "Timeout while waiting for DNS added callback"); + if (IS_ENABLED(CONFIG_DNS_RECONFIGURE_CLEANUP)) { + if (k_sem_take(&dns_removed, WAIT_TIME)) { + zassert_true(false, + "Timeout while waiting for DNS removed callback"); + } + + /* Wait for DNS added callback after reconfiguring DNS */ + if (k_sem_take(&dns_added, WAIT_TIME)) { + zassert_true(false, + "Timeout while waiting for DNS added callback"); + } } ret = dns_resolve_close(&resv_ipv4); diff --git a/tests/net/lib/dns_addremove/testcase.yaml b/tests/net/lib/dns_addremove/testcase.yaml index 5551a41cfba..a6f56003f46 100644 --- a/tests/net/lib/dns_addremove/testcase.yaml +++ b/tests/net/lib/dns_addremove/testcase.yaml @@ -14,3 +14,6 @@ tests: net.dns.no_ipv4: extra_configs: - CONFIG_NET_IPV4=n + net.dns.addremove.reconfigure_cleanup: + extra_configs: + - CONFIG_DNS_RECONFIGURE_CLEANUP=y diff --git a/tests/net/lib/dns_dispatcher/src/main.c b/tests/net/lib/dns_dispatcher/src/main.c index 21e60466066..e0353e8ac1f 100644 --- a/tests/net/lib/dns_dispatcher/src/main.c +++ b/tests/net/lib/dns_dispatcher/src/main.c @@ -175,11 +175,14 @@ static void *test_init(void) ZTEST(dns_dispatcher, test_dns_dispatcher) { struct dns_resolve_context *ctx; - int sock1, sock2 = -1; + int ret, sock1, sock2 = -1; ctx = dns_resolve_get_default(); - dns_resolve_init_default(ctx); + dns_resolve_close(ctx); + + ret = dns_resolve_init_default(ctx); + zassert_equal(ret, 0, "Cannot initialize DNS resolver (%d)", ret); sock1 = ctx->servers[0].sock; diff --git a/tests/net/lib/dns_dispatcher/testcase.yaml b/tests/net/lib/dns_dispatcher/testcase.yaml index 314097ece66..b3ffd7c6479 100644 --- a/tests/net/lib/dns_dispatcher/testcase.yaml +++ b/tests/net/lib/dns_dispatcher/testcase.yaml @@ -8,3 +8,9 @@ common: tests: net.dns.dispatch: min_ram: 21 + net.dns.dispatch.ctx_cleanup: + extra_configs: + - CONFIG_DNS_RECONFIGURE_CLEANUP=y + net.dns.dispatch.ctx_no_cleanup: + extra_configs: + - CONFIG_DNS_RECONFIGURE_CLEANUP=n From 12f727467969a2bfb0a3941ca4c927fad1d84d29 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 12 Jun 2025 14:36:23 +0300 Subject: [PATCH 05/11] [nrf fromtree] net: dns: Add uninitialized state to resolver context Make sure that uninitialized state is 0 so that we can catch when the context is not initialized properly. Signed-off-by: Jukka Rissanen (cherry picked from commit 514cdd6b3a21e32565634316751ecc6ec72c17c6) Signed-off-by: Robert Lubos --- include/zephyr/net/dns_resolve.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/zephyr/net/dns_resolve.h b/include/zephyr/net/dns_resolve.h index 05ee7154d1c..62781b51892 100644 --- a/include/zephyr/net/dns_resolve.h +++ b/include/zephyr/net/dns_resolve.h @@ -320,6 +320,7 @@ typedef void (*dns_resolve_cb_t)(enum dns_resolve_status status, /** @cond INTERNAL_HIDDEN */ enum dns_resolve_context_state { + DNS_RESOLVE_CONTEXT_UNINITIALIZED = 0, DNS_RESOLVE_CONTEXT_ACTIVE, DNS_RESOLVE_CONTEXT_DEACTIVATING, DNS_RESOLVE_CONTEXT_INACTIVE, From defbc90a3d62c9e95073bbab90746d0e7cd15efe Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 12 Jun 2025 13:18:28 +0300 Subject: [PATCH 06/11] [nrf fromtree] net: shell: conn: Print packet socket information correctly The "net conn" command printed AF_PACKET socket as AF_UNK(3). Fix this by printing "AF_PACKET" in this case. Signed-off-by: Jukka Rissanen (cherry picked from commit 57c452fb79a6189fb05eb9e984ad052a74d20012) Signed-off-by: Robert Lubos --- subsys/net/lib/shell/conn.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/subsys/net/lib/shell/conn.c b/subsys/net/lib/shell/conn.c index 32dc32fcf44..ca852f1d15d 100644 --- a/subsys/net/lib/shell/conn.c +++ b/subsys/net/lib/shell/conn.c @@ -89,6 +89,8 @@ static void conn_handler_cb(struct net_conn *conn, void *user_data) } else if (conn->local_addr.sa_family == AF_UNSPEC) { snprintk(addr_local, sizeof(addr_local), "AF_UNSPEC"); + } else if (conn->local_addr.sa_family == AF_PACKET) { + snprintk(addr_local, sizeof(addr_local), "AF_PACKET"); } else { snprintk(addr_local, sizeof(addr_local), "AF_UNK(%d)", conn->local_addr.sa_family); From fff41ed615f7313b9303e86075bc5e2037779c71 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 12 Jun 2025 16:53:49 +0300 Subject: [PATCH 07/11] [nrf fromtree] drivers: wifi: esp_at: Bind DNS to device net interface Make sure received DNS server information is bound to the wifi network interface. Signed-off-by: Jukka Rissanen (cherry picked from commit 0e57844b2d9544b096b9a3fa22bb4bbe91965009) Signed-off-by: Robert Lubos --- drivers/wifi/esp_at/esp.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/wifi/esp_at/esp.c b/drivers/wifi/esp_at/esp.c index c68ea98ee0f..25c47412c4c 100644 --- a/drivers/wifi/esp_at/esp.c +++ b/drivers/wifi/esp_at/esp.c @@ -490,18 +490,23 @@ static void esp_dns_work(struct k_work *work) struct dns_resolve_context *dnsctx; struct sockaddr_in *addrs = data->dns_addresses; const struct sockaddr *dns_servers[ESP_MAX_DNS + 1] = {}; + int interfaces[ESP_MAX_DNS]; size_t i; - int err; + int err, ifindex; + + ifindex = net_if_get_by_ifindex(data->net_iface); for (i = 0; i < ESP_MAX_DNS; i++) { if (!addrs[i].sin_addr.s_addr) { break; } dns_servers[i] = (struct sockaddr *) &addrs[i]; + interfaces[i] = ifindex; } dnsctx = dns_resolve_get_default(); - err = dns_resolve_reconfigure(dnsctx, NULL, dns_servers); + err = dns_resolve_reconfigure_with_interfaces(dnsctx, NULL, dns_servers, + interfaces); if (err) { LOG_ERR("Could not set DNS servers: %d", err); } From 80b11fab4b21796cafdb1099b40c7ebf258acd06 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Mon, 16 Jun 2025 15:05:28 +0300 Subject: [PATCH 08/11] [nrf fromtree] net: ppp: Bind DNS to device ppp network interface Make sure received DNS server information is bound to the ppp network interface. Signed-off-by: Jukka Rissanen (cherry picked from commit 8704b3add72df1d1703c469b81e10ede68dce310) Signed-off-by: Robert Lubos --- subsys/net/l2/ppp/ipcp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/subsys/net/l2/ppp/ipcp.c b/subsys/net/l2/ppp/ipcp.c index b345802e68e..e3e377adefd 100644 --- a/subsys/net/l2/ppp/ipcp.c +++ b/subsys/net/l2/ppp/ipcp.c @@ -327,6 +327,8 @@ static void ipcp_set_dns_servers(struct ppp_fsm *fsm) (struct sockaddr *) &dns2, NULL }; + int ifindex = net_if_get_by_iface(ctx->iface); + int interfaces[2] = { ifindex, ifindex }; int ret; if (!dns1.sin_addr.s_addr) { @@ -338,7 +340,8 @@ static void ipcp_set_dns_servers(struct ppp_fsm *fsm) } dnsctx = dns_resolve_get_default(); - ret = dns_resolve_reconfigure(dnsctx, NULL, dns_servers); + ret = dns_resolve_reconfigure_with_interfaces(dnsctx, NULL, dns_servers, + interfaces); if (ret < 0) { NET_ERR("Could not set DNS servers"); return; From 38fbb459b60789bb0a4e71280590d7193a42a272 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 24 Jun 2025 14:01:51 +0300 Subject: [PATCH 09/11] [nrf fromtree] net: dns: Save info about source when configuring DNS servers Remember which DNS server was added by a source like DHCPv4 or v6 message. This will allow system to remove DNS servers that were added by that source. Then when stopping for example DHCP, we can remove those specific DNS servers and not leaving DNS servers hanging in the system. Signed-off-by: Jukka Rissanen (cherry picked from commit cdc6c324d785742291460e742be261cb37705d65) Signed-off-by: Robert Lubos --- drivers/modem/hl7800.c | 3 +- drivers/wifi/esp_at/esp.c | 3 +- include/zephyr/net/dns_resolve.h | 38 +++++++++++++++- subsys/net/ip/ipv6_nbr.c | 3 +- subsys/net/l2/ppp/ipcp.c | 3 +- subsys/net/lib/dhcpv4/dhcpv4.c | 6 ++- subsys/net/lib/dhcpv6/dhcpv6.c | 6 ++- subsys/net/lib/dns/resolve.c | 61 +++++++++++++++++++++----- tests/net/lib/dns_addremove/src/main.c | 3 +- 9 files changed, 103 insertions(+), 23 deletions(-) diff --git a/drivers/modem/hl7800.c b/drivers/modem/hl7800.c index 58e4f663838..33064290639 100644 --- a/drivers/modem/hl7800.c +++ b/drivers/modem/hl7800.c @@ -1966,7 +1966,8 @@ static void dns_work_cb(struct k_work *work) } } else { LOG_DBG("Reconfiguring DNS resolver"); - ret = dns_resolve_reconfigure(dnsCtx, (const char **)dns_servers_str, NULL); + ret = dns_resolve_reconfigure(dnsCtx, (const char **)dns_servers_str, NULL, + DNS_SOURCE_MANUAL); if (ret < 0) { LOG_ERR("dns_resolve_reconfigure fail (%d)", ret); retry = true; diff --git a/drivers/wifi/esp_at/esp.c b/drivers/wifi/esp_at/esp.c index 25c47412c4c..427d548350b 100644 --- a/drivers/wifi/esp_at/esp.c +++ b/drivers/wifi/esp_at/esp.c @@ -506,7 +506,8 @@ static void esp_dns_work(struct k_work *work) dnsctx = dns_resolve_get_default(); err = dns_resolve_reconfigure_with_interfaces(dnsctx, NULL, dns_servers, - interfaces); + interfaces, + DNS_SOURCE_MANUAL); if (err) { LOG_ERR("Could not set DNS servers: %d", err); } diff --git a/include/zephyr/net/dns_resolve.h b/include/zephyr/net/dns_resolve.h index 62781b51892..e8789e8621d 100644 --- a/include/zephyr/net/dns_resolve.h +++ b/include/zephyr/net/dns_resolve.h @@ -42,6 +42,24 @@ enum dns_query_type { DNS_QUERY_TYPE_AAAA = 28 }; +/** + * Entity that added the DNS server. + */ +enum dns_server_source { + /** Source is unknown */ + DNS_SOURCE_UNKNOWN = 0, + /** Server information is added manually, for example by an application */ + DNS_SOURCE_MANUAL, + /** Server information is from DHCPv4 server */ + DNS_SOURCE_DHCPV4, + /** Server information is from DHCPv6 server */ + DNS_SOURCE_DHCPV6, + /** Server information is from IPv6 SLAAC (router advertisement) */ + DNS_SOURCE_IPV6_RA, + /** Server information is from PPP */ + DNS_SOURCE_PPP, +}; + /** Max size of the resolved name. */ #ifndef DNS_MAX_NAME_SIZE #define DNS_MAX_NAME_SIZE 20 @@ -345,6 +363,9 @@ struct dns_resolve_context { */ int if_index; + /** Source of the DNS server, e.g., manual, DHCPv4/6, etc. */ + enum dns_server_source source; + /** Is this server mDNS one */ uint8_t is_mdns : 1; @@ -518,12 +539,14 @@ int dns_resolve_close(struct dns_resolve_context *ctx); * @param servers_sa DNS server addresses as struct sockaddr. The array * is NULL terminated. Port numbers are optional in struct sockaddr, the * default will be used if set to 0. + * @param source Source of the DNS servers, e.g., manual, DHCPv4/6, etc. * * @return 0 if ok, <0 if error. */ int dns_resolve_reconfigure(struct dns_resolve_context *ctx, const char *servers_str[], - const struct sockaddr *servers_sa[]); + const struct sockaddr *servers_sa[], + enum dns_server_source source); /** * @brief Reconfigure DNS resolving context with new server list and @@ -543,13 +566,15 @@ int dns_resolve_reconfigure(struct dns_resolve_context *ctx, * @param interfaces Network interfaces to which the DNS servers are bound. * This is an array of network interface indices. The array must be * the same length as the servers_str and servers_sa arrays. + * @param source Source of the DNS servers, e.g., manual, DHCPv4/6, etc. * * @return 0 if ok, <0 if error. */ int dns_resolve_reconfigure_with_interfaces(struct dns_resolve_context *ctx, const char *servers_str[], const struct sockaddr *servers_sa[], - int interfaces[]); + int interfaces[], + enum dns_server_source source); /** * @brief Remove servers from the DNS resolving context. @@ -701,6 +726,15 @@ static inline int dns_cancel_addr_info(uint16_t dns_id) /** @cond INTERNAL_HIDDEN */ +/** + * @brief Get string representation of the DNS server source. + * + * @param source Source of the DNS server. + * + * @return String representation of the DNS server source. + */ +const char *dns_get_source_str(enum dns_server_source source); + /** * @brief Initialize DNS subsystem. */ diff --git a/subsys/net/ip/ipv6_nbr.c b/subsys/net/ip/ipv6_nbr.c index 19a0405f6ea..1cf7e53ad48 100644 --- a/subsys/net/ip/ipv6_nbr.c +++ b/subsys/net/ip/ipv6_nbr.c @@ -2509,7 +2509,8 @@ static inline bool handle_ra_rdnss(struct net_pkt *pkt, uint8_t len) /* TODO: Handle lifetime. */ ctx = dns_resolve_get_default(); ret = dns_resolve_reconfigure_with_interfaces(ctx, NULL, dns_servers, - interfaces); + interfaces, + DNS_SOURCE_IPV6_RA); if (ret < 0) { NET_DBG("Failed to set RDNSS resolve address: %d", ret); } diff --git a/subsys/net/l2/ppp/ipcp.c b/subsys/net/l2/ppp/ipcp.c index e3e377adefd..35b7bac018b 100644 --- a/subsys/net/l2/ppp/ipcp.c +++ b/subsys/net/l2/ppp/ipcp.c @@ -341,7 +341,8 @@ static void ipcp_set_dns_servers(struct ppp_fsm *fsm) dnsctx = dns_resolve_get_default(); ret = dns_resolve_reconfigure_with_interfaces(dnsctx, NULL, dns_servers, - interfaces); + interfaces, + DNS_SOURCE_PPP); if (ret < 0) { NET_ERR("Could not set DNS servers"); return; diff --git a/subsys/net/lib/dhcpv4/dhcpv4.c b/subsys/net/lib/dhcpv4/dhcpv4.c index c475b1fb4d2..5dce854aa90 100644 --- a/subsys/net/lib/dhcpv4/dhcpv4.c +++ b/subsys/net/lib/dhcpv4/dhcpv4.c @@ -1185,9 +1185,11 @@ static bool dhcpv4_parse_options(struct net_pkt *pkt, status = dns_resolve_reconfigure_with_interfaces(ctx, NULL, dns_servers, - interfaces); + interfaces, + DNS_SOURCE_DHCPV4); } else { - status = dns_resolve_reconfigure(ctx, NULL, dns_servers); + status = dns_resolve_reconfigure(ctx, NULL, dns_servers, + DNS_SOURCE_DHCPV4); } if (status < 0) { diff --git a/subsys/net/lib/dhcpv6/dhcpv6.c b/subsys/net/lib/dhcpv6/dhcpv6.c index 2e1441b0331..d8b1e3d7a6d 100644 --- a/subsys/net/lib/dhcpv6/dhcpv6.c +++ b/subsys/net/lib/dhcpv6/dhcpv6.c @@ -1426,9 +1426,11 @@ static int dhcpv6_handle_dns_server_option(struct net_pkt *pkt) status = dns_resolve_reconfigure_with_interfaces(ctx, NULL, dns_servers, - interfaces); + interfaces, + DNS_SOURCE_DHCPV6); } else { - status = dns_resolve_reconfigure(ctx, NULL, dns_servers); + status = dns_resolve_reconfigure(ctx, NULL, dns_servers, + DNS_SOURCE_DHCPV6); } if (status < 0) { diff --git a/subsys/net/lib/dns/resolve.c b/subsys/net/lib/dns/resolve.c index 234d48ba843..2b8d6f975fb 100644 --- a/subsys/net/lib/dns/resolve.c +++ b/subsys/net/lib/dns/resolve.c @@ -488,13 +488,34 @@ static int get_free_slot(struct dns_resolve_context *ctx) return -ENOENT; } +const char *dns_get_source_str(enum dns_server_source source) +{ + switch (source) { + case DNS_SOURCE_UNKNOWN: + return "unknown"; + case DNS_SOURCE_MANUAL: + return "manual"; + case DNS_SOURCE_DHCPV4: + __fallthrough; + case DNS_SOURCE_DHCPV6: + return "DHCP"; + case DNS_SOURCE_IPV6_RA: + return "IPv6 RA"; + case DNS_SOURCE_PPP: + return "PPP"; + } + + return ""; +} + /* Must be invoked with context lock held */ static int dns_resolve_init_locked(struct dns_resolve_context *ctx, const char *servers[], const struct sockaddr *servers_sa[], const struct net_socket_service_desc *svc, uint16_t port, int interfaces[], - bool do_cleanup) + bool do_cleanup, + enum dns_server_source source) { #if defined(CONFIG_NET_IPV6) struct sockaddr_in6 local_addr6 = { @@ -608,6 +629,8 @@ static int dns_resolve_init_locked(struct dns_resolve_context *ctx, } } + ctx->servers[idx].source = source; + addr = &ctx->servers[idx].dns_server; (void)memset(addr, 0, sizeof(*addr)); @@ -624,13 +647,16 @@ static int dns_resolve_init_locked(struct dns_resolve_context *ctx, dns_postprocess_server(ctx, idx); - NET_DBG("[%d] %.*s%s%s%s%s", i, (int)server_len, servers[i], + NET_DBG("[%d] %.*s%s%s%s%s%s%s%s", i, (int)server_len, servers[i], IS_ENABLED(CONFIG_MDNS_RESOLVER) ? (ctx->servers[i].is_mdns ? " mDNS" : "") : "", IS_ENABLED(CONFIG_LLMNR_RESOLVER) ? (ctx->servers[i].is_llmnr ? " LLMNR" : "") : "", iface_str != NULL ? " via " : "", - iface_str != NULL ? iface_str : ""); + iface_str != NULL ? iface_str : "", + source != DNS_SOURCE_UNKNOWN ? " (" : "", + source != DNS_SOURCE_UNKNOWN ? dns_get_source_str(source) : "", + source != DNS_SOURCE_UNKNOWN ? ")" : ""); idx++; } @@ -656,6 +682,8 @@ static int dns_resolve_init_locked(struct dns_resolve_context *ctx, break; } + ctx->servers[idx].source = source; + memcpy(&ctx->servers[idx].dns_server, servers_sa[i], sizeof(ctx->servers[idx].dns_server)); @@ -668,7 +696,7 @@ static int dns_resolve_init_locked(struct dns_resolve_context *ctx, dns_postprocess_server(ctx, idx); - NET_DBG("[%d] %s%s%s%s%s", i, + NET_DBG("[%d] %s%s%s%s%s%s%s%s", i, net_sprint_addr(servers_sa[i]->sa_family, &net_sin(servers_sa[i])->sin_addr), IS_ENABLED(CONFIG_MDNS_RESOLVER) ? @@ -676,7 +704,10 @@ static int dns_resolve_init_locked(struct dns_resolve_context *ctx, IS_ENABLED(CONFIG_LLMNR_RESOLVER) ? (ctx->servers[i].is_llmnr ? " LLMNR" : "") : "", interfaces != NULL ? " via " : "", - interfaces != NULL ? iface_str : ""); + interfaces != NULL ? iface_str : "", + source != DNS_SOURCE_UNKNOWN ? " (" : "", + source != DNS_SOURCE_UNKNOWN ? dns_get_source_str(source) : "", + source != DNS_SOURCE_UNKNOWN ? ")" : ""); idx++; } @@ -871,7 +902,7 @@ int dns_resolve_init_with_svc(struct dns_resolve_context *ctx, const char *serve } ret = dns_resolve_init_locked(ctx, servers, servers_sa, svc, port, - interfaces, true); + interfaces, true, DNS_SOURCE_UNKNOWN); k_mutex_unlock(&lock); @@ -2055,7 +2086,8 @@ static int do_dns_resolve_reconfigure(struct dns_resolve_context *ctx, const char *servers[], const struct sockaddr *servers_sa[], int interfaces[], - bool do_close) + bool do_close, + enum dns_server_source source) { int err; @@ -2092,7 +2124,8 @@ static int do_dns_resolve_reconfigure(struct dns_resolve_context *ctx, err = dns_resolve_init_locked(ctx, servers, servers_sa, &resolve_svc, 0, interfaces, - do_close); + do_close, + source); unlock: k_mutex_unlock(&ctx->lock); @@ -2104,26 +2137,30 @@ static int do_dns_resolve_reconfigure(struct dns_resolve_context *ctx, int dns_resolve_reconfigure_with_interfaces(struct dns_resolve_context *ctx, const char *servers[], const struct sockaddr *servers_sa[], - int interfaces[]) + int interfaces[], + enum dns_server_source source) { return do_dns_resolve_reconfigure(ctx, servers, servers_sa, interfaces, IS_ENABLED(CONFIG_DNS_RECONFIGURE_CLEANUP) ? - true : false); + true : false, + source); } int dns_resolve_reconfigure(struct dns_resolve_context *ctx, const char *servers[], - const struct sockaddr *servers_sa[]) + const struct sockaddr *servers_sa[], + enum dns_server_source source) { return do_dns_resolve_reconfigure(ctx, servers, servers_sa, NULL, IS_ENABLED(CONFIG_DNS_RECONFIGURE_CLEANUP) ? - true : false); + true : false, + source); } int dns_resolve_remove(struct dns_resolve_context *ctx, int if_index) diff --git a/tests/net/lib/dns_addremove/src/main.c b/tests/net/lib/dns_addremove/src/main.c index 3ff9bb2aabe..1b6f55efd73 100644 --- a/tests/net/lib/dns_addremove/src/main.c +++ b/tests/net/lib/dns_addremove/src/main.c @@ -466,7 +466,8 @@ ZTEST(dns_addremove, test_dns_reconfigure_callback) "Timeout while waiting for DNS added callback"); } - ret = dns_resolve_reconfigure(&resv_ipv4, dns2_servers_str, NULL); + ret = dns_resolve_reconfigure(&resv_ipv4, dns2_servers_str, NULL, + DNS_SOURCE_MANUAL); zassert_equal(ret, 0, "Cannot reconfigure DNS server"); /* Wait for DNS removed callback after reconfiguring DNS */ From 419bae5f8a1d2152d83c128f42bc3fe68eaf0a7c Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 24 Jun 2025 14:07:35 +0300 Subject: [PATCH 10/11] [nrf fromtree] net: dhcp: Remove only added DNS servers when stopping Make sure that we remove only the added DNS servers when the DHCP is stopped. Signed-off-by: Jukka Rissanen (cherry picked from commit 25084203c29ff5a259b4d161fce62c8c4cb2f323) Signed-off-by: Robert Lubos --- include/zephyr/net/dns_resolve.h | 13 +++++++++++++ subsys/net/lib/dhcpv4/dhcpv4.c | 11 +++++++++-- subsys/net/lib/dhcpv6/dhcpv6.c | 11 +++++++++-- subsys/net/lib/dns/resolve.c | 20 +++++++++++++++++++- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/include/zephyr/net/dns_resolve.h b/include/zephyr/net/dns_resolve.h index e8789e8621d..400af6b77e6 100644 --- a/include/zephyr/net/dns_resolve.h +++ b/include/zephyr/net/dns_resolve.h @@ -586,6 +586,19 @@ int dns_resolve_reconfigure_with_interfaces(struct dns_resolve_context *ctx, */ int dns_resolve_remove(struct dns_resolve_context *ctx, int if_index); +/** + * @brief Remove servers from the DNS resolving context that were added by + * a specific source. + * + * @param ctx DNS context + * @param if_index Network interface from which the DNS servers are removed. + * @param source Source of the DNS servers, e.g., manual, DHCPv4/6, etc. + * + * @return 0 if ok, <0 if error. + */ +int dns_resolve_remove_source(struct dns_resolve_context *ctx, int if_index, + enum dns_server_source source); + /** * @brief Cancel a pending DNS query. * diff --git a/subsys/net/lib/dhcpv4/dhcpv4.c b/subsys/net/lib/dhcpv4/dhcpv4.c index 5dce854aa90..2096bf330d9 100644 --- a/subsys/net/lib/dhcpv4/dhcpv4.c +++ b/subsys/net/lib/dhcpv4/dhcpv4.c @@ -1677,8 +1677,9 @@ static void dhcpv4_iface_event_handler(struct net_mgmt_event_callback *cb, * comes back up. */ if (IS_ENABLED(CONFIG_NET_DHCPV4_DNS_SERVER_VIA_INTERFACE)) { - dns_resolve_remove(dns_resolve_get_default(), - net_if_get_by_iface(iface)); + dns_resolve_remove_source(dns_resolve_get_default(), + net_if_get_by_iface(iface), + DNS_SOURCE_DHCPV4); } } } else if (mgmt_event == NET_EVENT_IF_UP) { @@ -1944,6 +1945,12 @@ void net_dhcpv4_stop(struct net_if *iface) NET_DBG("state=%s", net_dhcpv4_state_name(iface->config.dhcpv4.state)); + if (IS_ENABLED(CONFIG_NET_DHCPV4_DNS_SERVER_VIA_INTERFACE)) { + dns_resolve_remove_source(dns_resolve_get_default(), + net_if_get_by_iface(iface), + DNS_SOURCE_DHCPV4); + } + sys_slist_find_and_remove(&dhcpv4_ifaces, &iface->config.dhcpv4.node); diff --git a/subsys/net/lib/dhcpv6/dhcpv6.c b/subsys/net/lib/dhcpv6/dhcpv6.c index d8b1e3d7a6d..c1282c2a6f1 100644 --- a/subsys/net/lib/dhcpv6/dhcpv6.c +++ b/subsys/net/lib/dhcpv6/dhcpv6.c @@ -2226,8 +2226,9 @@ static void dhcpv6_iface_event_handler(struct net_mgmt_event_callback *cb, * comes back up. */ if (IS_ENABLED(CONFIG_NET_DHCPV6_DNS_SERVER_VIA_INTERFACE)) { - dns_resolve_remove(dns_resolve_get_default(), - net_if_get_by_iface(iface)); + dns_resolve_remove_source(dns_resolve_get_default(), + net_if_get_by_iface(iface), + DNS_SOURCE_DHCPV6); } } else if (mgmt_event == NET_EVENT_IF_UP) { NET_DBG("Interface %p coming up", iface); @@ -2323,6 +2324,12 @@ void net_dhcpv6_stop(struct net_if *iface) (void)dhcpv6_enter_state(iface, NET_DHCPV6_DISABLED); + if (IS_ENABLED(CONFIG_NET_DHCPV6_DNS_SERVER_VIA_INTERFACE)) { + dns_resolve_remove_source(dns_resolve_get_default(), + net_if_get_by_iface(iface), + DNS_SOURCE_DHCPV6); + } + sys_slist_find_and_remove(&dhcpv6_ifaces, &iface->config.dhcpv6.node); diff --git a/subsys/net/lib/dns/resolve.c b/subsys/net/lib/dns/resolve.c index 2b8d6f975fb..9025501971e 100644 --- a/subsys/net/lib/dns/resolve.c +++ b/subsys/net/lib/dns/resolve.c @@ -2163,7 +2163,9 @@ int dns_resolve_reconfigure(struct dns_resolve_context *ctx, source); } -int dns_resolve_remove(struct dns_resolve_context *ctx, int if_index) +static int dns_resolve_remove_and_check_source(struct dns_resolve_context *ctx, int if_index, + bool check_source, + enum dns_server_source source) { int i; int ret = -ENOENT; @@ -2188,6 +2190,10 @@ int dns_resolve_remove(struct dns_resolve_context *ctx, int if_index) continue; } + if (check_source && ctx->servers[i].source != source) { + continue; + } + ctx->servers[i].if_index = 0; /* See comment in dns_resolve_close_locked() about @@ -2209,6 +2215,18 @@ int dns_resolve_remove(struct dns_resolve_context *ctx, int if_index) return st; } +int dns_resolve_remove(struct dns_resolve_context *ctx, int if_index) +{ + return dns_resolve_remove_and_check_source(ctx, if_index, false, + DNS_SOURCE_UNKNOWN); +} + +int dns_resolve_remove_source(struct dns_resolve_context *ctx, int if_index, + enum dns_server_source source) +{ + return dns_resolve_remove_and_check_source(ctx, if_index, true, source); +} + struct dns_resolve_context *dns_resolve_get_default(void) { return &dns_default_ctx; From bf3a6a2bd3e3abaafe6f86df6601b4eff4758c46 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Tue, 24 Jun 2025 14:08:42 +0300 Subject: [PATCH 11/11] [nrf fromtree] net: shell: dns: Print info about DHCP added servers Print information which DNS servers were added by DHCP when listing DNS servers in "net dns" command. This helps debugging DNS server issues. Signed-off-by: Jukka Rissanen (cherry picked from commit f1a9ff97cb5694e110c4fc4ea27f6904e9d63622) Signed-off-by: Robert Lubos --- subsys/net/lib/shell/dns.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/subsys/net/lib/shell/dns.c b/subsys/net/lib/shell/dns.c index 6c6285990d8..e01b58096c3 100644 --- a/subsys/net/lib/shell/dns.c +++ b/subsys/net/lib/shell/dns.c @@ -93,22 +93,30 @@ static void print_dns_info(const struct shell *sh, } if (ctx->servers[i].dns_server.sa_family == AF_INET) { - PR("\t%s:%u%s%s\n", + PR("\t%s:%u%s%s%s%s%s\n", net_sprint_ipv4_addr( &net_sin(&ctx->servers[i].dns_server)-> sin_addr), ntohs(net_sin(&ctx->servers[i].dns_server)->sin_port), printable_iface(iface_name, " via ", ""), - printable_iface(iface_name, iface_name, "")); + printable_iface(iface_name, iface_name, ""), + ctx->servers[i].source != DNS_SOURCE_UNKNOWN ? " (" : "", + ctx->servers[i].source != DNS_SOURCE_UNKNOWN ? + dns_get_source_str(ctx->servers[i].source) : "", + ctx->servers[i].source != DNS_SOURCE_UNKNOWN ? ")" : ""); } else if (ctx->servers[i].dns_server.sa_family == AF_INET6) { - PR("\t[%s]:%u%s%s\n", + PR("\t[%s]:%u%s%s%s%s%s\n", net_sprint_ipv6_addr( &net_sin6(&ctx->servers[i].dns_server)-> sin6_addr), ntohs(net_sin6(&ctx->servers[i].dns_server)->sin6_port), printable_iface(iface_name, " via ", ""), - printable_iface(iface_name, iface_name, "")); + printable_iface(iface_name, iface_name, ""), + ctx->servers[i].source != DNS_SOURCE_UNKNOWN ? " (" : "", + ctx->servers[i].source != DNS_SOURCE_UNKNOWN ? + dns_get_source_str(ctx->servers[i].source) : "", + ctx->servers[i].source != DNS_SOURCE_UNKNOWN ? ")" : ""); } }