Meine Implementierung von ping

Hintergrund

Das Hilfsprogramm ping ist aus dem Werkzeugkasten des TCP/IP-Netzwerkers nicht wegzudenken. Es versendet ICMP-"Echo-Request"-Pakete und wertet die Antworten aus, um Aussagen über die Beschaffenheit der Netzwerkverbindung treffen zu können. Die Bezeichnung "ping" ist eine Anlehnung an die Geräusche eines Sonar-Systems zur Unterwasser-Ortung. Dazu gibt es eine interessante Seite: The Story of the PING Program von Mike Muuss, dem Erfinder und Autor des ersten Ping-Programms.

Implementierung

Auch meine Implementierung nutzt einen Raw Socket zum Versenden von ICMP-Echo-Requests. In meiner ersten Verison von 2002 wird der Sendezeitpunkt als Timestamp (gettimeofday) in den Nutzdaten des ICMP-Echo-Requests gespeichert und geht mit auf die Reise. Das funktioniert deshalb, weil das ICMP-Echo-Reply eine Kopie der Nutzdaten enthält. Die große Eleganz dieses Ansatzes liegt darin, dass sich das Sendeprogramm nicht viel merken muss. Ein Nachteil ist jedoch, dass die Größe der Ping-Pakete nach unten hin begrenzt ist, weil ja noch ein Timestamp in die Nutzdaten passen muss. Für den eigentlichen Anwendungszweck ist das allerdings egal.

Meine überarbeitete Version von 2010 verwendet einen deutlich komplexeren Ansatz, bei dem der Sendezeitpunkt in einer Liste vermerkt wird und nicht mehr Teil des Datagramms ist. Außerdem werden die ICMP-Pakete nicht im Kontext eines SIGALRM-Handlers versendet, sondern im Hauptkontext. Dazu verwende ich eine Sendesteuerung auf Basis von select(2) die sowohl auf eingehende Antworten warten als auch einen Timeout überwachen kann. Einen Signal-Handler benutze ich dennoch, nämlich um kontrolliert auf SIGINT (Ctrl+C) reagieren zu können; das Programm soll ja nicht einfach nur abbrechen, sondern die Endstatistik ausgeben.

Das eigentliche Problem mit Signal-Handlern ist die Nebenläufigkeit, die durch den zusätzlichen Ausführungskontext zwangsläufig auftritt. Dadurch handelt man sich in scheinbar einfachen Programmen eine Reihe von Problemen ein, die man sonst nur aus Multi-Threading-Umgebungen kennt. Im Fall des SIGINT-Handlers ergibt sich eine Wettlaufsituation, die ich durch Verwendung des Self-Pipe-Tricks entschärfe. Die Kurzfassung: man legt eine Pipe mit in das Select-Deskriptorset, in dessen Schreib-Ende der Signal-Handler Kommandos an das Hauptprogramm schreibt. In diesem Fall reicht bereits die Information, dass die Pipe lesbar geworden ist, aber in anderen Szenarien kann es nützlich sein, im Hauptprogramm differenziert auf die Daten zu reagieren.

Die Ausgabe meines eigenen Programms ähnelt sehr dem ping, das ich auf meinem Linux-System vorgefunden habe:

$ ./ping -c 4 www.zotteljedi.de
PING www.zotteljedi.de (82.165.104.242) 56(84) bytes of data.
64 bytes from kundenserver.de (82.165.104.242): icmp_seq=1 ttl=57 time=24.3 ms
64 bytes from kundenserver.de (82.165.104.242): icmp_seq=2 ttl=57 time=24.1 ms
64 bytes from kundenserver.de (82.165.104.242): icmp_seq=3 ttl=57 time=24.2 ms
64 bytes from kundenserver.de (82.165.104.242): icmp_seq=4 ttl=57 time=24.5 ms

--- www.zotteljedi.de ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3052ms
rtt min/avg/max = 24.136/24.278/24.471 ms

Die möglichen Optionen sind hingegen etwas magerer:

Usage: ping [-n] [-c count] [-i interval] [-s packetsize]
            [-W timeout] destination

Der Quellcode compiliert unter Linux (getestet mit Debian und Ubuntu) und FreeBSD (getestet mit FreeBSD 8.0). Das Programm muss entweder als root gestartet werden, oder root gehören und mit dem SUID-Bit versehen worden sein. Diese root-Rechte werden direkt nach dem Anlegen des Raw Sockets abgelegt (effektive User-ID wird auf reale User-ID gesetzt).

Downloads

Datum Version Datei Beschreibung
2010-05-30 2.1 ping-2.1.tar.gz Neuimplementierung mit verbesserter Sendesteuerung, Statusführung und weiteren Optionen
2002-05-11 1.1 ping-1.1.tar.gz Ursprüngliche Implementierung

Zurück zur Hauptseite