From aaf68fddfcb8bd04b6ea11d057b4315a27647031 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 7 Nov 2024 16:24:31 +0200 Subject: [PATCH 1/4] net: utils: Port parsing failure in net_ipaddr_parse() If trying to parse a string like 192.0.2.2:80/foobar and setting the length to 12 which means to parse the IP address and port, the parsing failed because it used one extra character from the string. This issue was not present if the input string was terminated after the port number. Add a test case to catch this problem. Signed-off-by: Jukka Rissanen --- subsys/net/ip/utils.c | 4 ++-- tests/net/utils/src/main.c | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) 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/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, From 924eb6781f0b88c855cc5727a5272f5b6609e38a Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 7 Nov 2024 17:17:30 +0200 Subject: [PATCH 2/4] 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 --- include/zephyr/net/dns_resolve.h | 5 ++ subsys/net/lib/dns/Kconfig | 6 ++ subsys/net/lib/dns/resolve.c | 98 +++++++++++++++++++++++++++++--- 3 files changed, 102 insertions(+), 7 deletions(-) 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/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); } From f7f912ef58f4bd342808c218af605e7973f08f65 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 7 Nov 2024 17:23:06 +0200 Subject: [PATCH 3/4] net: shell: dns: Print DNS server with network interface If network interface is specified in the DNS server, then send the queries to the server via the network interface. Print this information in the server list. Signed-off-by: Jukka Rissanen --- subsys/net/lib/shell/dns.c | 41 +++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) 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, "")); } } From 1de55ee50b1a48be9a018faadd16651f554b9193 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Fri, 8 Nov 2024 10:15:19 +0200 Subject: [PATCH 4/4] samples: net: mdns_responder: Increase interface name len for VLAN In this sample, VLAN has longer interface name so increase it to max. Signed-off-by: Jukka Rissanen --- samples/net/mdns_responder/overlay-vlan.conf | 3 +++ 1 file changed, 3 insertions(+) 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