From nobody Wed May 31 10:11:31 2023 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4QWQ5w0n8Mz4YRxZ; Wed, 31 May 2023 10:11:32 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4QWQ5w073Tz3pGM; Wed, 31 May 2023 10:11:32 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1685527892; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=wg8CZ5sRCbJyWFPeyrCCnlkZrc810xwIjcK77fo9tDQ=; b=pCBMRCYt/DQSUXRcoOMLALL4Z4ETTgH7aL5HXTBELd2uvG41R5tFV4G/dINpVN9JEHO/26 ouFyQ5+jZI35kpWHTckQLzGWkcxHMLlIR+K5D56/a9xLUygUPYQDxQkVxikuWfCb1RGX0K FpkgOb753CjaUGuVdNDr34j4yFLYjnv/U+dpdf8uiites+ZIRuJs1/f16D4dEkTpHlETf6 njOmHpDZWFKNaSCWhPVCUGDxWbLEClt4nB+CKtB+C+1mWeUoRwIijuFPmgUzn+Gx+n2a+Z 7OjfPhJfhZ7fWhvXr4zw4K5qdeMImiVbjuqdjRkWwrAzm4xLNArTrshD7bwg5g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1685527892; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=wg8CZ5sRCbJyWFPeyrCCnlkZrc810xwIjcK77fo9tDQ=; b=chC0VgZxLwSiEK6NAiSri3ZDkj8e/+mvSDi2hcT5oHalrbdbtbKDVkf3mwo2I0cqz5Dv1K ayD3k2V02uK9jdzxkXoV+Dche8tahW5TK88Moge968+GaRRxHttoPExyTRJPCeRJi3bOjG EwZNjcVz/RX8U/InGzs/WItJneFE7Am21vGVBDofgm09JfBpt18eYCOTUei/u1jcEbLaa8 PIV6Rlvx5jFb6gNeTg/Vp9Y+veX8kHfHgNpjnCeqjXLmCe/rfheB1T/h7AGY5O2RbYaYfT eKpqtGC0TwpOiIsU7BdYuUmOjUluLf+NMO20CqrMcQN12zTBMmR39Yg2sAHHag== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1685527892; a=rsa-sha256; cv=none; b=MHifAdI1sd8WpP8x6Hrg067JW3l3wYzZ9Dfyxsp5fK4AoUn3f0TAIOCIrBkPisN9SVP9Ed 3ewl6PdrMILqexmYYzfACYehPZkuw/OX1dRHjv2isRcaZnHE71p3sGLPy4tecTBP3rHndB zAkB7uwWQcnkQaxi9WCaNOqdtNmRbxhM3Smb7JxaB7VonSrH3zWn2379BFYmCAvo07mrXP WIT9kXSw3owy49O8L1wq0ITcFc5VzRsoM4JhiFeNGukMNuiD5oCx+gbzxdZeCTKmO/4I+I XAY7xLQvpIp8vq7CCIv1cxqd72SBMb5gqsod13a5u+PjffFd5wjsPtmgVhsEfA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4QWQ5v6HQ1zWBX; Wed, 31 May 2023 10:11:31 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 34VABVcm006124; Wed, 31 May 2023 10:11:31 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 34VABVwJ006123; Wed, 31 May 2023 10:11:31 GMT (envelope-from git) Date: Wed, 31 May 2023 10:11:31 GMT Message-Id: <202305311011.34VABVwJ006123@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Doug Rabson Subject: git: 5ab151574c8a - main - netinet*: Fix redirects for connections from localhost List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: dfr X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 5ab151574c8a1824c6cd8eded28506cb983284bc Auto-Submitted: auto-generated X-ThisMailContainsUnwantedMimeParts: N The branch main has been updated by dfr: URL: https://cgit.FreeBSD.org/src/commit/?id=5ab151574c8a1824c6cd8eded28506cb983284bc commit 5ab151574c8a1824c6cd8eded28506cb983284bc Author: Doug Rabson AuthorDate: 2023-05-24 13:11:37 +0000 Commit: Doug Rabson CommitDate: 2023-05-31 10:11:05 +0000 netinet*: Fix redirects for connections from localhost Redirect rules use PFIL_IN and PFIL_OUT events to allow packet filter rules to change the destination address and port for a connection. Typically, the rule triggers on an input event when a packet is received by a router and the destination address and/or port is changed to implement the redirect. When a reply packet on this connection is output to the network, the rule triggers again, reversing the modification. When the connection is initiated on the same host as the packet filter, it is initially output via lo0 which queues it for input processing. This causes an input event on the lo0 interface, allowing redirect processing to rewrite the destination and create state for the connection. However, when the reply is received, no corresponding output event is generated; instead, the packet is delivered to the higher level protocol (e.g. tcp or udp) without reversing the redirect, the reply is not matched to the connection and the packet is dropped (for tcp, a connection reset is also sent). This commit fixes the problem by adding a second packet filter call in the input path. The second call happens right before the handoff to higher level processing and provides the missing output event to allow the redirect's reply processing to perform its rewrite. This extra processing is disabled by default and can be enabled using pfilctl: pfilctl link -o pf:default-out inet-local pfilctl link -o pf:default-out6 inet6-local PR: 268717 Reviewed-by: kp, melifaro MFC-after: 2 weeks Differential Revision: https://reviews.freebsd.org/D40256 --- sys/netinet/ip_input.c | 22 ++++++++- sys/netinet/ip_var.h | 4 ++ sys/netinet6/ip6_input.c | 19 ++++++++ sys/netinet6/ip6_var.h | 4 ++ tests/sys/netpfil/common/Makefile | 1 + tests/sys/netpfil/{pf => common}/rdr.sh | 84 +++++++++++++++++++++++++++++---- tests/sys/netpfil/common/utils.subr | 4 ++ tests/sys/netpfil/pf/Makefile | 1 - 8 files changed, 127 insertions(+), 12 deletions(-) diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index 5de09a32a2f5..5eef4be5ada8 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -136,7 +136,9 @@ SYSCTL_BOOL(_net_inet_ip, OID_AUTO, source_address_validation, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip_sav), true, "Drop incoming packets with source address that is a local address"); -VNET_DEFINE(pfil_head_t, inet_pfil_head); /* Packet filter hooks */ +/* Packet filter hooks */ +VNET_DEFINE(pfil_head_t, inet_pfil_head); +VNET_DEFINE(pfil_head_t, inet_local_pfil_head); static struct netisr_handler ip_nh = { .nh_name = "ip", @@ -327,6 +329,10 @@ ip_vnet_init(void *arg __unused) args.pa_headname = PFIL_INET_NAME; V_inet_pfil_head = pfil_head_register(&args); + args.pa_flags = PFIL_OUT; + args.pa_headname = PFIL_INET_LOCAL_NAME; + V_inet_local_pfil_head = pfil_head_register(&args); + if (hhook_head_register(HHOOK_TYPE_IPSEC_IN, AF_INET, &V_ipsec_hhh_in[HHOOK_IPSEC_INET], HHOOK_WAITOK | HHOOK_HEADISINVNET) != 0) @@ -816,6 +822,20 @@ ours: return; #endif /* IPSTEALTH */ + /* + * We are going to ship the packet to the local protocol stack. Call the + * filter again for this 'output' action, allowing redirect-like rules + * to adjust the source address. + */ + if (PFIL_HOOKED_OUT(V_inet_local_pfil_head)) { + if (pfil_mbuf_out(V_inet_local_pfil_head, &m, V_loif, NULL) != + PFIL_PASS) + return; + if (m == NULL) /* consumed by filter */ + return; + ip = mtod(m, struct ip *); + } + /* * Attempt reassembly; if it succeeds, proceed. * ip_reass() will return a different mbuf. diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h index c25bae4b394f..cb4e4fbd1f42 100644 --- a/sys/netinet/ip_var.h +++ b/sys/netinet/ip_var.h @@ -255,6 +255,10 @@ VNET_DECLARE(struct pfil_head *, inet_pfil_head); #define V_inet_pfil_head VNET(inet_pfil_head) #define PFIL_INET_NAME "inet" +VNET_DECLARE(struct pfil_head *, inet_local_pfil_head); +#define V_inet_local_pfil_head VNET(inet_local_pfil_head) +#define PFIL_INET_LOCAL_NAME "inet-local" + void in_delayed_cksum(struct mbuf *m); /* Hooks for ipfw, dummynet, divert etc. Most are declared in raw_ip.c */ diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index 23550acb1fcd..1e2c3a631856 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -208,6 +208,7 @@ SYSCTL_PROC(_net_inet6_ip6, IPV6CTL_INTRDQMAXLEN, intr_direct_queue_maxlen, #endif VNET_DEFINE(pfil_head_t, inet6_pfil_head); +VNET_DEFINE(pfil_head_t, inet6_local_pfil_head); VNET_PCPUSTAT_DEFINE(struct ip6stat, ip6stat); VNET_PCPUSTAT_SYSINIT(ip6stat); @@ -245,6 +246,10 @@ ip6_vnet_init(void *arg __unused) args.pa_headname = PFIL_INET6_NAME; V_inet6_pfil_head = pfil_head_register(&args); + args.pa_flags = PFIL_OUT; + args.pa_headname = PFIL_INET6_LOCAL_NAME; + V_inet6_local_pfil_head = pfil_head_register(&args); + if (hhook_head_register(HHOOK_TYPE_IPSEC_IN, AF_INET6, &V_ipsec_hhh_in[HHOOK_IPSEC_INET6], HHOOK_WAITOK | HHOOK_HEADISINVNET) != 0) @@ -884,6 +889,20 @@ passin: return; } + /* + * We are going to ship the packet to the local protocol stack. Call the + * filter again for this 'output' action, allowing redirect-like rules + * to adjust the source address. + */ + if (PFIL_HOOKED_OUT(V_inet6_local_pfil_head)) { + if (pfil_mbuf_out(V_inet6_local_pfil_head, &m, V_loif, NULL) != + PFIL_PASS) + return; + if (m == NULL) /* consumed by filter */ + return; + ip6 = mtod(m, struct ip6_hdr *); + } + /* * Tell launch routine the next header */ diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h index bfc9f72be8ea..c267503d7151 100644 --- a/sys/netinet6/ip6_var.h +++ b/sys/netinet6/ip6_var.h @@ -325,6 +325,10 @@ VNET_DECLARE(struct pfil_head *, inet6_pfil_head); #define V_inet6_pfil_head VNET(inet6_pfil_head) #define PFIL_INET6_NAME "inet6" +VNET_DECLARE(struct pfil_head *, inet6_local_pfil_head); +#define V_inet6_local_pfil_head VNET(inet6_local_pfil_head) +#define PFIL_INET6_LOCAL_NAME "inet6-local" + #ifdef IPSTEALTH VNET_DECLARE(int, ip6stealth); #define V_ip6stealth VNET(ip6stealth) diff --git a/tests/sys/netpfil/common/Makefile b/tests/sys/netpfil/common/Makefile index 5ea3e7d9b687..99c81de3462e 100644 --- a/tests/sys/netpfil/common/Makefile +++ b/tests/sys/netpfil/common/Makefile @@ -9,6 +9,7 @@ ATF_TESTS_SH+= \ dummynet \ pass_block \ nat \ + rdr \ tos \ fragments \ forward diff --git a/tests/sys/netpfil/pf/rdr.sh b/tests/sys/netpfil/common/rdr.sh similarity index 51% rename from tests/sys/netpfil/pf/rdr.sh rename to tests/sys/netpfil/common/rdr.sh index a41b7cf57209..c66a3a012098 100644 --- a/tests/sys/netpfil/pf/rdr.sh +++ b/tests/sys/netpfil/common/rdr.sh @@ -26,17 +26,19 @@ # SUCH DAMAGE. . $(atf_get_srcdir)/utils.subr +. $(atf_get_srcdir)/runner.subr -atf_test_case "basic" "cleanup" basic_head() { - atf_set descr 'Basic rdr test' + atf_set descr 'Basic IPv4 NAT test' atf_set require.user root } basic_body() { - pft_init + firewall=$1 + firewall_init $firewall + nat_init $firewall epair=$(vnet_mkepair) @@ -48,10 +50,13 @@ basic_body() jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up jexec alcatraz sysctl net.inet.ip.forwarding=1 - # Enable pf! - jexec alcatraz pfctl -e - pft_set_rules alcatraz \ - "rdr pass on ${epair}b proto tcp from any to 198.51.100.0/24 port 1234 -> 192.0.2.1 port 4321" + # Enable redirect filter rule + firewall_config alcatraz ${firewall} \ + "pf" \ + "rdr pass on ${epair}b proto tcp from any to 198.51.100.0/24 port 1234 -> 192.0.2.1 port 4321" \ + "ipfnat" \ + "rdr ${epair}b from any to 198.51.100.0/24 port = 1234 -> 192.0.2.1 port 4321 tcp" + echo "foo" | jexec alcatraz nc -N -l 4321 & sleep 1 @@ -64,10 +69,69 @@ basic_body() basic_cleanup() { - pft_cleanup + firewall=$1 + firewall_cleanup $firewall } -atf_init_test_cases() +local_redirect_head() { - atf_add_test_case "basic" + atf_set descr 'Redirect local traffic test' + atf_set require.user root } + +local_redirect_body() +{ + firewall=$1 + firewall_init $firewall + nat_init $firewall + + bridge=$(vnet_mkbridge) + ifconfig ${bridge} 192.0.2.1/24 up + + epair1=$(vnet_mkepair) + epair2=$(vnet_mkepair) + + vnet_mkjail first ${epair1}b + ifconfig ${epair1}a up + ifconfig ${bridge} addm ${epair1}a + jexec first ifconfig ${epair1}b 192.0.2.2/24 up + jexec first ifconfig lo0 127.0.0.1/8 up + + vnet_mkjail second ${epair2}b + ifconfig ${epair2}a up + ifconfig ${bridge} addm ${epair2}a + jexec second ifconfig ${epair2}b 192.0.2.3/24 up + jexec second ifconfig lo0 127.0.0.1/8 up + jexec second sysctl net.inet.ip.forwarding=1 + + # Enable redirect filter rule + firewall_config second ${firewall} \ + "pf" \ + "rdr pass proto tcp from any to 192.0.2.3/24 port 1234 -> 192.0.2.2 port 4321" \ + "ipfnat" \ + "rdr '*' from any to 192.0.2.3/24 port = 1234 -> 192.0.2.2 port 4321 tcp" + + echo "foo" | jexec first nc -N -l 4321 & + sleep 1 + + # Verify that second can use its rule to redirect local connections to first + result=$(jexec second nc -N -w 3 192.0.2.3 1234) + if [ "$result" != "foo" ]; then + atf_fail "Redirect failed" + fi +} + +local_redirect_cleanup() +{ + firewall=$1 + firewall_cleanup $firewall +} + +setup_tests \ + basic \ + pf \ + ipfnat \ + local_redirect \ + pf \ + ipfnat + diff --git a/tests/sys/netpfil/common/utils.subr b/tests/sys/netpfil/common/utils.subr index 3475dc23bf65..f4eec24618a7 100644 --- a/tests/sys/netpfil/common/utils.subr +++ b/tests/sys/netpfil/common/utils.subr @@ -58,12 +58,16 @@ firewall_config() jexec ${jname} pfctl -e jexec ${jname} pfctl -F all jexec ${jname} pfctl -f $cwd/pf.rule + jexec ${jname} pfilctl link -o pf:default-out inet-local + jexec ${jname} pfilctl link -o pf:default-out6 inet6-local elif [ ${fw} == "ipf" ]; then jexec ${jname} ipf -E jexec ${jname} ipf -Fa -f $cwd/ipf.rule elif [ ${fw} == "ipfnat" ]; then jexec ${jname} service ipfilter start jexec ${jname} ipnat -CF -f $cwd/ipfnat.rule + jexec ${jname} pfilctl link -o ipfilter:default-ip4 inet-local + jexec ${jname} pfilctl link -o ipfilter:default-ip6 inet6-local else atf_fail "$fw is not a valid firewall to configure" fi diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile index 1117f0dcc239..8e923b6971b8 100644 --- a/tests/sys/netpfil/pf/Makefile +++ b/tests/sys/netpfil/pf/Makefile @@ -25,7 +25,6 @@ ATF_TESTS_SH+= altq \ pfsync \ prio \ proxy \ - rdr \ ridentifier \ route_to \ rtable \