From: Denis Fondras Subject: IPv4 route with IPv6 gateway To: tech@openbsd.org Date: Mon, 20 Jul 2026 17:39:24 +0200 Hello, Inspired by https://labs.ripe.net/author/remco-van-mook/a-farewell-to-arps-ipv4-service-on-ipv6-only-networks/ I decided to check how OpenBSD behaves when an IPv4 route uses an IPv6 gateway. With the help of a small diff below, I can : ``` $ doas ifconfig iwm0 192.168.21.234/32 up $ doas ifconfig iwm0 inet6 eui64 $ doas route add default fe80::764d:28ff:fe83:fbdc%iwm0 $ route -n get default route to: 0.0.0.0 destination: 0.0.0.0 mask: 0.0.0.0 gateway: fe80::764d:28ff:fe83:fbdc%iwm0 interface: iwm0 if address: 192.168.21.234 priority: 12 () flags: use mtu expire 665 0 0 $ ping 8.8.8.8 PING 8.8.8.8 (8.8.8.8): 56 data bytes 64 bytes from 8.8.8.8: icmp_seq=0 ttl=116 time=12.392 ms 64 bytes from 8.8.8.8: icmp_seq=1 ttl=116 time=11.987 ms ^C --- 8.8.8.8 ping statistics --- 2 packets transmitted, 2 packets received, 0.0% packet loss round-trip min/avg/max/std-dev = 11.987/12.190/12.392/0.202 ms $ arp -an Host Ethernet Address Netif Expire Flags 192.168.21.234 fe:e1:ba:d0:c9:9b iwm0 permanent l $ ndp -an Neighbor Linklayer Address Netif Expire S Flags fe80::764d:28ff:fe83:fbdc%iwm0 74:4d:28:83:fb:dc iwm0 23h59m6s S R fe80::fce1:baff:fed0:c99b%iwm0 fe:e1:ba:d0:c9:9b iwm0 permanent R l ``` Index: net/if.c =================================================================== RCS file: /cvs/src/sys/net/if.c,v diff -u -p -r1.763 if.c --- net/if.c 22 Mar 2026 23:14:00 -0000 1.763 +++ net/if.c 20 Jul 2026 15:28:03 -0000 @@ -928,8 +928,12 @@ if_output_tso(struct ifnet *ifp, struct { uint32_t ifcap; int error; + int af = dst->sa_family; - switch (dst->sa_family) { + if (*mp != NULL) + af = (*mp)->m_pkthdr.ph_family; + + switch (af) { case AF_INET: ifcap = IFCAP_TSOv4; break; @@ -939,7 +943,7 @@ if_output_tso(struct ifnet *ifp, struct break; #endif default: - unhandled_af(dst->sa_family); + unhandled_af(af); } /* @@ -953,7 +957,7 @@ if_output_tso(struct ifnet *ifp, struct return error; if ((*mp)->m_pkthdr.len <= mtu) { - switch (dst->sa_family) { + switch (af) { case AF_INET: in_hdr_cksum_out(*mp, ifp); in_proto_cksum_out(*mp, ifp); Index: net/if_ethersubr.c =================================================================== RCS file: /cvs/src/sys/net/if_ethersubr.c,v diff -u -p -r1.308 if_ethersubr.c --- net/if_ethersubr.c 19 Dec 2025 02:04:13 -0000 1.308 +++ net/if_ethersubr.c 20 Jul 2026 15:28:03 -0000 @@ -265,7 +265,10 @@ ether_resolve(struct ifnet *ifp, struct error = nd6_resolve(ifp, rt, m, dst, eh->ether_dhost); if (error) return (error); - eh->ether_type = htons(ETHERTYPE_IPV6); + if (rt != NULL && rt->rt_dest->sa_family == AF_INET) + eh->ether_type = htons(ETHERTYPE_IP); + else + eh->ether_type = htons(ETHERTYPE_IPV6); break; #endif #ifdef MPLS Index: netinet/ip_output.c =================================================================== RCS file: /cvs/src/sys/netinet/ip_output.c,v diff -u -p -r1.417 ip_output.c --- netinet/ip_output.c 6 May 2026 11:36:13 -0000 1.417 +++ netinet/ip_output.c 20 Jul 2026 15:28:03 -0000 @@ -434,6 +434,7 @@ reroute: /* * If TSO or small enough for interface, can just send directly. */ + m->m_pkthdr.ph_family = AF_INET; error = if_output_tso(ifp, &m, sintosa(dst), ro->ro_rt, mtu); if (error || m == NULL) goto done; Index: netinet6/ip6_forward.c =================================================================== RCS file: /cvs/src/sys/netinet6/ip6_forward.c,v diff -u -p -r1.130 ip6_forward.c --- netinet6/ip6_forward.c 23 Jun 2026 15:45:00 -0000 1.130 +++ netinet6/ip6_forward.c 20 Jul 2026 15:28:03 -0000 @@ -323,6 +323,7 @@ reroute: } #endif + m->m_pkthdr.ph_family = AF_INET6; error = if_output_tso(ifp, &m, dst, rt, ifp->if_mtu); if (error) ip6stat_inc(ip6s_cantforward); Index: netinet6/ip6_output.c =================================================================== RCS file: /cvs/src/sys/netinet6/ip6_output.c,v diff -u -p -r1.306 ip6_output.c --- netinet6/ip6_output.c 25 Jun 2026 13:16:44 -0000 1.306 +++ netinet6/ip6_output.c 20 Jul 2026 15:28:03 -0000 @@ -661,6 +661,7 @@ reroute: * transmit packet without fragmentation */ if (dontfrag || tlen <= mtu) { /* case 1-a and 2-a */ + m->m_pkthdr.ph_family = AF_INET6; error = if_output_tso(ifp, &m, sin6tosa(dst), ro->ro_rt, ifp->if_mtu); if (error || m == NULL)