GRE Basics
The topic for this CCNP session is GRE. GRE falls under the “Configure and verify data path virtualization technologies” section of the CCNP ENCOR Exam topics. This section also includes VRFs and IPsec, but both of those are big enough topics to deserve their own post. For comparison, the GRE section of the official certification guide (or at least my copy with a copyright of 2020) covers less than 5 pages, while IPsec is close to 20 pages.
What is GRE
GRE (Generic Routing Encapsulation) was initially developed by Cisco but it’s supported by every major networking vendor. GRE is IP protocol 47, and adds a minimum of 24 bytes to the packet header. While its original purpose was to carry any network protocol, along with legacy protocols (like IPX), it’s now primarily used to carry IPv4 or IPv6 over an existing IPv4 network, creating an overlay.
The most important bits of that last paragraph that are worth repeating are that GRE is IP protocol 47 and GRE adds a minimum of 24 bytes to the packet header. Make sure IP protocol 47 isn’t blocked, and that your IP MTU is increased to support GRE or fragmentation will occur!
GRE works by adding a new header containing the remote tunnel endpoint as the destination IP. Adding a new header allows the packet to now traverse the network without the underlying packet being visible by routers it traverses. However, GRE doesn’t include any encryption. If you look at a GRE packet in Wireshark, you’ll clearly see both headers in the packet. After the packet arrives at the tunnel endpoint, the new header is removed, leaving the original packet.
I’ve been asking AI for a fun fact about topics I’m reading about to add something memorable. For the sake of clarity… keep in mind that AI makes mistakes. I’ve paraphrased it below.
GRE tunnels are stateless, and they maintain no information about the state of the tunnel itself. The router has no idea if the other end is up or down. Traffic just gets encapsulated and thrown into the void. This is why you often pair GRE with keepalives or run it alongside routing protocols. It’s a bit like mailing a letter with no return address: the sender has no built-in mechanism to know if it arrived.
This all seems to be pretty true. GRE will bring one end of the tunnel up without knowing about the other end. Mailing a letter with no return address is a pretty good analogy. If one end of the tunnel is up, and you have a static route pointing traffic across the tunnel, that tunnel will send it, whether the remote side is up or not.
The phrasing of “pairing” it with keepalives makes it sound like you are using some other protocol outside of GRE to help determine if the tunnel is up. Keepalives are a part of the GRE configuration, not something separate. Although its worth noting that if you want to use keepalives, you must configure them under the tunnel interface.
GRE Config
The best way to learn is by doing. Here’s a simple lab I put together to practice GRE. All testing and configuration was performed in Containerlab.
We’ll be building a GRE tunnel from csr1 to csr3. On the underlay, static routing is already in place to get reachability between loopbacks. We’ll create an OSPF adjacency across the GRE tunnel, and then OSPF will share the Lo1 networks.
![[gre-lab.png]]
Bring it UP
What’s the minimum config needed to bring the tunnel up?
All other IP addressing and static routing for the existing underlay configuration is already in place.
interface Tunnel0
tunnel source Loopback0
tunnel destination 3.3.3.3
!
interface Tunnel0
tunnel source Loopback0
tunnel destination 1.1.1.1
!
That’s it. Not convinced?
csr1#show ip int tu0
Tunnel0 is up, line protocol is up
Internet protocol processing disabled
csr1#show ip int br | i Tunnel
Tunnel0 unassigned YES unset up up
csr1#show int tu0
Tunnel0 is up, line protocol is up
Hardware is Tunnel
MTU 9976 bytes, BW 100 Kbit/sec, DLY 50000 usec,
reliability 255/255, txload 1/255, rxload 1/255
Encapsulation TUNNEL, loopback not set
Keepalive not set
Tunnel linestate evaluation up
Tunnel source 1.1.1.1 (Loopback0), destination 3.3.3.3
*** Trimmed output for brevity***
csr3#show ip int tu0
Tunnel0 is up, line protocol is up
Internet protocol processing disabled
csr3#show ip int br | i Tunnel
Tunnel0 unassigned YES unset up up
csr3#show int tu0
Tunnel0 is up, line protocol is up
Hardware is Tunnel
MTU 9976 bytes, BW 100 Kbit/sec, DLY 50000 usec,
reliability 255/255, txload 1/255, rxload 1/255
Encapsulation TUNNEL, loopback not set
Keepalive not set
Tunnel linestate evaluation up
Tunnel source 3.3.3.3 (Loopback0), destination 1.1.1.1
*** Trimmed output for brevity***
As shown above, the tunnel is Up on both routers. Notice in the show int output it shows the tunnel source IP and interface, and the tunnel destination.
In fact, the tunnel was up on csr1 before any config was put in place for csr3. GRE doesn’t include any stateful way of verifying that the other end is available. As soon as the tunnel is configured on the source router, and the tunnel source interface is Up (Lo0 in this case), the tunnel will come Up. This also shows the importance of using a Loopback as the tunnel source, so that it’s not tied to something prone to failure like a physical interface.
Of course, the tunnel isn’t very useful yet. We haven’t pointed any traffic across it. Before we do that, let’s add keepalives to make it more “stateful”.
Keepalives
As stated earlier, keepalives are a way for GRE tunnels to verify that bidirectional communication exists before the line protocol of the tunnel is brought up. The defaults for keepalives are 10 seconds with 3 retries. We’ll just use defaults.
The command for keepalives is simply keepalive under tunnel interface configuration mode.
csr1(config)#int tu0
csr1(config-if)#keepalive
csr1(config-if)#
csr1#show ip int tu0
Tunnel0 is up, line protocol is down
Internet protocol processing disabled
As expected, the tunnel goes down on csr1. There isn’t bidirectional communication across the tunnel yet. However, csr3 still sees the tunnel up. csr3 doesn’t care about csr1’s keepalives.
csr3#show ip int tu0
Tunnel0 is up, line protocol is up
Internet protocol processing disabled
After configuring keepalives on csr3, the tunnel also goes down.
csr3(config)#int tu0
csr3(config-if)#keepalive
csr3(config-if)#do sho ip int tu0
Tunnel0 is up, line protocol is down
Internet protocol processing disabled
After some time, the tunnel isn’t coming up though… What’s happening? Well I purposefully skipped over a step listed as NOT optional for GRE configuration, which is configuring an IP address on the tunnel interfaces. Let’s do that now.
What should you use for the tunnel IP addressing? It could technically be anything. Best practices I’ve read have leaned towards using 169.254 address space, just make sure you aren’t overlapping any of your own internal IP space.
csr1(config)#int tu0
csr1(config-if)#ip add 169.254.13.1 255.255.255.0
Immediately after the tunnel IP address is configured, a keepalive is sent.
![[gre-lab-keepalives.png]]
csr3(config)#int tu0
csr3(config-if)#ip add 169.254.13.3 255.255.255.0
After both sides of the tunnel are configured with IPs, the tunnel came up. Interestingly, the tunnels don’t need you to specify what the source or destination IP are for the keepalives, they just need you to configure an IP and subnet on the tunnel interface.
What happens if you configure the tunnels with IPs in different subnets? Let’s change csr3 and find out.
csr3(config-if)#no ip add
csr3(config-if)#ip add 169.254.14.3 255.255.255.0
csr1(config-if)#do sho ip int br
Interface IP-Address OK? Method Status Protocol
GigabitEthernet1 10.0.0.15 YES TFTP up up
GigabitEthernet2 172.16.12.1 YES manual up up
Loopback0 1.1.1.1 YES manual up up
Loopback1 10.1.0.1 YES manual up up
Tunnel0 169.254.13.1 YES manual up up
csr3(config-if)#do sho ip int br
Interface IP-Address OK? Method Status Protocol
GigabitEthernet1 10.0.0.15 YES TFTP up up
GigabitEthernet2 172.16.23.3 YES manual up up
Loopback0 3.3.3.3 YES manual up up
Loopback1 10.3.0.3 YES manual up up
Tunnel0 169.254.14.3 YES manual up up
It doesn’t seem to matter for keeping the tunnel up. You just need an IP configured for keepalives to send. Both sides still see the tunnel as up. Just to be certain, the previous verifications were taken beyond the keepalive timer of 30 seconds (10 seconds, 3 retries) for both csr1 and csr3.
Now let’s send some traffic.
Adding Traffic
The whole point of building this tunnel was to send some traffic over it. We can start by adding a static route across the tunnel for the networks configured on Lo1.
It’s worth mentioning that we’re using the interface as the next-hop rather than a next-hop IP.
csr1(config)#ip route 10.3.0.0 255.255.0.0 tunnel0
csr1(config)#do sho ip route 10.3.0.0
Routing entry for 10.3.0.0/16
Known via "static", distance 1, metric 0 (connected)
Routing Descriptor Blocks:
* directly connected, via Tunnel0
Route metric is 0, traffic share count is 1
csr3(config)#ip route 10.1.0.0 255.255.0.0 tu0
csr3(config)#do sho ip route 10.1.0.0
Routing entry for 10.1.0.0/16
Known via "static", distance 1, metric 0 (connected)
Routing Descriptor Blocks:
* directly connected, via Tunnel0
Route metric is 0, traffic share count is 1
csr1#ping 10.3.0.3 source 10.1.0.1
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.3.0.3, timeout is 2 seconds:
Packet sent with a source address of 10.1.0.1
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/1 ms
csr1#traceroute 10.3.0.3 source 10.1.0.1
Type escape sequence to abort.
Tracing the route to 10.3.0.3
VRF info: (vrf in name/id, vrf out name/id)
1 169.254.14.3 1 msec 1 msec *
csr3#ping 10.1.0.1 source 10.3.0.3
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.1.0.1, timeout is 2 seconds:
Packet sent with a source address of 10.3.0.3
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/1 ms
csr3#traceroute 10.1.0.1 source 10.3.0.3
Type escape sequence to abort.
Tracing the route to 10.1.0.1
VRF info: (vrf in name/id, vrf out name/id)
1 169.254.13.1 1 msec 1 msec *
It works! The traceroute shows that the tunnel is being used to send these pings. Checking Wireshark on those pings shows both the inner and outer headers with the tunnel source/destinations (Lo0s) and the actual traffic source/destination IPs (Lo1s). Since GRE doesn’t provide any encryption, the contained inner packet header and payload are all visible.
![[gre-lab-traffic-packetcapture.png]]
So does the IP address on the tunnel matter? It does indeed; this only works because we used the tunnel interface as the next hop. Had we used a next-hop IP, it wouldn’t have worked.
For completeness, let’s test it.
csr1(config)#no ip route 10.3.0.0 255.255.0.0 Tunnel0
csr1(config)#ip route 10.3.0.0 255.255.0.0 169.254.14.3
csr3(config)#do sho ip route 10.1.0.0
% Subnet not in table
csr3(config)#no ip route 10.1.0.0 255.255.0.0 Tunnel0
csr3(config)#ip route 10.1.0.0 255.255.0.0 169.254.13.1
csr1(config)#do sho ip route 10.3.0.0
% Subnet not in table
csr1 doesn’t know about the 169.254.14.0/24 network, so that new static route never gets added to the routing table. Let’s fix it.
csr3(config)#int tu0
csr3(config-if)#no ip add
csr3(config-if)#ip add 169.254.13.3 255.255.255.0
csr1(config)#no ip route 10.3.0.0 255.255.0.0 169.254.14.3
csr1(config)#ip route 10.3.0.0 255.255.0.0 169.254.13.3
csr1(config)#do ping 10.3.0.3 source 10.1.0.1
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.3.0.3, timeout is 2 seconds:
Packet sent with a source address of 10.1.0.1
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/1 ms
csr1(config)#do traceroute 10.3.0.3 source 10.1.0.1
Type escape sequence to abort.
Tracing the route to 10.3.0.3
VRF info: (vrf in name/id, vrf out name/id)
1 169.254.13.3 1 msec 1 msec *
And everything is back up!
Dynamic Routing Through GRE
To test some dynamic routing across a GRE tunnel we’ll configure OSPF to share the subnets on Lo1 instead of using static routing.
Let’s get started by removing the static routes, configuring the OSPF process, and configuring OSPF directly on the tunnel interface.
csr1(config)#no ip route 10.3.0.0 255.255.0.0 169.254.13.3
csr1(config)#router ospf 1
csr1(config-router)#router-id 1.1.1.1
csr1(config)#int tu0
csr1(config-if)#ip ospf 1 area 0
csr3(config)#no ip route 10.1.0.0 255.255.0.0 169.254.13.1
csr3(config)#router ospf 1
csr3(config-router)#router-id 3.3.3.3
csr3(config-router)#int tu0
csr3(config-if)#ip ospf 1 area 0
This is all that’s needed to bring OSPF up. However, we aren’t advertising any prefixes yet. It’s also worth mentioning that configuring OSPF directly on a tunnel interface sets the Network Type to Point-to-Point - something you typically have to explicitly configure.
csr1(config-if)#do sho ip ospf nei
Neighbor ID Pri State Dead Time Address Interface
3.3.3.3 0 FULL/ - 00:00:34 169.254.13.3 Tunnel0
csr3#show ip ospf nei
Neighbor ID Pri State Dead Time Address Interface
1.1.1.1 0 FULL/ - 00:00:32 169.254.13.1 Tunnel0
Now let’s advertise the subnets configured on our Lo1 interfaces.
csr1(config-if)#int lo1
csr1(config-if)#ip ospf 1 area 0
csr3(config)#int lo1
csr3(config-if)#ip ospf 1 area 0
We learn an OSPF route for 10.3.0.3 from csr3, and we can reach it over the GRE tunnel!
csr1#show ip route ospf
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
E1 - OSPF external type 1, E2 - OSPF external type 2, m - OMP
n - NAT, Ni - NAT inside, No - NAT outside, Nd - NAT DIA
i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
ia - IS-IS inter area, * - candidate default, U - per-user static route
H - NHRP, G - NHRP registered, g - NHRP registration summary
o - ODR, P - periodic downloaded static route, l - LISP
a - application route
+ - replicated route, % - next hop override, p - overrides from PfR
& - replicated local route overrides by connected
Gateway of last resort is 172.16.12.2 to network 0.0.0.0
10.0.0.0/8 is variably subnetted, 3 subnets, 2 masks
O 10.3.0.3/32 [110/1001] via 169.254.13.3, 00:01:37, Tunnel0
csr1#ping 10.3.0.3
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.3.0.3, timeout is 2 seconds:
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/1 ms
csr1#traceroute 10.3.0.3
Type escape sequence to abort.
Tracing the route to 10.3.0.3
VRF info: (vrf in name/id, vrf out name/id)
1 169.254.13.3 1 msec 1 msec *
Thanks for reading!
2358 Words
2026-03-12 00:00