From patchwork Fri Jan 31 15:41:35 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gert Doering X-Patchwork-Id: 4113 Return-Path: Delivered-To: patchwork@openvpn.net Received: by 2002:a05:7000:c520:b0:5e7:b9eb:58e8 with SMTP id jx32csp749114mab; Fri, 31 Jan 2025 07:41:51 -0800 (PST) X-Forwarded-Encrypted: i=2; AJvYcCW7gIlV8JMcY+bR6sgp2wNL1U6G5hOrQhHZmKJyHbRakLpvHPnU1vHY5d6NlMbUbyvawrNeD/azY88=@openvpn.net X-Google-Smtp-Source: AGHT+IFVsuDlLJ+5pn111FDvBPfCNshRvZuB6YrD51fLRtdP0eTxjldt0Yc/tlzPw8AAMJqly4Zi X-Received: by 2002:a05:6808:2dc6:b0:3eb:58bd:1a42 with SMTP id 5614622812f47-3f33c420bc8mr3384103b6e.12.1738338111111; Fri, 31 Jan 2025 07:41:51 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1738338111; cv=none; d=google.com; s=arc-20240605; b=SuTK/oJHZksSCfvTvnz9qghgyb0fpMNXZkDSz/Is1pRQ0EMm4HE0sDdqr0DNsjgGjh FWV7NOpTwROtS9bsUAUbIcQ4tq6gMLcCNciqoxELQ03w0/30rxmyYtnTGz5bERwL9FxX uRTcy26qXxbMsrz4Nny4kTG0vR1p52VM3beNwaHJu0s/UWY3B9ZVd5Fphv65c/mhUe8T 4L4T/zP0dr5OtRWfMB+P275C7ohffiDcVWI5fAKh0GRAIFROKkNUjtLOYicx8+ilIlfU EqhJNMHtnGvcQy0E/uwzT3Hz/qy3J/MQbC7Sik0iQGe8Zg4GCt65C2vhCR78rjOlMlz6 KIgQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=errors-to:content-transfer-encoding:list-subscribe:list-help :list-post:list-archive:list-unsubscribe:list-id:precedence:subject :mime-version:references:in-reply-to:message-id:date:to:from :dkim-signature:dkim-signature; bh=u9B/Ln81VTSLoOz2/K2bGNOoBAx7KaTzh9gkMKGGKy0=; fh=4NbAC/LsuMLI0S0hprUlLSLCiHwg6SCAifhH718Jh0Q=; b=R7vG1utBZHwhXB1jWPswivuZXM52MTSfRtPxj3ebgwVB1vZgLeMMB7k8czl5zA+W1g 1qmdShGXRrfkwKWbpRlFZFGuwSoM8mhuRrPyp6gm6Ar29yBYgmiHQW+bsD5DvFiBN+0y 8l4/HWRLnPnNyuEznEv3DkyYsLPT97A8dSHAjg2ge7AkUnXPMF/AUuCB09VfJHxCgv34 YD6/qr93fe9kpDk0IGjbu2rhqac9l3Y07sCXtVSxjQm72J059cRf5YKjQopCV4+dsaya PrQwa5zWa8uArXEHQbq75TNnPqvyEVo6sMgjYE88KkdV0IJdoH/mRtwEPqTBw6uDM9Ly 1xDQ==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=PnRi02ix; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b="Jp9E/vi+"; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=muc.de Received: from lists.sourceforge.net (lists.sourceforge.net. [216.105.38.7]) by mx.google.com with ESMTPS id 006d021491bc7-5fc104e4850si3990796eaf.35.2025.01.31.07.41.50 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Fri, 31 Jan 2025 07:41:50 -0800 (PST) Received-SPF: pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) client-ip=216.105.38.7; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=PnRi02ix; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b="Jp9E/vi+"; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=muc.de Received: from [127.0.0.1] (helo=sfs-ml-2.v29.lw.sourceforge.com) by sfs-ml-2.v29.lw.sourceforge.com with esmtp (Exim 4.95) (envelope-from ) id 1tdt9P-00044W-Ed; Fri, 31 Jan 2025 15:41:47 +0000 Received: from [172.30.29.66] (helo=mx.sourceforge.net) by sfs-ml-2.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95) (envelope-from ) id 1tdt9N-000447-Jf for openvpn-devel@lists.sourceforge.net; Fri, 31 Jan 2025 15:41:45 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Transfer-Encoding:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=CBvNPSN3EkPmi4xqiabpdPulfgANP9dmjksg1PJ6o7Y=; b=PnRi02ixhlgMioWrjjH9/fcVxk GeO9ebXy6RhGu2AE7FKY260xDmKjqSVF9Y7zqcRtM5aD9s/Qfd+DuqzBA4kUmxWKLo6CCh2IQ8yZn U1BI3b+yLMzmv1eVCtlNiSp8R1nUzy7u98tPE0EJXyxLWCbpTcqUdEr/HWdDvO78AWlQ=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-ID: Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=CBvNPSN3EkPmi4xqiabpdPulfgANP9dmjksg1PJ6o7Y=; b=Jp9E/vi+t3ldEN6s7LMvNr23hs cKNMQhzVTu1xLnMXd3FSE/we7f5m0wZtWwR4w9IArRNDuP/MpIGdxThTZSmBnVeXqHxGcyPOTbaae lSY9WWRXy3EZ1ER8OpLaMosd1XfGUuLaFJgK0SZWHoMAOopg7EkltuGMvYA2bGT9QekM=; Received: from dhcp-174.greenie.muc.de ([193.149.48.174] helo=blue.greenie.muc.de) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.95) id 1tdt9K-0005Zi-OT for openvpn-devel@lists.sourceforge.net; Fri, 31 Jan 2025 15:41:44 +0000 Received: from blue.greenie.muc.de (localhost [127.0.0.1]) by blue.greenie.muc.de (8.17.1.9/8.17.1.9) with ESMTP id 50VFfaOj032179 for ; Fri, 31 Jan 2025 16:41:36 +0100 Received: (from gert@localhost) by blue.greenie.muc.de (8.17.1.9/8.17.1.9/Submit) id 50VFfagJ032178 for openvpn-devel@lists.sourceforge.net; Fri, 31 Jan 2025 16:41:36 +0100 From: Gert Doering To: openvpn-devel@lists.sourceforge.net Date: Fri, 31 Jan 2025 16:41:35 +0100 Message-ID: <20250131154135.32169-1-gert@greenie.muc.de> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Score: 0.0 (/) X-Spam-Report: Spam detection software, running on the system "util-spamd-1.v13.lw.sourceforge.com", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: From: Lev Stipakov When adding host route for IPv4, we use the default gateway. There are cases, however, when this does not work - for example when remote is not accessible via default gateway but via dedicated route. Content analysis details: (0.0 points, 6.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 RCVD_IN_VALIDITY_CERTIFIED_BLOCKED RBL: ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. [193.149.48.174 listed in sa-accredit.habeas.com] 0.0 RCVD_IN_VALIDITY_RPBL_BLOCKED RBL: ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. [193.149.48.174 listed in bl.score.senderscore.com] -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.0 SPF_PASS SPF: sender matches SPF record X-Headers-End: 1tdt9K-0005Zi-OT Subject: [Openvpn-devel] [PATCH v5] route.c: improve get_default_gateway() logic on Windows X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox X-GMAIL-THRID: =?utf-8?q?1822779623406572926?= X-GMAIL-MSGID: =?utf-8?q?1822779623406572926?= From: Lev Stipakov When adding host route for IPv4, we use the default gateway. There are cases, however, when this does not work - for example when remote is not accessible via default gateway but via dedicated route. Factor out code to look for the best gateway to reach the host from get_default_gateway_ipv6() and generalize is for IPv4/6. Change-Id: I6c7e1cef637fe9fb3f3bc6ff4fb2c65599cd86fb Signed-off-by: Lev Stipakov Acked-by: Gert Doering --- This change was reviewed on Gerrit and approved by at least one developer. I request to merge it to master. Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/879 This mail reflects revision 5 of this Change. Acked-by according to Gerrit (reflected above): Gert Doering diff --git a/src/openvpn/route.c b/src/openvpn/route.c index dd37fb9..d895e1c 100644 --- a/src/openvpn/route.c +++ b/src/openvpn/route.c @@ -2732,40 +2732,105 @@ return ret; } +/** + * @brief Determines the best route to a destination for both IPv4 and IPv6. + * + * Uses `GetBestInterfaceEx` and `GetBestRoute2` to find the optimal route + * and network interface for the specified destination address. + * + * @param gc Pointer to struct gc_arena for internal string allocation. + * @param dest The destination IP address (IPv4 or IPv6). + * @param best_route Pointer to a `MIB_IPFORWARD_ROW2` structure to store the best route. + * @return DWORD `NO_ERROR` on success, or an error code. + */ +static DWORD +get_best_route(struct gc_arena *gc, SOCKADDR_INET *dest, MIB_IPFORWARD_ROW2 *best_route) +{ + DWORD best_if_index; + DWORD status; + + CLEAR(*best_route); + + /* get the best interface index to reach dest */ + status = GetBestInterfaceEx((struct sockaddr *)dest, &best_if_index); + if (status != NO_ERROR) + { + msg(D_ROUTE, "NOTE: GetBestInterfaceEx returned error: %s (code=%u)", + strerror_win32(status, gc), + (unsigned int)status); + goto done; + } + + msg(D_ROUTE_DEBUG, "GetBestInterfaceEx() returned if=%d", (int)best_if_index); + + /* get the routing information (such as NextHop) for the destination and interface */ + NET_LUID luid; + CLEAR(luid); + SOCKADDR_INET best_src; + CLEAR(best_src); + status = GetBestRoute2(&luid, best_if_index, NULL, + dest, 0, best_route, &best_src); + if (status != NO_ERROR) + { + msg(D_ROUTE, "NOTE: GetIpForwardEntry2 returned error: %s (code=%u)", + strerror_win32(status, gc), + (unsigned int)status); + goto done; + } + +done: + return status; +} + void get_default_gateway(struct route_gateway_info *rgi, in_addr_t dest, openvpn_net_ctx_t *ctx) { - struct gc_arena gc = gc_new(); - - const IP_ADAPTER_INFO *adapters = get_adapter_info_list(&gc); - const MIB_IPFORWARDTABLE *routes = get_windows_routing_table(&gc); - const MIB_IPFORWARDROW *row = get_default_gateway_row(routes); - DWORD a_index; - const IP_ADAPTER_INFO *ai; - CLEAR(*rgi); - if (row) + struct gc_arena gc = gc_new(); + + /* convert in_addr_t into SOCKADDR_INET */ + SOCKADDR_INET sa; + CLEAR(sa); + sa.si_family = AF_INET; + sa.Ipv4.sin_addr.s_addr = htonl(dest); + + /* get the best route to the destination */ + MIB_IPFORWARD_ROW2 best_route; + CLEAR(best_route); + DWORD status = get_best_route(&gc, &sa, &best_route); + if (status != NO_ERROR) { - rgi->gateway.addr = ntohl(row->dwForwardNextHop); - if (rgi->gateway.addr) + goto done; + } + + rgi->flags = RGI_ADDR_DEFINED | RGI_IFACE_DEFINED; + rgi->gateway.addr = ntohl(best_route.NextHop.Ipv4.sin_addr.S_un.S_addr); + rgi->adapter_index = best_route.InterfaceIndex; + + if (rgi->gateway.addr == INADDR_ANY) + { + rgi->flags |= RGI_ON_LINK; + } + + /* get netmask and MAC address */ + const IP_ADAPTER_INFO *adapters = get_adapter_info_list(&gc); + const IP_ADAPTER_INFO *ai = get_adapter(adapters, rgi->adapter_index); + if (ai) + { + memcpy(rgi->hwaddr, ai->Address, 6); + rgi->flags |= RGI_HWADDR_DEFINED; + + /* get netmask for non-onlink routes */ + in_addr_t nm = inet_addr(ai->IpAddressList.IpMask.String); + if (!(rgi->flags & RGI_ON_LINK) && (nm != INADDR_NONE)) { - rgi->flags |= RGI_ADDR_DEFINED; - a_index = adapter_index_of_ip(adapters, rgi->gateway.addr, NULL, &rgi->gateway.netmask); - if (a_index != TUN_ADAPTER_INDEX_INVALID) - { - rgi->adapter_index = a_index; - rgi->flags |= (RGI_IFACE_DEFINED|RGI_NETMASK_DEFINED); - ai = get_adapter(adapters, a_index); - if (ai) - { - memcpy(rgi->hwaddr, ai->Address, 6); - rgi->flags |= RGI_HWADDR_DEFINED; - } - } + rgi->gateway.netmask = ntohl(nm); + rgi->flags |= RGI_NETMASK_DEFINED; } } +done: gc_free(&gc); } @@ -2823,43 +2888,22 @@ const struct in6_addr *dest, openvpn_net_ctx_t *ctx) { struct gc_arena gc = gc_new(); - MIB_IPFORWARD_ROW2 BestRoute; - SOCKADDR_INET DestinationAddress, BestSourceAddress; - DWORD BestIfIndex; - DWORD status; - NET_LUID InterfaceLuid; - CLEAR(*rgi6); - CLEAR(InterfaceLuid); /* cleared = not used for lookup */ - CLEAR(DestinationAddress); + SOCKADDR_INET DestinationAddress; + CLEAR(DestinationAddress); DestinationAddress.si_family = AF_INET6; if (dest) { DestinationAddress.Ipv6.sin6_addr = *dest; } - status = GetBestInterfaceEx( (struct sockaddr *)&DestinationAddress, &BestIfIndex ); + MIB_IPFORWARD_ROW2 BestRoute; + CLEAR(BestRoute); + DWORD status = get_best_route(&gc, &DestinationAddress, &BestRoute); if (status != NO_ERROR) { - msg(D_ROUTE, "NOTE: GetBestInterfaceEx returned error: %s (code=%u)", - strerror_win32(status, &gc), - (unsigned int)status); - goto done; - } - - msg( D_ROUTE, "GetBestInterfaceEx() returned if=%d", (int) BestIfIndex ); - - status = GetBestRoute2( &InterfaceLuid, BestIfIndex, NULL, - &DestinationAddress, 0, - &BestRoute, &BestSourceAddress ); - - if (status != NO_ERROR) - { - msg(D_ROUTE, "NOTE: GetIpForwardEntry2 returned error: %s (code=%u)", - strerror_win32(status, &gc), - (unsigned int)status); goto done; } diff --git a/src/openvpn/route.h b/src/openvpn/route.h index 0bbedbb..98ea79e 100644 --- a/src/openvpn/route.h +++ b/src/openvpn/route.h @@ -334,6 +334,13 @@ bool is_special_addr(const char *addr_str); +/** + * @brief Retrieves the best gateway for a given destination based on the routing table. + * + * @param rgi Pointer to a struct to store the gateway information. + * @param dest Destination IP address in host byte order. + * @param ctx Pointer to a platform-specific network context struct. + */ void get_default_gateway(struct route_gateway_info *rgi, in_addr_t dest, openvpn_net_ctx_t *ctx);