kern: wg: remove overly-restrictive address family check

IPv4 packets can be routed via an IPv6 nexthop, so the handling of the
parsed address family is more strict than it needs to be.  If we have a
valid header that matches a known peer, then we have no reason to
decline the packet.

Convert it to an assertion that it matches the destination as viewed by
the stack below it, instead.  `dst` may be the gateway instead of the
destination in the case of a nexthop, so the `af` assignment must be
switched to use the destination in all cases.

Add a test case that approximates a setup like in the PR and
demonstrates the issue.

PR:		284857
Reviewed by:	markj (earlier version), zlei

(cherry picked from commit 2bef0d54f74dad6962ef7d1dfa407e95cb4fb4ad)
This commit is contained in:
Kyle Evans 2025-03-04 13:57:34 -06:00 committed by Franco Fichtner
parent ef741cec70
commit c78b0e3436
2 changed files with 82 additions and 5 deletions

View file

@ -2333,7 +2333,7 @@ wg_output(if_t ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro
if (dst->sa_family == AF_UNSPEC || dst->sa_family == pseudo_AF_HDRCMPLT)
memcpy(&af, dst->sa_data, sizeof(af));
else
af = dst->sa_family;
af = RO_GET_FAMILY(ro, dst);
if (af == AF_UNSPEC) {
xmit_err(ifp, m, NULL, af);
return (EAFNOSUPPORT);
@ -2358,10 +2358,8 @@ wg_output(if_t ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro
xmit_err(ifp, m, NULL, AF_UNSPEC);
return (ret);
}
if (parsed_af != af) {
xmit_err(ifp, m, NULL, AF_UNSPEC);
return (EAFNOSUPPORT);
}
MPASS(parsed_af == af);
mtu = (ro != NULL && ro->ro_mtu > 0) ? ro->ro_mtu : if_getmtu(ifp);
return (wg_xmit(ifp, m, parsed_af, mtu));
}

View file

@ -92,6 +92,84 @@ wg_basic_cleanup()
vnet_cleanup
}
atf_test_case "wg_basic_crossaf" "cleanup"
wg_basic_crossaf_head()
{
atf_set descr 'Create a wg(4) tunnel and pass IPv4 traffic over an IPv6 nexthop'
atf_set require.user root
}
wg_basic_crossaf_body()
{
local epair pri1 pri2 pub1 pub2 wg1 wg2
local endpoint1 endpoint2 tunnel1 tunnel2
local testnet testlocal testremote
kldload -n if_wg || atf_skip "This test requires if_wg and could not load it"
pri1=$(wg genkey)
pri2=$(wg genkey)
endpoint1=192.168.2.1
endpoint2=192.168.2.2
tunnel1=2001:db8:1::1
tunnel2=2001:db8:1::2
testnet=192.168.3.0/24
testlocal=192.168.3.1
testremote=192.168.3.2
epair=$(vnet_mkepair)
vnet_init
vnet_mkjail wgtest1 ${epair}a
vnet_mkjail wgtest2 ${epair}b
jexec wgtest1 ifconfig ${epair}a ${endpoint1}/24 up
jexec wgtest2 ifconfig ${epair}b ${endpoint2}/24 up
wg1=$(jexec wgtest1 ifconfig wg create)
echo "$pri1" | jexec wgtest1 wg set $wg1 listen-port 12345 \
private-key /dev/stdin
pub1=$(jexec wgtest1 wg show $wg1 public-key)
wg2=$(jexec wgtest2 ifconfig wg create)
echo "$pri2" | jexec wgtest2 wg set $wg2 listen-port 12345 \
private-key /dev/stdin
pub2=$(jexec wgtest2 wg show $wg2 public-key)
atf_check -s exit:0 -o ignore \
jexec wgtest1 wg set $wg1 peer "$pub2" \
endpoint ${endpoint2}:12345 allowed-ips ${tunnel2}/128,${testnet}
atf_check -s exit:0 \
jexec wgtest1 ifconfig $wg1 inet6 ${tunnel1}/64 up
atf_check -s exit:0 -o ignore \
jexec wgtest2 wg set $wg2 peer "$pub1" \
endpoint ${endpoint1}:12345 allowed-ips ${tunnel1}/128,${testnet}
atf_check -s exit:0 \
jexec wgtest2 ifconfig $wg2 inet6 ${tunnel2}/64 up
atf_check -s exit:0 jexec wgtest1 ifconfig $wg1 inet ${testlocal}/32
atf_check -s exit:0 jexec wgtest2 ifconfig $wg2 inet ${testremote}/32
# Generous timeout since the handshake takes some time.
atf_check -s exit:0 -o ignore jexec wgtest1 ping -c 1 -t 5 "$tunnel2"
# Setup our IPv6 endpoint and routing
atf_check -s exit:0 -o ignore \
jexec wgtest1 route add -inet ${testnet} -inet6 "$tunnel2"
atf_check -s exit:0 -o ignore \
jexec wgtest2 route add -inet ${testnet} -inet6 "$tunnel1"
# Now ping an address on the other side
atf_check -s exit:0 -o ignore jexec wgtest1 ping -c 1 -t 3 ${testremote}
}
wg_basic_crossaf_cleanup()
{
vnet_cleanup
}
atf_test_case "wg_basic_netmap" "cleanup"
wg_basic_netmap_head()
{
@ -349,6 +427,7 @@ wg_vnet_parent_routing_cleanup()
atf_init_test_cases()
{
atf_add_test_case "wg_basic"
atf_add_test_case "wg_basic_crossaf"
atf_add_test_case "wg_basic_netmap"
atf_add_test_case "wg_key_peerdev_shared"
atf_add_test_case "wg_key_peerdev_makeshared"