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 wantLinuxWindows
Basic tracetraceroute <host>tracert <host>
Trace as structured datan/a (parse text, or use -j JSON output on newer traceroute builds)Test-NetConnection -ComputerName <host> -TraceRoute
TCP-mode tracetraceroute -T -p <port> <host>no built-in equivalent
Live continuous trace+pingmtr <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.