Calculate time needed to send 1 byte back and forth within an SSH connection
This file contains 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/bash | |
# | |
me=sshpingpong | |
# | |
# Measure close to minimal packet latency (RTT) of an SSH connection | |
# | |
prgversion="$me * 2022-07-27 (c) Andreas Schamanek" | |
# | |
# @author Andreas Schamanek <https://andreas.schamanek.net> | |
# @license GPL <https://www.gnu.org/licenses/gpl.html> | |
# @copyright (c) 2022 Andreas Schamanek | |
# | |
usage="$prgversion | |
$me sshpingpong [-L] [-t] [-c N] [-i S] {sshlogin@host} | |
-c N ... quit after N measurements | |
-i S ... wait S seconds between pings; can be fractional, default 5s | |
-t ... terse/compact mode using _, 1, 2, ... for hundreds of ms | |
where 1 means 100<=RTT<200, 2 means 200<=RTT<300, ... | |
-L ... do not write to logfile | |
Cf. https://serverfault.com/q/807910/23900 | |
" | |
while [[ $1 == -* ]] ; do | |
case $1 in | |
-t) compactmode=yes; shift;; | |
-c) [[ $2 != *[!0-9]* ]] || { echo "invalid count" >&2; exit 1; } | |
count="$2"; shift 2;; | |
-i) [[ $2 != *[!0-9.-]* ]] || { echo "invalid interval" >&2; exit 1; } | |
sleep="$2"; shift 2;; | |
-L) logfile=/dev/null; shift;; | |
-\?|-h|--help) echo "$usage"; exit 0;; | |
esac | |
done | |
ssh="${1:?$me error: missing argument}" | |
if [[ -z $logfile ]] ; then | |
logfile="${ssh#*@}"; logfile="spp_${logfile//[: .]/_}".log | |
echo "Logfile is $logfile" | |
elif [[ $logfile == /dev/null ]] ; then | |
logfile= | |
fi | |
declare -i c=-1 now=0 sent=0 rcvd=0 rtt=0 count="${count:-0}" | |
declare rttms=0.0 | |
: "${sleep:=5}" | |
: "${initialsleep:=2}" | |
# compact mode factor 1 or 10: cf=10 will make _, 1, 2, 3, ... indicate RTTs | |
# of <10, <20, <30, ...; w/ cf=1 it will be <100, <200, <300, ... | |
: "${cf:=1}" | |
: "${trapsigs:="INT TERM EXIT"}" | |
if [[ -d $XDG_RUNTIME_DIR ]] ; then | |
mkdir -p "$XDG_RUNTIME_DIR/$me" | |
fifo="$XDG_RUNTIME_DIR/$me/$$" | |
else | |
fifo="$HOME/fifo$$" | |
fi | |
set_now() { now=$(date +%s%N); now="${now%???}"; } # microseconds | |
compactmodeoutput() { | |
if ((cf*rtt<100000)) ; then echo -n _ >&2 ; return; fi | |
if ((cf*rtt>999999)) ; then echo -n "#" >&2 ; return; fi | |
echo -n "$((cf*rtt/100000))" >&2 | |
} | |
# logline() also printing "seq=$c" at the time it was sent | |
#logline() { printf '%(%Y-%m-%d %T)T seq=%d %.3f\n' "${sent%??????}" "$c" "$rttms" ; } | |
# logline() just printing the RTT at the time the data was received | |
logline() { printf '%(%Y-%m-%d %T)T %.3f\n' "${rcvd%??????}" "$rttms" ; } | |
trap "trapexit" $trapsigs | |
trapexit() { | |
trap '' $trapsigs | |
[[ ! -e $fifo ]] || rm "$fifo" | |
if [[ -z "$logfile" ]] ; then | |
printf '\n' >&2 | |
else | |
printf '\nLogfile is %s\n' "$logfile" >&2 | |
fi | |
exit | |
} | |
[[ ! -e $fifo ]] || rm "$fifo" | |
mkfifo "$fifo" | |
# compact mode header and output redirection | |
if [[ -z $compactmode ]] ; then | |
[[ -z "$logfile" ]] || exec 1> >(tee -a "$logfile") | |
else | |
# in compact mode, if no logging is requested we need to redirect to null | |
# so that logline() is silenced | |
if [[ -z "$logfile" ]] ; then | |
exec 1>>/dev/null | |
else | |
exec 1>>"$logfile" | |
fi | |
if ((cf==10)) ; then | |
echo "0 _ 10 1 20 2 30 3 40 4 50 5 60 6 70 7 80 8 90 9 99 # ..." >&2 | |
else | |
echo "0 _ 100 1 200 2 300 3 400 4 500 5 600 6 700 7 800 8 900 9 999 # ..." >&2 | |
fi | |
fi | |
mepid="$$" | |
( sleep "$initialsleep"; echo "ignore initial ping" >"$fifo"; ) & | |
cat 0<> "$fifo" | ssh $sshargs $ssh cat \ | |
| while read -r R ; do | |
set_now; rcvd="$now" | |
rtt="$((rcvd-sent))"; rttms="${rtt%???}.${rtt: -3}" | |
if [[ $R == o ]] ; then | |
logline | |
[[ -z $compactmode ]] || compactmodeoutput | |
sleep "$sleep" | |
fi | |
if ((count>0 && c>=count)) ; then | |
# found no nicer way to make sure $ssh ends | |
# and w/o kill the subshell would never exit | |
pkill -2 --parent "$mepid" | |
exit | |
fi | |
c=$((c+1)) | |
echo 'o' >"$fifo" | |
set_now; sent="$now" | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
In Version
2022-07-27
I mostly changed option-c
(compact mode) to-t
(terse/compact mode) and introduced-c N
to quit the script after N ping-pongs.