Last active
April 30, 2025 23:19
-
-
Save bneils/8f337bedebffbe3c1bf2c59def7029d2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# pmtud.sh Efficient Pathway MTU Discovery Program | |
# Copyright (C) 2025 Ben Neilsen | |
# This program is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# You should have received a copy of the GNU General Public License | |
# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
# WARNING: REMOVING THIS CAN CAUSE A FORK BOMB | |
PLIMIT=1500 | |
ulimit -u $PLIMIT || ulimit -p $PLIMIT | |
# REMOVE $TMP_DIR IF PROCESSES WON'T BE KILLED | |
# Set $TARGETS to an IP that won't mind you (possibly spam) pinging them. | |
# If this organization employs you or could know your identity, ask permission. | |
# However, the purpose of this program is to *not* spam, but read the license! | |
TARGETS="gnu.org kernel.org debian.org" | |
# If /tmp does not exist on your system, change it here. | |
# ~ or /dev/shm will do. | |
# DO NOT SET $TMP_DIR TO THE ENTIRE DIRECTORY, SUCH AS /tmp OR $HOME | |
TMP_DIR=/tmp/pmtud-$$ | |
# PING_WAIT should be a real > 0, but increase it if needed. | |
# SPAWN_WAIT can be a real > 0 provided that R = PING_WAIT / SPAWN_WAIT <= 3 | |
# A higher R creates more pings with diminishing returns in execution time. | |
# It is recommended to instead lower PING_WAIT. | |
# Increase PING_WAIT for 3 or more TARGETS | |
PING_WAIT=1 | |
SPAWN_WAIT=0.5 | |
dns() { | |
dig -4 +short "$1" | head -1 | |
} | |
# measure() checks if a ping can be sent with size $1 without being fragmented | |
# within $PING_WAIT seconds. | |
# Pre-condition: the caller must have defined $host | |
# Post-condition: file at $PING_DIR/$1 containing 0 for "no frag needed" or | |
# 1 for "frag needed" | |
measure() { | |
if [ ! -d "$PING_DIR" ] || [ "$addr" = "" ] | |
then exit 1 | |
fi | |
replies=$({ | |
{ | |
{ | |
{ | |
ping -4 -c 1 -w $PING_WAIT -M "do" "$addr" -s $1 2>/dev/null; | |
echo $? >&3; | |
} | grep "received" | cut -d' ' -f4 >&4; | |
} 3>&1; | |
} | { | |
read xs; | |
# clamp $? to 0 or 1, because ping has other return codes | |
[ $xs -eq 0 ]; | |
echo $? > $PING_DIR/$1; | |
} | |
} 4>&1) | |
# Open the file only when the commands are done | |
echo $replies >> $TMP_DIR/num_pings; | |
} | |
# look() returns with code 0 if measurement of size $1 would update the | |
# known MTU bounds. Doesn't include measurements waiting for a response. | |
# If a measurement returned after look()'s invocation it is not included. | |
# Pre-conditions: | |
# - $PING_DIR exists | |
# - $PING_DIR only has files with digit names, no whitespace | |
# - Files either contain '0' or '1' | |
# Performance: O(n) | |
look() { | |
if [ ! -d "$PING_DIR" ] | |
then exit 1 | |
fi | |
cd "$PING_DIR" | |
for filename in $(ls) ; do | |
if [ ! -f "$filename" ] | |
then continue | |
fi | |
status=$(cat "$filename") | |
if [ "$filename" -ge "$1" ] && [ $status -eq 0 ] | |
then return 1 | |
elif [ "$filename" -le "$1" ] && [ $status -ne 0 ] | |
then return 1 | |
fi | |
done | |
return 0 | |
} | |
# binsearch() performs binary search on the domain [$1,$2] | |
# Pre-condition: two valid integers $1 and $2 | |
binsearch() { | |
# This is a protective measure against runaway children! | |
if [ ! -d $TMP_DIR ] | |
then return 1 | |
fi | |
x=$(expr \( "$1" + "$2" \) / 2) | |
if look $x ; then | |
measure $x & | |
sleep $SPAWN_WAIT | |
if [ "$1" -ne "$2" ] ; then | |
# Avoid infinite loops, by only bisecting if | |
# domain is non-zero | |
binsearch $1 $(expr $x - 1) & | |
binsearch $(expr $x + 1) $2 & | |
fi | |
wait | |
fi | |
} | |
# biggest() prints the size of the biggest ping that returned a response. | |
# Pre-condition: files exist in $PING_DIR containing either 1 or 0. | |
biggest() { | |
max=0 | |
cd "$PING_DIR" | |
for filename in $(ls) ; do | |
if [ ! -f "$filename" ] | |
then continue | |
fi | |
status=$(cat "$filename") | |
if [ $status -eq 0 ] && [ "$filename" -gt $max ] ; then | |
max=$filename | |
fi | |
done | |
echo $max | |
} | |
num_pings() { | |
total=0 | |
while read n | |
do total=$(echo $n + $total) | |
done < $TMP_DIR/num_pings | |
expr $total | |
} | |
find_mtu() { | |
binsearch 0 1472 | |
MAX_PING_PAYLOAD=$(biggest) | |
if [ "$MAX_PING_PAYLOAD" -gt 0 ] | |
then MTU=$(expr $MAX_PING_PAYLOAD + 28) | |
else MTU=0 | |
fi | |
echo $MTU $host >> $TMP_DIR/mtu | |
} | |
main() { | |
for host in $TARGETS ; do | |
addr=$(dns $host) | |
PING_DIR=$TMP_DIR/$(echo $host | sha1sum | cut -d' ' -f1) | |
mkdir $PING_DIR || exit 1 | |
find_mtu & | |
done | |
# Wait until all processes finish to print results | |
wait | |
echo $(num_pings) replies | |
cat $TMP_DIR/mtu | |
} | |
# Evaluated left-to-right, fails if these commands don't exist | |
{ which dig && which ping || exit 1; } > /dev/null | |
mkdir $TMP_DIR || exit 1 | |
main | |
rm -r $TMP_DIR |
Straight from lemmy, not bad. jumbo packet detection?
It sets the Do Not Fragment bit and if no response returns, it assumes the router could not route it without fragmenting it and attempted to respond with an ICMP message. The actual receiving the ICMP message is irrelevant since my script does not check that and because my ISP blocks all ICMP messages at their firewall.
edit: I've edited this script too many times, so I am moving it to a repo.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Straight from lemmy, not bad. jumbo packet detection?