From patchwork Sat Oct 11 11:44:42 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gert Doering X-Patchwork-Id: 4501 Return-Path: Delivered-To: patchwork@openvpn.net Received: by 2002:a05:7000:7d42:b0:72f:f16c:e055 with SMTP id fr2csp893985mab; Sat, 11 Oct 2025 04:45:07 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCWV9XjuyJGCyufVlRfISoZOZXSkhEUKcZN0xlYAYoUXXc9Z9Qo3yYaLUG7/RippXeJTztMuMmlQwdU=@openvpn.net X-Google-Smtp-Source: AGHT+IHunIwoM4LR4LLSX5FwO/D3h1Tx5+27ZwF5IHRXvRBzgDBna8VCjFCwkCBSACbDfVwD/0XC X-Received: by 2002:a05:6808:4f52:b0:43f:228a:2098 with SMTP id 5614622812f47-4417b3c97eemr5538402b6e.42.1760183106993; Sat, 11 Oct 2025 04:45:06 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1760183106; cv=none; d=google.com; s=arc-20240605; b=QILGEVyBU/UmEymKrgstJqrXk3DnjpoerWqUBXzftZbEShiJgUgzDm5cODoRI/B8VH 1Wmhg7Qgz4gi+xyv1WUP7UZDb3JHmBj0JHQMKbb0zdI1pI306kaxyrE5TMovbilM9gKE FFENd8Oy/SeOSkuBCjsXoUp28fkEeYTYcXGEDWIy/CstaXbWJR/R7gD+YRP3ocVXjojt yID6p3ilTFpnGyI/kMQxeRIposJgTxmbjBU3izkv2kHMf32MvEUQrpmHwSTQY+x7hrAi BzOs2rd9zdksuNOfyxGEunBkk33REX4F9qIhjVqJdmgSkf2brpNtRZW7Xyxhw0g8JSLn PIvg== 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:dkim-signature; bh=D4IkDloXAKjSvqDiR+JOuH2I+kqPUJZHagMyuJKN5WE=; fh=4NbAC/LsuMLI0S0hprUlLSLCiHwg6SCAifhH718Jh0Q=; b=d0uoLSfUZ2N4CJNwfz8VzQU7wNxY84Rg2CGhQwQn1QjXkS6I0AF48B/vCxJ+PSOR5E TlDlfKN46Z4cuPdvb+yR5tGsKzMnl+V3lUTDxIfveIRiANeWKJAYqDk30EF1jxJftz0t fOCYbglHdAdG11s7QoY1vQQ9k+2ZTkpEZ9/LHJZZlY3zz5Opc2eUYF0QIlnRpQ/4Iuty gm0bBSdmSZXA3GojYR1ATrgT3fiBcasFmB52QrjiGGS4M4mS86yeyxihWzuuZaJX5Smz TweUSnNwdv6o0cDi3Y0OAJon8roXXWsHuC+iAeZUKz404ZYzRNm3X9MnQ24GVCHKsVq2 G8iw==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@lists.sourceforge.net header.s=beta header.b=ZvdVZoZ4; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=cVrRaGPG; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=U+BDAylL; 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 5614622812f47-441989c7b6bsi1019799b6e.329.2025.10.11.04.45.06 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sat, 11 Oct 2025 04:45:06 -0700 (PDT) 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=pass header.i=@lists.sourceforge.net header.s=beta header.b=ZvdVZoZ4; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=cVrRaGPG; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=U+BDAylL; 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 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.sourceforge.net; s=beta; h=Content-Transfer-Encoding:Content-Type: List-Subscribe:List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id: Subject:MIME-Version:References:In-Reply-To:Message-ID:Date:To:From:Sender: Reply-To:Cc:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=D4IkDloXAKjSvqDiR+JOuH2I+kqPUJZHagMyuJKN5WE=; b=ZvdVZoZ4DulXbDI6N6rZhxFKXp u26RzFbwjunbuntRYMv+xqMjHlaMOWbxZ+ItskESu4QAaTSYz29D9Iq1h6VIp3p/tCt5zWY7CwPa5 khHqNWZj2aX8hjXR+eAUlRyP2gZQiLVZAg1jtE21x2ZnsMb2JSAOj01I3rVY89tHlkIg=; Received: from [127.0.0.1] (helo=sfs-ml-1.v29.lw.sourceforge.com) by sfs-ml-1.v29.lw.sourceforge.com with esmtp (Exim 4.95) (envelope-from ) id 1v7Y23-0006Fe-Om; Sat, 11 Oct 2025 11:45:03 +0000 Received: from [172.30.29.66] (helo=mx.sourceforge.net) by sfs-ml-1.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95) (envelope-from ) id 1v7Y22-0006F9-2a for openvpn-devel@lists.sourceforge.net; Sat, 11 Oct 2025 11:45:02 +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=/avoknLsSRygai3TRd2AtsOA6VlBXTR6y7dCMGcIy10=; b=cVrRaGPGdai5hJs2At9Y+/KRNV wFZjgHuWP/pdeZaBJJsg4vIF0oWD4wFnTpweCZdOttvx2N3cI+vsdqc0cRGeTh67ddyhwt6G2zTTm RAG8QHKvBr7RFA2scYgHuUjnDes8/fBws/dhlu3np4EY0T97ejBX1tqUK5yEpFNtmj8Q=; 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=/avoknLsSRygai3TRd2AtsOA6VlBXTR6y7dCMGcIy10=; b=U+BDAylLwe3Xdo/QFLaldfWYJx NvRA5dp45BG4wFwBCys9PH3R/RWpS2kZ60894ue8gaqs+kvBU3kOW3hAbNS06HswuJA/t8RUofBWK PWB20QBNyQngr+4UR4XEmeIQWvIh2FpJWyb+cNtSGyQlrg8mIhN6mwgWG+xl2Em5ltJY=; Received: from [193.149.48.134] (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 1v7Y20-00068D-Is for openvpn-devel@lists.sourceforge.net; Sat, 11 Oct 2025 11:45:01 +0000 Received: from blue.greenie.muc.de (localhost [127.0.0.1]) by blue.greenie.muc.de (8.18.1/8.18.1) with ESMTP id 59BBimP6014520 for ; Sat, 11 Oct 2025 13:44:48 +0200 Received: (from gert@localhost) by blue.greenie.muc.de (8.18.1/8.18.1/Submit) id 59BBimWu014519 for openvpn-devel@lists.sourceforge.net; Sat, 11 Oct 2025 13:44:48 +0200 From: Gert Doering To: openvpn-devel@lists.sourceforge.net Date: Sat, 11 Oct 2025 13:44:42 +0200 Message-ID: <20251011114448.14501-1-gert@greenie.muc.de> X-Mailer: git-send-email 2.49.1 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Score: 1.3 (+) X-Spam-Report: Spam detection software, running on the system "sfi-spamd-1.hosts.colo.sdot.me", 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 The existing recursive routing check drops TUN packets if their address matches the remote. While this works in most cases, a more fine-grained check is preferable for complex routing rules. Content analysis details: (1.3 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 1.3 RDNS_NONE Delivered to internal network by a host with no rDNS X-Headers-End: 1v7Y20-00068D-Is Subject: [Openvpn-devel] [PATCH v4] Make recursive routing check more fine-grained 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?1845685761673588609?= X-GMAIL-MSGID: =?utf-8?q?1845685761673588609?= From: Lev Stipakov The existing recursive routing check drops TUN packets if their address matches the remote. While this works in most cases, a more fine-grained check is preferable for complex routing rules. Since we only need to drop traffic originating from OpenVPN, all of the following values must match between the packet and the link: - IP protocol - Transport protocol (TCP/UDP) - Destination address - Destination port GitHub: #699 Change-Id: I6841e2f2a85275254a04e2d8ae3defe4420db8f6 Signed-off-by: Lev Stipakov Acked-by: Gert Doering Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/903 --- 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/+/903 This mail reflects revision 4 of this Change. Acked-by according to Gerrit (reflected above): Gert Doering diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index f342958..79a6fc7 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -1391,73 +1391,99 @@ static void drop_if_recursive_routing(struct context *c, struct buffer *buf) { - bool drop = false; - struct openvpn_sockaddr tun_sa; - int ip_hdr_offset = 0; - if (c->c2.to_link_addr == NULL) /* no remote addr known */ { return; } - tun_sa = c->c2.to_link_addr->dest; + struct openvpn_sockaddr *link_addr = &c->c2.to_link_addr->dest; + struct link_socket_info *lsi = get_link_socket_info(c); + uint16_t link_port = atoi(c->c2.link_sockets[0]->remote_port); - int proto_ver = get_tun_ip_ver(TUNNEL_TYPE(c->c1.tuntap), &c->c2.buf, &ip_hdr_offset); + int ip_hdr_offset = 0; + int tun_ip_ver = get_tun_ip_ver(TUNNEL_TYPE(c->c1.tuntap), &c->c2.buf, &ip_hdr_offset); - if (proto_ver == 4) + if (tun_ip_ver == 4) { - /* make sure we got whole IP header */ - if (BLEN(buf) < ((int)sizeof(struct openvpn_iphdr) + ip_hdr_offset)) + /* make sure we got whole IP header and TCP/UDP src/dst ports */ + if (BLEN(buf) < ((int)sizeof(struct openvpn_iphdr) + ip_hdr_offset + sizeof(uint16_t) * 2)) { return; } /* skip ipv4 packets for ipv6 tun */ - if (tun_sa.addr.sa.sa_family != AF_INET) + if (link_addr->addr.sa.sa_family != AF_INET) { return; } struct openvpn_iphdr *pip = (struct openvpn_iphdr *)(BPTR(buf) + ip_hdr_offset); - /* drop packets with same dest addr as gateway */ - if (memcmp(&tun_sa.addr.in4.sin_addr.s_addr, &pip->daddr, sizeof(pip->daddr)) == 0) + /* skip if tun protocol doesn't match link protocol */ + if ((lsi->proto == PROTO_TCP && pip->protocol != OPENVPN_IPPROTO_TCP) + || (lsi->proto == PROTO_UDP && pip->protocol != OPENVPN_IPPROTO_UDP)) { - drop = true; + return; + } + + + /* drop packets with same dest addr and port as remote */ + uint8_t *l4_hdr = (uint8_t *)pip + sizeof(struct openvpn_iphdr); + + /* TCP and UDP ports are at the same place in the header, and other protocols + * can not happen here due to the lsi->proto check above */ + uint16_t src_port = ntohs(*(uint16_t *)l4_hdr); + uint16_t dst_port = ntohs(*(uint16_t *)(l4_hdr + sizeof(uint16_t))); + if ((memcmp(&link_addr->addr.in4.sin_addr.s_addr, &pip->daddr, sizeof(pip->daddr)) == 0) && (link_port == dst_port)) + { + c->c2.buf.len = 0; + + struct gc_arena gc = gc_new(); + msg(D_LOW, "Recursive routing detected, packet dropped %s:%" PRIu16 " -> %s", + print_in_addr_t(pip->saddr, IA_NET_ORDER, &gc), + src_port, + print_link_socket_actual(c->c2.to_link_addr, &gc)); + gc_free(&gc); } } - else if (proto_ver == 6) + else if (tun_ip_ver == 6) { - /* make sure we got whole IPv6 header */ - if (BLEN(buf) < ((int)sizeof(struct openvpn_ipv6hdr) + ip_hdr_offset)) + /* make sure we got whole IPv6 header and TCP/UDP src/dst ports */ + if (BLEN(buf) < ((int)sizeof(struct openvpn_ipv6hdr) + ip_hdr_offset + sizeof(uint16_t) * 2)) { return; } /* skip ipv6 packets for ipv4 tun */ - if (tun_sa.addr.sa.sa_family != AF_INET6) + if (link_addr->addr.sa.sa_family != AF_INET6) { return; } struct openvpn_ipv6hdr *pip6 = (struct openvpn_ipv6hdr *)(BPTR(buf) + ip_hdr_offset); - /* drop packets with same dest addr as gateway */ - if (OPENVPN_IN6_ARE_ADDR_EQUAL(&tun_sa.addr.in6.sin6_addr, &pip6->daddr)) + /* skip if tun protocol doesn't match link protocol */ + if ((lsi->proto == PROTO_TCP && pip6->nexthdr != OPENVPN_IPPROTO_TCP) + || (lsi->proto == PROTO_UDP && pip6->nexthdr != OPENVPN_IPPROTO_UDP)) { - drop = true; + return; } - } - if (drop) - { - struct gc_arena gc = gc_new(); + /* drop packets with same dest addr and port as remote */ + uint8_t *l4_hdr = (uint8_t *)pip6 + sizeof(struct openvpn_ipv6hdr); + uint16_t src_port = ntohs(*(uint16_t *)l4_hdr); + uint16_t dst_port = ntohs(*(uint16_t *)(l4_hdr + sizeof(uint16_t))); + if ((OPENVPN_IN6_ARE_ADDR_EQUAL(&link_addr->addr.in6.sin6_addr, &pip6->daddr)) && (link_port == dst_port)) + { + c->c2.buf.len = 0; - c->c2.buf.len = 0; - - msg(D_LOW, "Recursive routing detected, drop tun packet to %s", - print_link_socket_actual(c->c2.to_link_addr, &gc)); - gc_free(&gc); + struct gc_arena gc = gc_new(); + msg(D_LOW, "Recursive routing detected, packet dropped %s:%" PRIu16 " -> %s", + print_in6_addr(pip6->saddr, IA_NET_ORDER, &gc), + src_port, + print_link_socket_actual(c->c2.to_link_addr, &gc)); + gc_free(&gc); + } } }