Same Job, Different Shell Part 2: Path Tracing
Part 1 covered reachability. This one covers the next question you ask once “is it up” comes back no or slow: which hops did the packet actually cross, and where did it stop.
The core tool: traceroute vs tracert
Both send probes with increasing TTL and record who sends back the ICMP “time exceeded” at each hop. The default probe type is where Linux and Windows disagree.
Linux defaults to UDP probes on unused high ports:
$ traceroute google.com
traceroute to google.com (74.125.132.113), 30 hops max, 60 byte packets
1 gateway (192.0.2.1) 0.311 ms 0.269 ms 0.262 ms
2 21.4.0.71 (21.4.0.71) 0.257 ms 0.252 ms 0.246 ms
3 * * *
4 * * *
5 * * *
Windows tracert defaults to ICMP echo, not UDP:
C:\> tracert google.com
Tracing route to google.com [142.250.152.113]
over a maximum of 30 hops:
That default difference is the whole reason the same destination can produce a different-looking route from the two OSes even on an identical network path. It’s not that one tool is smarter. They’re literally sending different protocols, and any firewall along the path that treats UDP, ICMP, and TCP differently (which is most of them) will let one through and drop another.
Real finding: switching the probe protocol changed the answer entirely
I hit this directly while writing this post. Default traceroute to google.com from this sandbox died after hop 2, all stars, exactly like the output above. Running the same trace with a TCP SYN probe on port 443 instead told a completely different story:
$ traceroute -T -p 443 google.com
traceroute to google.com (173.194.194.102), 30 hops max, 60 byte packets
1 gateway (192.0.2.1) 1.075 ms 4.054 ms 4.125 ms
2 iv-in-f102.1e100.net (173.194.194.102) 4.064 ms 6.849 ms 6.792 ms
Two hops, done, destination reached. Same box, same destination, same minute. The UDP-probe trace looked like a dead path past hop 2; the TCP-probe trace showed the destination replying immediately. The network wasn’t broken. UDP traceroute probes (and the ICMP time-exceeded replies they rely on) were being dropped somewhere past hop 2, while an outbound TCP SYN to port 443 sailed straight through, because that’s exactly the traffic the path is built to carry.
The lesson generalizes past this sandbox: if a UDP or ICMP-based traceroute dies partway and the service itself is reachable (a working curl or browser session says so), don’t conclude the network is broken. Switch the probe protocol before you escalate.
# TCP SYN traceroute (needs root on Linux; works through more firewalls than UDP/ICMP)
$ sudo traceroute -T -p 443 <host>
# ICMP traceroute explicitly, if you want to compare against tracert's default
$ sudo traceroute -I <host>
Windows: three tools, three probe strategies
Windows actually gives you more probe-protocol control than the classic tracert alone suggests, once you bring Test-NetConnection into it.
# classic tracert: ICMP, same as it's always been
PS> tracert google.com
# Test-NetConnection with -TraceRoute: also ICMP-based, but returns
# a structured TraceRoute array instead of console text
PS> Test-NetConnection -ComputerName google.com -TraceRoute -InformationLevel Detailed
Unlike Test-Connection from Part 1, this one is verified identical on both Windows PowerShell 5.1 and PowerShell 7.6.3, same fields, same behavior. Test-NetConnection lives in the Windows-only NetTCPIP module rather than PowerShell’s cross-platform core, so it never went through the rewrite that split Test-Connection in two. Here’s real, live output (home network addressing swapped for the standard placeholder ranges; the Google-side hops are real):
ComputerName : google.com
RemoteAddress : 142.250.129.102
NameResolutionResults : 142.250.129.102
142.250.129.101
142.250.129.139
142.250.129.113
142.250.129.100
142.250.129.138
InterfaceAlias : Wi-Fi
SourceAddress : 192.168.1.50
NetRoute (NextHop) : 192.168.1.1
PingSucceeded : True
PingReplyDetails (RTT) : 16 ms
TraceRoute : 192.168.1.1
192.168.2.1
203.0.113.1
0.0.0.0
0.0.0.0
74.125.253.31
142.250.58.18
0.0.0.0
0.0.0.0
0.0.0.0
0.0.0.0
0.0.0.0
0.0.0.0
142.250.129.102
Two things worth reading out of that. First, 0.0.0.0 in the TraceRoute array is Windows’ way of representing a hop that didn’t respond, the direct equivalent of Linux traceroute’s * * * for that hop. Second, the first two entries here are both private addresses before a public one shows up, that’s a double-NAT layout: a home router (192.168.1.1) sitting behind a second layer of private addressing (192.168.2.1) before the connection ever reaches a public IP. That second hop is either your own router-behind-router setup, or increasingly common, your ISP doing carrier-grade NAT (CGNAT) on their side before your traffic reaches the real internet. Either way, seeing two private hops in a row in a home trace isn’t a misconfiguration to chase, it’s normal on plenty of modern connections; TraceRoute’s array is exactly how you’d notice it’s happening in the first place.
Because Test-NetConnection’s trace result is a real array property, you can act on it programmatically, filter for specific hops, count them, or diff two traces run minutes apart, in ways tracert’s scrolling text output was never built for. Neither tracert nor Test-NetConnection -TraceRoute will do a TCP-mode trace, though. If a Windows box needs the TCP-probe workaround from above, psping (Sysinternals) or a third-party tool is the practical route; there’s no built-in equivalent to traceroute -T.
mtr: traceroute that doesn’t stop
mtr (My TraceRoute) combines traceroute and continuous ping into one live view, updating every hop’s loss and latency stats in real time instead of giving you a single-pass snapshot. Windows has no built-in equivalent; the closest is running Test-NetConnection -TraceRoute in a loop yourself, which gets you the data but none of the live per-hop statistics.
$ mtr --report --report-cycles 5 --tcp --port 443 google.com
Start: 2026-08-27T11:08:51+0000
HOST: vm Loss% Snt Last Avg Best Wrst StDev
1.|-- 192.0.2.1 0.0% 5 0.4 0.4 0.3 0.5 0.1
2.|-- iv-in-f113.1e100.net 0.0% 5 0.5 0.5 0.3 0.6 0.1
--tcp --port 443 here does the same protocol switch as traceroute -T -p 443 above, and for the same reason, this sandbox’s path answers TCP probes but drops the UDP/ICMP kind:
$ mtr --report --report-cycles 3 google.com
Start: 2026-08-27T11:09:01+0000
HOST: vm Loss% Snt Last Avg Best Wrst StDev
1.|-- 192.0.2.1 0.0% 3 0.4 0.4 0.3 0.6 0.1
2.|-- 21.4.0.71 0.0% 3 0.5 0.5 0.4 0.5 0.1
3.|-- ??? 100.0 3 0.0 0.0 0.0 0.0 0.0
--report runs a fixed number of cycles and prints a summary instead of the live full-screen display, which is what makes it usable in a script or a blog post. Drop --report for the interactive, continuously-updating table when you’re watching a flaky link live, which is mtr’s actual home turf and the reason it beats running traceroute in a loop by hand.
Quick reference
| What you want | Linux | Windows |
|---|---|---|
| Basic trace | traceroute <host> | tracert <host> |
| Trace as structured data | n/a (parse text, or use -j JSON output on newer traceroute builds) | Test-NetConnection -ComputerName <host> -TraceRoute |
| TCP-mode trace | traceroute -T -p <port> <host> | no built-in equivalent |
| Live continuous trace+ping | mtr <host> | no built-in equivalent |
| Set max hops | -m 15 | -Hops 15 |
What’s next
Part 3 moves from “where does the packet go” to “what does this box think its own address is”: ip addr/ifconfig against ipconfig/Get-NetIPAddress/Get-NetAdapter.
Cross-references: for the mechanics behind why a fragmented or oversized packet dies mid-path rather than at the destination, see Finding the Hop That’s Eating Your Packets: pmtud-sweeper, which builds a per-hop MTU discovery tool on top of exactly this traceroute-style probing.