Skip to main content
RunBook Academy

VyOSXXI · OSPFv3 / IPv6 RoutingIPv6 routing

Dual-stack OSPF — running OSPFv2 and OSPFv3 on the same network

Advanced⏱ ~24 minset protocols ospf area 0 networkset protocols ospfv3 area 0 interfaceset firewall ipv4 input filterset firewall ipv6 input filtershow ip ospf neighborshow ipv6 ospf6 neighborshow ip route ospfshow ipv6 route ospf6show firewall statisticstcpdump

What you'll learn

  • Configure OSPFv2 and OSPFv3 on the same dual-stack link as two independent processes
  • Explain where each process gets its router-id, and when you must supply one
  • Write the VyOS 1.5 input-filter rules that let both families' control packets reach the router
  • State why redistribution between OSPFv2 and OSPFv3 does not exist, and what the real answers are
  • Identify the production scenarios where dual-stack is the answer, and plan an IPv4 retirement

Prerequisites

Verified against VyOS 1.5.x LTS (circinus) · VyOS 1.4.x (sagitta) — legacy · FRRouting 10.x (VyOS 1.5) · Linux kernel 6.6 LTS (VyOS 1.5 base) · strongSwan 5.9.x (IPsec) · WireGuard 1.0.x (kernel module + userspace tooling) · 2026-08-19

Not yet marked complete on this device.

A dual-stack deployment runs IPv4 and IPv6 on the same network. For OSPF, that means OSPFv2 for IPv4 and OSPFv3 for IPv6 — two separate daemons, two separate adjacencies on the same wire, two separate link-state databases, two separate RIBs.

They do not interact. That is not a configuration choice you could change; it is what the two protocols are. OSPFv3 is a different protocol that happens to share a name and a heritage with OSPFv2, and the practical consequences of that independence are what this lesson is about.

The dual-stack configuration pattern

The minimum dual-stack OSPF configuration on a VyOS router:

# OSPFv2 for IPv4
set protocols ospf parameters router-id 10.255.0.1
set protocols ospf area 0 network 10.0.0.0/24
set protocols ospf area 0 network 10.1.0.0/24

# OSPFv3 for IPv6
set protocols ospfv3 parameters router-id 10.255.0.1
set protocols ospfv3 area 0 interface eth0
set protocols ospfv3 area 0 interface eth1
commit
save

Two processes, two configuration blocks. Note that the two blocks select interfaces differently: OSPFv2 matches interfaces by the IPv4 network statement, while OSPFv3 names the interface directly. That difference is not a VyOS quirk — it is RFC 5340. OSPFv3 runs “per link” rather than “per subnet”, which is also why an OSPFv3 interface needs no IPv6 prefix configured for the adjacency to form.

flowchart TB
  subgraph "VyOS R1 (dual-stack)"
    O2["ospfd<br/>router-id 10.255.0.1"]
    O3["ospf6d<br/>router-id 10.255.0.1"]
    E0["eth0<br/>IPv4 10.0.0.1/24<br/>IPv6 2001:db8:1::1/64<br/>link-local fe80::1"]
    E1["eth1<br/>IPv4 10.1.0.1/24<br/>IPv6 2001:db8:2::1/64"]
  end
  subgraph "R2 (neighbour)"
    O2R["ospfd"]
    O3R["ospf6d"]
    E0R["eth0<br/>IPv4 10.0.0.2/24<br/>IPv6 2001:db8:1::2/64<br/>link-local fe80::2"]
  end
  E0 -- "OSPFv2 Hello to 224.0.0.5<br/>OSPFv3 Hello to ff02::5" --> E0R
  E0R -- "OSPFv2 Hello + OSPFv3 Hello" --> E0
  O2 --> E0
  O3 --> E0
  O2R --> E0R
  O3R --> E0R

The two adjacencies share the wire and nothing else: OSPFv2 talks to 224.0.0.5, OSPFv3 to ff02::5. Both are IP protocol 89, which matters when you write firewall rules, because “protocol 89” alone does not distinguish them — the address family does.

The firewall both families need

The single most common dual-stack OSPF failure is a firewall that permits one family’s control packets and not the other’s. Getting this right needs two facts about VyOS 1.5 that operators regularly get wrong in opposite directions.

VyOS ships no firewall. A router with no firewall configuration accepts everything, in both families. There is no default rule set containing an OSPF permit, for IPv4 or for IPv6, because there is no default rule set at all. The moment you create an input filter with default-action drop, you own the whole problem — including the protocols the router itself speaks.

Traffic addressed to the router is filtered by the input chain. The per-interface firewall local binding of VyOS 1.3 is gone. In 1.5, packets destined for the router are matched by set firewall ipv4 input filter and set firewall ipv6 input filter, and the idiom for keeping per-interface policy readable is a rule in the input filter that jumps to a named set.

IPv4 — OSPFv2

set firewall ipv4 name WAN-LOCAL default-action drop
set firewall ipv4 name WAN-LOCAL rule 10 action accept
set firewall ipv4 name WAN-LOCAL rule 10 protocol ospf
set firewall ipv4 name WAN-LOCAL rule 10 source address 10.0.0.0/24
set firewall ipv4 name WAN-LOCAL rule 10 description 'OSPFv2 control plane'

set firewall ipv4 input filter default-action drop
set firewall ipv4 input filter rule 10 action jump
set firewall ipv4 input filter rule 10 jump-target WAN-LOCAL
set firewall ipv4 input filter rule 10 inbound-interface name eth0
commit
save

IPv6 — OSPFv3

set firewall ipv6 name WAN-LOCAL-6 default-action drop
set firewall ipv6 name WAN-LOCAL-6 rule 10 action accept
set firewall ipv6 name WAN-LOCAL-6 rule 10 protocol ospf
set firewall ipv6 name WAN-LOCAL-6 rule 10 source address fe80::/10
set firewall ipv6 name WAN-LOCAL-6 rule 10 description 'OSPFv3 control plane'
set firewall ipv6 name WAN-LOCAL-6 rule 20 action accept
set firewall ipv6 name WAN-LOCAL-6 rule 20 protocol icmpv6
set firewall ipv6 name WAN-LOCAL-6 rule 20 description 'Neighbour discovery — IPv6 does not work without it'

set firewall ipv6 input filter default-action drop
set firewall ipv6 input filter rule 10 action jump
set firewall ipv6 input filter rule 10 jump-target WAN-LOCAL-6
set firewall ipv6 input filter rule 10 inbound-interface name eth0
commit
save

Three details in that IPv6 block earn their place.

fe80::/10, not a global prefix. OSPFv3 packets are always sourced from the interface’s link-local address. A rule that permits OSPF from 2001:db8::/32 matches nothing, and the adjacency fails with a rule set that looks correct in review.

/10, not /64. The link-local prefix defined by the addressing architecture is fe80::/10. Writing fe80::/64 happens to match the addresses most hosts autoconfigure, and will keep working right up until it meets one that does not.

ICMPv6 is not optional. IPv6 has no ARP; neighbour discovery is ICMPv6. An input filter that drops ICMPv6 breaks the link layer under OSPFv3, so the adjacency fails for a reason that has nothing to do with OSPF. This is the trap that catches operators porting an IPv4 rule set — where dropping ICMP is merely rude — straight across to IPv6, where it is fatal.

Validation

# OSPFv2
show ip ospf neighbor
show ip ospf interface
show ip route ospf

# OSPFv3
show ipv6 ospf6 neighbor
show ipv6 ospf6 interface
show ipv6 route ospf6

# Wire-level — both families are IP protocol 89
tcpdump -ni eth0 ip proto 89
tcpdump -ni eth0 ip6 proto 89

# Firewall
show firewall ipv4 name WAN-LOCAL
show firewall ipv6 name WAN-LOCAL-6
show firewall statistics

VyOS spells the OSPFv3 operational tree to match its configuration node (show ipv6 ospfv3 ...), while FRR’s own CLI spells the daemon ospf6 (vtysh -c 'show ipv6 ospf6 neighbor'). Both reach the same daemon and print the same data. Tab-complete rather than guessing which spelling your image accepts.

A working dual-stack baseline shows:

  • Both adjacencies Full — and they are separate facts. One Full and one stuck is the normal shape of a dual-stack problem.
  • show ip route ospf and show ipv6 route ospf6 each populated.
  • tcpdump showing both Hello streams on the same interface.
  • show firewall statistics showing the OSPF accept rules counting in both families. A rule at zero on a link with an established adjacency means the traffic is being accepted somewhere else, and your rule set is not doing what you think it is.

Redistribution between OSPFv2 and OSPFv3 does not exist

This is worth stating plainly, because it is asked for often and because the request sounds reasonable.

Redistribution moves routes between routing protocols within one address family. OSPFv2 carries IPv4 prefixes with IPv4 next-hops. OSPFv3 carries IPv6 prefixes with IPv6 next-hops. There is no set protocols ospfv3 redistribute ospf, and there could not be one: an IPv4 prefix has no representation in an OSPFv3 LSA, and an IPv6 route has no IPv4 next-hop for zebra to install. A default route learned in OSPFv2 does not become ::/0 in OSPFv3 — those are different destinations on different internets.

What VyOS actually offers is redistribution into each process from sources in the matching family:

# Into OSPFv2 — IPv4 sources only
set protocols ospf redistribute connected route-map CONN-INTO-OSPF
set protocols ospf redistribute static route-map STATIC-INTO-OSPF
set protocols ospf redistribute bgp route-map BGP-INTO-OSPF

# Into OSPFv3 — IPv6 sources only
set protocols ospfv3 redistribute connected route-map CONN-INTO-OSPFv3
set protocols ospfv3 redistribute static route-map STATIC-INTO-OSPFv3
set protocols ospfv3 redistribute bgp route-map BGP-INTO-OSPFv3

Each of those still deserves a route-map, for the ordinary reason: unfiltered redistribution advertises every prefix the source knows, including ones that were never meant to leave the box.

Production scenarios for dual-stack

Scenario 1 — IPv4 and IPv6 internal, both routed independently

The common case, and the least interesting: the enterprise runs both families across the same estate. OSPFv2 carries IPv4, OSPFv3 carries IPv6, neither knows about the other, and the operational burden is that every routing change has to be made and verified twice. That doubling is the real cost of dual-stack, and it is the argument for treating it as a window rather than a destination.

Scenario 2 — IPv4 retirement migration

Dual-stack as a staged migration:

  1. Run both. OSPFv2 for IPv4, OSPFv3 for IPv6, with matching areas and matching costs so the two topologies converge on the same paths.
  2. Move applications to IPv6 over time, watching IPv4 traffic volume rather than IPv4 configuration as the signal.
  3. When IPv4 traffic on an interior link reaches near-zero, remove OSPFv2 from the interior routers.
  4. Keep OSPFv2 at the edge while any external IPv4 connectivity remains.
  5. Remove OSPFv2 entirely.

Matching the costs in step 1 matters more than it looks. If the two topologies prefer different paths, a dual-stack service will succeed over one family and fail over the other on the same nominal route, and the difference will be invisible to anyone testing with a single protocol.

Scenario 3 — IPv6 core, IPv4 islands at the edge

An IPv6-only core with IPv4 still present at customer edges. The core runs OSPFv3 only. Each edge router runs OSPFv2 towards its IPv4 island, and the IPv4 reachability across the core is carried by BGP with an IPv6 next-hop (the RFC 8950 case above) or by tunnels — not by the IGP.

flowchart LR
  subgraph CORE["Core IPv6 only"]
    SP6["OSPFv3"]
  end
  subgraph EDGE["Edge router"]
    E1["OSPFv3 to the core<br/>OSPFv2 to the island<br/>BGP carries IPv4 NLRI"]
  end
  subgraph ISLAND["Customer IPv4 island"]
    C4["OSPFv2"]
  end
  SP6 -- "OSPFv3 adjacency" --> E1
  E1 -- "OSPFv2 adjacency" --> C4
  E1 -- "IPv4 NLRI, IPv6 next-hop" --> SP6

The edge router is dual-stack; the core is not. Note what the diagram does not contain: an arrow between the two OSPF processes.

Scenario 4 — separating tenants on shared infrastructure

RFC 5340 gives OSPFv3 an instance-id in the packet header, which allows several logically separate OSPFv3 instances to share a link. VyOS does not expose a multi-instance OSPFv3 model: there is one set protocols ospfv3 tree per routing instance, and the way to run genuinely separate routing tables on VyOS is a VRF.

set vrf name CUST-A table 101
set interfaces ethernet eth2 vrf CUST-A
set vrf name CUST-A protocols ospfv3 parameters router-id 10.255.1.1
set vrf name CUST-A protocols ospfv3 area 0 interface eth2

Each VRF gets its own OSPFv3 instance, its own database and its own IPv6 RIB. Note the explicit router-id: a VRF with no IPv4 address in it has nothing for FRR to borrow one from, and the instance will not start without it.

Operational playbook

  1. Validate OSPFv2 first, if it is present. show ip ospf neighbor, show ip route ospf. Separating the families in time makes a dual-stack fault far easier to attribute.
  2. Confirm IPv6 is up on the link. ip -6 addr show dev eth0 must show a link-local address in fe80::/10. OSPFv3 needs only that; a global address is required for the prefixes you advertise, not for the adjacency.
  3. Write the IPv6 firewall rules before enabling OSPFv3, not after. Enabling the protocol into a drop policy produces a failure whose first suspect is the protocol configuration you just wrote, which is the wrong place to look.
  4. Configure OSPFv3.
  5. Validate the adjacency. show ipv6 ospf6 neighbor and tcpdump -ni eth0 ip6 proto 89 on both ends. One-way Hellos are the signature of a filter, and you can only see one-way from both ends.
  6. Validate the IPv6 RIB. show ipv6 route ospf6.
  7. Compare the two topologies. For each dual-stack destination, check that the IPv4 and IPv6 paths agree. Where they do not, decide deliberately whether that is intended.

Production failure modes

  • The IPv6 input filter has no OSPF rule. Hellos are dropped in one direction; the sending side sits in Init because it never sees itself listed in the neighbour’s Hello. show firewall statistics shows no rule counting the packets.
  • The OSPFv3 rule matches a global source address. The rule looks right and matches nothing: OSPFv3 sources from fe80::/10.
  • ICMPv6 is dropped by the input filter. Neighbour discovery fails, so the link-local next-hop is unresolvable and the adjacency cannot form. The OSPF configuration is not the problem.
  • The OSPF rule restricts the destination to multicast. Hellos pass, the unicast database exchange does not, and the adjacency parks in ExStart or Exchange.
  • IPv4 and IPv6 costs diverge. Both families converge, neither is broken, and traffic to a dual-stack service takes different paths depending on which address the client resolved. Diagnosed by comparing show ip route and show ipv6 route for the same destination, and by testing with both families explicitly rather than whichever one the client happened to pick.
  • An IPv6-only VRF or router has no router-id. The OSPFv3 instance does not start. There is no IPv4 address for FRR to borrow one from.

Rollback

# Remove one family's process, leaving the other running
delete protocols ospfv3
commit-confirm 5

# Remove an IPv6 firewall rule set
delete firewall ipv6 name WAN-LOCAL-6 rule 10
commit-confirm 5

# Or return to the previous revision entirely
rollback N
commit

Use commit-confirm for anything that touches an input filter you are reaching the router through. A firewall change that drops your own session is the one failure you cannot fix by typing faster, and it is easy to make in the family you are not connected over — an IPv6 filter change that also affects your IPv4 SSH session is rare, but an IPv6 SSH session cut by an IPv6 filter change is not.

Removing OSPFv3 withdraws every IPv6 route learned from it. If IPv6 is carrying production traffic, that is an outage for the IPv6 half of every dual-stack service, and the IPv4 half will keep working — producing a symptom that reads as “the application is slow” rather than “the network is down”, because clients fall back after a connection timeout.

Production discipline

Cross-course references

The OSPFv3 lessons vyos-xxi-01-ospfv3-concept (concept), vyos-xxi-02-ospfv3-config (configuration), vyos-xxi-03-ospfv3-vs-ospfv2 (comparison), and vyos-xxi-04-ospfv3-troubleshoot (troubleshooting) cover the OSPFv3 idioms this lesson assumes. The IPv6 lessons vyos-xi-01-ipv6-fundamentals through vyos-xi-06-ipv6-troubleshoot cover the link-local addressing and neighbour discovery the firewall section depends on. Part XV (vyos-xv-04-vrf-ipv6) covers the per-VRF OSPFv3 instance and the router-id problem in an IPv6-only VRF. The OSPF configuration lesson vyos-xix-04-ospf-redistribute covers the within-family redistribution model that OSPFv3 mirrors.

Quiz

Knowledge check · 4 questions

  1. Q1. Which configuration enables OSPFv3 on an interface that already runs OSPFv2?

  2. Q2. OSPFv2 and OSPFv3 automatically redistribute routes between each other on a dual-stack link.

  3. Q3. OSPFv2 is Full and OSPFv3 is stuck in Init on a dual-stack link. Diagnose it and fix it.

    R1 and R2 share a dual-stack link on eth0. Both run `set protocols ospf area 0 network 10.0.0.0/24` and `set protocols ospfv3 area 0 interface eth0`. The OSPFv2 adjacency is Full. On R1, `show ipv6 ospf6 neighbor` lists R2 in state Init and stays there. `tcpdump -ni eth0 ip6 proto 89` on R1 shows R1's own Hellos leaving and R2's Hellos arriving; the same capture on R2 shows R2's Hellos leaving and nothing arriving from R1. R2 has an IPv6 input filter with `default-action drop`, rules permitting ICMPv6 and SSH, and no rule for OSPF. R2's IPv4 input filter has an explicit OSPF accept rule that the operator wrote when OSPFv2 was deployed.

  4. Q4. A design document asks you to 'redistribute the IPv4 default route into OSPFv3 so the IPv6-only core can reach IPv4 destinations'. Respond to the request.

    A network is migrating to an IPv6-only core running OSPFv3. Several IPv4 sites remain, reachable through two edge routers that are dual-stack. The design document specifies `set protocols ospfv3 redistribute ospf` on both edge routers, with a route-map permitting only `0.0.0.0/0`, and states that this will advertise the IPv4 default into the IPv6 core. A junior engineer has already opened a change ticket to configure it.

Passing score: 75%. Answers are checked in this browser.