Hello Aleš, On Fri, Jul 10, 2026 at 02:20:42PM +0200, Aleš Kopecký via Bird-users wrote:
> [...] > Role: egress PE, seamless-MPLS BGP L3VPN (AS 197296), IPv4-only underlay > (OSPFv2 + BGP-LU), adding IPv6 as 6VPE (RFC 4659) over the IPv4 > transport, extended next hop capability enabled on the vpn6 > channel, no IPv6 IGP / LDPv6 anywhere in the underlay. This looks a little bit cursed in itself but whatever. There are probably legitimate uses of an IPv4-only underlay. > == Symptom (unchanged from the original report) == > Control plane resolution is complete and correct — BIRD resolves the VPNv6 > route recursively via the IPv4 labeled table and builds the correct double > MPLS label stack: > > show route table vpntab6 all > 2a02:d280:201::/64 mpls 1207 unicast [proto_bgp_rr] * (100/?) [?] > via 172.21.1.1 on lan mpls 24482/16 > hostentry: via 100.127.1.26 table master4_lu mpls 16 > > But kernel export fails for these routes: > > show route table table_vpn6_overlay > 2a02:d280:201::/64 unicast [l3_overlay] ! (80/?) > via 172.21.1.1 on lan mpls 24482/16 > > <bird>: Netlink: Invalid argument (repeated, once per scan cycle) > > == Root cause == > Confirmed by manual reproduction outside BIRD: > > # ip -6 route add 2a02:d280:300::/40 encap mpls 24481/1033 via inet > 172.21.1.1 dev lan table 20 > Error: IPv6 does not support RTA_VIA attribute. > > net/ipv6/route.c in mainline Linux does not accept RTA_VIA (the netlink > attribute for a cross-family nexthop) on an IPv6 route, full stop — > regardless of whether the address is a raw IPv4 address or an IPv4-mapped > IPv6 address. Since our underlay is IPv4-only, every VPNv6 route we import > resolves (via the IPv4-labeled IGP table) to a physically IPv4-family > adjacency, so every such route hits this kernel limitation. My primary thought would be to push this into the kernel, or to deploy an IPv6 underlay. Do I assume correctly that your whole network has been IPv4-only until now, and you are trying to get IPv6 working, which means that deploying an IPv6 overlay would be way harder than doing this kind of hack? > [...] > > == The patch == > Confirmed the neighboring router already sends IPv6 Router Advertisements > on the segment (independent of any IPv6 IGP — RA is link-scoped), so the > kernel already keeps a working ND cache entry for it: > > ip -6 neigh show dev lan > fe80::1 lladdr dc:68:0c:32:ed:b2 router STALE Well, this is in itself kinda fragile, as we can't really take for granted that an RA would arrive. > The attached patch adds a small on-link-router cache to netlink.c (mirrors > the existing AF_BRIDGE FDB cache/parsing that's already in the file, just > generalized to AF_INET6 neighbors flagged NTF_ROUTER), refreshed once per > KRT scan cycle. In nl_add_nexthop(), when exporting an IPv6 route whose > resolved gateway is IPv4-family, it looks up this cache by egress interface > and substitutes the neighbor's real link-local address for RTA_GATEWAY > instead of building the (rejected) RTA_VIA — the MPLS encap is untouched. > No interface name or address is hardcoded anywhere; it's fully derived from > what the kernel already knows via ND. If the cache has no entry yet for > that interface, behavior is unchanged from today (route stays uninstalled, > self-heals on the next scan) — no regression. I'm not reading the patch in full but it looks like you expect that the link is point-to-point, i.e. there is only one remote router per link. That is generally not true. You would actually need a full translation table between IPv4 and IPv6 next hop, populated by reading both ARP and ND cache and matching the entries. And even that needs some caution. > [...] > > == Questions for the list / maintainers == > 1. Is this an acceptable place/approach to fix this, or would you rather > see it handled differently (e.g. in rt-table.c nexthop resolution > itself, or as an explicit config knob rather than automatic ND-based > discovery)? It looks like a crude hack with no inspectability. We generally prefer tables to be accessible by users from CLI. > 2. Known edge case: if a segment ever has more than one NTF_ROUTER > neighbor on the same interface, the cache currently keeps whichever > one was last seen in the dump (non-deterministic ordering). Not an > issue in our topology (always exactly one router per segment), but > worth flagging. This is not an edge case, this is a blocker for upstreaming. > 3. Cache refresh is tied to the existing KRT scan interval rather than > the async netlink notification path, to avoid touching the shared > AF_BRIDGE dispatch code. Happy to change this if an event-driven > refresh is preferred upstream. I would expect that a reasonable ARP-ND cache would be actually tied to both, as other tables are. This whole topic will probably need a senior meeting to discus possible caveats in the implementation, so that BIRD stays maintainable and well-performing into future. But before I put this into our backlog, there may be some tricks hidden in the hostentry mechanism and autopeering, which could resolve your whole problem way easier. Or not, I don't have enough information. We can look into your problem best effort if you send us your config and tell us more about how the network actually looks like, or it can be done faster if you include [email protected] in the loop and get [BIRD Support](https://bird.nic.cz/commercial-services/#deployments-isp). I hope this helps. Note: I see various indications that the e-mail I'm replying to was written by Claude Sonnet 5, and I would appreciate disclosing such information. We are experimenting with using LLMs to aid development, currently still with miserable results, and knowing which LLMs generate which kind of output, would be nice, to better calibrate our approach to LLMs. -- Maria Matejka (she/her) | BIRD Team Leader | CZ.NIC, z.s.p.o.
