#!/usr/bin/env bash # # JbTecWiz Support Centre -- generated fix script # # Fault : Connections establish then hang -- MTU and path MTU discovery # Fix : Set the MTU and clamp the segment size # Source: https://jbtecwiz.com/support/lnx-net-mtu # # Run as : Root shell # Expect : 25 minutes # Risk : medium # Reversible : yes # # WHEN THIS IS THE RIGHT FIX # You know the working size, or a tunnel is involved. # # HOW TO UNDO IT # ip link set dev eth0 mtu 1500 restores the default until the next # reboot; revert the permanent configuration to undo it fully. # # 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 5" 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 " Set the MTU and clamp the segment size" echo echo " Risk: medium Reversible 25 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 'Test the value temporarily before making it permanent.' '' cmd 'sudo ip link set dev eth0 mtu 1420'; then sudo ip link set dev eth0 mtu 1420 if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 1 failed. The rest of the fix may depend on it." >&2 fi fi step 2 'For WireGuard, 1420 is the usual value; for PPPoE, 1492; for a GRE tunnel, 1476. Start from the standard for the encapsulation rather than guessing.' '' manual || true if step 3 'Make it permanent in the network configuration -- netplan, NetworkManager or the interface file, depending on the distribution.' '' cmd 'sudo nmcli connection modify '\''Wired connection 1'\'' 802-3-ethernet.mtu 1420' 'sudo nmcli connection up '\''Wired connection 1'\'''; then sudo nmcli connection modify 'Wired connection 1' 802-3-ethernet.mtu 1420 sudo nmcli connection up 'Wired connection 1' 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 'On a router or a machine that forwards traffic, clamp the TCP maximum segment size so clients behind it are corrected automatically.' 'MSS clamping fixes every client behind the router without touching any of them. It is the standard answer for a site behind a tunnel and it avoids having to set the MTU on each machine.' cmd 'sudo nft add rule inet filter forward tcp flags syn tcp option maxseg size set rt mtu'; then sudo nft add rule inet filter forward tcp flags syn tcp option maxseg size set rt mtu if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 4 failed. The rest of the fix may depend on it." >&2 fi fi if step 5 'Make sure ICMP is permitted through the firewall in both directions -- blocking it wholesale is what turned a small MTU into a hang rather than a slow transfer.' '' cmd 'sudo nft list ruleset | grep -i icmp'; then sudo nft list ruleset | grep -i icmp if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 5 failed. The rest of the fix may depend on it." >&2 fi fi rule " Confirm it worked" prose 'A large transfer completes at full speed and ping -M do succeeds at the new size.' if [ "$DRYRUN" = "0" ]; then ping -M do -s $((1420-28)) -c 3 8.8.8.8 fi 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: ip link set dev eth0 mtu 1500 restores the default until the next reboot; revert the permanent configuration to undo it fully.' rule