git: e27970ae8fff - main - netinet: handle blackhole routes

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Wed, 20 Nov 2024 17:00:44 UTC
The branch main has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=e27970ae8fffd7d14cd106efc150e60ae307607a

commit e27970ae8fffd7d14cd106efc150e60ae307607a
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2024-11-12 15:55:50 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2024-11-20 15:52:41 +0000

    netinet: handle blackhole routes
    
    If during ip_forward() we find a blackhole (or reject) route we should stop
    processing and count this in the 'cantforward' counter, just like we already do
    for IPv6.
    Blackhole routes are set to use the loopback interface, so we don't actually
    incorrectly forward traffic, but we do fail to count it as unroutable.
    
    Test this, both for IPv4 and IPv6.
    
    Reviewed by:    melifaro
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D47529
---
 sys/netinet/ip_input.c         | 12 ++++++++++
 tests/sys/netinet/forward.sh   | 53 +++++++++++++++++++++++++++++++++++++++++
 tests/sys/netinet6/forward6.sh | 54 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 119 insertions(+)

diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c
index 82d7acdd0710..e00f3b77c74c 100644
--- a/sys/netinet/ip_input.c
+++ b/sys/netinet/ip_input.c
@@ -942,6 +942,18 @@ ip_forward(struct mbuf *m, int srcrt)
 	flowid = m->m_pkthdr.flowid;
 	ro.ro_nh = fib4_lookup(M_GETFIB(m), ip->ip_dst, 0, NHR_REF, flowid);
 	if (ro.ro_nh != NULL) {
+		if (ro.ro_nh->nh_flags & (NHF_BLACKHOLE | NHF_BROADCAST)) {
+			IPSTAT_INC(ips_cantforward);
+			m_freem(m);
+			NH_FREE(ro.ro_nh);
+			return;
+		}
+		if (ro.ro_nh->nh_flags & NHF_REJECT) {
+			IPSTAT_INC(ips_cantforward);
+			NH_FREE(ro.ro_nh);
+			icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0);
+			return;
+		}
 		ia = ifatoia(ro.ro_nh->nh_ifa);
 	} else
 		ia = NULL;
diff --git a/tests/sys/netinet/forward.sh b/tests/sys/netinet/forward.sh
index e16927a27d07..3ae83eb3edc5 100755
--- a/tests/sys/netinet/forward.sh
+++ b/tests/sys/netinet/forward.sh
@@ -259,6 +259,58 @@ fwd_ip_icmp_gw_slow_success_cleanup() {
 	vnet_cleanup
 }
 
+atf_test_case "fwd_ip_blackhole" "cleanup"
+fwd_ip_blackhole_head() {
+
+	atf_set descr 'Test blackhole routes'
+	atf_set require.user root
+}
+
+fwd_ip_blackhole_body() {
+	jname="v4t-fwd_ip_blackhole"
+
+	vnet_init
+
+	epair=$(vnet_mkepair)
+	epair_out=$(vnet_mkepair)
+
+	ifconfig ${epair}a 192.0.2.2/24 up
+
+	vnet_mkjail ${jname} ${epair}b ${epair_out}b
+	jexec ${jname} ifconfig lo0 127.0.0.1/8 up
+	jexec ${jname} ifconfig ${epair}b 192.0.2.1/24 up
+	jexec ${jname} ifconfig ${epair_out}b 198.51.100.1/24 up
+	jexec ${jname} sysctl net.inet.ip.forwarding=1
+
+	route add default 192.0.2.1
+
+	atf_check -s exit:2 -o ignore \
+	    ping -c 1 -t 1 198.51.100.2
+	atf_check -s exit:0 -o match:"0 packets not forwardable" \
+	    jexec ${jname} netstat -s -p ip
+
+	# Create blackhole route
+	jexec ${jname} /sbin/route add 198.51.100.2 -blackhole -fib 0
+	jexec ${jname} netstat -rn
+
+	# Include an IP option to ensure slow path
+	atf_check -s exit:2 -o ignore \
+	    ping -c 1 -t 1 -R 198.51.100.2
+	atf_check -s exit:0 -o match:"1 packet not forwardable" \
+	    jexec ${jname} netstat -s -p ip
+
+	# Now try via the fast path
+	atf_check -s exit:2 -o ignore \
+	    ping -c 1 -t 1 198.51.100.2
+	atf_check -s exit:0 -o match:"2 packets not forwardable" \
+	    jexec ${jname} netstat -s -p ip
+}
+
+fwd_ip_blackhole_cleanup() {
+
+	vnet_cleanup
+}
+
 atf_init_test_cases()
 {
 
@@ -266,6 +318,7 @@ atf_init_test_cases()
 	atf_add_test_case "fwd_ip_icmp_gw_fast_success"
 	atf_add_test_case "fwd_ip_icmp_iface_slow_success"
 	atf_add_test_case "fwd_ip_icmp_gw_slow_success"
+	atf_add_test_case "fwd_ip_blackhole"
 }
 
 # end
diff --git a/tests/sys/netinet6/forward6.sh b/tests/sys/netinet6/forward6.sh
index b3ccd30aea62..40d17837d6f2 100755
--- a/tests/sys/netinet6/forward6.sh
+++ b/tests/sys/netinet6/forward6.sh
@@ -466,6 +466,59 @@ fwd_ip6_gu_icmp_gw_ll_slow_success_cleanup() {
 	vnet_cleanup
 }
 
+atf_test_case "fwd_ip6_blackhole" "cleanup"
+fwd_ip6_blackhole_head() {
+
+	atf_set descr 'Test blackhole routing'
+	atf_set require.user root
+}
+
+fwd_ip6_blackhole_body() {
+	jname="v6t-fwd_ip6_blackhole"
+
+	vnet_init
+
+	epair=$(vnet_mkepair)
+	epair_out=$(vnet_mkepair)
+
+	ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+
+	vnet_mkjail ${jname} ${epair}b ${epair_out}b
+	jexec ${jname} ifconfig lo0 inet6 ::1/128 up no_dad
+	jexec ${jname} ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+	jexec ${jname} ifconfig ${epair_out}b inet6 2001:db8:1::1/64 up no_dad
+	jexec ${jname} sysctl net.inet6.ip6.forwarding=1
+
+	route -6 add default 2001:db8::1
+
+	atf_check -s exit:2 -o ignore \
+	    ping6 -c 1 -t 1 2001:db8:1::2
+	atf_check -s exit:0 -o match:"0 packets not forwardable" \
+	    jexec ${jname} netstat -s -p ip6
+
+	# Create blackhole route
+	jexec ${jname} route -6 add 2001:db8:1::2 -blackhole
+
+	# Force slow path
+	jexec ${jname} sysctl net.inet6.ip6.redirect=1
+	atf_check -s exit:2 -o ignore \
+	    ping6 -c 1 -t 1 2001:db8:1::2
+	atf_check -s exit:0 -o match:"1 packet not forwardable" \
+	    jexec ${jname} netstat -s -p ip6
+
+	# Now try the fast path
+	jexec ${jname} sysctl net.inet6.ip6.redirect=0
+	atf_check -s exit:2 -o ignore \
+	    ping6 -c 1 -t 1 2001:db8:1::2
+	atf_check -s exit:0 -o match:"2 packets not forwardable" \
+	    jexec ${jname} netstat -s -p ip6
+}
+
+fwd_ip6_blackhole_cleanup() {
+
+	vnet_cleanup
+}
+
 atf_init_test_cases()
 {
 
@@ -475,6 +528,7 @@ atf_init_test_cases()
 	atf_add_test_case "fwd_ip6_gu_icmp_iface_slow_success"
 	atf_add_test_case "fwd_ip6_gu_icmp_gw_gu_slow_success"
 	atf_add_test_case "fwd_ip6_gu_icmp_gw_ll_slow_success"
+	atf_add_test_case "fwd_ip6_blackhole"
 }
 
 # end