Skip to main content
RunBook Academy

← All labs in VyOS

Lab · advanced · ~100 min

Lab: MTU and PMTUD

B · Nested virtualisationC · Simulation

Objectives

  • Establish a path MTU baseline with a do-not-fragment ping before anything is wrong
  • Introduce a smaller hop and locate it from the ICMP Fragment Needed message rather than from the topology diagram
  • Break PMTUD with one firewall rule and identify the black hole from the four-outcome ladder rather than by guessing
  • Name the chain a Fragment Needed message traverses, and explain why the forward chain and the output chain are different problems
  • Show that a black hole breaks TCP while leaving ping working, which is the symptom users actually report
  • Apply MSS clamping on the constrained interface and prove it with a paired capture on ingress and egress
  • Reproduce the clamp-mss-to-pmtu trap, in which a correct-looking clamp advertises the wrong number and changes nothing
  • Compute GRE tunnel overhead, set the inner MTU, and put the clamp on the interface that is actually the bottleneck

Prerequisites

Objective

“The network works for small packets but not for big ones” is the most misdiagnosed report in this course, because every cheap test passes. Ping works. DNS works. SSH connects. The routing table is correct and the interface counters show no errors. And a file transfer stalls at a few kilobytes and never recovers.

By the end of this lab you will have built that failure deliberately, twice — once where PMTUD works and tells you exactly which hop is the constraint, and once where a single firewall rule turns the same topology into a silent black hole. You will walk the four-outcome diagnostic ladder from Part LI-05 with real output at each rung, and you will fix it two different ways: correctly, by permitting the ICMP that PMTUD depends on, and unilaterally, by clamping MSS when the drop is somebody else’s.

Along the way you will reproduce the trap that catches operators who have read about clamping but not about where its value comes from — a clamp that commits, shows up in show interfaces, and advertises a number that changes nothing.

Architecture

Four VyOS routers in a line. The line matters: MTU problems are path problems, and a two-node topology cannot have a hop in the middle.

  +----------+        +----------+        +----------+        +----------+
  |  client  |        |    r1    |        |    r2    |        |  server  |
  | eth1 .5  |--------| eth1 .1  |        | eth1 .2  |        | eth1 .2  |
  |          |  LAN   | eth2 .1  |--------| eth2 .1  |--------|          |
  +----------+        +----------+ TRANSIT+----------+  FAR   +----------+
   192.168.10.0/24                198.51.100.0/30      203.0.113.0/30
                                                        ^
                                              the constrained hop:
                                              r2 eth2, MTU reduced in Task 2
SegmentPrefixclientr1r2server
LAN192.168.10.0/24eth1 · .5eth1 · .1
TRANSIT198.51.100.0/30eth2 · .1eth1 · .2
FAR203.0.113.0/30eth2 · .1eth1 · .2

There is deliberately no NAT. Addresses are visible end to end, so an ICMP error names a hop you can go and look at, and a capture on any router shows the same tuple.

eth0 on each router is a management interface on whatever bridge your hypervisor already uses. No task configures it and no route points at it — which matters here more than usual, because Task 3 installs a firewall rule and Task 6 reduces an MTU, and you want a way in that neither of those touches.

Requirements

  • A hypervisor with roughly 4 GiB of free RAM and 32 GiB of free disk — four VMs at 1 GiB and 8 GiB each.
  • The VyOS 1.5 LTS ISO. MSS clamping moved onto the interface in 1.4 and the firewall lost its per-interface bindings at the same time; nothing here commits on 1.3.
  • Three isolated layer-2 segments — bridges with no physical port.
  • Console access to all four routers.
  • Roughly 100 minutes.
  • Two consoles on client: several tasks run a probe in one and a transfer in the other.

Scenario

A site has just moved its backups to an off-site target. The backup job runs for about four seconds, transfers roughly nothing, and then sits there until it is killed. Everything else on the circuit is fine. The monitoring system pings the off-site target every minute and has never reported a failure, which is being cited in the ticket as evidence that the network is not the problem.

Your job is to establish what the path can actually carry, decide whether the mechanism that is supposed to negotiate that is working, and — if it is not — work out whose it is to fix and what you can do without them.

Tasks

Configuration blocks are written for the [edit] prompt. Where a block opens with configure and you are already at [edit], skip that line. Blocks tagged bash are the operational-mode shell, as their comments say.

Task 1 — Build the line and establish the path MTU baseline

Install four routers, give each a hostname and a management address on eth0, and capture the starting state.

# Run from operational mode on each of the four routers.
JOURNAL="$HOME/lab21"
mkdir -p "$JOURNAL"
show configuration commands > "$JOURNAL/pre-lab-config.txt"
ip -brief link show > "$JOURNAL/pre-lab-mtus.txt"
cat "$JOURNAL/pre-lab-mtus.txt"

That second file is the beginning of this lab’s MTU inventory, and it is worth having before anything is changed.

r1:

configure
set system host-name r1
set interfaces ethernet eth1 address 192.168.10.1/24
set interfaces ethernet eth1 description 'LAN'
set interfaces ethernet eth2 address 198.51.100.1/30
set interfaces ethernet eth2 description 'TRANSIT to r2'
set protocols static route 203.0.113.0/30 next-hop 198.51.100.2
commit
save

r2:

configure
set system host-name r2
set interfaces ethernet eth1 address 198.51.100.2/30
set interfaces ethernet eth1 description 'TRANSIT to r1'
set interfaces ethernet eth2 address 203.0.113.1/30
set interfaces ethernet eth2 description 'FAR - the hop this lab constrains'
set protocols static route 192.168.10.0/24 next-hop 198.51.100.1
commit
save

client and server:

configure
set system host-name client
set interfaces ethernet eth1 address 192.168.10.5/24
set protocols static route 0.0.0.0/0 next-hop 192.168.10.1
commit
save
configure
set system host-name server
set interfaces ethernet eth1 address 203.0.113.2/30
set protocols static route 192.168.10.0/24 next-hop 203.0.113.1
set service ssh port 22
commit
save

Now the baseline, from the client, because the client is the host that will eventually be suffering:

ping 203.0.113.2 count 3
ping 203.0.113.2 size 1472 do-not-fragment count 3
Read-only / Safeclient
$ ping 203.0.113.2 size 1472 do-not-fragment count 3
PING 203.0.113.2 (203.0.113.2) 1472(1500) bytes of data.
1480 bytes from 203.0.113.2: icmp_seq=1 ttl=62 time=1.31 ms
1480 bytes from 203.0.113.2: icmp_seq=2 ttl=62 time=1.02 ms
1480 bytes from 203.0.113.2: icmp_seq=3 ttl=62 time=0.98 ms

--- 203.0.113.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss

Illustrative output

The arithmetic in the first line is the thing to internalise, because it is the source of most confusion about this probe. size is the ICMP payload. Add 8 bytes of ICMP echo header and 20 bytes of IPv4 header and you have the IP packet: 1472 + 28 = 1500. Which is why the operator’s rule is subtract 28 from the MTU you are testing.

Write into the journal: path MTU is at least 1500 today. Nothing in this lab is comparable to anything until that line exists.

Task 2 — Introduce the smaller hop, and let PMTUD find it

On r2, reduce the MTU on the far segment. This is the ordinary case: a hop somewhere along the path that is not 1500, because it is a tunnel, a provider handoff, a PPPoE circuit or a partner network.

configure
set interfaces ethernet eth2 mtu 1400
compare
commit-confirm 5

commit-confirm because an MTU change on a live interface briefly takes the link down while the NIC re-negotiates, and on a production router that is the interface you may be reaching it through. Confirm once you still have a session:

run show interfaces ethernet eth2
confirm
save

Now re-run the same probe from the client:

Read-only / Safeclient
$ ping 203.0.113.2 size 1472 do-not-fragment count 3
PING 203.0.113.2 (203.0.113.2) 1472(1500) bytes of data.
From 198.51.100.2 icmp_seq=1 Frag needed and DF set (mtu = 1400)
From 198.51.100.2 icmp_seq=2 Frag needed and DF set (mtu = 1400)

Illustrative output

Read that line twice, because both halves are load-bearing and each answers a different question.

  • Frag needed and DF set is the ICMP a router sent because it could not forward your packet. Its presence means Path MTU Discovery is working. Do not reach for a clamp here; the mechanism is doing its job.
  • mtu = 1400 is the size that hop can forward. Your source now caches 1400 as the path MTU for this destination.
  • From 198.51.100.2 names the hop whose MTU is the constraint. That is the device to go and look at — and here it is r2, which is exactly where you put it.

An error attributed to your own address instead — ping: local error: message too long — means the outgoing interface on the box you are typing on is the limit, and nothing upstream is involved at all. Different problem, different router.

Now bisect, so that the number in your journal is measured rather than read off an ICMP you might be misinterpreting:

# On the client, operational mode. Work from small to large and read the boundary.
for size in 1300 1372 1400 1472; do
  echo "=== payload $size ==="
  ping -M do -s "$size" -c 2 -W 2 203.0.113.2
done

1372 is the payload that makes a 1400-byte packet — the same subtract-28 rule. Expect 1300 and 1372 to succeed and 1400 and 1472 to fail, which puts the path MTU at exactly 1400 and agrees with the ICMP.

Note in the journal that the monitoring system’s minute-by-minute ping has been succeeding throughout, because a default ping is 64 bytes and 64 bytes fit anywhere. The ticket’s claim that “the network is fine” was true of every packet the monitoring ever sent.

Task 3 — Break PMTUD with one rule, and find the drop on your own counter

PMTUD depends on ICMP getting back to the original sender. Take it away, using the rule that a security review produces about once a year.

On r1:

configure
set firewall ipv4 forward filter default-action accept
set firewall ipv4 forward filter rule 10 description 'over-zealous ICMP filter - the fault this task builds'
set firewall ipv4 forward filter rule 10 protocol icmp
set firewall ipv4 forward filter rule 10 icmp type-name fragmentation-needed
set firewall ipv4 forward filter rule 10 action drop
set firewall ipv4 forward filter rule 10 log
compare
commit

Now work the ladder from Part LI-05, from the client, and record what each rung returns.

# On the client. Four rungs, in this order, and read each before running the next.
ping -c 3 203.0.113.2                      # rung 1: does it work at all?
ping -M do -s 1372 -c 3 203.0.113.2        # rung 2: does it work below the bottleneck?
ping -M do -s 1472 -c 3 203.0.113.2        # rung 3: does the ICMP come back?
ping -M dont -s 1472 -c 3 203.0.113.2      # rung 4: can the path carry fragments?

The pattern that identifies a black hole is precise: rungs 1, 2 and 4 succeed and rung 3 hangs with no ICMP at all. That is outcome 3 of the four in Part LI-05 — the path can deliver fragments but cannot deliver a non-fragmented large packet, because the message that would have told the sender to make it smaller never arrives.

Compare that with Task 2, where rung 3 returned Frag needed and DF set. Same topology, same MTU, one firewall rule between them, and completely different evidence.

Now find the drop. This is your firewall, so the counter is yours to read:

Read-only / Safer1
$ show firewall ipv4 forward filter
ipv4 firewall forward filter

Rule     Action   Protocol   Packets   Bytes   Conditions
-----    ------   --------   -------   -----   ----------
10       drop     icmp       6         336     icmp type 3 code 4
default  accept   all        482       51204

Illustrative output

Six packets, matching the six large probes you sent across two runs. Read the Packets column, not the rule’s existence — a correct-looking ICMP rule with a zero counter is not evidence that ICMP is arriving, it is evidence that nothing is reaching that rule.

Fix it, which for a filter you own is the right fix and not a workaround:

set firewall ipv4 forward filter rule 10 action accept
set firewall ipv4 forward filter rule 10 description 'ICMP fragmentation-needed - required for PMTUD'
compare
commit
save

Re-run rung 3 from the client and confirm Frag needed and DF set (mtu = 1400) is back. Write into the journal: when the filter is yours, permit the message. Clamping over a working PMTUD is a change that hides a fault instead of fixing it, and it will still be there when the path MTU changes.

Task 4 — Prove it breaks TCP, not just ping

Restore the black hole, because the next three tasks need it — and this time watch what it does to something a user cares about.

configure
set firewall ipv4 forward filter rule 10 action drop
commit

On server, start the listener:

monitor bandwidth-test accept

On client, console one — a session that uses small packets:

ssh vyos@203.0.113.2

That connects and works. Type in it; it behaves normally. Now console two — a transfer that uses full-size segments:

monitor bandwidth-test initiate 203.0.113.2

That stalls. The client’s TCP negotiated an MSS of 1460 from its own 1500-byte interface, builds 1500-byte packets, and every one of them is discarded at r2 without a word. TCP retransmits the same oversized segment, on a timer that doubles, forever.

Capture the shape of it from r1:

# On r1, operational mode. Watch the same segment go out again and again.
sudo tcpdump -ni eth2 -c 10 'tcp port 5001'

Task 5 — Move the drop out of reach, and clamp

In production, the ICMP is usually being discarded somewhere you do not administer. Simulate that by moving the drop onto r2 — the router that generates the message — where you have no authority and, importantly, where the traffic is locally originated and therefore in the output chain.

On r1, remove your rule entirely:

configure
delete firewall ipv4 forward filter
commit
save

On r2, playing the provider:

configure
set firewall ipv4 output filter default-action accept
set firewall ipv4 output filter rule 10 description 'provider does not emit ICMP unreachables'
set firewall ipv4 output filter rule 10 protocol icmp
set firewall ipv4 output filter rule 10 icmp type-name fragmentation-needed
set firewall ipv4 output filter rule 10 action drop
commit
save

Confirm from the client that rung 3 hangs again, and that r1’s firewall is now empty — the drop is no longer yours, and no amount of work on r1’s rule set will recover it.

Now apply the fix you can make unilaterally. The path MTU is 1400, so the IPv4 MSS is 1400 − 40 (20 IPv4 + 20 TCP) = 1360, and the IPv6 value on a dual-stack link would be 1400 − 60 = 1340.

configure
set interfaces ethernet eth2 ip adjust-mss 1360
set interfaces ethernet eth2 ipv6 adjust-mss 1340
compare
commit-confirm 5

Three properties of that node decide whether it does what you intended, and all three come from Part LI-04:

  • It rewrites the MSS option in TCP SYN packets leaving that interface. It does not touch established connections, non-SYN packets, or anything that is not TCP.
  • ip and ipv6 are separate nodes with separate values. Setting one does nothing for the other, and forgetting the second produces a router where IPv4 transfers work and IPv6 transfers hang — a much harder ticket to read than “everything is broken”.
  • It applies to every TCP flow crossing that interface. There is no source, port or rule-set selector.

Prove it with a paired capture, on two interfaces, for the same connection. Open a fresh connection: the clamp acts on the handshake, so a session that is already up keeps whatever MSS it negotiated, and a rollback that “did not work” is usually a rollback tested on an old connection.

# On r1, console one - the LAN side, before the clamp is applied.
sudo tcpdump -ni eth1 -c 3 'tcp[tcpflags] & tcp-syn != 0'
# On r1, console two - the WAN side, after the clamp.
sudo tcpdump -ni eth2 -c 3 'tcp[tcpflags] & tcp-syn != 0'
Read-only / Safer1, eth2
$ sudo tcpdump -ni eth2 -c 3 'tcp[tcpflags] & tcp-syn != 0'
IP 192.168.10.5.44112 > 203.0.113.2.5001: Flags [S], seq 2884113, win 64240, options [mss 1360,sackOK,TS val 12 ecr 0,nop,wscale 7], length 0

Illustrative output

mss 1460 on eth1 and mss 1360 on eth2 is the proof. One reading is suggestive; the pair is conclusive, and it is the pair that distinguishes “the clamp is on the wrong interface” from “the clamp is not committed”.

Re-run the bandwidth test. It should now complete: the far end never sends a segment larger than the path can carry, so nothing needs the ICMP that is being discarded.

Confirm and save.

Task 6 — The clamp-mss-to-pmtu trap

adjust-mss also accepts a keyword instead of a number, and its name invites exactly the wrong assumption. Reproduce the trap rather than reading about it.

configure
set interfaces ethernet eth2 ip adjust-mss clamp-mss-to-pmtu
compare
commit

Open a fresh connection and re-run the eth2 capture:

Read-only / Safer1, eth2
$ sudo tcpdump -ni eth2 -c 3 'tcp[tcpflags] & tcp-syn != 0'
IP 192.168.10.5.44118 > 203.0.113.2.5001: Flags [S], seq 9911024, win 64240, options [mss 1460,sackOK,TS val 44 ecr 0,nop,wscale 7], length 0

Illustrative output

mss 1460. The node is committed, show interfaces ethernet eth2 reports it, and the bandwidth test stalls again exactly as it did before any clamp existed.

The reason is that clamp-mss-to-pmtu derives its value from this interface’s own MTU, not from the path. eth2 is at the default 1500, so it advertises 1500 − 40 = 1460, and the bottleneck two hops away at 1400 is untouched. The keyword is useful precisely when the constrained interface is the one carrying the clamp — a tunnel whose own MTU is the ceiling, for instance — and useless when the constraint is somewhere else.

Put the number back:

set interfaces ethernet eth2 ip adjust-mss 1360
compare
commit
save

Write both captures into the journal, side by side. The trap is worth a page in your own notes, because everything about it looks correct: the configuration is committed, the syntax is valid, the feature is the right feature, and the number on the wire is wrong.

Task 7 — A tunnel, its overhead, and where the clamp belongs

Restore the topology to a clean 1500 everywhere, so the only constraint left is one you compute rather than one you configured.

On r2:

configure
delete interfaces ethernet eth2 mtu
delete firewall ipv4 output filter
commit
save

On r1:

configure
delete interfaces ethernet eth2 ip adjust-mss
delete interfaces ethernet eth2 ipv6 adjust-mss
commit
save

Confirm from the client that ping 203.0.113.2 size 1472 do-not-fragment count 3 succeeds again before going on.

Now build a GRE tunnel between r1 and r2 over the transit segment, and route the client’s traffic through it.

On r1:

configure
set interfaces tunnel tun0 encapsulation gre
set interfaces tunnel tun0 source-address 198.51.100.1
set interfaces tunnel tun0 remote 198.51.100.2
set interfaces tunnel tun0 address 192.0.2.1/30
set interfaces tunnel tun0 description 'GRE to r2 - inner MTU is 1500 minus 24'
set protocols static route 203.0.113.0/30 next-hop 192.0.2.2
delete protocols static route 203.0.113.0/30 next-hop 198.51.100.2
commit
save

On r2:

configure
set interfaces tunnel tun0 encapsulation gre
set interfaces tunnel tun0 source-address 198.51.100.2
set interfaces tunnel tun0 remote 198.51.100.1
set interfaces tunnel tun0 address 192.0.2.2/30
set interfaces tunnel tun0 description 'GRE to r1'
set protocols static route 192.168.10.0/24 next-hop 192.0.2.1
delete protocols static route 192.168.10.0/24 next-hop 198.51.100.1
commit
save

Do the arithmetic before you read the interface. GRE over IPv4 adds a 20-byte outer IPv4 header and a 4-byte GRE header: 24 bytes. The transit segment is 1500, so the tunnel’s inner MTU is 1500 − 24 = 1476. Now read what the kernel chose:

run show interfaces tunnel tun0

Linux derives a GRE tunnel’s MTU from the underlying interface, so this is very likely to already be 1476 and your arithmetic is a check rather than a configuration step. Set it explicitly only if it disagrees with the number you computed — and if it does, find out why before overriding it:

configure
set interfaces tunnel tun0 mtu 1476
commit

Confirm the new path MTU empirically from the client, and notice that the constraint has moved without anyone configuring an MTU anywhere:

# On the client. 1448 payload is a 1476-byte packet; 1472 is 1500.
ping -M do -s 1448 -c 2 203.0.113.2
ping -M do -s 1472 -c 2 203.0.113.2

The second should return Frag needed and DF set (mtu = 1476) from 192.168.10.1r1 itself this time, because r1 is the router that cannot fit the packet into tun0. That is a different hop reporting a different constraint from the same probe, and it is why an MTU inventory records the arithmetic and not just the number.

Finally, put the clamp where it belongs. Not on eth2, which is at 1500 and is no longer the bottleneck — on the tunnel interface, whose MTU is the ceiling:

configure
set interfaces tunnel tun0 ip adjust-mss 1436
set interfaces tunnel tun0 ipv6 adjust-mss 1416
compare
commit
save

1476 − 40 = 1436 for IPv4, and 1476 − 60 = 1416 for IPv6. Note that this is the one case where clamp-mss-to-pmtu would have produced the right answer, because here the interface carrying the clamp is the interface whose MTU is the constraint. Try it, capture the SYN on tun0, and confirm you get 1436 either way — then decide which form you want in your own configurations, and write down why.

Close by building the MTU inventory this lab has been accumulating: every interface on all four routers, its MTU, and for every value that is not 1500, the arithmetic that produced it. That table is the deliverable. A number without its arithmetic is a number nobody dares change.

Validation

  • The Task 1 baseline exists: ping 203.0.113.2 size 1472 do-not-fragment succeeded before anything was constrained.
  • The Task 2 bisection is in the journal and puts the boundary at a 1400-byte packet, agreeing with the mtu = 1400 the ICMP reported.
  • The journal names 198.51.100.2 as the hop that reported the constraint, and identifies it as r2.
  • The four-rung ladder was run under the black hole and the results match outcome 3: rungs 1, 2 and 4 succeed, rung 3 hangs.
  • show firewall ipv4 forward filter on r1 showed rule 10’s packet counter incrementing by the number of large probes sent.
  • A paired SYN capture exists showing mss 1460 on eth1 and mss 1360 on eth2, taken on a freshly opened connection.
  • A capture exists showing mss 1460 on eth2 while clamp-mss-to-pmtu was configured, alongside the note that the path MTU was 1400 at the time.
  • After Task 7, show interfaces tunnel tun0 reports MTU 1476, and a 1472-payload probe from the client returns mtu = 1476 from 192.168.10.1.
  • The MTU inventory covers all four routers and carries arithmetic for every non-1500 value.

Expected Outcome

A four-router path whose MTU you have measured rather than assumed, and a working understanding of the three states it can be in: PMTUD working and telling you which hop to fix; PMTUD black-holed by a filter you own, which you fix by permitting the message; and PMTUD black-holed by somebody else, which you work around by clamping MSS on the interface that faces the constraint.

Plus one negative result that is worth as much as the rest: a clamp that committed, appeared in the configuration, and advertised a number that changed nothing.

Troubleshooting

ping ... size 1472 do-not-fragment fails from the start. Check the client’s own interface MTU with show interfaces ethernet eth1. If the error is local error: message too long, the constraint is on the box you are typing on and nothing on the path is involved.

No ICMP comes back even with the firewall rule removed. Confirm the rule is actually gone — show firewall ipv4 forward filter with no rules is different from a rule set whose default action is drop. Then check r2’s output chain; a leftover rule from Task 5 produces exactly this.

The bandwidth test works even before clamping. Check that r2’s eth2 MTU really committed: show interfaces ethernet eth2 on r2. Also confirm the traffic is taking the path you think — with the tunnel from Task 7 present, the 1400-byte hop may no longer be on the route.

tcpdump shows no SYN packets at all. The filter expression is matching nothing because the connection is being reused. Kill the session and open a new one; a clamp only ever acts on a handshake.

The clamp is committed and eth2 still shows mss 1460. Either the value is clamp-mss-to-pmtu and you are looking at Task 6’s trap, or the clamp is on an interface this traffic does not leave through. Read show configuration commands | match 'adjust-mss' and compare against show ip route 203.0.113.2.

The GRE tunnel is up but nothing crosses it. Confirm both ends have each other as remote and that the static routes were switched to the tunnel next-hop and the old ones deleted — two default paths to the same prefix, one through the tunnel and one around it, is a coin toss.

A transfer that used to work fails after Task 7. The tunnel lowered the path MTU by 24 bytes and nothing has clamped yet. That is the lab working; apply the clamp from the end of Task 7.

Cleanup

Everything is on isolated bridges, so cleanup restores a known state rather than service.

Step 1. If you are keeping the topology for the MTU break/fix scenario, stop here and save on each router. Note in the journal that traffic is running through a GRE tunnel with a 1476-byte MTU and a 1436 clamp, so the next person does not spend an hour discovering it.

Step 2. To return to the Task 1 state, on r1:

configure
delete interfaces tunnel tun0
set protocols static route 203.0.113.0/30 next-hop 198.51.100.2
delete protocols static route 203.0.113.0/30 next-hop 192.0.2.2
delete firewall ipv4 forward filter
compare
commit
save

and on r2:

configure
delete interfaces tunnel tun0
set protocols static route 192.168.10.0/24 next-hop 198.51.100.1
delete protocols static route 192.168.10.0/24 next-hop 192.0.2.1
delete interfaces ethernet eth2 mtu
delete firewall ipv4 output filter
compare
commit
save

Note the order inside each block: the replacement static route is set in the same commit that removes the tunnel route. Deleting the tunnel first and adding the route afterwards leaves the prefix unreachable in between, which on this lab is harmless and on a production edge is an outage you scheduled by accident.

Step 3. Confirm the restoration empirically rather than by reading the configuration back. From the client:

ping -M do -s 1472 -c 3 203.0.113.2

A clean 1500-byte path is the Task 1 baseline, and matching it is the proof that cleanup finished.

Step 4. To reset a router entirely, load the configuration captured in Task 1:

configure
load /config/pre-lab-21.boot
compare
commit
save

Step 5. To remove the topology, delete the four VMs and the three bridges on the hypervisor.

What You Learned

  • size is the ICMP payload, and the constant is 28. Subtract 28 from the MTU you want to test. Almost every confused MTU conversation contains somebody who is off by that number.
  • An ICMP Fragment Needed is good news. It means PMTUD is working, it names the constraint, and it names the hop that reported it. The correct response is to go and look at that hop, not to reach for a clamp.
  • The four-outcome ladder separates four different problems. You ran it and got outcome 3 — fragments cross, non-fragmented large packets do not — and that single result told you the cause without any guessing.
  • The chain matters. A Fragment Needed message crossing a router toward a host behind it is transit traffic and hits the forward chain; the same message generated by the router itself hits the output chain. “We already allow ICMP” is a claim about one of those and usually not the other.
  • Small things working is the symptom, not the reassurance. Ping, DNS and SSH all worked throughout the black hole. The monitoring system never registered a fault, because it never sent a packet large enough to find one.
  • Where the filter is yours, permit the message. Clamping over a working PMTUD hides the fault and outlives the path MTU it was written for.
  • A clamp goes on the interface that faces the constraint, and it rewrites only what leaves that interface, only in SYN packets, only for TCP, and only for the address family whose node you set.
  • clamp-mss-to-pmtu uses the local interface MTU, not the path. You watched it advertise 1460 into a 1400-byte path, from a configuration that committed cleanly and looked exactly right.
  • Tunnel overhead is arithmetic you do before you read the interface. GRE over IPv4 costs 24 bytes; the MSS is the inner MTU minus 40 for IPv4 and minus 60 for IPv6; and the inventory records the arithmetic, because a number without it is a number nobody will ever dare change.

Deliverables

  • · A lab journal with the bisection that located the path MTU, and the address of the hop that reported it
  • · A pre-lab copy of the configuration from all four routers, used by Cleanup
  • · The four-outcome ladder worked through for the black hole, with the evidence for each rung
  • · Paired SYN captures on the LAN and WAN interfaces, before and after clamping, showing the MSS rewrite
  • · The clamp-mss-to-pmtu capture showing MSS 1460 on a path whose MTU is 1400
  • · An MTU inventory for all four routers: every interface, its MTU, and the arithmetic behind any value that is not 1500

Verification status

Executed end to end
not yet run on hardware

The commands and configuration here have been reviewed against the verified software versions, but nobody has run this lab start to finish on a system meeting its prerequisites. Treat the Expected Outcome as the intended result rather than an observed one, and keep the Cleanup section to hand.