Re: IPv4 route with IPv6 gateway

Denis Fondras <[email protected]> Tue, 21 Jul 2026 17:08:31 +0200
Newsgroups gmane.os.openbsd.tech
Message-ID <[email protected]>
Le Tue, Jul 21, 2026 at 02:13:50PM +0000, Nitsche, Elia a écrit :
> 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
> 

Thank you for your diff. Adding a flag is a good idea :)

> 
> 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)