From e27970ae8fffd7d14cd106efc150e60ae307607a Mon Sep 17 00:00:00 2001 From: Kristof Provost Date: Tue, 12 Nov 2024 16:55:50 +0100 Subject: [PATCH] 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