Skip to content

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.

Clamp MSS for all traffic leaving a PPPoE interface:

/ip firewall mangle
add 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 mangle
add 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.

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.

ScenarioWhy 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 tunnelESP encapsulation adds 50–80 bytes of overhead depending on cipher and NAT-T
GRE / IP-in-IP tunnelFixed 24-byte (GRE) or 20-byte (IP-in-IP) outer header reduces inner MTU
OpenVPNVariable 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

The preferred approach. RouterOS calculates the correct MSS automatically as path-MTU - 40 (IPv4) or path-MTU - 60 (IPv6):

/ip firewall mangle
add 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.

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 = 1452
add 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.

ParameterPurpose
chain=forwardMatches routed traffic; use chain=output for router-originated sessions
protocol=tcpRequired — MSS is a TCP-only concept
tcp-flags=synOnly 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-pmtuDynamic: RouterOS calculates MTU - 40 (IPv4) or MTU - 60 (IPv6)
new-mss=<value>Fixed: explicit MSS value in bytes

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=yes

For router-originated traffic (e.g., the router itself initiating a connection over a VPN), use chain=output with matching out-interface:

/ip firewall mangle
add 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"

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:

ProtocolHeader OverheadMSS FormulaExample (MTU 1492)
IPv420 (IP) + 20 (TCP) = 40 bytesMTU - 401452
IPv640 (IPv6) + 20 (TCP) = 60 bytesMTU - 601432

With clamp-to-pmtu, RouterOS applies the correct formula automatically per address family. For fixed-value rules, calculate separately for each.

Tunnel TypeUnderlay MTUInner MTUIPv4 MSSIPv6 MSS
PPPoE1500149214521432
WireGuard1500~1420~1380~1360
VXLAN1500~1450~1410~1390
IPsec (no NAT-T)1500~1440–1480variesvaries
GRE1500147614361416

For IPsec and OpenVPN, use clamp-to-pmtu rather than fixed values because overhead varies by cipher suite and negotiated options.

/ipv6 firewall mangle
add 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"

When several tunnel types need clamping, use an interface list to avoid duplicate rules:

/interface list
add name=TUNNELS
/interface list member
add list=TUNNELS interface=pppoe-out1
add list=TUNNELS interface=wg-hub
add list=TUNNELS interface=l2tp-out1
add list=TUNNELS interface=gre-to-hq
/ip firewall mangle
add 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 mangle
add 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"

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=3

Decrease 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 sniffer
set file-name=mss-capture filter-interface=ether1 filter-port=80,443
start
# ... trigger a new TCP connection from a LAN client ...
stop

Stream live to a Wireshark host over TZSP:

/tool sniffer
set streaming-enabled=yes streaming-server=192.168.1.100 \
filter-interface=pppoe-out1 filter-ip-address=0.0.0.0/0
start

On 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.mss

A correctly clamped session shows:

  • Incoming SYN from LAN client: MSS = 1460 (default Ethernet)
  • Outgoing SYN on PPPoE interface: MSS = 1452 (after clamping)

Check that the mangle rule is matching traffic:

/ip firewall mangle print stats

The packet and byte counters on the MSS clamp rule should increment when new TCP connections pass through the interface.

Sessions connect but stall during data transfer

Section titled “Sessions connect but stall during data transfer”

This is the classic PMTU black hole. Confirm:

  1. MSS clamp rule exists and has non-zero counters (/ip firewall mangle print stats)
  2. Rule is placed before FastTrack in the mangle table
  3. Both chain=forward and chain=output rules exist if the router itself initiates sessions
  4. IPv6 rules exist in /ipv6 firewall mangle if IPv6 traffic is affected

If PMTUD is broken because ICMP is filtered by an upstream device:

# Allow ICMP type 3 (destination unreachable) through the firewall
/ip firewall filter
add chain=forward protocol=icmp icmp-options=3:4 action=accept \
comment="Allow fragmentation needed (PMTUD)" place-before=0

For IPv6:

/ipv6 firewall filter
add chain=forward protocol=icmpv6 icmp-options=2:0 action=accept \
comment="Allow ICMPv6 Packet Too Big (PMTUD)" place-before=0

Common causes:

  • Missing protocol=tcp matcher
  • Missing tcp-flags=syn (rule fires on all packets, not just SYN)
  • out-interface does 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