#!/usr/bin/env bash # # JbTecWiz Support Centre -- generated fix script # # Fault : Connections establish then hang -- MTU and path MTU discovery # Fix : Measure the largest packet that gets through # Source: https://jbtecwiz.com/support/lnx-net-mtu # # Run as : Root shell # Expect : 20 minutes # Risk : low # Reversible : yes # # WHEN THIS IS THE RIGHT FIX # Confirm the diagnosis before changing any interface. # # HOW TO UNDO IT # Nothing was changed. # # Walks the fix one step at a time and asks before each. Steps with no # command are yours to do -- it prints those and waits. DRYRUN=1 prints # without executing; UNATTENDED=1 does not ask. # # -------------------------------------------------------------------- # NO WARRANTY - USE AT YOUR OWN RISK # # This script is provided by JbTecWiz as-is and with no warranty of any # kind, express or implied. You run it entirely at your own risk. # # JbTecWiz accepts no liability for any loss or damage arising from its # use, including but not limited to data loss, downtime, or configuration # changes that turn out to be wrong for your system. # # You are responsible for reading this script before running it, for # satisfying yourself that it suits the machine in front of you, and for # having a working backup first. Some steps cannot be undone. # -------------------------------------------------------------------- set -uo pipefail DRYRUN="${DRYRUN:-0}" UNATTENDED="${UNATTENDED:-0}" failed=0 if [ "$(id -u)" -ne 0 ]; then echo " This fix is documented as needing root. Re-run with sudo." >&2 exit 3 fi rule() { printf "\n%s\n" "$(printf '-%.0s' $(seq 1 70))"; if [ $# -gt 0 ]; then echo "$1"; fi; } prose() { echo "$1" | fold -s -w 74 | sed "s/^/ /"; } # Returns 0 when the caller should run the command, 1 when it should not. # A manual step always returns 1 -- there is nothing for the caller to run. step() { # step [command lines...] local n="$1" dotext="$2" why="$3" mode="$4"; shift 4 rule " Step $n of 4" prose "$dotext" if [ -n "$why" ]; then echo; prose "$why"; fi if [ "$mode" = "manual" ]; then echo; echo " -> Do this yourself, then press Enter to carry on." if [ "$UNATTENDED" = "0" ] && [ "$DRYRUN" = "0" ]; then read -r _; fi return 1 fi echo; printf " %s\n" "$@"; echo if [ "$DRYRUN" = "1" ]; then echo " (dry run -- not executed)"; return 1; fi if [ "$UNATTENDED" = "0" ]; then read -r -p " Run this step? [Y]es / [S]kip / [Q]uit " a case "$a" in [Qq]*) echo " Stopped at your request."; exit 0 ;; [Ss]*) echo " Skipped."; return 1 ;; esac fi return 0 } rule echo " Connections establish then hang -- MTU and path MTU discovery" echo " Measure the largest packet that gets through" echo echo " Risk: low Reversible 20 minutes" echo prose 'No warranty. Use at your own risk - JbTecWiz accepts no liability. Read it before you run it, and have a backup.' rule echo if [ "$UNATTENDED" = "0" ] && [ "$DRYRUN" = "0" ]; then read -r -p " Ready? [y/N] " go case "$go" in [Yy]*) ;; *) echo " Nothing was changed."; exit 0;; esac fi if step 1 'Send progressively larger packets with fragmentation forbidden. The largest that succeeds, plus 28, is the path MTU.' '1472 plus 28 bytes of headers is 1500, the standard ethernet MTU. If 1472 fails and 1400 succeeds, something in the path is smaller than 1500 and that is the entire fault.' cmd 'ping -M do -s 1472 -c 2 8.8.8.8' 'ping -M do -s 1400 -c 2 8.8.8.8' 'ping -M do -s 1300 -c 2 8.8.8.8'; then ping -M do -s 1472 -c 2 8.8.8.8 ping -M do -s 1400 -c 2 8.8.8.8 ping -M do -s 1300 -c 2 8.8.8.8 if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 1 failed. The rest of the fix may depend on it." >&2 fi fi if step 2 'Check the interface'\''s configured MTU.' '' cmd 'ip -brief link show' 'ip link show dev eth0 | grep -o '\''mtu [0-9]*'\'''; then ip -brief link show ip link show dev eth0 | grep -o 'mtu [0-9]*' if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 2 failed. The rest of the fix may depend on it." >&2 fi fi if step 3 'Check whether the kernel has learned a lower path MTU for that destination.' '' cmd 'ip route get 8.8.8.8' 'ip route show cache'; then ip route get 8.8.8.8 ip route show cache if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 3 failed. The rest of the fix may depend on it." >&2 fi fi if step 4 'Confirm ICMP is not being filtered -- path MTU discovery depends entirely on ICMP type 3 code 4 getting back.' '' cmd 'sudo tcpdump -ni any icmp -c 10'; then sudo tcpdump -ni any icmp -c 10 if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 4 failed. The rest of the fix may depend on it." >&2 fi fi rule " Confirm it worked" prose 'You have a number: the largest working payload size.' rule if [ "$failed" -gt 0 ]; then echo " Finished with $failed failed step(s)." echo " Read the full write-up at https://jbtecwiz.com/support/lnx-net-mtu" else echo " Finished." fi echo prose 'To undo: Nothing was changed.' rule