Skip to main content
RunBook Academy

← All break/fix scenarios in VyOS

advancedvyos-tunnel~30 min

Tunnel Up, No Routes Learned

Reported symptoms

  • Branch 7's new VLAN 192.168.17.0/24 has been unreachable from HQ since it was commissioned on Thursday. The branch's original LAN, 192.168.7.0/24, works perfectly and always has
  • The tunnel is healthy by every measure the team has: a handshake seconds old at both ends, transfer counters climbing in both directions, and `ping 10.10.10.2` across the tunnel subnet with no loss
  • `show ip ospf neighbor` is empty on both routers — and the capture archived when the link was built in March is empty too, which nobody read at the time
  • `show ip ospf interface wg0` shows the interface in area 0 sending hellos on schedule, with a neighbour count of zero
  • The same OSPF configuration works on the six branches that reach HQ over ethernet handoffs, so "the configuration is proven" is the working assumption
  • On Friday somebody applied the fix that every forum recommends — `network point-to-point` on the tunnel interface — and nothing changed. It was left in place
  • A traceroute from HQ to the branch LAN completes normally, which is being read as proof that routing over the tunnel is fine

Evidence

  • · `show interfaces wireguard wg0 summary` — latest handshake 34 seconds ago against the branch public key, with bytes transferred both ways
  • · `ping -c 3 10.10.10.2` from HQ — three replies across the tunnel subnet, 0% loss
  • · `show ip ospf neighbor` on both routers — no output at all, no neighbour in any state
  • · `show ip ospf interface wg0` — area 0, hello 10, dead 40, `Neighbor Count is 0`
  • · `sudo tcpdump -nei wg0 proto ospf` at HQ — a hello every ten seconds, addressed to 224.0.0.5
  • · `sudo tcpdump -nei wg0 proto ospf` at the branch, run at the same time — nothing arrives
  • · `ip link show wg0` — flags read `POINTOPOINT,NOARP,UP,LOWER_UP`, with no `MULTICAST` among them
  • · `ip link show eth0` on the same router, for comparison — `BROADCAST,MULTICAST,UP,LOWER_UP`
  • · `show interfaces wireguard wg0 allowed-ips` — the tunnel /30 and the two branch prefixes; 224.0.0.5/32 is not in the set
  • · `show ip route 192.168.7.0/24` at HQ — `S>*` via 10.10.10.2, a static route dated to the March build
  • · `show ip route ospf` at HQ — routes from the six ethernet branches, and nothing from branch 7
Diagnosis and resolutionclick to reveal

Root cause

OSPF finds its neighbours by sending hellos to the multicast address 224.0.0.5, and this tunnel cannot carry them. Two independent obstacles stand in the way and it is worth keeping them apart, because fixing one changes nothing. The first is the netdev itself: a WireGuard interface is registered by its driver with the point-to-point and no-ARP flags and never with the multicast flag, which is visible in one line of `ip link show wg0` and is a property of the Linux driver rather than a VyOS or FRR decision — the xfrm interface VyOS presents as a VTI is no better, so switching the estate to IPsec would not have helped. The second is specific to WireGuard: `allowed-ips` is consulted after the routing decision, and a set built from the tunnel /30 and the branch prefixes does not contain 224.0.0.5, so the hello is discarded on the way out before the flag question is ever reached. The captures show both halves of that: hellos leave HQ addressed to 224.0.0.5 on schedule, and the branch, capturing at the same moment, sees nothing arrive. The change made on Friday could not have helped either, and that is the part worth learning properly. Setting the network type to `point-to-point` alters the adjacency model — no designated router, a different representation of the link in the database — and leaves the destination address exactly where it was; on VyOS 1.5 the only network type that unicasts a hello to each configured neighbour is `non-broadcast`. So the configuration on this interface has been asking for an adjacency that the transport cannot deliver since the day it was written. What hid it for five months was a static route. The March build added `192.168.7.0/24 via 10.10.10.2` to get the branch working, traffic flowed, the link was signed off, and the OSPF stanza underneath it has never once produced a neighbour. The tunnel's health was never evidence of anything either: a WireGuard interface is UP as soon as it is configured, the handshake proves the peers agree on keys, and a successful ping across the /30 proves the unicast data path works — which is precisely the path OSPF's discovery does not use. The first prefix that depended on the adjacency rather than on the static was the new VLAN, and it failed the moment it was created.

Remediation

Separate service restoration from the design fix, and do them in that order. Restoration is one static route for 192.168.17.0/24 via 10.10.10.2 with the prefix present in the peer's `allowed-ips` on both ends — without that second half the route forwards into a drop, because cryptokey routing is a filter underneath the routing table rather than a description of it — and with an explicit expiry on the ticket, because a static route added under pressure is exactly how the original one came to hide a missing adjacency for five months. Then choose the durable fix deliberately. BGP over the tunnel is the option that does not have to reason about multicast at all: it is unicast TCP to a configured neighbour address on the tunnel /30, it needs no tunnel-specific concessions, and it is what the estate should probably standardise on for every tunnelled site. It is also a design change rather than a repair, since it brings an AS-numbering decision, a policy decision — an eBGP session with no inbound and outbound policy attached can come up and exchange nothing at all, which would present as this identical ticket — and a redistribution boundary, so it belongs in a change window rather than in the incident. If the branches are genuinely one OSPF domain and the tunnel is one more link in it, the configuration that works is `network non-broadcast` on the tunnel interface at both ends plus a static `protocols ospf neighbor` entry naming the far end's tunnel address, with the tunnel /30 inside `allowed-ips`, matching hello and dead intervals, and an awareness that this network type elects a designated router so priority matters as the spoke count grows. Refuse three things. Refuse the change already on the box: `point-to-point` did not work here, and leaving a disproved fix in place turns it into tomorrow's confusing evidence. Refuse adding 224.0.0.5/32 to `allowed-ips` and declaring victory — it is a necessary condition for multicast OSPF over WireGuard and it is not known to be a sufficient one, because the netdev is still not registered as multicast-capable, and this course has not verified that path in either direction. And refuse the migration to IPsec that will be proposed as the way around the whole problem, because the VTI has the same limitation for the same reason.

Verification

The adjacency, not the tunnel. `show ip ospf neighbor` must list the far end in Full state at both ends, and a capture on the tunnel must show hellos addressed to the neighbour's tunnel address rather than to 224.0.0.5 — the destination address is the thing that was wrong, so it is the thing to read, and an adjacency that came up is not by itself proof that you fixed the mechanism you think you fixed. Then confirm the route is real and is arriving the way the design says: `show ip route 192.168.17.0/24` shows an OSPF route via the tunnel address out of `wg0`, and `ip route get` for a host inside that VLAN returns the same, because the kernel is what decides. Remove the temporary static before you verify anything, or you will be verifying the static: a prefix that survives the deletion of the route that was covering it is the only evidence that the protocol is carrying it. Prove the check can fail — shut the VLAN interface at the branch, watch the prefix leave the HQ table within the dead interval, and bring it back — because a dynamic route nobody has watched appear and disappear is an assumption rather than a verified behaviour, which is the precise mistake the March sign-off made. Verify from hosts rather than from routers, at both ends. And re-check the tunnel's MTU and MSS handling once real traffic is crossing it, since this path has been carrying almost nothing until now and a working route is the first time the overhead will be exercised.

Prevention

Monitor adjacency counts, not interface states. An OSPF interface configured in an area with zero neighbours for longer than its dead interval is a defect whatever caused it, and that single check would have raised this in March rather than in August. The same discipline applies to the tunnel: "up" is not a health signal for WireGuard, because the interface is up as soon as it is configured — the signals that mean something are the age of the last handshake and an end-to-end probe across the link. Check the netdev flags before choosing a routing protocol for a link. `ip link show` costs nothing, answers "can multicast work here at all", and answers it in a way that no amount of protocol configuration can override. Treat a build as complete only when every mechanism the design names has been individually verified, not when traffic flows. This link carried traffic on day one and was signed off on that basis, while one of the two mechanisms in its design had never worked; the static route was doing all of the work and none of the documentation said so. Standardise on BGP for tunnelled sites and make OSPF-over-a-tunnel the exception that carries a written reason, a documented static neighbour list, and a note explaining why the network type is what it is — because the next engineer to tidy up `non-broadcast` into `point-to-point` will break it silently and get no error to tell them so. Finally, remove changes that did not work. A disproved fix left in the configuration is indistinguishable from a deliberate one six months later, and it costs the next incident an hour.

Reported symptoms

Branch 7 reaches HQ over a WireGuard tunnel built in March. The tunnel subnet is 10.10.10.0/30, HQ holds .1 and the branch holds .2, and the design on file says the branch prefixes are carried by OSPF across the tunnel in the same area as the rest of the estate.

On Thursday the branch commissioned a second VLAN, 192.168.17.0/24, for a new production line. It has never been reachable from HQ. The branch’s original LAN, 192.168.7.0/24, works and has worked since day one.

The evidence that arrived on the ticket over the first hour all pointed away from the tunnel:

  • The tunnel is healthy on every measure anybody has. show interfaces wireguard wg0 summary reports a handshake seconds old at both ends with bytes moving both ways, and ping 10.10.10.2 from HQ is clean.
  • The OSPF configuration on wg0 is the same stanza that works on six other branches. Those six reach HQ over ethernet handoffs and their adjacencies have never given trouble.
  • A traceroute from HQ into 192.168.7.0/24 completes, which reads as proof that routing over the tunnel works.

Two facts arrived later and are worth more than the rest combined. First, show ip ospf neighbor is empty on both routers — and so is the capture that was archived when the link was commissioned in March, which nobody read at the time. Second, on Friday an engineer applied the change that every search result recommends for OSPF over a tunnel, setting the interface network type to point-to-point, and it changed nothing at all. It is still in the configuration.

Evidence provided

Read-only / Safethe tunnel everybody agrees is fine — and they are right, it is
vyos@edge1:~$ show interfaces wireguard wg0 summary
interface: wg0
public key: 6PN0kRVL2Yy1kW6h3hAcQOx2m6QqQZ5Y7uZ0mM8wXjk=
listening port: 51820

peer: rSuuvkAuSoyqvogoA5OJjYBbJKKaYS13pWs4K69PTOE=
endpoint: 203.0.113.47:51820
allowed ips: 10.10.10.0/30, 192.168.7.0/24, 192.168.17.0/24
latest handshake: 34 seconds ago
transfer: 41.28 MiB received, 63.91 MiB sent

Illustrative output

Read-only / Safethe unicast data path across the tunnel, working perfectly
vyos@edge1:~$ ping -c 3 10.10.10.2
PING 10.10.10.2 (10.10.10.2) 56(84) bytes of data.
64 bytes from 10.10.10.2: icmp_seq=1 ttl=64 time=11.4 ms
64 bytes from 10.10.10.2: icmp_seq=2 ttl=64 time=11.1 ms
64 bytes from 10.10.10.2: icmp_seq=3 ttl=64 time=11.6 ms

--- 10.10.10.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss

Illustrative output

Read-only / Safehellos being sent on schedule, to nobody
vyos@edge1:~$ show ip ospf interface wg0
wg0 is up
Internet Address 10.10.10.1/30, Area 0.0.0.0
MTU mismatch detection: enabled
Router ID 10.255.0.1, Network Type POINTOPOINT, Cost: 10
Transmit Delay is 1 sec, State Point-To-Point, Priority 1
Timer intervals configured, Hello 10s, Dead 40s, Retransmit 5s
  Hello due in 3.418s
Neighbor Count is 0, Adjacent neighbor count is 0

Illustrative output

Read-only / SafeHQ is talking — read the destination address
vyos@edge1:~$ sudo tcpdump -nei wg0 -c 3 proto ospf
10:12:04.881207 ip: 10.10.10.1 > 224.0.0.5: OSPFv2, Hello, length 44
10:12:14.882031 ip: 10.10.10.1 > 224.0.0.5: OSPFv2, Hello, length 44
10:12:24.882844 ip: 10.10.10.1 > 224.0.0.5: OSPFv2, Hello, length 44

Illustrative output

Read-only / Safethe branch, capturing at the same moment, receives none of them
vyos@br7:~$ sudo tcpdump -nei wg0 -c 3 proto ospf
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on wg0, link-type RAW (Raw IP), snapshot length 262144 bytes
^C
0 packets captured

Illustrative output

Read-only / Safeone line, four flags, and the whole incident is in what is not listed
vyos@edge1:~$ ip link show wg0
7: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN
  link/none

Illustrative output

Read-only / Safethe same command on the interface where the same OSPF stanza works
vyos@edge1:~$ ip link show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP
  link/ether 52:54:00:0e:33:a1 brd ff:ff:ff:ff:ff:ff

Illustrative output

Read-only / Safethe branch LAN that works, and the reason it works
vyos@edge1:~$ show ip route 192.168.7.0/24
Routing entry for 192.168.7.0/24
Known via "static", distance 1, metric 0, best
Last update 22w4d ago
* 10.10.10.2, via wg0

Illustrative output

Work the evidence before reading on

Two captures taken at the same moment disagree about whether a packet exists, and both of them are correct.

  1. HQ sends a hello every ten seconds and the branch receives nothing. The tunnel demonstrably carries packets in both directions at the same moment — the ping proves it. What is different about the hello?
  2. Read the destination address on the hello, then read the flag list on ip link show wg0, then read the same flag list on eth0, where this exact OSPF configuration works. Which word appears on one and not the other, and what does it govern?
  3. The interface’s allowed-ips set is printed in the tunnel summary. Is the hello’s destination address inside it? What does WireGuard do with a packet that has been routed out of wg0 and is not covered by any peer’s set?
  4. Friday’s change set the network type to point-to-point and produced no change in behaviour. Assume the engineer applied it correctly. What would that setting have to alter for it to have fixed this, and does it alter that?
  5. show ip ospf neighbor was empty in March too. If the adjacency has never existed, what has been carrying 192.168.7.0/24 for five months, and why did the new VLAN behave differently from the old one on the very first day?

Then the question that resolves it: the tunnel is up, the peers are handshaking, and the /30 pings clean. Which of those three facts says anything at all about whether a multicast packet can cross this link?

Root cause

1. The interface is not a multicast-capable device

A Linux network device advertises what it can do in its flags, and the flag that matters here is the one that says the device can carry multicast. The WireGuard driver registers its interface as point-to-point and no-ARP, and never sets it. That is why ip link show wg0 prints four flags and ip link show eth0 prints a list containing MULTICAST, and it is the whole difference between the six branches whose adjacencies work and the one whose adjacency does not.

This is a property of the Linux driver rather than a VyOS setting or an FRR setting, so there is no configuration node anywhere that will change it. It also applies to the xfrm interface that VyOS presents as a VTI, which matters because “use IPsec instead” is the standard suggestion at this point in the investigation and it would have moved the problem nowhere.

2. On WireGuard, allowed-ips discards it before that even matters

WireGuard applies a second check underneath the routing table. Once the kernel has decided a packet leaves via wg0, cryptokey routing asks which peer’s allowed-ips set covers the destination; if none does, the packet is dropped.

The set on this tunnel is the /30 plus the two branch prefixes, which is a correct and ordinary set. It does not contain 224.0.0.5, so a hello addressed there is discarded on the way out, before the netdev flag question is reached.

Two independent obstacles, then, and it is worth keeping them apart, because removing one of them changes nothing observable. The captures show exactly that: the hello exists on the sending side, where tcpdump taps the interface before the driver disposes of it, and does not exist anywhere else.

3. point-to-point changes the adjacency model, not the destination

Friday’s change was applied correctly and could not have worked. The network type governs how the link is represented and whether a designated router is elected; it does not change where OSPF addresses its hellos. On VyOS 1.5 the only network type that stops OSPF using the multicast group and instead unicasts a hello to each statically configured neighbour is non-broadcast.

The reason this belief is so durable is that it is usually followed by success. On a genuine point-to-point link — a serial circuit, a GRE tunnel, which really is multicast-capable — setting point-to-point is the right thing to do, and it resolves several unrelated failure modes that also stop an adjacency forming. The change gets credit for those, and the one case it cannot fix produces no error message to contradict it.

4. A static route hid a design that has never worked

The adjacency did not break on Thursday. It has never existed.

In March the link was built, the OSPF stanza was written, and the branch LAN was made to work with a static route via the tunnel address. Traffic flowed, the link was signed off, and the archived show ip ospf neighbor capture — empty — was filed rather than read. For five months every packet to branch 7 followed a static route while the documentation described a dynamic one.

The new VLAN was the first prefix that depended on the mechanism instead of on the workaround, which is why a five-month-old defect presented as a Thursday outage.

Resolution

  1. Restore service first and keep it separate from the design fix. A static route for 192.168.17.0/24 via 10.10.10.2 gets the production line working; it needs the prefix present in the peer allowed-ips on both ends or it forwards into a drop, and it needs an expiry on the ticket, because an unexpiring temporary static is exactly what hid this since March.
  2. Record the negative result from Friday before touching the configuration again, then either revert the network type or set it deliberately as part of the fix. Do not leave a disproved change in place unexplained.
  3. Decide the durable fix on design grounds, not incident grounds. BGP over the tunnel is unicast TCP to a configured address and never has to reason about multicast; it is the better standard for tunnelled sites and it is what the other tunnels in the estate should converge on.
  4. Treat the BGP option as a change, not a repair. It brings an AS-numbering decision, a redistribution boundary, and a policy requirement — an eBGP session with no inbound and outbound policy attached can establish and exchange nothing, which would produce this exact ticket a second time.
  5. If the estate is genuinely one OSPF domain and this tunnel is one more link in it, configure the network type as non-broadcast at both ends and add a static protocols ospf neighbor entry naming the far end tunnel address. The tunnel /30 must be inside allowed-ips, and the hello and dead intervals must match on both routers.
  6. Refuse the 224.0.0.5 shortcut. Putting the multicast group into allowed-ips is a necessary condition for multicast OSPF over WireGuard and is not a verified sufficient one, because the interface is still not registered as multicast-capable. If you want to know, test it in a lab and read the destination address in a capture.
  7. Refuse the migration to IPsec that will be proposed as the way around all of this. The interface VyOS presents as a VTI has the same limitation for the same driver-level reason.
  8. Re-read the March sign-off and fix the record. The design document describes a mechanism that has never run; leaving that in place guarantees somebody rebuilds the same link the same way.

Verification

  1. show ip ospf neighbor lists the far end in Full state at both routers, with the neighbour router-id you expect rather than one you have to look up afterwards.
  2. A capture on the tunnel shows hellos addressed to the neighbour tunnel address rather than to 224.0.0.5. The destination address is what was wrong, so it is what you read; an adjacency that came up is not proof that you fixed the mechanism you think you fixed.
  3. Remove the temporary static before verifying the prefix, or you will be verifying the static. A prefix that survives the deletion of the route covering it is the only evidence the protocol is carrying it.
  4. show ip route 192.168.17.0/24 shows an OSPF route via the tunnel address out of wg0, and ip route get for a host inside that VLAN returns the same egress, because the kernel is what decides.
  5. The check can fail. Shut the VLAN interface at the branch, watch the prefix leave the HQ table within the dead interval, and bring it back. A dynamic route nobody has watched appear and disappear is an assumption, and that assumption is what the March sign-off recorded as a fact.
  6. Traffic is tested from hosts at both ends, not from the routers. A router pinging across a /30 was one of the three checks that made this look healthy for five months.
  7. MTU and MSS handling on the tunnel is re-checked once real traffic crosses it. This path has been carrying almost nothing, and a working route is the first time the encapsulation overhead is exercised at production packet sizes.

Prevention

  • Alert on OSPF interfaces with zero neighbours. An interface configured into an area and adjacent to nobody for longer than its dead interval is a defect whatever caused it, and that one check would have raised this in March instead of in August.
  • Stop treating “up” as a health signal for WireGuard. The interface is up because it is configured. The signals that mean something are the age of the last handshake and an end-to-end probe over the link, and both belong in monitoring.
  • Check the netdev flags before choosing a routing protocol for a link. ip link show is free, it answers whether multicast can work here at all, and no amount of protocol configuration overrides its answer.
  • Verify each mechanism a design names, not just that traffic flows. This link passed its build review on the strength of a static route while one of its two documented mechanisms had never worked once.
  • Standardise on BGP for tunnelled sites, and make OSPF over a tunnel an exception that carries a written reason, its static neighbour list, and a note explaining the network type — because the next engineer to tidy non-broadcast into point-to-point will break it silently and get no error to tell them so.
  • Remove changes that did not work, and write down that they did not. A disproved fix left in a configuration is indistinguishable from a deliberate setting six months later.