Mental model
You have two routers separated by the public internet. You want them to feel like they’re directly connected with a private link — so you can run OSPF between them, route between private subnets, send multicast, etc.
GRE (Generic Routing Encapsulation) is the trick. Each router has a virtual Tunnel0 interface that, when you put a packet in, wraps it in another IP header and shoots it across the internet to the other router. That router unwraps and forwards normally.
Inner packet: [ IP: 10.1.0.5 → 10.2.0.10 ][ TCP / payload ]
After GRE: [ IP: pubA → pubB ][ GRE hdr ][ IP: 10.1.0.5 → 10.2.0.10 ][ TCP / payload ]
^^ added by GRE encapsulation
The outer IP uses the routers’ public addresses. The inner IP is whatever the original packet was — private addresses, IPv6, whatever. The internet’s routers see only the outer addressing.
Two things GRE is good at, one thing it’s not
| Capability | |
|---|---|
| ✓ | Carries any Layer-3 protocol — IPv4, IPv6, multicast, AppleTalk, IPX |
| ✓ | Multicast / OSPF / EIGRP can run inside (they need a directly-connected link feel) |
| ✗ | No encryption — contents are visible to anyone on the path |
That last point is why you almost always pair GRE with IPsec:
- GRE-over-IPsec = GRE first, then IPsec wraps the GRE
- IPsec gives encryption, GRE gives multicast support and protocol flexibility
Pure IPsec can encrypt unicast IP, but doesn’t natively carry multicast / OSPF — which is why GRE + IPsec is the standard combo for site-to-site VPNs that need internal routing protocols.
Configuration — minimal GRE tunnel
! R-A (one side of the tunnel)
R-A(config)# interface Tunnel0
R-A(config-if)# ip address 10.99.99.1 255.255.255.252 ! tunnel IPs (private)
R-A(config-if)# tunnel source 198.51.100.1 ! my public IP
R-A(config-if)# tunnel destination 203.0.113.50 ! their public IP
R-A(config-if)# tunnel mode gre ip ! default — explicit for clarity
! R-B (other side — mirror)
R-B(config)# interface Tunnel0
R-B(config-if)# ip address 10.99.99.2 255.255.255.252
R-B(config-if)# tunnel source 203.0.113.50
R-B(config-if)# tunnel destination 198.51.100.1
R-B(config-if)# tunnel mode gre ip
Now 10.99.99.1 and 10.99.99.2 can ping each other through the tunnel — and the tunnel rides whatever internet path connects 198.51.100.1 and 203.0.113.50.
Add routes to use the tunnel
A static route pointing the remote private network through the tunnel:
R-A(config)# ip route 10.2.0.0 255.255.255.0 10.99.99.2
R-B(config)# ip route 10.1.0.0 255.255.255.0 10.99.99.1
Or run OSPF over the tunnel:
R-A(config)# router ospf 1
R-A(config-router)# network 10.99.99.0 0.0.0.3 area 0
R-A(config-router)# network 10.1.0.0 0.0.0.255 area 0
OSPF forms a neighborship across the tunnel as if R-A and R-B were directly connected. Pure IPsec can’t do that — GRE makes it possible.
MTU — the gotcha everyone hits
GRE adds 24 bytes of overhead (20-byte outer IP + 4-byte GRE header). The inner packet’s effective MTU drops:
Default MTU: 1500
- GRE overhead: -24
- IPsec overhead (if used): -52 (typical)
= Effective MTU inside tunnel: ~1424
If the inner packet is 1500 bytes with Don’t-Fragment set, the router must fragment it after encapsulation, and many networks block fragmented IPsec. Users see “everything works except large transfers” — classic MTU symptom.
Fix:
R-A(config-if)# ip mtu 1400
R-A(config-if)# ip tcp adjust-mss 1360 ! shrink TCP MSS for hosts
ip tcp adjust-mss is especially useful — the router rewrites TCP MSS in passing SYNs so endpoints negotiate a small-enough segment size. No user reconfiguration needed.
Verification
R-A# show interfaces Tunnel0
R-A# show ip route ! routes via tunnel
R-A# ping 10.99.99.2 ! is the tunnel up?
R-A# show ip protocols ! OSPF running over tunnel
show interfaces Tunnel0 confirms the tunnel state — up/up means good. up/down usually means the tunnel destination is unreachable (verify with a ping to the destination’s public IP).
GRE over IPsec — the standard combo
In production you almost always want encryption. Two ways to combine:
Tunnel-mode IPsec (encrypts the GRE-wrapped packet):
crypto isakmp policy 10
encr aes
authentication pre-share
group 14
crypto isakmp key supersecret address 203.0.113.50
crypto ipsec transform-set TS-AES esp-aes 256 esp-sha256-hmac
crypto ipsec profile MYPROFILE
set transform-set TS-AES
interface Tunnel0
ip address 10.99.99.1 255.255.255.252
tunnel source 198.51.100.1
tunnel destination 203.0.113.50
tunnel protection ipsec profile MYPROFILE ! the magic line
tunnel protection ipsec is the modern way — encrypts the tunnel without separate crypto-map ACL drudgery.
Common mistakes
-
Forgetting MTU adjustment. Tunnel works for ping but breaks for HTTPS, file transfers, video. Always set
ip mtuandip tcp adjust-msson the tunnel interface. -
Recursive routing loops. Tunnel source/destination are public IPs. If the router’s routing table sends those public IPs through the tunnel itself, you’ve created a loop. Symptom:
%TUN-5-RECURDOWN: Tunnel0 temporarily disabled due to recursive routing. Fix: ensure tunnel endpoints are reachable via the underlying internet path, not via the tunnel. -
Identical tunnel interfaces on both sides (e.g. both /32). Use a small /30 or /31 — both ends need IPs in the same subnet.
-
Trying to run multicast over IPsec without GRE. IPsec doesn’t natively carry multicast. Wrap in GRE first.
-
Skipping
tunnel mode gre ip. Default on Cisco IOS, but explicit is clearer. Other modes exist (ipv6,ipsec ipv4,gre multipoint) and the wrong one fails silently. -
Trusting GRE alone for sensitive data. GRE is plain text on the wire. Anyone capturing on the path can read everything. Always layer with IPsec for production.
Lab to try tonight
- Two routers in CML or any IPsec-capable simulator with “public” links between them and private LANs behind.
- Configure GRE Tunnel0 on both sides (without IPsec first).
- Confirm tunnel comes up:
show interfaces Tunnel0→up/up. Ping between tunnel IPs. - Configure static routes through the tunnel. Verify end-to-end ping from LAN-A to LAN-B.
- Run OSPF area 0 across the tunnel. Confirm OSPF neighbor adjacency forms over Tunnel0.
- Now layer in IPsec with
tunnel protection ipsec profile. Wireshark the public link and confirm ESP packets only (no inner addressing visible). - Set
ip mtu 1400and test large-file transfer behavior.
Cheat strip
| Concept | Plain English |
|---|---|
| GRE | Wraps IP inside IP — creates virtual point-to-point links |
| Tunnel source / destination | The routers’ public IPs |
| Tunnel interface | Virtual L3 interface, gets its own IP |
| No encryption | GRE is plain text. Use IPsec for privacy. |
| GRE over IPsec | The standard combo for site VPNs with internal routing |
| MTU caveat | Set ip mtu 1400 and ip tcp adjust-mss 1360 |
| Recursive routing | Avoid sending tunnel destination through the tunnel itself |
| OSPF works | Routing protocols run inside the tunnel as if directly connected |
tunnel protection ipsec | Modern way to add IPsec encryption to a GRE tunnel |