EIGRP over DMVPN works beautifully, right up until the moment you realise none of your spokes can reach any of your other spokes. The tunnels are up. NHRP is registered. The neighbours are all in the table. And the routes are simply not there.
The cause is split horizon, doing exactly what it was designed to do in 1988 on a protocol running over a physical, point-to-point serial link - and doing precisely the wrong thing on a multipoint GRE cloud. This article covers split horizon and next-hop-self on mGRE, dual-hub design, and the loop that a dual-homed site creates - all with real output from a CML lab. For the fundamentals, see the EIGRP guide and the DMVPN pillar.
Why split horizon breaks mGRE
Split horizon is a loop-prevention rule from the distance-vector era: do not advertise a route back out of the interface you learned it on. On a point-to-point link that is unarguably correct. The only router out there is the one who told you about it.
An mGRE tunnel is not a point-to-point link. It is one logical interface with many neighbours behind it. Every spoke reaches the hub through Tunnel0, and so does every other spoke. When the hub learns SPOKE1's LAN on Tunnel0 and split horizon stops it re-advertising out Tunnel0, it is not preventing a loop - it is preventing SPOKE2 from ever hearing about SPOKE1.
The fix is one command. It has to be on the hub, and it has to be on the tunnel:
router eigrp DMVPN
address-family ipv4 unicast autonomous-system 100
af-interface Tunnel0
no split-horizonSpokes do not need it. A spoke has exactly one neighbour of interest per hub, and it should not be re-advertising other spokes' routes anyway.
next-hop-self: the second command nobody tells you about
Turning off split horizon gets the routes to the spokes. It does not get the traffic there directly.
By default, EIGRP sets itself as the next hop on routes it advertises. So when the hub re-advertises SPOKE1's LAN to SPOKE2, it says "reachable via me". SPOKE2 sends the traffic to the hub, and the hub forwards it to SPOKE1. Every spoke-to-spoke packet crosses the WAN twice and traverses the hub's CPU. Your DMVPN is a hub-and-spoke network wearing a dynamic-mesh costume.
The second command tells the hub to preserve the originating spoke as the next hop:
af-interface Tunnel0
no split-horizon
no next-hop-selfNow SPOKE2 learns SPOKE1's LAN with SPOKE1's tunnel address as the next hop. NHRP resolves that to SPOKE1's real NBMA address, a direct spoke-to-spoke tunnel forms, and traffic goes straight across.
Both, visible in one output
From the lab. SPOKE1 looking at SPOKE2's loopback, with both commands in place on both hubs:
SPOKE1#show ip eigrp topology 12.12.12.12/32
Descriptor Blocks:
10.1.1.12 (Ethernet0/1), from 10.1.1.12, Send flag is 0x0
Composite metric is (131153920/163840), route is Internal
Hop count is 1
Originating router is 12.12.12.12
10.0.0.12 (Tunnel0), from 10.0.0.2, Send flag is 0x0
Composite metric is (13107281920/9830481920), route is Internal
Hop count is 2
Originating router is 12.12.12.12
10.0.0.12 (Tunnel0), from 10.0.0.1, Send flag is 0x0
Composite metric is (13107281920/9830481920), route is Internal
Hop count is 2
Originating router is 12.12.12.12Read those last two blocks carefully, because both commands are visible in them:
from 10.0.0.2andfrom 10.0.0.1- the hubs re-advertised a route they learned on Tunnel0 back out of Tunnel0. That isno split-horizonworking.10.0.0.12 (Tunnel0)- the next hop is 10.0.0.12, which is SPOKE2 itself, not either hub. That isno next-hop-selfworking.
Break split horizon, and the paths vanish
Turn split-horizon back on at both hubs:
SPOKE1#show ip eigrp topology 12.12.12.12/32
Descriptor Blocks:
10.1.1.12 (Ethernet0/1), from 10.1.1.12, Send flag is 0x0
Composite metric is (131153920/163840), route is InternalBoth tunnel paths are gone. In our lab the spokes happen to share a site LAN, so one path survives. In a real DMVPN, where spokes are in different cities, there would be nothing left at all. Spoke-to-spoke reachability would simply not exist.
Break next-hop-self, and the traffic hairpins
Turn next-hop-self back on (the default):
SPOKE1#show ip eigrp topology 12.12.12.12/32 | include Descriptor|Tunnel0|Ethernet
Descriptor Blocks:
10.1.1.12 (Ethernet0/1), from 10.1.1.12, Send flag is 0x0
10.0.0.2 (Tunnel0), from 10.0.0.2, Send flag is 0x0 <-- next hop is HUB2
10.0.0.1 (Tunnel0), from 10.0.0.1, Send flag is 0x0 <-- next hop is HUB1The routes are still there. But the next hop is now the hub. Every packet from SPOKE1 to SPOKE2 goes to a hub first. The tunnels can still form dynamically via NHRP shortcut in Phase 3, but the routing table is no longer pointing at the spoke, and you have made the hub a bottleneck for traffic that never needed to touch it.
Dual-hub: single cloud vs dual cloud
Single cloud is simpler and is the right default when both hubs sit on the same transport. Dual cloud is what you want when the two paths are genuinely different networks and you need to reason about them, police them, or apply per-transport policy independently.
In our single-cloud lab, the DMVPN comes up with both hubs and both spokes on one mGRE:
HUB1#show dmvpn
Interface: Tunnel0, IPv4 NHRP Details
Type:Hub, NHRP Peers:3,
# Ent Peer NBMA Addr Peer Tunnel Add State UpDn Tm Attrb
1 100.64.2.1 10.0.0.2 UP 00:00:28 D
1 100.64.11.1 10.0.0.11 UP 00:00:37 D
1 100.64.12.1 10.0.0.12 UP 00:00:32 DMaking one hub primary
Two hubs advertising identical metrics means spokes load-share, which is usually not what you want. Express the preference once, on the backup hub, with an offset list:
HUB2(config)#access-list 50 permit any
HUB2(config-router-af-topology)#offset-list 50 out 100000000 Tunnel0One command on one router. Every spoke, present and future, now prefers HUB1 and keeps HUB2 as a feasible successor - ready for instant failover, with no reconvergence.
The dual-homed site problem
Now the failure mode that a dual-hub design creates and that most DMVPN guides never mention.
Site A has two spokes on one LAN. Both advertise the site LAN into the WAN. Which means SPOKE2 learns its own LAN back from the WAN:
SPOKE2#show ip eigrp topology 10.1.1.0/24
Descriptor Blocks:
0.0.0.0 (Ethernet0/1), from Connected, Send flag is 0x0
Originating router is 12.12.12.12 <-- its own LAN, connected
10.0.0.11 (Tunnel0), from 10.0.0.2, Send flag is 0x0
Hop count is 2
Originating router is 11.11.11.11 <-- the SAME LAN, back from the WAN
10.0.0.11 (Tunnel0), from 10.0.0.1, Send flag is 0x0
Originating router is 11.11.11.11The connected route wins today. But if SPOKE2's LAN interface flaps, it will install a WAN path to its own directly-attached network - and start sending local traffic across the internet and back. EIGRP has no AS-path, so nothing in the protocol stops this.
The fix is a route tag applied on every router at the site: stamp everything leaving, drop anything arriving with your own stamp.
route-map TAG-SITE-A permit 10
set tag 1001
!
route-map BLOCK-SITE-A deny 10
match tag 1001
route-map BLOCK-SITE-A permit 20
!
router eigrp DMVPN
address-family ipv4 unicast autonomous-system 100
topology base
distribute-list route-map TAG-SITE-A out Tunnel0
distribute-list route-map BLOCK-SITE-A in Tunnel0SPOKE2#show ip eigrp topology 10.1.1.0/24
Descriptor Blocks:
0.0.0.0 (Ethernet0/1), from Connected, Send flag is 0x0 <-- only the connected path remainsFull treatment, including what happened when we tried Cisco's official SoO mechanism on this platform, in EIGRP Site of Origin.
The complete hub template
interface Tunnel0
ip address 10.0.0.1 255.255.255.0
no ip redirects
ip mtu 1400
ip nhrp authentication PINGLABZ
ip nhrp map multicast dynamic
ip nhrp network-id 100
ip nhrp holdtime 300
ip nhrp redirect
ip tcp adjust-mss 1360
tunnel source Ethernet0/1
tunnel mode gre multipoint
tunnel key 100
!
router eigrp DMVPN
address-family ipv4 unicast autonomous-system 100
af-interface default
passive-interface
exit-af-interface
af-interface Tunnel0
no passive-interface
hello-interval 5
hold-time 15
no split-horizon
no next-hop-self
exit-af-interface
network 10.0.0.0 0.0.0.255
eigrp router-id 1.1.1.1Three details in there that people skip:
ip mtu 1400andip tcp adjust-mss 1360. GRE plus IPsec eats header space. Without these, large packets fragment or get dropped, and you get the classic "ping works, applications hang" symptom that costs people days.af-interface default / passive-interface. Make everything passive by default and explicitly un-passive the interfaces you want. It is the only way to run EIGRP safely, and named mode makes it a two-line pattern.- Tuned hellos (5/15). The default on a multipoint interface is 60/180, which means a dead hub is not noticed for three minutes. 5/15 is aggressive but appropriate for a WAN overlay. Better still, pair it with BFD.
Key takeaways
no split-horizonon the hub's tunnel is what lets spokes learn each other's routes. Without it, spoke-to-spoke reachability does not exist at all.no next-hop-selfon the hub's tunnel is what makes the traffic go directly. Without it the routes exist but every packet hairpins through the hub.- Both go on the hub, on the tunnel interface. Spokes need neither.
- Single-cloud dual-hub is the simpler default. Dual-cloud is for genuinely separate transports.
- Make one hub primary with an outbound offset list on the backup hub - one command, applies to every spoke, keeps the backup as a feasible successor.
- A dual-homed site will learn its own LAN back from the WAN. Tag on the way out, deny on the way in, on every router at the site.
- Set
ip mtu 1400andip tcp adjust-mss 1360or you will spend a week debugging a fragmentation problem.
Next: multi-protocol redistribution and the scenarios that break networks. The full cluster indexes live on the EIGRP pillar and the DMVPN pillar.