GRE and IP-in-IP are the two simplest ways to carry one IP packet inside another. IP-in-IP (RFC 2003, October 1996) puts a second IPv4 header in front of the original packet and marks it with IP protocol 4; GRE (RFC 1701 in October 1994, simplified by RFC 2784 in March 2000) adds a small header of its own, uses protocol 47 and can carry almost any payload. Neither one encrypts, authenticates or hides anything, so a secure deployment pairs them with IPsec and strict endpoint filtering.
They are also the tunnels most likely to cause the "small pings work, big pages hang" problem. Every byte of header you add comes out of the path MTU, and when ICMP is filtered somewhere on the way, Path MTU Discovery fails silently. This post goes one level deeper than our history of VPNs and our walk through how a VPN moves one packet. It covers the headers field by field, the overhead arithmetic with worked numbers, GRE-in-UDP and IPv6, what RFC 6169 says about tunnel security, and a Linux lab that builds both tunnel types, clamps MSS and then wraps GRE in IPsec transport mode.
Key takeaways
- IP-in-IP is protocol 4 and adds exactly one outer IPv4 header: 20 bytes. 6in4 (IPv6 inside IPv4, RFC 4213) is protocol 41 and also adds 20 bytes. GRE over IPv4 is protocol 47 and adds 20 bytes of outer header plus a GRE header of 4 to 16 bytes.
- The RFC 2784 GRE header is 4 bytes: a checksum present bit, reserved bits, a 3 bit version (0) and a 2 byte Protocol Type that holds an EtherType such as 0x0800 for IPv4 or 0x86DD for IPv6. RFC 2890 (September 2000) adds an optional 4 byte Key and 4 byte Sequence Number.
- The GRE Key is a flow or tunnel identifier, not a secret. It travels in clear text and anyone on the path can read it. RFC 2890 itself says IPsec MUST be used to protect the header if you rely on sequence numbers.
- On a 1500 byte Ethernet path, IP-in-IP leaves an inner MTU of 1480, plain GRE leaves 1476, and GRE with a key leaves 1472. GRE inside IPsec ESP transport mode with AES-GCM leaves 1442 (1434 behind NAT traversal), so a TCP MSS of 1402 or lower.
- MTU problems come from four places: added headers, filtered ICMP "fragmentation needed" or "Packet Too Big" messages, the DF bit copied from the inner packet, and fragment reassembly at the far end. RFC 4459 (April 2006) describes the options; MSS clamping fixes TCP only.
- GRE-in-UDP (RFC 8086, March 2017) puts GRE behind a UDP header on port 4754 so the source port can carry flow entropy for load balancing and the traffic can cross NAT. Port 4755 is reserved for the DTLS protected variant.
- RFC 6169 (April 2011) lists what tunnels do to security: they bypass network inspection, carry inner addresses that border ingress filters never see, can source route past the endpoint, and open holes through NAT. Accept tunnel packets only from known peers, filter the inner addresses after decapsulation, and encrypt.
What GRE and IP-in-IP are, and why they are still everywhere
Both protocols solve the same problem: you want a packet to reach an intermediate point that normal routing would not pick, then continue to its real destination. RFC 2003 describes encapsulation as a way "to alter the normal IP routing" by delivering a packet to an intermediate node, which strips the outer header and forwards the original. The node that adds the header is the tunnel entry point and the node that removes it is the exit point.
IP-in-IP came out of Mobile IP. RFC 2003 says the Mobile IP working group needed a way to deliver packets from a mobile node's home network to wherever the node currently was, and it lists other uses: multicast, preferential billing, routes with chosen security properties and general policy routing. It also explains why the IETF preferred encapsulation to the IP loose source route option: source routing had unsolved security problems, routers handled IP options slowly and inconsistently, and firewalls often dropped source routed packets.
GRE is more general. RFC 1701 and RFC 2784 both describe it as a way to carry "an arbitrary network layer protocol over another arbitrary network layer protocol". The original motivation was combinatorial: instead of a separate "X over Y" specification for each pair of protocols, one header with a payload type field turns an O(n squared) problem into a manageable one. That generality is why GRE carried non-IP protocols in the 1990s and why it still carries IPv4, IPv6, MPLS and Ethernet frames today.
They have outlived many fancier designs because they are cheap, stateless and supported almost everywhere. Common uses today:
- Site to site links between routers, often with a routing protocol such as OSPF or BGP running across the tunnel. GRE carries multicast, so OSPF hellos and other link-local multicast routing traffic pass through it, which a pure IPsec tunnel mode policy does not do on its own.
- Cloud transit. AWS documents Transit Gateway Connect as using GRE "for high performance" with two BGP sessions over each GRE tunnel to a third party appliance. Other providers offer similar GRE based attachments.
- DDoS scrubbing return paths. A common design for scrubbing services is to announce the customer's prefix, clean the traffic in the provider's network and return the clean packets to the customer's edge over a GRE tunnel.
- IPv6 transition. 6in4 configured tunnels (RFC 4213) carry IPv6 across IPv4 only networks with protocol 41 and are still how many tunnel broker services and lab networks reach IPv6.
- Overlays and inside the kernel. Linux exposes
ipip,sit,gre,gretap,ip6gre,ip6gretapandip6tnllink types, and container and load balancer data planes use IP-in-IP and GRE for host to host forwarding.
Dated timeline of the tunnelling standards
Dates come from each RFC header. Category is the RFC's own status line.
| Date | Document | What it did |
|---|---|---|
| November 1990 | RFC 1191 | Path MTU Discovery for IPv4, the mechanism tunnels depend on for sizing |
| October 1994 | RFC 1701 (Informational) | Original GRE: checksum, routing, key, sequence and strict source route bits plus a recursion control field |
| October 1994 | RFC 1702 (Informational) | GRE over IPv4 networks |
| October 1996 | RFC 2003 (Standards Track) | IP Encapsulation within IP, IP protocol 4, with TTL, DF, ICMP and loop rules |
| December 1998 | RFC 2473 (Standards Track) | Generic packet tunnelling in IPv6; IANA lists it as the reference for protocol 41 |
| March 2000 | RFC 2784 (Standards Track) | Simplified GRE: drops routing, key, sequence and recursion fields, protocol 47 |
| May 2000 | RFC 2827 (BCP 38) | Network ingress filtering against spoofed source addresses |
| September 2000 | RFC 2890 (Standards Track) | Restores optional Key and Sequence Number fields to RFC 2784 GRE |
| March 2004 | RFC 3704 (BCP 84) | Ingress filtering for multihomed networks |
| October 2005 | RFC 4213 (Standards Track) | Basic IPv6 transition mechanisms: configured 6in4 tunnels, MTU 1280 to 1480 |
| December 2005 | RFC 4301, RFC 4303 | Security Architecture for IP and ESP, the basis for GRE over IPsec |
| April 2006 | RFC 4459 (Informational) | MTU and fragmentation issues with in the network tunnelling |
| May 2007 | RFC 4890 (Informational) | ICMPv6 filtering recommendations: Packet Too Big must not be dropped |
| April 2011 | RFC 6169 (Informational) | Security concerns with IP tunnelling |
| February 2013 | RFC 6864 | Updates the IPv4 Identification field rules in RFC 791, RFC 1122 and RFC 2003 |
| August 2014 | RFC 7348 (Informational) | VXLAN, the UDP based layer 2 overlay often compared with GRE |
| July 2015 | RFC 7588 (Informational) | Documents how many vendors already handle GRE fragmentation |
| October 2015 | RFC 7676 (Standards Track) | IPv6 as GRE payload and as GRE delivery protocol |
| March 2017 | RFC 8086 (Standards Track) | GRE-in-UDP on UDP port 4754, with DTLS on 4755 |
| July 2017 | RFC 8201 | Path MTU Discovery for IPv6 |
| September 2020 | RFC 8900 (BCP 230) | IP fragmentation considered fragile |
What each header looks like and what it costs
IP-in-IP (protocol 4)
RFC 2003 inserts an outer IPv4 header before the existing one and changes nothing else. The outer Source and Destination Addresses are the tunnel endpoints; the inner addresses still name the original sender and receiver. The encapsulator sets the outer fields like this:
| Outer field | Value set by the encapsulator (RFC 2003 section 3.1) |
|---|---|
| Version | 4 |
| IHL | Length of the outer header in 32 bit words, normally 5 |
| TOS | Copied from the inner header |
| Total Length | Outer header plus the whole inner packet |
| Identification, Flags, Fragment Offset | Normal IPv4 rules, but if the inner DF bit is set the outer DF bit MUST be set |
| TTL | Whatever is needed to reach the tunnel exit point |
| Protocol | 4 |
| Source and Destination | The tunnel entry and exit addresses |
| Options | Inner options are not copied; tunnel specific options may be added |
Two TTL rules matter in practice. When a router tunnels a packet as part of forwarding, it decrements the inner TTL by one first, and it must not encapsulate a packet whose TTL is zero. On the far side the decapsulator does not touch the inner TTL; normal forwarding decrements it again. The result is that a tunnel looks like one hop to a traceroute running inside it, however many routers the outer packet crossed.
RFC 2003 also has loop protection. A router must not tunnel a packet whose source address is one of its own addresses, or whose source address is the tunnel's own exit point. Without those checks a routing mistake can send packets around the tunnel forever.
Overhead: 20 bytes on an IPv4 underlay with no options. 6in4 is the same shape with an IPv6 packet inside and protocol 41 in the outer header, so it also costs 20 bytes.
One naming trap: the IANA protocol registry lists protocol 4 as "IPv4 encapsulation" (RFC 2003) and a separate, older protocol 94 as "IPIP, IP-within-IP Encapsulation Protocol". Linux ipip interfaces and essentially all modern equipment use protocol 4. If a firewall rule says "IPIP" check which number it means.
GRE (protocol 47)
A GRE packet has three parts: the delivery header (the outer IPv4 or IPv6 header), the GRE header, and the payload packet. RFC 2784 defines the basic GRE header:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|C| |K|S| Reserved0 | Ver | Protocol Type |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum (optional) | Reserved1 (optional) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Key (optional) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number (optional) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
That diagram combines RFC 2784 with the RFC 2890 extension. Field by field:
- C, Checksum Present (bit 0). When set, a 2 byte checksum over the GRE header and payload follows, plus 2 reserved bytes. A compliant receiver must accept and process it.
- K, Key Present (bit 2) and S, Sequence Number Present (bit 3). Added back by RFC 2890 in the same bit positions RFC 1701 used, for compatibility.
- Reserved0. RFC 2784 says a receiver must discard a packet with any of bits 1 to 5 set unless it implements RFC 1701. Those bits held the old routing, key, sequence and strict source route flags and RFC 1701's 3 bit recursion control, which counted how many more encapsulations were allowed.
- Ver (bits 13 to 15). Must be 0. RFC 2784's IANA section notes that version 1 is used by PPTP.
- Protocol Type. An EtherType. 0x0800 means the payload is IPv4 (RFC 2784 section 3), 0x86DD means IPv6 (RFC 7676), and IANA's EtherType registry lists 0x6558 as Transparent Ethernet Bridging (citing RFC 1701), the type used when GRE carries whole Ethernet frames, as Linux
gretapinterfaces do. - Key (4 bytes). RFC 2890 says the key identifies "an individual traffic flow within a tunnel". Many implementations use it to tell apart several tunnels between the same two addresses.
- Sequence Number (4 bytes). A free running 32 bit counter starting at 0, so the receiver can drop out of order packets or briefly buffer them to restore order.
GRE header sizes therefore range from 4 bytes (no options), through 8 (key, or checksum), 12 (two options) to 16 bytes (checksum, key and sequence). On an IPv4 underlay the total overhead is 24 to 36 bytes; on an IPv6 underlay with no extension headers it is 44 to 56 bytes.
GRE over IPv6 and IPv6 over GRE
RFC 7676 (October 2015) filled the gap that RFC 2784 left: GRE procedures for IPv6. As a payload, IPv6 uses Protocol Type 0x86DD. As a delivery protocol, the IPv6 Next Header field (or the last extension header's Next Header) is 47.
The MTU rule is the interesting part. RFC 7676 says a GRE tunnel MUST be able to carry a 1280 byte IPv6 packet, the IPv6 minimum link MTU, without fragmenting the payload. The ingress should check that before activating the tunnel and periodically afterwards, for example by sending a 1280 byte packet to itself through the tunnel. If a payload is bigger than the tunnel MTU and that MTU is at least 1280, the ingress drops it and sends an ICMPv6 Packet Too Big with the tunnel MTU. Only when the tunnel MTU is below 1280 may the ingress fragment the delivery packet, and the egress must then reassemble.
RFC 7676 also points out that the GRE checksum is partly redundant with the inner TCP or UDP checksum, but that with the checksum off, the GRE header itself is protected by nothing. That is one more reason to put integrity protection around the whole tunnel.
Overhead at a glance
| Encapsulation | IP protocol or port | Bytes added on an IPv4 underlay | Inner MTU on a 1500 byte path |
|---|---|---|---|
| IP-in-IP (IPv4 in IPv4) | Protocol 4 | 20 | 1480 |
| 6in4 (IPv6 in IPv4) | Protocol 41 | 20 | 1480 (RFC 4213 default 1280) |
| GRE, no options | Protocol 47 | 24 | 1476 |
| GRE with key | Protocol 47 | 28 | 1472 |
| GRE with checksum, key and sequence | Protocol 47 | 36 | 1464 |
| GRE-in-UDP, no options | UDP 4754 | 32 | 1468 |
| GRE over an IPv6 underlay, no options | Next Header 47 | 44 | 1456 |
Why tunnels break on MTU
The usual symptom is that the tunnel comes up, ping works, SSH connects, and then a web page, a file copy or a TLS handshake with a large certificate chain stalls. Small packets fit and big packets disappear. The mechanics:
- Added headers shrink the space. A host on Ethernet assumes it can send 1500 byte packets. Add 24 bytes of GRE and the outer packet would be 1524 bytes, too big for the next 1500 byte link.
- The DF bit decides what happens next. RFC 2003 says that if the inner packet has Don't Fragment set, the outer header must have it too. Modern TCP stacks set DF on nearly every packet because they run Path MTU Discovery. So the router that cannot forward the outer packet must drop it and send back an ICMP "fragmentation needed" (IPv4 type 3 code 4) or ICMPv6 Packet Too Big.
- The ICMP message goes to the tunnel entry, not the original sender. The router that drops the oversized outer packet sees the tunnel endpoints as source and destination. RFC 2003 section 5.1 therefore requires encapsulators to keep "soft state" about the tunnel MTU and to send their own correct ICMP messages back to the original sender, advertising the tunnel MTU minus the encapsulation header.
- Filtered ICMP turns this into a black hole. If a firewall on either the underlay path or the inner path drops those ICMP messages, nobody learns the smaller size. The sender keeps retransmitting full sized segments that are dropped every time. RFC 4890 lists ICMPv6 Packet Too Big among the messages that must not be dropped, because Path MTU Discovery cannot work without it.
- Fragmentation is a poor fallback. If DF is clear, the outer packet can be fragmented and the far endpoint must reassemble before decapsulating. RFC 4459 explains why that is expensive: reassembly needs buffers, attackers can exhaust them by sending incomplete fragment sets, and at high data rates the 16 bit IPv4 Identification field wraps. RFC 6864 notes that strict uniqueness of that field would limit a connection to about 6.4 Mbps at typical sizes. RFC 8900 (BCP 230, September 2020) goes further and calls IP fragmentation fragile, recommending that protocols avoid relying on it.
The four fixes RFC 4459 describes
RFC 4459 lays out the design space for in the network tunnels and is honest that none is perfect:
- Fragment and reassemble at the tunnel endpoints. Simple, but costly in hardware and fragile at speed.
- Signal the lower MTU to the sources (Path MTU Discovery). Correct in principle, but it depends on ICMP getting through, and ICMP can be forged: an attacker sending fake "too big" messages with a tiny MTU can cut throughput.
- Encapsulate only where there is spare MTU. Raise the underlay MTU, for example to a 9000 byte jumbo frame data center fabric, so tunnel overhead never pushes a 1500 byte packet over the limit. This is the cleanest fix when you control the underlay.
- Fragment the inner packet before encapsulation. Possible only for IPv4 packets without DF, and not available for IPv6.
It also describes MSS clamping, calling it a technique "or rather a hack" that works only for TCP: the tunnel endpoint rewrites the Maximum Segment Size option in TCP SYN packets so both ends agree on segments small enough to fit. It does nothing for UDP, QUIC or anything else without an MSS, and it only helps if all traffic passes through the clamping device. RFC 7588 (July 2015) documents the configurable approach many vendors already shipped for GRE fragmentation. Packetization Layer Path MTU Discovery (RFC 4821, March 2007) is the end host alternative: probe with real packets and do not depend on ICMP.
Worked example 1: plain GRE and IP-in-IP on Ethernet
Underlay MTU 1500, IPv4, no IP options.
- IP-in-IP: 1500 minus 20 (outer IPv4) = 1480 inner MTU. TCP MSS for IPv4 inside = 1480 minus 20 (inner IPv4) minus 20 (TCP) = 1440.
- GRE without options: 1500 minus 20 minus 4 = 1476. TCP MSS = 1476 minus 40 = 1436.
- GRE with a key: 1500 minus 20 minus 8 = 1472. TCP MSS = 1432.
- IPv6 traffic inside GRE with a key: MTU 1472, IPv6 TCP MSS = 1472 minus 40 (IPv6) minus 20 (TCP) = 1412.
To test the path with DF set from inside the GRE tunnel with a key, the largest ICMP echo payload is 1472 minus 20 (IPv4) minus 8 (ICMP) = 1444 bytes. A ping with -s 1444 -M do should pass and -s 1445 -M do should fail locally with "message too long".
Worked example 2: GRE over IPsec ESP transport mode with AES-GCM
This is the common "secure GRE" design: a GRE tunnel between two routers, with every GRE packet protected by ESP in transport mode. ESP transport mode keeps the original outer IP header and inserts ESP between it and the GRE header.
The byte budget, using AES-GCM with a 16 byte ICV as specified in RFC 4106:
| Piece | Bytes | Source |
|---|---|---|
| Outer IPv4 header | 20 | RFC 791 |
| ESP header: SPI and Sequence Number | 8 | RFC 4303 |
| AES-GCM explicit IV | 8 | RFC 4106: "MUST be eight octets" |
| GRE header, no options | 4 | RFC 2784 |
| Inner packet | X | |
| Padding | 0 to 3 | RFC 4303: ciphertext must end on a 4 byte boundary |
| Pad Length and Next Header | 2 | RFC 4303 |
| ICV | 16 | RFC 4106 allows 8, 12 or 16; 16 is the usual choice |
Fixed overhead outside the encrypted part is 20 + 8 + 8 + 16 = 52 bytes. That leaves 1500 minus 52 = 1448 bytes for the encrypted portion, which is GRE (4) plus X plus padding plus 2. 1448 is a multiple of 4, so the largest X with zero padding is 1448 minus 4 minus 2 = 1442. The inner TCP MSS for IPv4 is 1442 minus 40 = 1402.
Three variations change the answer:
- NAT traversal. If either side is behind NAT, IKEv2 switches ESP into UDP on port 4500 (RFC 3948), adding 8 bytes. The encrypted portion drops to 1440, so X = 1440 minus 6 = 1434 and MSS 1394.
- GRE with a key. The GRE header grows to 8 bytes, so X = 1448 minus 10 = 1438 and MSS 1398.
- IPsec tunnel mode instead of transport mode. Tunnel mode adds a second 20 byte IP header inside ESP. X = 1448 minus 26 = 1422 and MSS 1382. Twenty bytes per packet for no benefit is the main reason GRE over IPsec usually uses transport mode.
Many operators round down further, to a tunnel MTU of 1400 and an MSS of 1360, to leave room for an extra option or a different cipher. That is a reasonable margin; just know which numbers you are rounding from.
Worked example 3: an IPv6 underlay
Underlay MTU 1500, IPv6 outer header with no extension headers, GRE with no options carrying IPv4:
- 1500 minus 40 (outer IPv6) minus 4 (GRE) = 1456 inner MTU, IPv4 TCP MSS 1416.
- Carrying IPv6 inside instead: MSS = 1456 minus 60 = 1396.
Both are comfortably above RFC 7676's 1280 byte floor. Now put that tunnel on a path that already has a 1280 byte link somewhere (some mobile and older tunnelled IPv6 paths do). The inner MTU becomes 1280 minus 44 = 1236, which is below 1280. RFC 7676 then allows the ingress to fragment the delivery packet, and the egress must reassemble. If your equipment cannot, RFC 7676 says it must not activate the tunnel.
GRE-in-UDP, NAT and load balancing
Plain GRE has two operational weaknesses that have nothing to do with security.
Load balancing. Routers spread traffic across equal cost paths (ECMP) by hashing the source address, destination address, protocol and, for TCP and UDP, the ports. RFC 8086 explains that for GRE only the two addresses and the protocol number go into the hash, so every flow inside one GRE tunnel takes the same path. One fat tunnel cannot use more than one link.
NAT. Port translating NAT devices, the most common kind, multiplex many inside hosts behind one public address using UDP and TCP ports. GRE has no ports, so a port translating NAT device has nothing to multiplex on. At best it can track GRE by addresses alone, which limits how many inside hosts can run GRE through one public address.
RFC 8086 (March 2017) answers both by placing the GRE header behind a UDP header:
- Destination port 4754 means GRE-in-UDP. Port 4755 means GRE-in-UDP protected with DTLS, and that port must not be used for anything else. IANA's port registry lists both, assigned on 5 October 2016.
- The source port carries entropy. The encapsulator hashes the inner flow and puts the result in the UDP source port, which RFC 8086 says should come from the ephemeral range 49152 to 65535. That gives 14 bits of entropy, and the standard ECMP five tuple hash now spreads flows. On IPv6 the same value should also go into the flow label.
- Checksums. A UDP checksum should be used on IPv4 and must be used on IPv6. RFC 8086 recommends the GRE checksum when the UDP checksum is off.
- Congestion. A default GRE-in-UDP tunnel must not carry traffic that is not congestion controlled, which rules out most IP multicast on the general Internet. A "traffic managed controlled environment" such as a provider core with capacity planning and rate limiters has relaxed rules.
- Fragmentation. Fragment the payload before encapsulating where possible, and keep the same UDP source port on every fragment so they take the same path.
The security consideration is short and blunt: GRE-in-UDP "does not affect security for the payload protocol", the GRE security considerations still apply, and DTLS should be used to secure the traffic. It also notes that a well known destination port makes off path attacks easier, and suggests agreeing an alternate port between the two endpoints where that matters.
On Linux, the closest built in equivalent is Foo-over-UDP (FOU). ip fou add port PORT ipproto 47 creates a receive port that treats UDP payloads as GRE, and a gre link created with encap fou encap-sport auto encap-dport PORT sends GRE behind UDP with a flow hashed source port. The result has the same layout as RFC 8086, but check interoperability before pairing a Linux host with a router that implements RFC 8086 natively.
Why GRE and IP-in-IP have no security of their own
Neither protocol was designed to be secure, and the specifications say so.
- No confidentiality. The inner packet is copied byte for byte. Anyone on the underlay path can read it.
- No authentication. The only thing that identifies the sender is the outer source address, which is trivial to spoof from anywhere that does not enforce source address validation. An endpoint that accepts protocol 47 or protocol 4 from any address will decapsulate packets from anyone.
- The GRE Key is not a password. It is sent in clear and RFC 2890 describes it only as a flow identifier. Treat any configuration that calls it a "tunnel key" for authentication as a labelling convenience.
- Sequence numbers enable a denial of service. RFC 2890's security section warns that an attacker can inject packets with arbitrary sequence numbers, and says IPsec MUST be used to protect the GRE header and payload when sequencing is used. A forged high sequence number can make the receiver discard real traffic as "out of sequence".
- Filtering becomes harder. RFC 2003's security section says encapsulation "makes it difficult for border routers to filter datagrams", because the real addresses, protocol and ports are no longer where filters look. RFC 2784 says packet filtering needs either a firewall that looks inside GRE or filtering at the tunnel endpoint, and suggests terminating the tunnel at the firewall where that matters.
What RFC 6169 says about tunnel security
RFC 6169, "Security Concerns with IP Tunneling" (April 2011), is the reference list. It was written with automatic IPv6 transition tunnels such as Teredo and 6to4 in mind, but every point applies to GRE and IP-in-IP.
- Tunnels bypass network security. Inspection and policy devices that are not tunnel aware see only the outer header. Defence in depth can drop "perhaps down to zero". The RFC recommends making tunnels easy to disable and treating a tunnel as an "interface of last resort" when native access exists.
- Inner addresses skip ingress and egress filtering. Border filters such as those in BCP 38 check outer addresses. The inner source address can be anything, and RFC 6169 says only the tunnel endpoints can filter it. The recommendation is for tunnel servers and clients to apply ingress and egress checks to the tunnelled addresses themselves.
- Source routing after the tunnel. An inner packet can carry a routing header or source route option that sends it onward from the decapsulator to places your network policy would not allow. Tunnel endpoints should discard tunnelled packets that request extra routing.
- Deep inspection of tunnelled traffic is inefficient. Finding every tunnel among all traffic is costly, and nested or encrypted tunnels defeat inspection.
- NAT holes increase exposure. UDP based tunnels hold NAT bindings open, and a host behind NAT can become reachable from the Internet without its administrators realising.
- Tunnel addresses can be guessed or profiled, and a changed tunnel server setting can redirect traffic.
Its section on mechanisms is short: run tunnel servers inside your own network so tunnelled traffic does not cross border routers unnoticed, steer traffic to them with internal routing, and configure firewalls to allow known, controlled tunnelling mechanisms and disallow unknown ones.
There is a reflection angle too. A decapsulator that accepts tunnel packets from anyone will forward the inner packet with whatever inner source address the sender chose. That lets an attacker use an open tunnel endpoint to inject spoofed traffic into a network from a trusted looking interface, or to bounce traffic toward a victim. RFC 4213 addresses this for 6in4 directly: the decapsulator MUST check that the outer source address matches a configured tunnel before processing the packet, and its section on spoofing threats says tunnels must not be usable to bypass ingress filtering.
How to secure a GRE or IP-in-IP tunnel
Security for these tunnels is layered around them, not inside them. In order of importance:
- Encrypt and authenticate with IPsec. GRE over IPsec in transport mode, negotiated with IKEv2, gives confidentiality, integrity, origin authentication and anti replay for every GRE packet. Our post on IKEv2 and IPsec covers algorithm choices. The IPsec policy should match only GRE (protocol 47) between the two endpoint addresses, and the inbound policy should be required, so a GRE packet that arrives without ESP is dropped.
- Accept tunnel protocols only from the configured peer. On each endpoint, allow protocol 4, 41 or 47 (and ESP, UDP 500 and 4500 if you run IPsec) from the peer's address only, and drop them from everyone else. This is the RFC 4213 source check written as a firewall rule, and it stops random hosts from injecting packets through your tunnel.
- Filter inner addresses after decapsulation. Treat the tunnel interface like any other untrusted interface. Turn on strict reverse path filtering for it, and allow only the inner source prefixes you expect from that peer. This closes the RFC 6169 ingress filtering gap.
- Drop source routed inner packets. RFC 5095 (December 2007) deprecated the IPv6 type 0 routing header for exactly this kind of abuse. On Linux, the kernel documentation lists the IPv4
accept_source_routedefault as true on a router and false on a host, so set it to 0 explicitly on routers that terminate tunnels. - Do not carry what you do not need. If the tunnel exists only for IPv4 unicast, do not enable IPv6 or multicast routing across it. A
gretapinterface bridges whole Ethernet segments and widens the blast radius; use plaingreunless you need layer 2. - Filter at the border. Block inbound protocol 4, 41 and 47 at the network edge except to the hosts that terminate tunnels, and block outbound tunnel protocols from hosts that should not be originating them. That is the RFC 6169 "allow known tunnels, deny unknown ones" rule.
- Monitor liveness and changes. GRE has optional keepalives in many router implementations, and you can run BFD or a routing protocol's hellos across the tunnel to detect failure. Alert on tunnel interfaces that appear, change peer or change MTU, and on unexpected volumes of protocol 47 at the edge.
- Keep ICMP that matters. Allow ICMP "fragmentation needed" and ICMPv6 Packet Too Big through every firewall on the path, rate limited if you must, and clamp MSS on the tunnel interface as a backstop for TCP.
Step by step: build GRE and IP-in-IP tunnels on Linux, then add IPsec
This lab uses two Linux hosts with documentation addresses: host A at 198.51.100.1 and host B at 203.0.113.1 on their eth0 interfaces. The tunnel uses 10.255.0.0/30 inside. Run the commands as root or with sudo. You need iproute2, nftables and tcpdump. Commands are shown for host A; on host B swap the local and remote addresses and use 10.255.0.2.
None of these commands erase data, but adding policies and firewall rules to a remote machine can cut off your own SSH session. Work from a console, or put a timed rollback in place before you start.
1. Confirm the underlay and the path MTU
# Check that the peer is reachable and the underlay MTU is 1500
ip -br addr show dev eth0
ip link show dev eth0 | grep -o 'mtu [0-9]*'
# Largest unfragmented IPv4 packet on a 1500 byte path: 1472 bytes of ICMP payload
ping -c 3 -M do -s 1472 203.0.113.1
ping -c 3 -M do -s 1473 203.0.113.1 # should fail
If the second ping fails with "message too long" locally, the local MTU is 1500. If the first one times out, something on the path is smaller or is dropping ICMP; find that before building anything on top.
2. Build a GRE tunnel with a key
modprobe ip_gre
ip link add gre1 type gre local 198.51.100.1 remote 203.0.113.1 ttl 64 key 1001
ip link set gre1 mtu 1472 # 1500 - 20 outer IPv4 - 8 GRE with key
ip addr add 10.255.0.1/30 dev gre1
ip link set gre1 up
ip -d link show gre1 # shows local, remote, ttl, ikey/okey, pmtudisc
The ip-link manual page documents these options: key sets the same key in both directions (ikey and okey set them separately), csum turns on GRE checksums, seq turns on sequencing, and pmtudisc is on by default. The manual also notes that a fixed ttl is incompatible with nopmtudisc, because a tunnel with a fixed TTL always performs Path MTU Discovery.
Test from host A once host B is configured:
ping -c 3 10.255.0.2
ping -c 3 -M do -s 1444 10.255.0.2 # 1472 - 20 - 8: should pass
ping -c 3 -M do -s 1445 10.255.0.2 # should fail locally
3. Build an IP-in-IP tunnel
modprobe ipip
ip link add ipip1 type ipip local 198.51.100.1 remote 203.0.113.1 ttl 64
ip link set ipip1 mtu 1480 # 1500 - 20
ip addr add 10.255.1.1/30 dev ipip1
ip link set ipip1 up
IP-in-IP has no key, so you can only have one such tunnel per pair of endpoint addresses. That is one practical reason operators prefer GRE.
4. Watch the encapsulation with tcpdump
# On host A, in one terminal
tcpdump -ni eth0 -v 'ip proto 47 or ip proto 4'
# In another terminal
ping -c 2 10.255.0.2
ping -c 2 10.255.1.2
You should see outer packets between 198.51.100.1 and 203.0.113.1 marked GREv0 with the key printed, and IP-in-IP packets shown as an IPv4 packet inside another. The inner ICMP is fully readable: that is the point to remember before you send anything sensitive across it.
5. Clamp TCP MSS on the tunnel
The nftables documentation shows how to clamp the MSS option in TCP SYN packets to the route MTU, and notes it is equivalent to the older TCPMSS --clamp-mss-to-pmtu target. MSS is announced separately in each direction, so the rule must see both the SYN and the SYN-ACK.
nft add table inet tunnel_mss
nft add chain inet tunnel_mss forward '{ type filter hook forward priority mangle; policy accept; }'
nft add rule inet tunnel_mss forward oifname "gre1" tcp flags syn tcp option maxseg size set rt mtu
nft add rule inet tunnel_mss forward iifname "gre1" tcp flags syn tcp option maxseg size set rt mtu
If the tunnel endpoint is also the TCP endpoint (not a router), the kernel already uses the interface MTU for its own connections and the clamp only matters for forwarded traffic. If you prefer a fixed value, set 1432 matches worked example 1 for GRE with a key.
6. Lock the endpoints down
Accept tunnel protocols only from the peer, and filter the inner addresses.
nft -f - <<'EOF'
table inet tunnel_guard {
chain input {
type filter hook input priority 0; policy accept;
# Tunnel and IPsec protocols from the configured peer only
ip protocol { 4, 47, 50 } ip saddr != 203.0.113.1 drop
udp dport { 500, 4500 } ip saddr != 203.0.113.1 drop
}
chain forward {
type filter hook forward priority 0; policy accept;
# Only the expected inner source prefix may arrive through the tunnel
iifname "gre1" ip saddr != { 10.255.0.2, 10.20.0.0/16 } drop
}
}
EOF
sysctl -w net.ipv4.conf.gre1.rp_filter=1
sysctl -w net.ipv4.conf.all.accept_source_route=0
sysctl -w net.ipv4.conf.gre1.accept_source_route=0
Replace 10.20.0.0/16 with the prefixes actually behind host B. If host A has other legitimate IPsec peers, list them instead of the single address. The kernel documentation notes that strict mode (rp_filter=1) follows RFC 3704 and that the higher of the all and per interface values applies, so check net.ipv4.conf.all.rp_filter too; use loose mode (2) if routing through the tunnel is asymmetric.
7. Protect GRE with IPsec transport mode
For production, negotiate keys with an IKEv2 daemon. Here is the shape of the configuration for strongSwan's swanctl.conf, an open source IKE daemon whose documentation describes mode = transport and protocol selectors in square brackets on local_ts and remote_ts:
connections {
gre-a-b {
version = 2
local_addrs = 198.51.100.1
remote_addrs = 203.0.113.1
proposals = aes256-sha256-x25519
local {
auth = pubkey
certs = hostA.pem
}
remote {
auth = pubkey
id = "CN=hostB.example.net"
}
children {
gre {
mode = transport
local_ts = dynamic[gre]
remote_ts = dynamic[gre]
esp_proposals = aes256gcm16-x25519
start_action = trap
}
}
}
}
The [gre] selector limits the security association to protocol 47 between the two endpoint addresses, mode = transport avoids the extra 20 byte header of tunnel mode, and start_action = trap installs a policy that triggers negotiation when the first GRE packet tries to leave. Use certificates or a long random pre-shared key, never a short shared password.
For a quick lab only, you can install the same protection by hand with ip xfrm and static keys. Static keys never rotate and provide no peer authentication beyond knowing the key, so never use this outside a lab:
# LAB ONLY: generate a 20 byte key (16 byte AES key + 4 byte salt) and copy it to both hosts
KEY=0x$(head -c 20 /dev/urandom | od -An -tx1 | tr -d ' \n')
# Outbound and inbound states (swap src and dst on host B)
ip xfrm state add src 198.51.100.1 dst 203.0.113.1 proto esp spi 0x1001 mode transport \
aead 'rfc4106(gcm(aes))' $KEY 128
ip xfrm state add src 203.0.113.1 dst 198.51.100.1 proto esp spi 0x1002 mode transport \
aead 'rfc4106(gcm(aes))' $KEY 128
# Policies: only GRE between the two endpoints, required in both directions
ip xfrm policy add src 198.51.100.1 dst 203.0.113.1 proto gre dir out \
tmpl src 198.51.100.1 dst 203.0.113.1 proto esp mode transport
ip xfrm policy add src 203.0.113.1 dst 198.51.100.1 proto gre dir in \
tmpl src 203.0.113.1 dst 198.51.100.1 proto esp mode transport
With the inbound policy in place, the kernel drops GRE from the peer that did not arrive through the matching ESP security association. Now lower the tunnel MTU to the value from worked example 2 for GRE with a key:
ip link set gre1 mtu 1438
nft flush chain inet tunnel_mss forward
nft add rule inet tunnel_mss forward oifname "gre1" tcp flags syn tcp option maxseg size set 1398
nft add rule inet tunnel_mss forward iifname "gre1" tcp flags syn tcp option maxseg size set 1398
8. Verify with tcpdump and counters
# Nothing should match: GRE must no longer be visible on the wire
tcpdump -ni eth0 -c 5 'ip proto 47'
# ESP should carry the traffic instead
tcpdump -ni eth0 -c 5 esp
# In a third terminal
ping -c 5 10.255.0.2
ping -c 3 -M do -s 1410 10.255.0.2 # 1438 - 28: should pass
# Security associations and their byte counters
ip -s xfrm state
ip xfrm policy
If the first tcpdump shows GRE packets, the policy is not matching: check that the selector uses the same endpoint addresses as the GRE local and remote, and that the GRE interface has a fixed local address. If pings at full size fail but small ones work, recompute the MTU for your cipher and NAT situation.
Comparison: IP-in-IP, GRE, GRE-in-UDP, VXLAN, IPsec tunnel mode and WireGuard
Overheads are for an IPv4 underlay with no options. VXLAN carries an inner Ethernet frame, so its overhead includes the 14 byte inner Ethernet header. For background on VXLAN see our VXLAN and EVPN guide.
| IP-in-IP | GRE | GRE-in-UDP | VXLAN | IPsec ESP tunnel mode | WireGuard | |
|---|---|---|---|---|---|---|
| Identifier on the wire | IP protocol 4 | IP protocol 47 | UDP 4754 (4755 with DTLS) | UDP 4789 | IP protocol 50, or UDP 4500 with NAT traversal | UDP, port chosen by configuration |
| Payload | IPv4 only | Any EtherType: IPv4, IPv6, MPLS, Ethernet | Same as GRE | Ethernet frames | IP packets | IPv4 and IPv6 packets |
| Bytes added | 20 | 24 to 36 | 32 to 44 | 50 including inner Ethernet | 54 to 57 with AES-GCM, 62 to 65 with NAT traversal | 60 to 75 (20 IP, 8 UDP, 16 header, 16 tag, plus padding to a multiple of 16) |
| Multicast | Possible but rarely used | Yes, a common reason to choose it | Not for uncontrolled traffic on the Internet (RFC 8086) | Yes, via flood and learn or EVPN | Not natively; usually paired with GRE | No |
| Encryption | None | None | None, or DTLS on 4755 | None | Yes | Yes, ChaCha20-Poly1305 |
| Authentication | None | None (the key is not a secret) | None, or DTLS | None | IKEv2 with certificates or PSK | Static public keys |
| ECMP entropy | Addresses only | Addresses only (key in some hardware) | UDP source port | UDP source port | SPI, or UDP source port with NAT-T | UDP port per peer |
| NAT friendly | No | No | Yes | Yes | With NAT traversal | Yes |
| Typical use | Host overlays, Mobile IP, simple router links | Site links with routing protocols, cloud transit, scrubbing return paths | Load balanced provider cores | Data center layer 2 overlays | Site to site and remote access encryption | Remote access and small site links |
The pattern in the table is clear: the cheap encapsulations (the first four columns) solve reachability and multiplexing, the last two solve confidentiality and authentication, and real deployments usually stack one of each.
Where Safeguard fits
Safeguard does not configure routers, terminate tunnels, inspect packets or analyse network firmware, and nothing in the lab above depends on it. The fit is with the software around the tunnels: the automation repositories that render GRE and IPsec configurations, the controllers and exporters that watch tunnel state, and the container images teams use to run open source routing, IKE and VPN stacks. Those are built from open source packages and shipped as images. Safeguard scans repositories, container images and SBOM files (safeguard scan --dir ., --image <name>, --sbom <file>), shows which projects carry a given component and version, matches them against vulnerability data, and applies policies and gates. When a fix ships for a library inside your network automation, that tells you which builds still carry the vulnerable version. Router and firewall operating systems from vendors are outside that scope, and their fixes still come from those vendors.
Common mistakes
- Treating the GRE key as authentication. It is a clear text flow identifier. Anyone on the path can read it and anyone off the path can guess it.
- Accepting protocol 47 or protocol 4 from anywhere. An endpoint without a source check is an open injection point into your network. Filter to the configured peer.
- Forgetting the inner addresses. Border filters see only outer addresses. Without reverse path filtering or explicit prefix filters on the tunnel interface, any inner source address is accepted.
- Leaving the tunnel MTU at the default and hoping. Calculate it from the real stack of headers, set it on the interface, and test with DF set pings at the boundary.
- Blocking all ICMP. Dropping "fragmentation needed" and Packet Too Big messages is what turns a slightly smaller MTU into a black hole. RFC 4890 is explicit about Packet Too Big.
- Relying on MSS clamping for everything. It fixes TCP only. QUIC, DNS over UDP with large responses and VPN traffic inside the tunnel still need a correct MTU.
- Using IPsec tunnel mode under GRE. It works, but it costs 20 extra bytes per packet compared with transport mode for the same protection.
- Writing an IPsec policy that does not match the GRE traffic. If the selector names different addresses from the GRE
localandremote, GRE leaves in clear and everything appears to work. Verify withtcpdump 'ip proto 47'on the physical interface. - Turning on GRE sequence numbers without IPsec. RFC 2890 warns that forged sequence numbers enable a denial of service; it requires IPsec when you use them.
- Using
gretapwhengrewould do. Bridging a whole Ethernet segment across sites also bridges its broadcast, ARP and neighbour discovery problems.
FAQ
What is a GRE tunnel?
A GRE tunnel wraps a packet in a small Generic Routing Encapsulation header and an outer IP header, then sends it to a tunnel endpoint that removes both and forwards the original packet. GRE is IP protocol 47 and is defined in RFC 2784 (March 2000), with optional key and sequence fields in RFC 2890. It can carry IPv4, IPv6, MPLS and Ethernet.
What is the difference between GRE and IP-in-IP?
IP-in-IP (RFC 2003, protocol 4) adds only an outer IPv4 header and can carry only IPv4. GRE adds a 4 to 16 byte header of its own with a Protocol Type field, so it can carry many protocols, supports multicast and can use a key to run several tunnels between the same two addresses. IP-in-IP is 4 bytes cheaper per packet.
Is GRE encrypted?
No. GRE has no encryption and no authentication. The payload travels in clear text, and the key field is visible to anyone on the path. To protect a GRE tunnel, run it over IPsec, normally ESP in transport mode negotiated with IKEv2.
What MTU should I use for a GRE tunnel?
On a 1500 byte IPv4 path, use 1476 for plain GRE and 1472 for GRE with a key. With GRE over IPsec ESP transport mode and AES-GCM the exact maximum is 1442 (1438 with a key, 1434 with NAT traversal); many operators round down to 1400 and clamp TCP MSS to 1360 for margin.
What is MSS clamping and why do tunnels need it?
MSS clamping rewrites the Maximum Segment Size option in TCP SYN and SYN-ACK packets so both ends send segments small enough to fit the tunnel. It avoids the black holes that happen when ICMP "fragmentation needed" messages are filtered. It only works for TCP; RFC 4459 calls it a hack for that reason.
What IP protocol numbers do tunnels use?
IANA lists protocol 4 for IPv4 encapsulation (IP-in-IP), 41 for IPv6 encapsulation (6in4 and IPv6 in IPv6), 47 for GRE, 50 for ESP and 51 for AH. GRE-in-UDP uses UDP port 4754, or 4755 with DTLS. VXLAN uses UDP 4789.
What is GRE-in-UDP?
GRE-in-UDP, defined in RFC 8086 (March 2017), puts the GRE header behind a UDP header on destination port 4754. The UDP source port carries a hash of the inner flow so routers can load balance flows across equal cost paths, and the UDP header lets the traffic pass through port translating NAT. It adds 8 bytes compared with plain GRE.
Can GRE pass through NAT?
Usually not well. Plain GRE has no ports, so a port translating NAT device cannot multiplex it the way it multiplexes TCP and UDP. GRE-in-UDP, or GRE inside IPsec with NAT traversal on UDP 4500, solves this.
Why use GRE over IPsec instead of plain IPsec?
GRE carries multicast and non-IP payloads and gives you a real interface, so a routing protocol such as OSPF or BGP can run across it and route many prefixes through one tunnel. IPsec then adds the encryption and authentication GRE lacks. Pure IPsec tunnel mode protects traffic by policy and is simpler when you only need a few static subnets.
What does RFC 6169 recommend for tunnel security?
RFC 6169 lists the ways tunnels weaken security: they bypass inspection, their inner addresses escape ingress filtering, inner packets can be source routed onward and UDP tunnels hold NAT holes open. It recommends making tunnels easy to disable, filtering tunnelled addresses at the endpoints, running tunnel servers inside your own network, and allowing only known tunnelling mechanisms through firewalls.
Fix it without waiting for upstream
GRE and IP-in-IP are protocols, but the systems that build, configure and watch tunnels are ordinary software: configuration generators, IKE daemons packaged in container images, telemetry exporters and lab tooling are all assembled from open source dependencies. When one of those dependencies has a vulnerability, the fix path is the same as for any other code you build, so the habits below apply to network automation as much as to product code.
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.
3. Gold Registry: start from components that are already fixed
registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | The Gold scan GitHub Action in CI, plus Safeguard policies and gates |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch (early access) |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
The first demo is a real session with the Safeguard CLI. The assistant reads a finding and its advisory, asks before each command, upgrades a dependency, runs the tests and pushes nothing. The same flow applies when a library inside your tunnel configuration generator or a containerised IKE daemon needs an upgrade.
Click through it at your own pace, or open it full screen.
The second demo sets up the Safeguard runner on your own machine, approves it with a device code and then scans from the terminal. Network automation repositories and router lab images often live on internal hosts that should not be exposed, and the runner lets you scan them from a machine you control.
Click through it at your own pace, or open it full screen.
The third demo connects Claude to the Safeguard MCP server with OAuth and lets you choose which tools Claude can use. Granting a narrow set of tools is the same habit as accepting protocol 47 only from the configured peer: allow only what each party needs.
Click through it at your own pace, or open it full screen.
Download the apps at safeguard.sh/download and read the CLI docs at docs.safeguard.sh/docs/cli.
Quick check
You run GRE with a key over a 1500 byte IPv4 path, protected by ESP transport mode with AES-GCM and a 16 byte ICV, and one side is behind NAT. What is the largest inner packet that fits?
1430 bytes. Outer IPv4 (20), NAT traversal UDP (8), ESP header (8), IV (8) and ICV (16) total 60, leaving 1440 for the encrypted part. Subtract the 8 byte GRE header and 2 bytes of pad length and next header: 1430. 1440 is a multiple of 4, so no padding is needed. TCP MSS would be 1390.
A colleague says the GRE tunnel is safe because both ends use key 1001. Is it?
No. The GRE key is a clear text flow identifier from RFC 2890, not a secret. Anyone who can see the traffic reads it, and anyone who can spoof the peer's address can include it. Protect the tunnel with IPsec, accept protocol 47 only from the peer's address, and filter inner source addresses after decapsulation.
Sources
- RFC 1701: Generic Routing Encapsulation (GRE) (October 1994)
- RFC 1702: Generic Routing Encapsulation over IPv4 networks (October 1994)
- RFC 2003: IP Encapsulation within IP (October 1996)
- RFC 2473: Generic Packet Tunneling in IPv6 Specification (December 1998)
- RFC 2784: Generic Routing Encapsulation (GRE) (March 2000)
- RFC 2890: Key and Sequence Number Extensions to GRE (September 2000)
- RFC 4213: Basic Transition Mechanisms for IPv6 Hosts and Routers (October 2005)
- RFC 7676: IPv6 Support for Generic Routing Encapsulation (GRE) (October 2015)
- RFC 8086: GRE-in-UDP Encapsulation (March 2017)
- RFC 4459: MTU and Fragmentation Issues with In-the-Network Tunneling (April 2006)
- RFC 6169: Security Concerns with IP Tunneling (April 2011)
- RFC 7588: A Widely Deployed Solution to the GRE Fragmentation Problem (July 2015)
- RFC 6864: Updated Specification of the IPv4 ID Field (February 2013)
- RFC 8900: IP Fragmentation Considered Fragile (September 2020)
- RFC 1191: Path MTU Discovery (November 1990)
- RFC 8201: Path MTU Discovery for IP version 6 (July 2017)
- RFC 4821: Packetization Layer Path MTU Discovery (March 2007)
- RFC 5095: Deprecation of Type 0 Routing Headers in IPv6 (December 2007)
- RFC 4890: Recommendations for Filtering ICMPv6 Messages in Firewalls (May 2007)
- RFC 2827: Network Ingress Filtering (BCP 38, May 2000)
- RFC 3704: Ingress Filtering for Multihomed Networks (BCP 84, March 2004)
- RFC 4301: Security Architecture for the Internet Protocol (December 2005)
- RFC 4303: IP Encapsulating Security Payload (ESP) (December 2005)
- RFC 4106: The Use of Galois/Counter Mode (GCM) in IPsec ESP (June 2005)
- RFC 3948: UDP Encapsulation of IPsec ESP Packets (January 2005)
- RFC 7296: Internet Key Exchange Protocol Version 2 (IKEv2) (October 2014)
- RFC 8221: Cryptographic Algorithm Implementation Requirements for ESP and AH (October 2017)
- RFC 7348: Virtual eXtensible Local Area Network (VXLAN) (August 2014)
- IANA: Assigned Internet Protocol Numbers
- IANA: IEEE 802 Numbers (EtherTypes)
- IANA: Service Name and Transport Protocol Port Number Registry
- Linux manual page: ip-link(8)
- Linux manual page: ip-fou(8)
- Linux manual page: ip-xfrm(8)
- Linux kernel documentation: IP sysctl
- nftables wiki: Mangling packet headers (TCP MSS clamping)
- strongSwan documentation: swanctl.conf
- WireGuard: Protocol and Cryptography
- AWS documentation: Transit Gateway Connect attachments and Connect peers
Related reading: History of VPNs, IKEv2 and IPsec explained, How a VPN works, IPv4 vs IPv6, OSPF explained and VXLAN and EVPN explained.
Last reviewed: 24 October 2026. Tunnel MTU values depend on your cipher, options and NAT situation; recompute them for your own header stack and check your platform's documentation before relying on a specific behaviour.