After articles on WireGuard, IPsec, VPLS and VXLAN, it might seem like there’s nothing left for the old, dumb tunnels to do. Wrong. GRE, IPIP and EoIP survive because they’re simple: no key exchange, no control plane, no negotiation to debug at 3 AM. A header, an outer IP, done. When you need a routed path or an L2 patch between two boxes you control, and confidentiality is either irrelevant or handled elsewhere, the dumb tunnel is often the honest engineering answer.
The skill is knowing which one — and doing the two things people always skip: MTU arithmetic and keepalive/recursion discipline.
The Lineup
IPIP — IPv4-in-IPv4, 20 bytes of overhead, carries exactly one thing: unicast IPv4. No multicast, no IPv6 payload, no L2. Standardized (RFC 2003), interoperates with everything, and the leanest option when “route some IPv4 to the other side” is the entire requirement.
GRE — the generalist (RFC 2784). 24 bytes overhead (20 outer IP + 4 GRE). Carries IPv4, IPv6, and — critically — multicast, which means routing protocols run over it. OSPF over GRE works; OSPF over IPIP doesn’t get its multicast hellos through. If a routing protocol will ever ride the tunnel, it’s GRE.
EoIP — MikroTik’s proprietary Layer 2 tunnel: full Ethernet frames over GRE (protocol 47 with an EoIP twist and a tunnel-id). ~42 bytes of overhead before the inner frame even starts. Bridges like a physical port. The only one of the three that stretches L2, and the only one that locks you into MikroTik on both ends.
GRE: The Default Choice
# Site A (public 203.0.113.1) <-> Site B (public 198.51.100.1)/interface gre add name=gre-siteb remote-address=198.51.100.1 \ local-address=203.0.113.1 keepalive=10s,3 comment="To Site B"/ip address add address=172.31.255.1/30 interface=gre-siteb
# Site B mirrors with .2/interface gre add name=gre-sitea remote-address=203.0.113.1 \ local-address=198.51.100.1 keepalive=10s,3/ip address add address=172.31.255.2/30 interface=gre-siteaThen route real prefixes over the /30, statically or with OSPF (the OSPF article’s interface-template model applies unchanged — set the tunnel network as a ptp interface and skip DR election nonsense).
Keepalives deserve attention. keepalive=10s,3 marks the tunnel down after three missed probes. Without keepalives, a GRE interface stays “up” forever regardless of whether the far end exists — routes keep pointing into a black hole. With keepalives, interface state reflects reality, static routes over it withdraw, and failover works. The catch: keepalives must be enabled on both ends; one-sided keepalive marks a perfectly working tunnel down. And remember the firewall: GRE is IP protocol 47, not a UDP/TCP port —
/ip firewall filter add chain=input protocol=gre src-address=198.51.100.1 \ action=accept comment="GRE from Site B" place-before=0GRE + IPsec is the classic “routing protocols plus encryption” combo, and RouterOS makes it nearly one parameter: ipsec-secret=... on the GRE interface creates the transport-mode IPsec policy automatically. One catch verified the hard way: it must be paired with allow-fast-path=no on the same interface — current 7.x refuses the combination with fastpath enabled (“cannot enable fastpath together with ipsec”). For anything beyond a quick PSK setup, build the IPsec profile explicitly (the IKEv2 article covers proposals) — but for two sites you control, the shortcut is legitimate. That said: if you’re building new encrypted site-to-site and don’t need GRE-specific features, WireGuard from earlier in the series is simpler and faster. GRE+IPsec earns its place when you need multicast or interop with non-WireGuard equipment.
IPIP: When Lean Matters
/interface ipip add name=ipip-b remote-address=198.51.100.1 \ local-address=203.0.113.1 keepalive=10s,3/ip address add address=172.31.254.1/30 interface=ipip-bFour fewer bytes than GRE and nothing you don’t need — if you genuinely only carry unicast IPv4 and route statically. In practice I reach for GRE anyway in 90% of cases: the 4-byte saving has never mattered, and requirements grow. IPIP’s niche is interop with things that speak only IPIP, and squeezing MTU in transports where every byte is pain.
EoIP: The L2 Hack, Used Responsibly
# Both ends: tunnel-id must match, MACs auto-generated/interface eoip add name=eoip-b remote-address=198.51.100.1 \ local-address=203.0.113.1 tunnel-id=10 keepalive=10s,3/interface bridge port add bridge=br-lan interface=eoip-b horizon=1EoIP is what you use when you need a VLAN at the other site today, both ends are MikroTik, and standing up VXLAN or VPLS isn’t warranted. It bridges cleanly, survives NAT reasonably (it can run over other tunnels too), and takes ninety seconds to configure.
Its sins: proprietary, per-tunnel scaling (ten sites of full mesh = 45 tunnel definitions), ~42-byte overhead, and — being bridged L2 — it happily extends your broadcast storms, your STP domain, and your mistakes to the far site. The same split-horizon rule from the VPLS article applies when bridging multiple EoIP tunnels: same horizon value on all of them, or enjoy the loop. For anything permanent or multi-site, the VXLAN article’s approach is the better bet — standard, list-based VTEPs, one interface per segment.
Honest placement: EoIP over WireGuard is a fine encrypted-L2 quick fix between MikroTiks. Just write a ticket to replace it with VXLAN before it becomes load-bearing.
MTU: The Same Sermon, Shorter
Overhead cheat sheet: IPIP 20, GRE 24, GRE+IPsec ≈ 60+, EoIP ≈ 42 (+14 inner Ethernet), everything +20 more if the outer path is already tunneled. Over a 1500-byte internet path, set the tunnel MTU explicitly (1476 GRE, 1480 IPIP, 1458 EoIP inner) and clamp TCP MSS — RouterOS adds a clamping rule automatically for some tunnel types, but verify rather than trust:
/ip firewall mangle add chain=forward protocol=tcp tcp-flags=syn \ out-interface=gre-siteb action=change-mss new-mss=clamp-to-pmtu \ comment="MSS clamp into tunnel"Then prove it: /ping 172.31.255.2 size=1476 do-not-fragment from the router, and a full-size transfer from a LAN host. The VyOS series has a whole article on TCP MSS and PMTUD pathology; every word of it applies here.
Routing Over Tunnels Without Recursion Accidents
The classic self-inflicted outage: you route 0.0.0.0/0 (or the far site’s prefix) into the tunnel — and the tunnel’s own outer endpoint is covered by that route. The encapsulated packets try to reach the far end through the tunnel itself, the tunnel collapses, the route withdraws, the tunnel re-establishes, the route returns. Congratulations, it’s a flapping perpetual-motion machine.
The fix is a pinned host route for the tunnel endpoint via the real WAN:
/ip route add dst-address=198.51.100.1/32 gateway=203.0.113.254 \ comment="Pin tunnel endpoint via WAN - do not remove"Do this before routing anything broad into a tunnel, every time, even when today’s routes don’t overlap — the day someone adds a default route through the VPN, your pin is what keeps the lights on. If you run multiple WANs, pin via the policy the tunnel should actually use (the PBR article’s routing tables help here).
Verification
/interface print where type~"gre|ipip|eoip"# 'R' flag = running (keepalive-verified if enabled)/tool sniffer quick interface=ether1 ip-protocol=gre/ping 172.31.255.2 size=1476 do-not-fragment/ip route check 198.51.100.1 # must resolve via WAN, not the tunnelClosing Thoughts
Choosing is simple once the requirements are honest: routed IPv4 only → IPIP if you must, GRE realistically. Routing protocols or multicast → GRE, with ipsec-secret if it needs privacy and WireGuard if it doesn’t need to be GRE at all. Layer 2, both ends MikroTik, temporary → EoIP with a guilty conscience; permanent → VXLAN. Whatever you pick: keepalives on both ends, MSS clamped and proven with full-size frames, and the endpoint route pinned before the tunnel carries anything that matters. Dumb tunnels fail in smart ways only when you skip the boring parts.