The branch main has been updated by kevans:

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

commit 2bef0d54f74dad6962ef7d1dfa407e95cb4fb4ad
Author:     Kyle Evans <kev...@freebsd.org>
AuthorDate: 2025-03-04 19:57:34 +0000
Commit:     Kyle Evans <kev...@freebsd.org>
CommitDate: 2025-03-04 19:57:34 +0000

    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
    Differential Revision:  https://reviews.freebsd.org/D49172
---
 sys/dev/wg/if_wg.c     |  8 ++---
 tests/sys/net/if_wg.sh | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 82 insertions(+), 5 deletions(-)

diff --git a/sys/dev/wg/if_wg.c b/sys/dev/wg/if_wg.c
index 6683d0505b26..83e5d9e5ceb3 100644
--- a/sys/dev/wg/if_wg.c
+++ b/sys/dev/wg/if_wg.c
@@ -2335,7 +2335,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);
@@ -2360,10 +2360,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));
 }
diff --git a/tests/sys/net/if_wg.sh b/tests/sys/net/if_wg.sh
index b43b40f25018..e5df6afface1 100644
--- a/tests/sys/net/if_wg.sh
+++ b/tests/sys/net/if_wg.sh
@@ -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"

Reply via email to