Re: IPv4 route with IPv6 gateway
"Nitsche, Elia" <[email protected]> Tue, 21 Jul 2026 14:13:50 +0000
| Newsgroups | gmane.os.openbsd.tech |
|---|---|
| Message-ID | <[email protected]> |
Hi,
I started implementing this too, but I am using a route flag (currently set by
sbin/route). Your approach might be cleaner though.
Also my diff, does not set the correct ipv4 address in icmp message send from
router nodes without an ivp4 address. I dont see any code for this in your diff
either.
(currently sends as 0.0.0.0, should be 192.0.0.8; see RFC draft-ietf-intarea-v4-via)
elia
Index: route.c
===================================================================
RCS file: /cvs/src/sbin/route/route.c,v
diff -u -p -u -r1.267 route.c
--- route.c 9 May 2024 08:35:40 -0000 1.267
+++ route.c 21 Jul 2026 14:05:13 -0000
@@ -790,6 +790,11 @@ newroute(int argc, char **argv)
flags |= RTF_HOST;
if (iflag == 0)
flags |= RTF_GATEWAY;
+ if (so_dst.sin.sin_family == AF_INET && so_gate.sin6.sin6_family == AF_INET6) {
+ flags |= RTF_6NHOB;
+ fmask |= RTF_6NHOB;
+ }
for (attempts = 1; ; attempts++) {
errno = 0;
if ((ret = rtmsg(*cmd, flags, fmask, prio)) == 0)
Index: net/if.c
===================================================================
RCS file: /cvs/src/sys/net/if.c,v
diff -u -p -u -r1.763 if.c
--- net/if.c 22 Mar 2026 23:14:00 -0000 1.763
+++ net/if.c 21 Jul 2026 13:13:55 -0000
@@ -935,7 +935,10 @@ if_output_tso(struct ifnet *ifp, struct
break;
#ifdef INET6
case AF_INET6:
- ifcap = IFCAP_TSOv6;
+ if (rt == NULL || !(rt->rt_flags & RTF_6NHOP))
+ ifcap = IFCAP_TSOv6;
+ else
+ ifcap = IFCAP_TSOv4;
break;
#endif
default:
@@ -960,7 +963,13 @@ if_output_tso(struct ifnet *ifp, struct
break;
#ifdef INET6
case AF_INET6:
- in6_proto_cksum_out(*mp, ifp);
+ if (rt == NULL || !(rt->rt_flags & RTF_6NHOP)) {
+ in6_proto_cksum_out(*mp, ifp);
+ } else {
+ in_hdr_cksum_out(*mp, ifp);
+ in_proto_cksum_out(*mp, ifp);
+ }
break;
#endif
}
Index: net/if_ethersubr.c
===================================================================
RCS file: /cvs/src/sys/net/if_ethersubr.c,v
diff -u -p -u -r1.308 if_ethersubr.c
--- net/if_ethersubr.c 19 Dec 2025 02:04:13 -0000 1.308
+++ net/if_ethersubr.c 21 Jul 2026 13:13:55 -0000
@@ -265,7 +265,11 @@ 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_flags & RTF_6NHOP)) {
+ eh->ether_type = htons(ETHERTYPE_IPV6);
+ } else {
+ eh->ether_type = htons(ETHERTYPE_IP);
+ }
break;
#endif
#ifdef MPLS
Index: net/route.h
===================================================================
RCS file: /cvs/src/sys/net/route.h,v
diff -u -p -u -r1.218 route.h
--- net/route.h 14 Jul 2025 08:48:51 -0000 1.218
+++ net/route.h 21 Jul 2026 13:13:55 -0000
@@ -167,11 +167,12 @@ struct rtentry {
#define RTF_BROADCAST 0x400000 /* route associated to a bcast addr. */
#define RTF_CONNECTED 0x800000 /* interface route */
#define RTF_BFD 0x1000000 /* Link state controlled by BFD */
+#define RTF_6NHOP 0x2000000 /* v4 route with v6 next hop */
/* mask of RTF flags that are allowed to be modified by RTM_CHANGE */
#define RTF_FMASK \
(RTF_LLINFO | RTF_PROTO1 | RTF_PROTO2 | RTF_PROTO3 | RTF_BLACKHOLE | \
- RTF_REJECT | RTF_STATIC | RTF_MPLS | RTF_BFD)
+ RTF_REJECT | RTF_STATIC | RTF_MPLS | RTF_BFD | RTF_6NHOP)
/* Routing priorities used by the different routing protocols */
#define RTP_NONE 0 /* unset priority use sane default */
________________________________
From: [email protected] <[email protected]> on behalf of Denis Fondras <[email protected]>
Sent: Monday, July 20, 2026 5:39:24 PM
To: [email protected]
Subject: IPv4 route with IPv6 gateway
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: <UP,GATEWAY,DONE,STATIC>
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)