diff --git a/include/zephyr/net/dns_resolve.h b/include/zephyr/net/dns_resolve.h index 9e1591c0b8646..33398dabdf134 100644 --- a/include/zephyr/net/dns_resolve.h +++ b/include/zephyr/net/dns_resolve.h @@ -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; diff --git a/samples/net/mdns_responder/overlay-vlan.conf b/samples/net/mdns_responder/overlay-vlan.conf index 83d84cbf3fbab..bdab8a44350b1 100644 --- a/samples/net/mdns_responder/overlay-vlan.conf +++ b/samples/net/mdns_responder/overlay-vlan.conf @@ -32,3 +32,6 @@ CONFIG_NET_SAMPLE_IFACE3_VLAN_TAG=200 # we do not run out of them. CONFIG_NET_MAX_CONTEXTS=10 CONFIG_NET_MAX_CONN=10 + +CONFIG_NET_INTERFACE_NAME=y +CONFIG_NET_INTERFACE_NAME_LEN=15 diff --git a/subsys/net/ip/utils.c b/subsys/net/ip/utils.c index f130a184bcb27..5025e326b803b 100644 --- a/subsys/net/ip/utils.c +++ b/subsys/net/ip/utils.c @@ -864,8 +864,8 @@ static bool parse_ipv4(const char *str, size_t str_len, return true; } - memcpy(ipaddr, ptr + 1, str_len - end); - ipaddr[str_len - end] = '\0'; + memcpy(ipaddr, ptr + 1, str_len - end - 1); + ipaddr[str_len - end - 1] = '\0'; ret = convert_port(ipaddr, &port); if (!ret) { diff --git a/subsys/net/lib/dns/Kconfig b/subsys/net/lib/dns/Kconfig index 654f7c940136e..8067e85653d5d 100644 --- a/subsys/net/lib/dns/Kconfig +++ b/subsys/net/lib/dns/Kconfig @@ -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 diff --git a/subsys/net/lib/dns/resolve.c b/subsys/net/lib/dns/resolve.c index ff17b6b6257fe..5b9402768da31 100644 --- a/subsys/net/lib/dns/resolve.c +++ b/subsys/net/lib/dns/resolve.c @@ -27,6 +27,7 @@ LOG_MODULE_REGISTER(net_dns_resolve, CONFIG_DNS_RESOLVER_LOG_LEVEL); #include #include #include +#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); } diff --git a/subsys/net/lib/shell/dns.c b/subsys/net/lib/shell/dns.c index 54c768cecd1c8..6c6285990d889 100644 --- a/subsys/net/lib/shell/dns.c +++ b/subsys/net/lib/shell/dns.c @@ -8,6 +8,7 @@ #include LOG_MODULE_DECLARE(net_shell); +#include #include #include "net_shell_private.h" @@ -59,29 +60,55 @@ static void dns_result_cb(enum dns_resolve_status status, PR_WARNING("dns: Unhandled status %d received\n", status); } +static const char *printable_iface(const char *iface_name, + const char *found, + const char *not_found) +{ + if (iface_name[0] != '\0') { + return found; + } + + return not_found; +} + static void print_dns_info(const struct shell *sh, struct dns_resolve_context *ctx) { - int i; + int i, ret; PR("DNS servers:\n"); for (i = 0; i < CONFIG_DNS_RESOLVER_MAX_SERVERS + DNS_MAX_MCAST_SERVERS; i++) { + char iface_name[IFNAMSIZ] = { 0 }; + + if (ctx->servers[i].if_index > 0) { + ret = net_if_get_name( + net_if_get_by_index(ctx->servers[i].if_index), + iface_name, sizeof(iface_name)); + if (ret < 0) { + snprintk(iface_name, sizeof(iface_name), "%d", + ctx->servers[i].if_index); + } + } + if (ctx->servers[i].dns_server.sa_family == AF_INET) { - PR("\t%s:%u\n", + PR("\t%s:%u%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)); + ntohs(net_sin(&ctx->servers[i].dns_server)->sin_port), + printable_iface(iface_name, " via ", ""), + printable_iface(iface_name, iface_name, "")); + } else if (ctx->servers[i].dns_server.sa_family == AF_INET6) { - PR("\t[%s]:%u\n", + PR("\t[%s]:%u%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)); + ntohs(net_sin6(&ctx->servers[i].dns_server)->sin6_port), + printable_iface(iface_name, " via ", ""), + printable_iface(iface_name, iface_name, "")); } } diff --git a/tests/net/utils/src/main.c b/tests/net/utils/src/main.c index a4ef8db7e0ebb..15a51e8ca73ca 100644 --- a/tests/net/utils/src/main.c +++ b/tests/net/utils/src/main.c @@ -532,6 +532,21 @@ ZTEST(test_utils_fn, test_addr_parse) }, .verdict = true }, + { + .address = "192.0.2.3:80/foobar", + .len = sizeof("192.0.2.3:80") - 1, + .result = { + .sin_family = AF_INET, + .sin_port = htons(80), + .sin_addr = { + .s4_addr[0] = 192, + .s4_addr[1] = 0, + .s4_addr[2] = 2, + .s4_addr[3] = 3 + } + }, + .verdict = true + }, { .address = "192.0.2.3/foobar", .len = sizeof("192.0.2.3/foobar") - 1,