MSS Clamping
MSS Clamping
Section titled “MSS Clamping”TCP MSS (Maximum Segment Size) clamping is a mangle rule technique that rewrites the MSS field in TCP SYN packets to prevent sessions from stalling when Path MTU Discovery (PMTUD) is broken or blocked. RouterOS implements this via action=change-mss with new-mss=clamp-to-pmtu in the firewall mangle table.
Quick Start
Section titled “Quick Start”Clamp MSS for all traffic leaving a PPPoE interface:
/ip firewall mangleadd chain=forward action=change-mss protocol=tcp tcp-flags=syn \ tcp-mss=1453-65535 out-interface=pppoe-out1 new-mss=clamp-to-pmtu \ comment="Clamp MSS to PMTU on PPPoE egress"
/ipv6 firewall mangleadd chain=forward action=change-mss protocol=tcp tcp-flags=syn \ tcp-mss=1433-65535 out-interface=pppoe-out1 new-mss=clamp-to-pmtu \ comment="Clamp IPv6 MSS to PMTU on PPPoE egress"Place these rules before any FastTrack rules in the mangle table.
Why MSS Clamping Is Needed
Section titled “Why MSS Clamping Is Needed”TCP connections negotiate MSS during the three-way handshake. Each endpoint advertises the largest segment it can accept. If the negotiated MSS is larger than what the path can carry — because a tunnel or PPPoE link reduces the available MTU — oversized packets get dropped silently when PMTUD fails.
PMTUD works by relying on ICMP “fragmentation needed” (Type 3, Code 4 for IPv4; Packet Too Big for IPv6) messages to signal MTU bottlenecks back to the sender. When these ICMP messages are filtered by firewalls along the path, PMTUD breaks and sessions appear to connect successfully but then stall or hang when transferring data. This is called a PMTU black hole.
MSS clamping solves this at the TCP handshake by reducing the advertised MSS to fit the available MTU, so oversized segments never enter the path.
Common Scenarios Requiring MSS Clamping
Section titled “Common Scenarios Requiring MSS Clamping”| Scenario | Why MSS Clamping Helps |
|---|---|
| PPPoE client (MTU 1492) | 8-byte PPPoE header reduces effective MTU below standard 1500; unclamped sessions send 1460-byte segments that exceed path capacity |
| IPsec tunnel | ESP encapsulation adds 50–80 bytes of overhead depending on cipher and NAT-T |
| GRE / IP-in-IP tunnel | Fixed 24-byte (GRE) or 20-byte (IP-in-IP) outer header reduces inner MTU |
| OpenVPN | Variable overhead based on cipher and compression; PMTUD often unreliable over UDP mode |
| WireGuard | ~60 bytes IPv4 / ~80 bytes IPv6 outer overhead on a 1500-byte underlay |
| VXLAN | ~50 bytes of encapsulation overhead per packet |
MSS Clamping Rule Syntax
Section titled “MSS Clamping Rule Syntax”Dynamic Clamping (clamp-to-pmtu)
Section titled “Dynamic Clamping (clamp-to-pmtu)”The preferred approach. RouterOS calculates the correct MSS automatically as path-MTU - 40 (IPv4) or path-MTU - 60 (IPv6):
/ip firewall mangleadd chain=forward action=change-mss protocol=tcp tcp-flags=syn \ out-interface=pppoe-out1 new-mss=clamp-to-pmtu \ comment="Dynamic MSS clamp on PPPoE egress"clamp-to-pmtu is the literal string value for new-mss. RouterOS determines the PMTU at rule evaluation time and reduces the MSS only if the current MSS is larger than the calculated value.
Fixed-Value Clamping
Section titled “Fixed-Value Clamping”Use when clamp-to-pmtu is insufficient or the path MTU is known and stable:
/ip firewall mangle# PPPoE: MTU 1492, IPv4 MSS = 1492 - 40 = 1452add chain=forward action=change-mss protocol=tcp tcp-flags=syn \ tcp-mss=1453-65535 out-interface=pppoe-out1 new-mss=1452 \ comment="Fixed MSS clamp for PPPoE (IPv4)"The tcp-mss matcher filters out packets that already have a safe MSS value. Only rewrite packets with an MSS above the safe ceiling — in this example 1453-65535.
Matcher Reference
Section titled “Matcher Reference”| Parameter | Purpose |
|---|---|
chain=forward | Matches routed traffic; use chain=output for router-originated sessions |
protocol=tcp | Required — MSS is a TCP-only concept |
tcp-flags=syn | Only SYN and SYN-ACK carry the MSS option; skip established traffic |
tcp-mss=<range> | Optional range filter; avoids rewriting already-small MSS values |
out-interface= | Scope the rule to the low-MTU egress interface |
out-interface-list= | Use an interface list when multiple tunnel types need clamping |
new-mss=clamp-to-pmtu | Dynamic: RouterOS calculates MTU - 40 (IPv4) or MTU - 60 (IPv6) |
new-mss=<value> | Fixed: explicit MSS value in bytes |
Chain Placement
Section titled “Chain Placement”MSS clamping rules must appear in the mangle table before FastTrack rules. FastTrack accelerates established/related connections by bypassing normal firewall processing. If a SYN packet is somehow fasttracked, the MSS clamp never fires for that connection.
/ip firewall mangle# 1. MSS clamp (must come first)add chain=forward action=change-mss protocol=tcp tcp-flags=syn \ tcp-mss=1453-65535 out-interface=pppoe-out1 new-mss=clamp-to-pmtu
# 2. FastTrack (after MSS clamp)add chain=forward action=fasttrack-connection \ connection-state=established,related hw-offload=yesFor router-originated traffic (e.g., the router itself initiating a connection over a VPN), use chain=output with matching out-interface:
/ip firewall mangleadd chain=output action=change-mss protocol=tcp tcp-flags=syn \ out-interface=pppoe-out1 new-mss=clamp-to-pmtu \ comment="Clamp router-originated sessions over PPPoE"IPv4 and IPv6 Differences
Section titled “IPv4 and IPv6 Differences”MSS clamping requires separate rules in /ip firewall mangle and /ipv6 firewall mangle. The IPv6 header is 20 bytes larger than IPv4, so the MSS calculation differs:
| Protocol | Header Overhead | MSS Formula | Example (MTU 1492) |
|---|---|---|---|
| IPv4 | 20 (IP) + 20 (TCP) = 40 bytes | MTU - 40 | 1452 |
| IPv6 | 40 (IPv6) + 20 (TCP) = 60 bytes | MTU - 60 | 1432 |
With clamp-to-pmtu, RouterOS applies the correct formula automatically per address family. For fixed-value rules, calculate separately for each.
Common MSS Values by Tunnel Type
Section titled “Common MSS Values by Tunnel Type”| Tunnel Type | Underlay MTU | Inner MTU | IPv4 MSS | IPv6 MSS |
|---|---|---|---|---|
| PPPoE | 1500 | 1492 | 1452 | 1432 |
| WireGuard | 1500 | ~1420 | ~1380 | ~1360 |
| VXLAN | 1500 | ~1450 | ~1410 | ~1390 |
| IPsec (no NAT-T) | 1500 | ~1440–1480 | varies | varies |
| GRE | 1500 | 1476 | 1436 | 1416 |
For IPsec and OpenVPN, use clamp-to-pmtu rather than fixed values because overhead varies by cipher suite and negotiated options.
IPv6 MSS Clamp Example
Section titled “IPv6 MSS Clamp Example”/ipv6 firewall mangleadd chain=forward action=change-mss protocol=tcp tcp-flags=syn \ out-interface=pppoe-out1 new-mss=clamp-to-pmtu \ comment="IPv6 MSS clamp on PPPoE egress"
add chain=output action=change-mss protocol=tcp tcp-flags=syn \ out-interface=pppoe-out1 new-mss=clamp-to-pmtu \ comment="IPv6 MSS clamp for router-originated sessions"Multiple Tunnel Interfaces
Section titled “Multiple Tunnel Interfaces”When several tunnel types need clamping, use an interface list to avoid duplicate rules:
/interface listadd name=TUNNELS
/interface list memberadd list=TUNNELS interface=pppoe-out1add list=TUNNELS interface=wg-hubadd list=TUNNELS interface=l2tp-out1add list=TUNNELS interface=gre-to-hq
/ip firewall mangleadd chain=forward action=change-mss protocol=tcp tcp-flags=syn \ out-interface-list=TUNNELS new-mss=clamp-to-pmtu \ comment="Clamp MSS on all tunnel egress interfaces"
/ipv6 firewall mangleadd chain=forward action=change-mss protocol=tcp tcp-flags=syn \ out-interface-list=TUNNELS new-mss=clamp-to-pmtu \ comment="Clamp IPv6 MSS on all tunnel egress interfaces"Verifying MSS Clamping
Section titled “Verifying MSS Clamping”Step 1: Check Path MTU with DF Ping
Section titled “Step 1: Check Path MTU with DF Ping”Before configuring clamping, confirm the actual path MTU using a ping with the Don’t Fragment (DF) bit set:
# Find the MTU ceiling toward an internet host/ping 8.8.8.8 do-not-fragment size=1472 count=3/ping 8.8.8.8 do-not-fragment size=1452 count=3/ping 8.8.8.8 do-not-fragment size=1400 count=3Decrease size until pings succeed. The largest working size plus 28 bytes (20 IP + 8 ICMP) gives the path MTU. For PPPoE the ceiling is typically 1492.
Step 2: Capture SYN Packets with the Packet Sniffer
Section titled “Step 2: Capture SYN Packets with the Packet Sniffer”Use the RouterOS packet sniffer to capture SYN packets and verify the MSS option is being rewritten:
# Capture to file on router storage/tool snifferset file-name=mss-capture filter-interface=ether1 filter-port=80,443start# ... trigger a new TCP connection from a LAN client ...stopStream live to a Wireshark host over TZSP:
/tool snifferset streaming-enabled=yes streaming-server=192.168.1.100 \ filter-interface=pppoe-out1 filter-ip-address=0.0.0.0/0startOn the Wireshark host, listen on UDP port 37008. Use this display filter to see MSS values in SYN packets:
tcp.flags.syn == 1 && tcp.options.mssA correctly clamped session shows:
- Incoming SYN from LAN client: MSS = 1460 (default Ethernet)
- Outgoing SYN on PPPoE interface: MSS = 1452 (after clamping)
Step 3: Confirm with Active Mangle Stats
Section titled “Step 3: Confirm with Active Mangle Stats”Check that the mangle rule is matching traffic:
/ip firewall mangle print statsThe packet and byte counters on the MSS clamp rule should increment when new TCP connections pass through the interface.
Troubleshooting
Section titled “Troubleshooting”Sessions connect but stall during data transfer
Section titled “Sessions connect but stall during data transfer”This is the classic PMTU black hole. Confirm:
- MSS clamp rule exists and has non-zero counters (
/ip firewall mangle print stats) - Rule is placed before FastTrack in the mangle table
- Both
chain=forwardandchain=outputrules exist if the router itself initiates sessions - IPv6 rules exist in
/ipv6 firewall mangleif IPv6 traffic is affected
ICMP filtering on the path
Section titled “ICMP filtering on the path”If PMTUD is broken because ICMP is filtered by an upstream device:
# Allow ICMP type 3 (destination unreachable) through the firewall/ip firewall filteradd chain=forward protocol=icmp icmp-options=3:4 action=accept \ comment="Allow fragmentation needed (PMTUD)" place-before=0For IPv6:
/ipv6 firewall filteradd chain=forward protocol=icmpv6 icmp-options=2:0 action=accept \ comment="Allow ICMPv6 Packet Too Big (PMTUD)" place-before=0MSS clamp rule not matching
Section titled “MSS clamp rule not matching”Common causes:
- Missing
protocol=tcpmatcher - Missing
tcp-flags=syn(rule fires on all packets, not just SYN) out-interfacedoes not match the actual egress interface name- Rule is below a FastTrack rule that is already fasttracking SYN packets (uncommon but possible with connection helpers)
Verify the interface name:
/interface print where type=pppoe-out/ip firewall mangle print