# end2end-ping
#
# Ping a given Endpoint every second
# measure roundtrip time and generate summaries
# avg, min, max, lost (<1Sek)
#
# 20181114 initial Version based on end2end-pathping and end2end-udp3478
# 20190117 Fix bei IP-Adresse
# 20190119 Ausgabe schöner
# 20190702 Fix AVG berechnung ohne Loss Pakete, Farbausgabe,
# Convert DNS to net class and remote [ordered] to support PS2 Clients
# 20200217 Timestamp addiert
# 20200224 Errorhandling
# 20200226 DestinationNetworkUnreachable, Div/0 abgefangen
# 20210204 Added PRTGPush function, TTL based on 128, Repeatcount to stop after given number of Pings
# 20210205 Zusammenfassung nach Zeit statt Durchläufen. Pause zwischen zwei Ping mit Stoppuhr, CSV-File mit Ziel
# 20210206 PRTG Ausgabetext mit Hostname erweitert, "toslow" addiert
# 20210707 Fix mit backspacecount = 0 DIV0
#
# Pending
# TTL HopCount
# Currently IPv4 DNS only
# Umstellen von statischen Werten auf dynmische. Vorgabe des "erwartung" und dann +20%+50*+100%
#
# System.Net.NetworkInformation PingSend-Method
# https://docs.microsoft.com/de-de/dotnet/api/system.net.networkinformation.ping.send?view=netframework-4.7.2
# System.Net.NetworkInformation PingReply Class
# https://docs.microsoft.com/de-de/dotnet/api/system.net.networkinformation.pingreply?view=netframework-4.7.2
# IPStatus Enum
# https://docs.microsoft.com/de-de/dotnet/api/system.net.networkinformation.ipstatus
#
param (
[string]$hostname = "internetbeacon.msedge.net", # samples google = 8.8.8.8 SfBOnlineEdge = 13.107.8.2, outlook.office365.com, internetbeacon.msedge.net
[long]$pingidletimems = 1000, # ms to wait for the next ping (start to start) Defaut 1 Sec
[long]$reporttimes = 60, # Time to wait for a summary
[long]$buffersize = 160, # PING Buffersize
[long]$repeatcount = -1, # stop after this number of runs. -1 = endless
[long]$pingtimeoutms = 500, # ms timeout to wait for a reply. anything longer will be treated as loss/to slow
[long]$slowlimitms = 200, # every pakeet slower that that is handled as to slow. muss be lower than pingtimeoutms
[switch]$ipv4, # Use IPV4
[string]$csvfilename = "$PWD\end2end-ping_$($env:COMPUTERNAME)-$($hostname).csv", # Path to CSV File. Clear to disable CSV
[switch]$sendtopipeline = $false, # send the summary to the pipeline for further processing
[string]$prtgpushurl="" # specify PRTG-Push Url if required like http://prtg:5050/sensorid
)
set-psdebug -strict
Write-Host "end2end-ping: Start"
Write-Host " Param Hostname : $($hostname)"
Write-Host " Param pingidletimems : $($pingidletimems)"
Write-Host " Param reporttimes : $($reporttimes)"
Write-Host " Param Buffersize : $($buffersize)"
Write-Host " Param repeatcount : $($repeatcount)"
Write-Host " Param PingTimeout : $($pingtimeoutms)"
Write-Host " Param slowlimitms : $($slowlimitms)"
Write-Host " Param ipv4 : $($ipv4)"
Write-Host " Param CSVFile : $($csvfilename)"
Write-Host " Param sendtopipeline : $($sendtopipeline)"
Write-host " prtgpushurl : $($prtgpushurl)"
$host.ui.RawUI.WindowTitle = "End2End-PING to $($hostname)"
Write-Host "Check IP-Address:" -NoNewline
$remoteip = $null
if ($hostname -as [ipaddress]) {
write-host " Use given IP-Address $($hostname)"
$remoteip = $hostname
}
else {
Write-Host " Try DNS-Lookup $($hostname) " -NoNewline
$error.clear()
try {
#$remoteip = ((resolve-DnsName $hostname -type A -ErrorAction SilentlyContinue)| where-object {$_.ipaddress})[0].ipaddress
if ($ipv4) {
$remoteip = ([System.Net.Dns]::GetHostAddresses($hostname) | Where-Object {$_.AddressFamily -eq "InterNetwork"})[0].IPAddressToString
}
else {
$remoteip = ([system.net.dns]::GetHostAddresses($hostname))[0].ipaddresstostring
}
write-host "Using IP-Addresse $($remoteip)"
}
catch{
write-host "Unable to Resolve name. Use IP-Adresse"
exit
}
}
if ($null -ne $remoteip) {
Write-Host "End2End-Ping:Initialize ICMP Object"
$ping = new-object System.Net.NetworkInformation.Ping
#$pingoptions = new-object System.Net.NetworkInformation.PingOptions
#$pingoptions.DontFragment =$true;
#$pingoptions.Ttl=10
Write-Verbose "End2End-Ping:Fill ICMP Buffer with Size:$($buffersize)"
$asciiencoder = [system.Text.Encoding]::ASCII
$buffer = $asciiencoder.GetBytes([string]("www.msxfaq.de end2end-ping").padright(($buffersize),"x"))
if ($slowlimitms -gt $pingtimeoutms) {
write-warning "slowlimitms $($slowlimitms) greater that pingtimeoutms $($pingtimeoutms). Adjusting"
$slowlimitms = $pingtimeoutms
}
if ($csvfilename -ne "") {
Write-Host "End2End-Ping:Initialize CSV-File"$csvfilename
if (!(test-path $csvfilename -pathtype leaf)){
Write-Host "End2End-Ping:Adding CSV-Header"
"Timestamp,RemoteIP,Min,Avg,Max,Total,Lost,statuspacketloss,statusavg2min,statusmax2avg" | out-file $csvfilename -append
}
}
Write-Verbose "Initialize Result Property"
$result = [pscustomobject]@{
timestamp = ((get-date).ToUniversalTime().tostring("u"))
remoteip = [string]$($remoteip)
max = [long]0
min = [long]9999999
avg = [long]0
total = [long]0
loss = [long]0
error = [long]0
toslow = [long]0
#ttl = [long]0
statuspacketloss=0
statusavg2min=0
statusmax2avg=0
}
Write-Host "Start Endless ICMP Ping to $($remoteip) - press CTRL-C to Stop"
Write-host "Textcode: .=Succesful T=Timeout($($pingtimeoutms)ms X=ExpiredTTL U=Unreachable S=SizeExceeded N=Neterror E=Error ?=Unknown "
Write-host "Colorcode:" -nonewline
Write-Host "<=5ms" -nonewline -backgroundcolor blue
Write-Host "<=10ms" -nonewline -backgroundcolor green -foregroundcolor black
Write-Host "<=50ms" -nonewline -backgroundcolor yellow -foregroundcolor black
Write-Host "<=100ms" -nonewline -backgroundcolor magenta
Write-Host ">100ms" -nonewline -backgroundcolor red
Write-host " Starting test... Min/Avg/Max/Total/Loss"
[long]$backspacecount = [math]::max((1000/$pingidletimems) / (60/$reporttimes),1) # used to reduce write-host outputs
[long]$lastmaxms = 0
[long]$Avgsum = 0
$pingrepeattimewatch = [system.diagnostics.stopwatch]::new()
$reportwatch = [system.diagnostics.stopwatch]::new()
$reportwatch.Restart()
while ($repeatcount -ne 0) { # stop if repeatcount is 1
if ($result.total -eq 0) {write-host "$((get-date).ToUniversalTime().tostring("u")):$($repeatcount):" -nonewline}
$result.total++
if ($repeatcount -gt 0) {$repeatcount--}
# ping remote host
try {
#$pingresult=$ping.send($remoteip,$pingtimeoutms,$buffer,$pingoptions)
$pingrepeattimewatch.Restart()
$pingresult=$ping.send($remoteip,$pingtimeoutms,$buffer)
$pingresultstatus=$pingresult.Status.ToString()
}
catch {
$pingresult="CatchError"
$pingresultstatus="Error"
}
switch ($pingresultstatus) {
"Error" {
Write-Host "E" -nonewline -backgroundcolor red
$result.error++
}
"Success" {
#switch ($pingresult.roundtriptime) {
# {$_ -le 5} {Write-Host "." -nonewline -backgroundcolor blue;break;}
# {$_ -le 10} {Write-Host "." -nonewline -backgroundcolor green -foregroundcolor black;break;}
# {$_ -le 50} {Write-Host "." -nonewline -backgroundcolor yellow -foregroundcolor black;break;}
# {$_ -le 100} {Write-Host "." -nonewline -backgroundcolor magenta ;break;}
# default {Write-Host "." -nonewline -backgroundcolor red}
#}
$lastmaxms = [math]::Max($lastmaxms,$pingresult.roundtriptime)
if ($result.total%$backspacecount -eq 0) {
switch ($lastmaxms) {
{$_ -le 5} {Write-Host "." -nonewline -backgroundcolor blue;break;}
{$_ -le 10} {Write-Host "." -nonewline -backgroundcolor green -foregroundcolor black;break;}
{$_ -le 50} {Write-Host "." -nonewline -backgroundcolor yellow -foregroundcolor black;break;}
{$_ -le 100} {Write-Host "." -nonewline -backgroundcolor magenta ;break;}
default {Write-Host "." -nonewline -backgroundcolor red}
}
[long]$lastmaxms = 0
}
if ($pingresult.roundtriptime -le $result.min) {$result.min = $pingresult.roundtriptime}
if ($pingresult.roundtriptime -ge $result.max) {$result.max = $pingresult.roundtriptime}
if ($pingresult.roundtriptime -gt$slowlimitms ) {$result.toslow++}
$Avgsum+=$pingresult.roundtriptime
#$result.ttl = $pingresult.options.ttl
}
"TimedOut" {
Write-Host "T" -nonewline -backgroundcolor red
$result.loss++
}
"TtlExpired" {
Write-Host "X" -nonewline -backgroundcolor red
$result.loss++
}
"DestinationPortUnreachable" {
Write-Host "U" -nonewline -backgroundcolor red
$result.loss++
}
"DestinationHostUnreachable" {
Write-Host "U" -nonewline -backgroundcolor red
$result.loss++
}
"DestinationNetworkUnreachable" {
Write-Host "N" -nonewline -backgroundcolor red
$result.loss++
}
"PacketTooBig" {
Write-Host "S" -nonewline -backgroundcolor red
$result.loss++
}
default {
Write-Host "?" -nonewline
Write-Host $pingresult.Status
$result.loss++
}
}
if ($reportwatch.Elapsed.TotalSeconds -gt $reporttimes) {
$reportwatch.restart() # reset Time for next report
# calculating Average
if (($result.total-$result.loss) -ne 0) {
$result.avg = [long]($Avgsum/($result.total-$result.loss))
}
[long]$Avgsum = 0
Write-Host "$($result.min)" -NoNewline
write-host "/" -nonewline
if ($result.avg -gt (($result.min+1)*10)) { #ignore 0 as min
write-host "$($result.avg)" -foregroundcolor yellow -nonewline
$result.statusavg2min="1"
}
else {
write-host "$($result.avg)" -nonewline
}
write-host "/" -nonewline
if (($result.max) -gt (($result.avg+1)*10)) {
write-host "$($result.max)" -foregroundcolor yellow -NoNewline
#write-warning "Max 10 Times higher than Avg"
$result.statusmax2avg="1"
}
else {
write-host "$($result.max)" -NoNewline
}
write-host "/" -nonewline
write-host "$($result.total)" -NoNewline
write-host "/" -nonewline
if (($result.loss*10) -gt $result.total) {
write-host "$($result.loss)" -BackgroundColor Yellow -ForegroundColor black -NoNewline
$result.statuspacketloss="1"
}
else {
write-host "$($result.loss)" -NoNewline
}
#write-host "/" -nonewline
#write-host "$($result.ttl/$result.total)" -NoNewline
if ($csvfilename -ne "") {
"$($result.timestamp),$($result.RemoteIP),$($result.min),$($result.avg),$($result.max),$($result.total),$($result.loss),$($result.statuspacketloss),$($result.statusavg2min),$($result.statusmax2avg)" | out-file $csvfilename -append
}
if ($sendtopipeline) {
Write-Verbose "Sending result to Pipeline"
$result
}
#region Send data to PRTG if enabled
if ($env:cPRTGPUSHURL) {
write-verbose "SendTo-PRTG: Post Result to PRTGProbe $($env:prtgpushurl)"
Write-host "Pipe" -Nonewline
$Scriptname = (split-path $MyInvocation.ScriptName -Leaf).replace(".ps1","")
$prtgpushurl = "$($env:PRTGPUSHURL)/$($scriptname)_$($env:COMPUTERNAME)"
}
if ($prtgpushurl -ne "") {
write-verbose "end2end-ping: Build PRTG XML"
$prtgresult = ""
if ($result.total -ge 1) {
$prtgresult+= ""
$prtgresult+= " "
$prtgresult+= " Max"
$prtgresult+= " $($result.max)"
$prtgresult+= " Custom"
$prtgresult+= " ms"
$prtgresult+= " 0"
$prtgresult+= " "
$prtgresult+= " "
$prtgresult+= " Min"
$prtgresult+= " $($result.main)"
$prtgresult+= " Custom"
$prtgresult+= " ms"
$prtgresult+= " 0"
$prtgresult+= " "
$prtgresult+= " "
$prtgresult+= " Avg"
$prtgresult+= " $($result.avg)"
$prtgresult+= " Custom"
$prtgresult+= " ms"
$prtgresult+= " 0"
$prtgresult+= " "
$prtgresult+= " "
$prtgresult+= " Packets total"
$prtgresult+= " $($result.Total)"
$prtgresult+= " Custom"
$prtgresult+= " Pakets"
$prtgresult+= " 0"
$prtgresult+= " "
$prtgresult+= " "
$prtgresult+= " Packets lost"
$prtgresult+= " $($result.loss)"
$prtgresult+= " Custom"
$prtgresult+= " Pakets"
$prtgresult+= " 0"
$prtgresult+= " "
$prtgresult+= " "
$prtgresult+= " Errors"
$prtgresult+= " $($result.error)"
$prtgresult+= " Custom"
$prtgresult+= " Count"
$prtgresult+= " 0"
$prtgresult+= " "
$prtgresult+= " "
$prtgresult+= " Status packetloss"
$prtgresult+= " $($result.statuspacketloss)"
$prtgresult+= " Custom"
$prtgresult+= " Status"
$prtgresult+= " 0"
$prtgresult+= " "
$prtgresult+= " "
$prtgresult+= " Status toslow"
$prtgresult+= " $($result.toslow)"
$prtgresult+= " Custom"
$prtgresult+= " Status"
$prtgresult+= " 0"
$prtgresult+= " "
$prtgresult+= " "
$prtgresult+= " Status avg2min"
$prtgresult+= " $($result.statusavg2min)"
$prtgresult+= " Custom"
$prtgresult+= " Status"
$prtgresult+= " 1"
$prtgresult+= " 0"
$prtgresult+= " "
$prtgresult+= " "
$prtgresult+= " Status max2avg"
$prtgresult+= " $($result.statusmax2avg)"
$prtgresult+= " Custom"
$prtgresult+= " Status"
$prtgresult+= " 1"
$prtgresult+= " 0"
$prtgresult+= " "
#$prtgresult+= " "
#$prtgresult+= " statusmax2avg"
#$prtgresult+= " $(128-$result.ttl)"
#$prtgresult+= " Custom"
#$prtgresult+= " TTL"
#$prtgresult+= " 1"
#$prtgresult+= " 0"
#$prtgresult+= " "
$prtgresult+= " 0"
$prtgresult+= " Reply got from host $($hostname) IP:$($remoteip)"
$prtgresult+= ""
}
else {
$prtgresult+= ""
$prtgresult+= " 1"
$prtgresult+= " Unabled to Connect to host $($hostname) IP:$($remoteip)"
$prtgresult+= ""
}
Write-Verbose "PRTG: $($prtgpushurl)"
try {
$Answer=Invoke-RestMethod `
-method "GET" `
-timeout 5 `
-URI ("$($prtgpushurl)?content=$($prtgresult)")
if ($answer."Matching Sensors" -eq "1") {
write-verbose "SendTo-PRTG:Found 1 Sensors OK"
Write-host "PRTG" -ForegroundColor green -nonewline
}
elseif ($answer."Matching Sensors" -eq "0") {
write-verbose "SendTo-PRTG:Found 0 matching sensors. Retry next run"
Write-host "PRTG" -ForegroundColor Yellow -nonewline
}
else {
write-Verbose "SendTo-PRTG:Invalid reply"
Write-host "PRTG" -ForegroundColor Magenta -nonewline
$answer
}
}
catch {
Write-verbose " Unable to Send PRTG-Data"
Write-host "PRTG" -ForegroundColor red -nonewline
Write-host "Exception: $($_.Exception.Message)"
}
}
#endregion Send data to PRTG if enabled
#Clear Result
$result.timestamp = get-date -Format u
$result.remoteip = $($remoteip)
$result.max=0
$result.min=9999999
$result.avg = 0
$result.total = 0
$result.loss = 0
$result.error = 0
$result.toslow = 0
#$result.ttl = 0
$result.statuspacketloss=0
$result.statusavg2min=0
$result.statusmax2avg=0
write-host ""
}
# Wait for next interval
$waittime = $pingidletimems - $pingrepeattimewatch.Elapsed.Milliseconds
if ($waittime -ge 0) {
start-sleep -milliseconds $waittime
}
}
}
else {
Write-warning "end2end-ping: Stop - no IP-Address found"
}
Write-Host "end2end-ping: End"