Ping mit PowerShell
An verschiedenen Stellen nutze ich "Ping" oder besser das "Test-Connection"-Commandlet um die Erreichbarkeit von Systemen vorab zu prüfe oder auch einfache LAN/WAN-Checks zu machen. Dabei gilt es einige Besonderheiten zu beachten, die ich hier beschreibe.
Basis Ping
Wenn man sich in der PowerShell bewegt, sollte man auch die Vorteile der Pipeline und von Rückgabeobjekten nutzen. Wer also aus einer PowerShell einen "PING" machen will, sollte sicher nicht "PING.EXE" aufrufen und dann die Bildschirmausgabe auswerten. Aber auch der Umweg über WMI-Klassen ist heute eigentlich nicht mehr erforderlich. Ein einfaches "Test-Connection" sendet per Default 4 ICMP-Pakete a 32 Byte an die angegebene Gegenstelle und liefert sehr ausführliche Daten zurück. Hier mal ein Beispiel:
PS C:\> Test-Connection -Count 1 192.168.178.1 | fl __GENUS : 2 __CLASS : Win32_PingStatus __SUPERCLASS : __DYNASTY : Win32_PingStatus __RELPATH : Win32_PingStatus.Address="192.168.178.1",BufferSize=32,NoFragmentation=FALSE,RecordRoute=0,Resolve AddressNames=FALSE,SourceRoute="",SourceRouteType=0,Timeout=4000,TimestampRoute=0,TimeToLive=80,Ty peofService=0 __PROPERTY_COUNT : 24 __DERIVATION : {} __SERVER : NAWNBFC __NAMESPACE : root\cimv2 __PATH : \\NAWNBFC\root\cimv2:Win32_PingStatus.Address="192.168.178.1",BufferSize=32,NoFragmentation=FALSE, RecordRoute=0,ResolveAddressNames=FALSE,SourceRoute="",SourceRouteType=0,Timeout=4000,TimestampRou te=0,TimeToLive=80,TypeofService=0 Address : 192.168.178.1 BufferSize : 32 NoFragmentation : False PrimaryAddressResolutionStatus : 0 ProtocolAddress : 192.168.178.1 ProtocolAddressResolved : RecordRoute : 0 ReplyInconsistency : False ReplySize : 32 ResolveAddressNames : False ResponseTime : 3 ResponseTimeToLive : 64 RouteRecord : RouteRecordResolved : SourceRoute : SourceRouteType : 0 StatusCode : 0 Timeout : 4000 TimeStampRecord : TimeStampRecordAddress : TimeStampRecordAddressResolved : TimestampRoute : 0 TimeToLive : 80 TypeofService : 0 PSComputerName : NAWNBFC IPV4Address : 192.168.178.1 IPV6Address :
Viele Daten sind rein informativ aber einige Felder sind natürlich für eine weitere Verarbeitung interessant wie z.B.
- ResponseTime
Die Zeit in Millisekunden für die Antwort - ResponseTimeToLive
Wenn man davon ausgeht, dass der Absender eine Pakete mit einem TTL=64 sendet, kann man aus der Differenz so die Anzahl der Hops auf dem Rückweg ermitteln. Das ist aber nicht sicher, da der Absender den Startwert für TTL vorgibt.
Übrigens können Sie beim Versenden einen TTL mitgeben und damit die Entfernung abschätzen. Einfach ein PING mit hohem TTL senden um die generelle Erreichbarkeit zu prüfen und dann mit aufsteigendem TTL die Entfernung ermitteln.
Wenn die Gegenstelle aber nicht erreichbar ist, dann kommt gar nichts zurück und es gibt eine Exception, die Sie natürlich abfangen müssen:
PS D:\> Test-Connection -Count 1 192.168.178.2 | fl Test-Connection : Fehler beim Testen der Verbindung mit dem Computer "192.168.178.2": Fehler aufgrund von zu wenigen Ressourcen In Zeile:1 Zeichen:1 + Test-Connection -Count 1 192.168.178.2 | fl + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : ResourceUnavailable: (192.168.178.2:String) [Test-Connection], PingException + FullyQualifiedErrorId : TestConnectionException,Microsoft.PowerShell.Commands.TestConnectionCommand
Ein Try/Catch ist angebracht, wenn Sie auf den zurückgegebenen Daten weitere Informationen erwarten. Alternativ können Sie
Test-Connection -Count 1 192.168.178.2 -ErrorAction silentlycontinue -ErrorVariable c
Dann sollten Sie aber die Variable "$c" danach abfragen und auch nicht vergessen, dass der Fehler zusätzlich auch in $Error hinterlegt wird. Nicht dass eine spätere Anfrage nach $Error sie irrtümlich einen Fehler annehmen lässt.
"Stiller" Ping
Eine Sonderform erhalten Sie mit dem Parameter "-quiet". Dann gibt es keine Details aber auch keinen Fehler sondern nur ein "False" oder "True" als Antwort.
Test-Connection -Count 1 192.168.178.2 -Quiet False Test-Connection -Count 1 192.168.178.1 -Quiet True
Diese Funktion erfordert auch keine Fehlerbehandlung und es wird auch nichts in $error geloggt. Insofern ist dies der bessere Weg um die Erreichbarkeit eines Hosts abzufragen wenn die Dauer nicht relevant ist.
Paralleles Ping
Wenn ich mehrere Systeme prüfen will, kann ich natürlich mehrere "Test-Connection"-Aufrufe nacheinander ketten. Der Parameter "Computer" erlaubt aber auch die Angabe einer Liste von Gegenstellen. Das können Sie recht einfach testen mit:
PS D:\> Test-Connection -ComputerName www.msxfaq.de,www.netatwork.de,www.heise.de -Count 2 Source Destination IPV4Address IPV6Address Bytes Time(ms) ------ ----------- ----------- ----------- ----- -------- NAWNBFC www.heise.de 193.99.144.85 32 25 NAWNBFC www.msxfaq.de 217.160.0.234 32 28 NAWNBFC www.netatwork.de 178.77.109.224 32 38 NAWNBFC www.heise.de 193.99.144.85 32 27 NAWNBFC www.msxfaq.de 217.160.0.234 32 30 NAWNBFC www.netatwork.de 178.77.109.224 32 38
Leider macht Test-Connection hier keine Parallelisierung und pro "Ping" wartet er am Ende auch wieder 1 Sekunde, so dass die Abfrage der drei Hosts drei Sekunden dauert. Test-Connection erlaubt allerdings auch kein Übergabe der Hostnamen als Pipeline, so dass Sie mit einer Loop erst zum Ziel kommen.
"www.msxfaq.de","www.netatwork.de","www.heise.de" ` | %{Test-Connection $_ -Count 1}
So geht es schneller aber noch nicht "parallel". Wenn ein Host nicht erreichbar ist, dann wartet Test-Connection auf den Timeout.
Test-Connection und "-AsJob"
Sehr viele Kommandlets lassen sich einfach mit dem Parameter "-asJob" in den Hintergrund schicken. Da Test-Connection auch diesen Parameter kennt, habe ich das doch gleich mal ausprobiert. mit mehreren Hosts wäre der Aufruf dann ja sehr einfach
Ich habe mal schnell versucht, mit der Liste einfach entsprechende "Dauerping"-Jobs loszusetzen. Das geht ja recht schnell mit
foreach ($entry in import-csv .\hostlist.csv) { Test-connection ` -asjob ` -buffersize 512 ` -delay 1 ` -count 300 }
Dann müsste ich mit einem Receive Job einfach nur die Daten einsammeln und visualisieren. Ich habe es dann doch erst mal manuell gemacht. Einmal manuell und einmal als "job
Test-Connection www.msxfaq.de -Count 4 Test-Connection www.msxfaq.de -Count 4 -asjob
Der erste Aufruf hat wie erwartet 4 "PING" mit einem Abstand von 1 Sekunde versendet und empfangen. Der zweite Befehl hat einen Job angelegt, der das Gleiche im Hintergrund tun sollte. Ich sage absichtlich "sollte", denn als ich mit get-job den Status überprüfen wollte, war er schon "Completed". Als ich die Rückgabe mit "Receive-Job" abgeholt habe, habe ich aber vier Ergebnisse gesehen. Irgendwas war falsch gelaufen. Ich habe die gleiche Prozedur noch mal mit Wireshark mitgeschnitten.
Der Aufruf mit "AsJob" feuert ganz schnell sogar parallel die PINGs. ab. Ich habe dann mit den Parametern experimentiert aber keine Besserung erreicht. In der Betriebsart mit "-asJob" scheint der Parameter "-Delay" fest mit "0" hinterlegt zu sein und das Kommandlet flutet das Netzwerk. Wenn ich aber den Parameter "Delay" angebe, dann muss er in Sekunden angegeben werden und "0" ist nicht erlaubt. Sehr suspektes Verhalten.
Wenn ich aber z.B. 10 Pings in einer Sekunden absenden will, dann geht das nur wie folgt:
1..10 | % { test-connection www.msxfaq.de -count 1 start-sleep -milliseconds 100 }
So kommt "TestConnection" sofort wieder zurück und kann wieder gestartet werden. Allerdings sollten Sie das nicht mit jeder Gegenstelle machen, da Firewalls natürlich sehr schnell so einen "Ping Flood" unterbrechen, was die PowerShell so meldet
Passend dazu das Paket in Wireshark:
Im internen LAN/WAN kann man aber wohl damit arbeiten. So darf ich das Skript also nicht starten. Wenn ich viele Host "Parallel" abprüfen will, dann muss ich das mit einem Start-Job machen.
foreach ($entry in import-csv .\hostlist.csv) { Start-Job ` -ArgumentList @($entry) ` -ScriptBlock { ` Test-Connection ` -ComputerName $Args[0] -buffersize 512 ` -delay 1 ` -count 300 }
Die einzelnen Jobs laufen in dem Beispiel per Default schon einmal 5 Minuten aber stoppen dann alleine. Allerdings ist Start-Job nicht immer besonders Ressourcenschonend.
Messen
Ein Ping ist für eine Messung nicht repräsentativ aber 5 Minuten möchte ich auch nicht auf ein Ergebnis warten. Daher habe ich mir überlegt, dass ich alle 5 Sekunden ein Update mache und dazu 5 Pings mit 1KB Buffergröße sende. Die Auswertung kann darüber denn den Mittelweg, Max, Min erstellen und ausgeben. Ich erstelle also alle 5 Sekunden für jedes Ziel einen Job, der Pings versendet und die Ergebnisse dann liefert.
Achtung: Die Rückgabe von "Test-Connection" hat sich mit PowerShell 6 geändert. Statt "ResponseTime" muss "Latency" genutzt werden oder sie messen direkt den Aufruf von Test-Connection
1..50 ` | %{ ` Test-Connection 8.8.8.8 ` -Count 1 ` -BufferSize 1kb ; ` start-sleep ` -Milliseconds 50} ` | Measure-Object Responsetime ` -Minimum ` -Maximum ` -Average
Dieser Code wird einmal pro Ziel aufgerufen und am Ende sammle ich von allen Jobs die Ergebnisse ein und zeige sie an. Sie können.
Get-Content ` -path C:\Utilities\servers.txt ` | ForEach-Object { ` Test-Connection ` -ComputerName $_ ` -Count 1 ` -AsJob ` }` | Get-Job ` | Receive-Job ` -Wait ` | Select-Object @{Name='ComputerName';Expression={$_.Address}},` @{Name='Reachable';Expression={if ($_.StatusCode -eq 0) { $true } else { $false }}} ` | ft -AutoSize ResponseTimeToLive
Offen
Leider habe ich noch keinen Weg gefunden, wie ich per ICMP zurück erhaltene Pakete parsen kann. Gerade beim Versand von zu großen Paketen per ICMP in Verbindung mit dem "Don't Fragment (DF)"-Flag kommt ja ein "ICMP Size Exceeded" zurück. Dort steht aber sowohl die MTU Size als auch die IP-Adresse des generierenden Systems drin. Auch andere ICMP-Rückmeldungen wären für weitere Auswertungen eine interessante Quelle. Eventuell muss man tatsächlich ICMP komplette selbst empfangen und auswerten.
Aufgrund von Missbrauchs durch Viren um das Jahr 2000 hat Microsoft die Verwendung von Raw-Sockets auf Nicht-Server-Editionen des Windows-Betriebssystems eingeschränkt.
On Windows 7, Windows Vista, Windows XP
with Service Pack 2 (SP2), and Windows XP with Service
Pack 3 (SP3), the ability to send traffic over raw sockets
has been restricted in several ways:
https://docs.microsoft.com/en-us/windows/win32/winsock/tcp-ip-raw-sockets-2#limitations-on-raw-sockets
Das scheint aber gar nicht so einfach zu sein. Microsoft selbst hat das Commandlet "Test-Connection" mit der Option "MTUSizeDetect" ausgestattet und die macht auch ein "Annähern" an die MTU-Size durch mehrere Versuche. Eine genauere Beschreibung dazu finden Sie auf MTU.
- Traceroute TURN
- MTU
-
Powershell-ICMP/Powershell-ICMP-Listener.ps1
https://github.com/api0cradle/Powershell-ICMP/blob/master/Powershell-ICMP-Listener.ps1 - How to write a basic sniffer in
PowerShell
http://www.drowningintechnicaldebt.com/RoyAshbrook/archive/2013/03/08/how-to-write-a-basic-sniffer-in-powershell.aspx
Weitere Links
- PowerShell und parallele Verarbeitung
- PRTG - Monitoring für den KMU
- Ende zu Ende Monitoring
- End2End-Ping
-
Use PowerShell to Test Connectivity on Remote Servers
https://devblogs.microsoft.com/scripting/use-powershell-to-test-connectivity-on-remote-servers/ - Powershell – Fast Asynchronous Ping a specified IP range
http://www.mathewjbray.com/uncategorized/powershell-fast-asynchronous-ping-a-specified-ip-range/ - Multi-node ping tests with PowerShell
http://www.onesimplescript.com/2011/11/multi-node-ping-tests-with-powershell.html - StatisticalPingXML
http://prtgtoolsfamily.com/downloads/sensorsxml - #PSTip How to speed up the Test-Connection command
http://www.powershellmagazine.com/2012/10/19/pstip-how-to-speed-up-the-test-connection-command/ - Tricks with Test-Connection
http://windowsitpro.com/blog/tricks-test-connection