Netzwerkanalyse mit Ping und Gnuplot

ping stat image

Für Netzwerkanalyse in Echtzeit ist ping gut geeignet, besser noch mrt. Wenn es aber darum geht Schwankungen in der Übertragung zu dokumentieren wird das mit diesen Tools schwierig. Dieses Skript kann die Pings zu einem Host über längere Zeiträume machen. Das Ergebnis wird grafisch aufbereitet und in PDF Format dokumentiert. Wie in diesem Beispiel, wo das zeitweise Aussetzen der Netzwerkschnittstelle eines OKI-Druckers dokumentiert wird, das nur sporadisch auftritt, aber auf einen Defekt hinweist.

Dieser Skript erfordert GhostScript, Gnuplot lokal installiert. Der eigentliche Ping kann auch über eine SSH Verbindung von einem anderen Host aus geschehen, dort wird lediglich ping und ein ssh-server benötigt. Empfohlen ist natürlich eine eingerichtete Hostkey-Authentifizierung. Der Host könnte auch ein BSD oder OSX sein, das sollte prinzipiell auch gehen. Für Linux sollte man iputils-ping installiert haben, andere Varianten funktionieren wahrscheinlich nicht, weil die Ausgaben von ping andere Formatierung haben.

Im Ergebnis sieht man sogar das nichtmonotone Eintreffen der ICMP Antworten, da die Verbindungslinien der Punkte chronologisch sind, die Punke aber in der Reihenfolge der ICMP Anfragen sind. Paketverluste sind auffällig durch jeweils eine senkrechte Linie zu sehen. Das ist schon bedeutend aussagekräftiger als nur eine Zusammenfassung wie bei ping, wo man zwar sieht dass es Paketverluste gibt, aber nicht wo genau.

Auf einer zweiten Seite werden die Ergebnisse in einem Histogramm zusammengefasst, und auf einer dritten Seite sind statistische Parameter gelistet.

pingstat.sh:

download

#!/bin/bash
#  Ping-Statistik mit geringem Aufwand (Vergleich zu smokeping)
#  erfordert Gnuplot, ping (iputils)
#
usage() {
    echo "usage:"
    echo "  locally  (root): ping -s size -i interval -c count target > result-file"
    echo "  remotely (root): ssh root@hostname \"ping -s size -i interval -c count target\" > result-file"
    echo " then postprocess:    pingstat.sh result-file"
    exit 0
}

[ "$1" = "-h" ] && usage

PINGAUSGABE=$1
TimeStamp=$(ls -l --time-style=full-iso  "$PINGAUSGABE" |awk '{printf"%sT%s\n",$6,$7}')
PingZiel=$(head -1 "$PINGAUSGABE"|awk '{ print $2}')
Psize=$(head -1 "$PINGAUSGABE"|awk '{ print $4}')
RESULT=$(grep received "$PINGAUSGABE")
RTT=$(grep rtt "$PINGAUSGABE")
echo "Datei: $TimeStamp Ziel: $PingZiel"

STATDATEI=$PINGAUSGABE.stats
CSV1=$PINGAUSGABE.csv
CSV2=$PINGAUSGABE-missing
TMPPNG1=$PINGAUSGABE-zeit.png
TMPPNG2=$PINGAUSGABE-stat.png
#TMPPNG3=$PINGAUSGABE-temp3.png
GnuPlotFile=$PINGAUSGABE.p
#------------------------------------------
CreateGnuplotFile() {
#  PDF-Datei nachbearbeiten, dazu Datei vornbereiten…
cat > "$1" <<EOF
title1='Pingzeit to $PingZiel, ($Psize b). $INTV s, $RESULT'
title2='Pingstatistik $BREITE, $RTT, $TimeStamp'
datum='$TimeStamp'
datei1='$CSV1'
datei2='$CSV2'
ausgabe1='$TMPPNG1'
ausgabe2='$TMPPNG2'
miss='$MISS'
set terminal png size 1200,800
EOF
cat /usr/lib/pingstat/pingstat.p >> "$GnuPlotFile"
}

run_test() {
    echo "Teste $1"
    if [ -n "$REMOTEHOST" ]
    then ssh "root@$REMOTEHOST" "ping -n -s \"$PSIZE\" -i \"$INTV\" -c \"$ANZAHL\" \"$1\" " >"$2"
    else ping -n -s "$PSIZE" -i "$INTV" -c "$ANZAHL" "$ZIEL" >"$2"
    fi
}
get_missing()  {
    YYY=0
    grep "time=" "$1"|awk '{print $6}'|cut -d'=' -f2|sort -n|while read -r XXX  # grep sequence number
    do
    #	echo $XXX : $YYY
    	YYY=$((YYY+1))
    	{
    	while [ $YYY -lt "$XXX" ]
    	do
    		echo "$YYY"
    		YYY=$((YYY+1))
    	done } >"$2"
    done
}
#
##echo "Pingtest von $QUELLHOST auf $ZIEL mit $PSIZE × $ANZAHL pings im Intervall $INTV"
##run_test  "$ZIEL"   "$PINGAUSGABE"
#
pingresult_to_csv() {
#  64 bytes from fritz.box (192.168.178.1): icmp_seq=1 ttl=64 time=1.24 ms
#  1    2    3       4             5            6        7        8      9
#                                                    |             |
    awk '{printf "%s,%s\n",$6,$8}' "$1" |sed -e "s/time=//g" -e "s/icmp_[sr]eq=//g" |head -n -4|tail -n +2 >"$2"
}

count_missing_lines() {
    MISS=$(awk '{x++}END{ print x}' "$1")
    [ -z "$MISS" ] && MISS=0
}
pingresult_to_csv "$PINGAUSGABE" "$CSV1"
get_missing "$PINGAUSGABE" "$CSV2"
count_missing_lines "$CSV2"
CreateGnuplotFile "$GnuPlotFile"
#
gnuplot "$GnuPlotFile" 2>"$STATDATEI" || exit 1

Dazu gehört die Datei, die GnuPlot verwendet. pingstat.p:

download

# Definiere die Breite der Bins (“bin width”, ∆x)
# und eine sog. “binning”-Funktion:
hist(x,s)=s*ceil(x/s) - 0.5*s

# Die Funktion ceil(x) rundet den Wert von x ab
# Die Anzahl der Datenpunkte (für die Normierung)
set datafile separator ","
stats datei1 ; N = STATS_records
set output ausgabe1
plot datei1  using 1:2
ymax = GPVAL_DATA_Y_MAX
ymin = GPVAL_DATA_Y_MIN
bw = (10.0 ** floor(log10(ymax))) /10.0

set output ausgabe1
set size 0.95,0.95
set origin 0.03,0.03
set grid #linestyle 30, linestyle 31
set grid xtics mxtics ytics mytics

set title title1
#"Pingzeit Verlauf"
set xlabel "seq."
set ylabel "Zeit in ms"
#set yrange [*<0:*]
set logscale y
set mytics
if ( miss >0 ) {
plot datei1  using 1:2 with linespoints lc rgb "blue" pt 7 ps 0.3 title "rtt", \
     datei2  using 1:(ymax * 0.95) with impulses lc rgb "red" title "loss"
} else {
plot datei1  using 1:2 with linespoints lc rgb "blue" pt 7 ps 0.3 title "rtt"
}
# mit logscale y muss man die max und min Werte entsprechend korrigieren
print "   Breite ", bw, " ms ", ymax
set boxwidth bw
print title1
print datum

set output ausgabe2
set title title2
set xlabel "Pingzeit in ms"
set ylabel "Relative Häufigkeit in %"
unset logscale y
set xrange [0:]
set yrange [0:*]
#set ytics default
unset mytics
# Das Histogramm wird erzeugt und dargestellt durch:
plot datei1 u (hist($2,bw)):(1./N*100.0) smooth frequency with boxes lc rgb "blue" title "success"
# Dabei werden die Daten in Bins aufgeteilt (“gebinnt”) und durch die
# Direktive smooth frequency die Treffer eines jeden Bins aufsummiert;
# das Ergebnis wird als Funktion des Bins aufgetragen, und zwar mit
# Balken (“boxes”).