net: dns: Bind DNS server to a network interface

Allow user to specify a network interface in the DNS server
list. User can append "%" and network interface name to the
DNS server to use this. If the network interface is mentioned
in the server list, then the DNS queries are sent via this network
interface.
For example setting the interfaces like this:
   192.0.2.2%eth1
   [2001:db8::2]:5353%ppp0

would cause the DNS queries to sent to 192.0.2.1 via eth1 in the first
example, and to 2001:db8::2 via ppp0 in the second example.

Signed-off-by: Jukka Rissanen <jukka.rissanen@nordicsemi.no>
This commit is contained in:
Jukka Rissanen 2024-11-07 17:17:30 +02:00 committed by Anas Nashif
parent 5f3e6212af
commit 6c95daf0ae
3 changed files with 102 additions and 7 deletions

View file

@ -344,6 +344,11 @@ struct dns_resolve_context {
/** Connection to the DNS server */
int sock;
/** Network interface index if the DNS resolving should be done
* via this interface. Value 0 indicates any interface can be used.
*/
int if_index;
/** Is this server mDNS one */
uint8_t is_mdns : 1;

View file

@ -87,6 +87,12 @@ config DNS_SERVER1
192.0.2.1:5353
2001:db8::1
[2001:db8::1]:5353
It is possible to bind the DNS connection via a certain network
interface by appending "%" and network interface name to the server
address. For example: 192.0.2.1%eth1 would bind the connection socket
to the network interface eth1. This is optional and by default the
resolver connects to server by selecting the output network interface
using normal IP routing.
It is not mandatory to use this Kconfig option at all.
The one calling dns_resolve_init() can use this option or not
to populate the server list. If the DNS server addresses are

View file

@ -27,6 +27,7 @@ LOG_MODULE_REGISTER(net_dns_resolve, CONFIG_DNS_RESOLVER_LOG_LEVEL);
#include <zephyr/net/net_mgmt.h>
#include <zephyr/net/dns_resolve.h>
#include <zephyr/net/socket_service.h>
#include "../../ip/net_private.h"
#include "dns_pack.h"
#include "dns_internal.h"
#include "dns_cache.h"
@ -319,6 +320,31 @@ static int register_dispatcher(struct dns_resolve_context *ctx,
return dns_dispatcher_register(&server->dispatcher);
}
static int bind_to_iface(int sock, const struct sockaddr *addr, int if_index)
{
struct ifreq ifreq = { 0 };
int ret;
ret = net_if_get_name(net_if_get_by_index(if_index), ifreq.ifr_name,
sizeof(ifreq.ifr_name));
if (ret < 0) {
LOG_DBG("Cannot get interface name for %d (%d)", if_index, ret);
return ret;
}
ret = zsock_setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
&ifreq, sizeof(ifreq));
if (ret < 0) {
ret = -errno;
NET_DBG("Cannot bind %s to %d (%d)",
net_sprint_addr(addr->sa_family, &net_sin(addr)->sin_addr),
if_index, ret);
}
return ret;
}
/* Must be invoked with context lock held */
static int dns_resolve_init_locked(struct dns_resolve_context *ctx,
const char *servers[],
@ -363,24 +389,56 @@ static int dns_resolve_init_locked(struct dns_resolve_context *ctx,
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++;
if (server_len == 0) {
NET_DBG("Empty server name");
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);
}
} else {
server_len = strlen(servers[i]);
ctx->servers[idx].if_index = 0;
}
(void)memset(addr, 0, sizeof(*addr));
ret = net_ipaddr_parse(servers[i], strlen(servers[i]),
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",
server_len, servers[i]);
}
continue;
}
dns_postprocess_server(ctx, idx);
NET_DBG("[%d] %s%s%s", i, servers[i],
NET_DBG("[%d] %.*s%s%s%s%s", i, 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" : "") : "");
(ctx->servers[i].is_llmnr ? " LLMNR" : "") : "",
iface_str != NULL ? " via " : "",
iface_str != NULL ? iface_str : "");
idx++;
}
}
@ -441,14 +499,40 @@ static int dns_resolve_init_locked(struct dns_resolve_context *ctx,
ctx->servers[i].sock = ret;
/* Try to bind to the interface if it is set */
if (ctx->servers[i].if_index > 0) {
ret = bind_to_iface(ctx->servers[i].sock,
&ctx->servers[i].dns_server,
ctx->servers[i].if_index);
if (ret < 0) {
zsock_close(ctx->servers[i].sock);
ctx->servers[i].sock = -1;
continue;
}
iface = net_if_get_by_index(ctx->servers[i].if_index);
NET_DBG("Binding %s to %d",
net_sprint_addr(ctx->servers[i].dns_server.sa_family,
&net_sin(&ctx->servers[i].dns_server)->sin_addr),
ctx->servers[i].if_index);
} else {
iface = NULL;
}
if (ctx->servers[i].dns_server.sa_family == AF_INET6) {
iface = net_if_ipv6_select_src_iface(
if (iface == NULL) {
iface = net_if_ipv6_select_src_iface(
&net_sin6(&ctx->servers[i].dns_server)->sin6_addr);
}
addr6 = net_if_ipv6_select_src_addr(iface,
&net_sin6(&ctx->servers[i].dns_server)->sin6_addr);
} else {
iface = net_if_ipv4_select_src_iface(
if (iface == NULL) {
iface = net_if_ipv4_select_src_iface(
&net_sin(&ctx->servers[i].dns_server)->sin_addr);
}
addr4 = net_if_ipv4_select_src_addr(iface,
&net_sin(&ctx->servers[i].dns_server)->sin_addr);
}