Re: Source address selection failure when using RFC5549-style (v4-via-v6) routes

From: Zhenlei Huang <zlei_at_FreeBSD.org>
Date: Wed, 06 Aug 2025 08:55:53 UTC

> On Aug 5, 2025, at 10:09 PM, Felix <felix@ffetc.net> wrote:
> 
> I've been experimenting a bit with routing IPv4 via IPv6 next-hops, something that FreeBSD has supported since 13.1
> [https://reviews.freebsd.org/D30398].
> 
> My understanding (note: I'm not a professional network engineer) is that this is normally used with routing protocols like BGP (RFC5549, often called "extended nexthop") or Babel (RFC9229), where it's extremely convenient - the links between your routers only need link-local v6 addresses to forward both v4 and v6 traffic, reducing configuration and saving IP addresses. Of course, each router should have /one/ routable v4 and v6 address so that it can return ICMP messages and so on, which one would typically assign on the loopback interface.

Yes.

> 
> For experimentation, you can create these routes directly, without running one of these protocols, like so:
> 
> # route add -net 203.0.113.2/32 -inet6 fe80::2%bge2
> add net 203.0.113.2: gateway fe80::2%bge2
> 
> # netstat -r4n
> Routing tables
> 
> Internet:
> Destination        Gateway            Flags         Netif Expire
> ...
> 203.0.113.2        fe80::2%bge2       UGHS           bge2
> ...
> 
> What I've discovered: trying to /originate/ a connection from a machine with such a route doesn't seem to work properly. Any attempts to open a TCP connection (e.g. ssh) will fail with EHOSTUNREACH:
> 
> # truss ssh 203.0.113.2
> ...
> connect(3,{ AF_INET 203.0.113.2:22 },16)         ERR#65 'No route to host'
> ...
> ssh: connect to host 203.0.113.2 port 22: No route to host
> ...
> process exit, rval = 255
> 
> When choosing a source address for an outgoing connection, FreeBSD does a route lookup (which succeeds in this case), and as a comment in sys/netinet/in_pcb.c helpfully tells us:
> 
> 	/*
> 	 * If the outgoing interface on the route found is not
> 	 * a loopback interface, use the address from that interface.
> 	 */
> 
> Because the outgoing interface doesn't /have/ a v4 address, a consistency check at the end of the function returns EHOSTUNREACH.

This is a known limitation of current IPv4 over IPv6 nexthop. I have ever asked on #net mailing list about the IPv4 source selection but no good luck. So currently it is mandatory to have an IPv4 address assigned to the outgoing interface, if originate a connection from the host. Well certainly that restrictioni is not needed for forward packets.

You can apply Lexi's workaround to overcome the issue.

> 
> Linux also supports these v4-via-v6 routes, so I can compare how it behaves. Firstly, it will use the address specified in the `src` attribute of the route if there is one (FreeBSD, AFAIK, has no such notion). Otherwise, it follows some process to pick one of the machine's addresses to use as a source address, and will (if necessary, like in this scenario) pick an address from a different interface.
> 
> I'm not sure what the right thing for FreeBSD to do in this circumstance is. What do you think?

I think we should borrow some rules from IPv6 source address selection. I have not tried Linux, but I think use a global unicast address from loopback interface is acceptable, especially when the host is a router.

> 
> I also haven't tested whether this same issue affects the generation of ICMP responses (e.g. TTL expired, packet too big). If it does, that seems like much more of a concern for using FreeBSD on real routers.

True. For IPv4 when generating the ICMP responses, IPv4 source address selection is also involved.

> 
> Thanks,
> - Felix
> 

Best regards,
Zhenlei