For some reason I wanted to write a trace route script in PowerShell, but decided to run it through Google first to see if it had been done. Surprisingly few results came up, and as far as I can tell, only one true PowerShell script doing Trace Route without just being a wrapper for tracert or something similar: https://snoj.us/75/traceroute-with-starting-ttls/
Naturally I wanted to tweak it and give it my personal touch, and here is my result:
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
function Trace-Route { | |
<# | |
.SYNOPSIS | |
Trace the route between source computer and a target machine. | |
.DESCRIPTION | |
Trace the route between source computer and a target machine. | |
.EXAMPLE | |
Trace-Route Computer01 | |
Perform trace route to Computer01 | |
.EXAMPLE | |
Trace-Route -Target http://www.microsoft.com -ResolveHostname | |
Perform trace route to http://www.microsoft.com and try to resolve hostname for each hop. | |
Note! This will slow down the function somewhat. | |
.NOTES | |
Author: Øyvind Kallstad | |
Date: 28.10.2014 | |
Version: 1.1 | |
#> | |
[CmdletBinding()] | |
param( | |
# Hostname or IP to trace to. | |
[Parameter(Position = 0, ValueFromPipeline = $true)] | |
[string] $Target = $env:COMPUTERNAME, | |
# Set starting hop. | |
[Parameter()] | |
[int] $BeginHop = 1, | |
# Set maximum number of hops. | |
[Parameter()] | |
[int] $MaxHops = 30, | |
# Define timeout in milliseconds. | |
[Parameter()] | |
[int] $Timeout = 1000, | |
# Try to resolve hostname for IP in each hop. | |
[Parameter()] | |
[switch] $ResolveHostname = $false | |
) | |
# verify that we can reach target system | |
try{ | |
$ping = New-Object System.Net.NetworkInformation.Ping | |
$pingResult = $ping.Send($Target) | |
if(-not($pingResult.Status -eq 'Success')){ | |
Write-Warning "Unable to resolve target system $Target" | |
exit | |
} | |
} | |
catch{ | |
Write-Warning "Unable to resolve target system $Target" | |
exit | |
} | |
# define some data to send | |
$sendBytes = @([byte][char]'a'..[byte][char]'z') | |
for($i = $BeginHop; $i -lt $MaxHops; $i++) { | |
# define ping options; set start hop and fragmentation to true | |
$pingOptions = new-object System.Net.NetworkInformation.PingOptions $i, $true | |
# perform ping | |
$pingReply = $ping.Send($Target, $Timeout, $sendBytes, $pingOptions) | |
# get ip for current hop if possible | |
if($pingReply.Address -ne $null){ | |
$ip = $pingReply.Address | |
} | |
else{ | |
$ip = '*' | |
} | |
# get roundtrip time | |
$roundtripTime = $pingReply.RoundtripTime | |
# get status | |
$hopStatus = $pingReply.Status | |
# resolve hostname | |
if ($ResolveHostname) { | |
try{ | |
$resolvedHostname = "[$(([System.Net.Dns]::GetHostEntry($ip)).HostName)]" | |
} | |
catch{ | |
$resolvedHostname = '' | |
} | |
} | |
# create custom object and send to pipeline | |
Write-Output ([PSCustomObject] [Ordered] @{ | |
Hop = $i.ToString() | |
IP = "$($ip) $($resolvedHostname)" | |
Status = $hopStatus | |
RoundtripTime = $roundtripTime | |
}) | |
# clean up | |
Remove-Variable ip, roundtripTime, hopStatus, resolvedHostname –ErrorAction 'SilentlyContinue' | |
# stop loop if current ip matches the target ip | |
if($pingReply.Address -eq $pingResult.Address){break} | |
} | |
} |