Something’s slow, or something’s down, and you need to know which link in the chain is the problem: your machine, your router, your ISP, or the other end entirely. Three tools cover almost every version of that question, and most people only really know one of them.
This is ping, traceroute and mtr the way I actually reach for them, in the order I reach for them, with the parts that trip people up called out specifically. Everything below ran on Ubuntu 24.04 and Red Hat Enterprise Linux 9.7, because the two disagree about which of these tools come preinstalled, and that alone wastes five minutes if you don’t know it going in.
Start with ping: is it even up
Ping answers one question. Is the host reachable, and how long does a round trip take.
ping -c4 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=56 time=4.05 ms
64 bytes from 1.1.1.1: icmp_seq=2 ttl=56 time=5.11 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=56 time=5.15 ms
64 bytes from 1.1.1.1: icmp_seq=4 ttl=56 time=5.80 ms
--- 1.1.1.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 4.049/4.941/5.799/0.634 ms
-c4 stops after four, which you always want; a bare ping runs until you Ctrl-C it. Four numbers matter in that summary. Packet loss should be 0%, and even 1 or 2% on a stable link is worth chasing before it gets worse. min/avg/max is the latency spread. And mdev, the last number, is roughly the jitter: how much the latency wobbles between packets. A mdev under 1ms on a wired connection is normal; 15ms of jitter on a call that keeps cutting out tells you exactly what to blame.
Two flags earn a permanent place in your muscle memory:
ping -c3 -i0.3 -W1 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=113 time=31.8 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=113 time=31.9 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=113 time=31.7 ms
--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 602ms
rtt min/avg/max/mdev = 31.661/31.798/31.889/0.098 ms
-i0.3 sends every 0.3 seconds instead of the default one-per-second, useful when you want a lot of samples fast. -W1 caps how long ping waits for a reply to 1 second, so a genuinely dead host doesn’t leave you staring at a hung terminal.
The ping trick nobody shows you: finding path MTU with -M do
This is the one that actually separates “I’ve used ping” from “I’ve used ping to fix something.” Large file transfers or VPN tunnels that hang or stall, while small requests work fine, are very often an MTU problem: something along the path can’t handle a full-size packet and is supposed to tell you so, but a misconfigured firewall drops the notification instead of forwarding it. Ping with -M do sets the don’t-fragment bit and lets you binary-search for the largest packet that actually gets through.
My local interface here has an MTU of 1472 (a Hyper-V virtual NIC quirk, most physical links are 1500), so a payload of 1444 bytes, plus the 28 bytes of ICMP and IP header, lands exactly on the limit:
ip link show eth0 | grep mtu
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1472 ...
ping -M do -s 1444 -c2 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 1444(1472) bytes of data.
1452 bytes from 1.1.1.1: icmp_seq=1 ttl=56 time=4.34 ms
1452 bytes from 1.1.1.1: icmp_seq=2 ttl=56 time=4.43 ms
--- 1.1.1.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
ping -M do -s 1445 -c2 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 1445(1473) bytes of data.
--- 1.1.1.1 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1006ms
One byte over the limit and it fails outright rather than fragmenting, because that’s the entire point of the don’t-fragment flag. In a real investigation you start high (1472) and step down until it succeeds, or start low and step up until it breaks; either way you’ve found the actual path MTU, and from there you know whether to fix the interface MTU, the tunnel’s MSS clamping, or the firewall that’s eating the “packet too big” ICMP message it should be forwarding.
traceroute: where does it actually break
Ping tells you the destination is unreachable. It doesn’t tell you where the path fails. That’s traceroute’s job: send packets with a rising TTL, one, two, three, and let each router along the way reply once its TTL hits zero, building the path one hop at a time.
First surprise, and it belongs early in this article because it wastes real time otherwise: traceroute isn’t installed by default on Ubuntu 24.04.
traceroute 1.1.1.1
bash: traceroute: command not found
sudo apt install traceroute
RHEL 9 ships it out of the box, along with tracepath, which doesn’t need root and does its own path-MTU discovery along the way (more on that below). Worth knowing which side of that line your box is on before you’re mid-incident.
Once it’s there, the default probe is UDP to high, normally-closed ports:
traceroute -n 1.1.1.1
traceroute to 1.1.1.1 (1.1.1.1), 30 hops max, 60 byte packets
1 172.21.0.1 0.595 ms 0.801 ms 0.559 ms
2 192.168.100.1 2.476 ms 2.467 ms 4.586 ms
3 10.15.234.230 7.603 ms 7.596 ms 7.588 ms
4 10.15.248.89 7.519 ms 7.512 ms 7.504 ms
5 10.15.248.177 23.035 ms 23.028 ms 7.484 ms
6 10.15.248.129 7.561 ms 7.010 ms 6.987 ms
7 192.168.146.98 6.736 ms 5.967 ms 4.939 ms
8 58.65.206.245 8.730 ms 5.711 ms 5.691 ms
9 1.1.1.1 5.746 ms 5.738 ms 5.731 ms
-n skips reverse DNS lookups, which is the difference between an instant result and thirty seconds of waiting on lookups that time out anyway. Three times per hop because each column is a separate probe; a wide spread between them at one hop (look at hop 5 above, 23ms next to 7ms) usually means that router is a little busy answering traceroute itself, not that the path is actually degraded there.
Switch the probe type when the default gives you nothing, which happens constantly against anything behind a real firewall:
# ICMP probes instead of UDP
sudo traceroute -I -n 1.1.1.1
# TCP SYN to a specific port -- the one most likely to get past a firewall,
# because it looks exactly like the real connection you're troubleshooting
sudo traceroute -T -p 443 -n 1.1.1.1
traceroute to 1.1.1.1 (1.1.1.1), 30 hops max, 60 byte packets
1 172.21.0.1 0.278 ms 0.230 ms 0.219 ms
2 * * *
3 * * *
4 * * *
5 * * *
6 * * *
7 * * *
8 * * *
9 1.1.1.1 5.612 ms 5.458 ms 5.590 ms
That run is a genuine, unedited example of the single most common false alarm in this whole toolkit. Seven hops of nothing but asterisks, and the destination still answers on the last line at a perfectly normal 5ms. Nothing broke. Those intermediate routers simply don’t reply to whatever probe reached them, either because they rate-limit ICMP time-exceeded messages (very common, and reasonable: replying to every traceroute in the world costs a router CPU) or because a firewall on that hop just drops what it doesn’t recognise without saying so. The path is fine. Read the last line, not the middle ones, when you’re just confirming reachability.
tracepath: traceroute without needing root
If you don’t have sudo, or you specifically want path MTU discovery without the ceremony of -M do and a manual binary search, tracepath does both at once and needs no privileges at all:
tracepath 1.1.1.1
1?: [LOCALHOST] pmtu 1472
1: _gateway 0.214ms
1: _gateway 0.465ms
2: 192.168.100.1 3.968ms
3: 10.15.234.230 3.765ms
4: 10.15.248.89 3.905ms
5: 10.15.248.177 70.379ms
6: 10.15.248.129 6.177ms
7: 192.168.146.98 4.709ms
8: 58.65.206.245 4.352ms
9: no reply
10: no reply
The pmtu 1472 on the first line is the path MTU it found, no manual probing needed. And this particular run shows the other common false alarm alongside the useful result: hops 9 and 10 came back “no reply” on this pass even though the same destination answered cleanly seconds earlier in the traceroute example above. A single lost probe on a public path is normal noise. It’s only a real problem if it repeats.
mtr: ping and traceroute, continuously, in one view
Here’s what traceroute can’t do: three probes per hop, once, is a snapshot. If a hop is flaky only sometimes, a single traceroute run can walk right past it. mtr fixes that by pinging every hop on the path continuously and keeping a running loss and latency stat for each one, which is why it’s the tool for “the connection drops randomly” rather than “the connection is down right now.”
Live mode is the default and it’s genuinely useful for watching a path in real time, but for anything you want to paste into a ticket, ask a teammate about, or run from a script, use report mode:
mtr -r -c 5 -n 1.1.1.1
Start: 2026-02-17T04:46:53+0500
HOST: my-machine Loss% Snt Last Avg Best Wrst StDev
1.|-- 172.21.0.1 0.0% 5 0.6 0.5 0.5 0.6 0.1
2.|-- 192.168.100.1 0.0% 5 2.1 2.3 2.1 2.5 0.2
3.|-- 10.15.234.230 0.0% 5 3.5 5.0 3.2 10.8 3.3
4.|-- 10.15.248.89 0.0% 5 3.5 3.4 2.9 3.7 0.3
5.|-- 10.15.248.177 0.0% 5 3.3 3.5 3.2 3.9 0.3
6.|-- 10.15.248.129 0.0% 5 3.6 5.1 3.6 7.4 1.6
7.|-- 192.168.146.98 0.0% 5 4.1 4.8 3.9 6.5 1.0
8.|-- 58.65.206.245 0.0% 5 5.0 6.9 3.9 15.0 4.6
9.|-- 1.1.1.1 0.0% 5 3.4 3.8 2.6 4.7 0.8
-r is report mode (run, print, exit, instead of the live full-screen display), -c5 is five cycles, -n skips DNS the same as traceroute. Two columns matter most: Loss% per hop, and StDev, which is the jitter at that specific hop rather than end to end. A hop with real, consistent loss (not the odd dropped probe, actual loss that holds across a run) is where you look first. High StDev at one hop with everything downstream inheriting a similar wobble usually means congestion or a QoS policy right there, not a broken link.
Like traceroute, mtr defaults to ICMP and you can switch probe types, and it’s worth doing so for the same reason: to see the path the way your actual application traffic sees it.
sudo mtr -r -c 5 -n -T -P 443 1.1.1.1
Start: 2026-02-17T04:47:03+0500
HOST: my-machine Loss% Snt Last Avg Best Wrst StDev
1.|-- 172.21.0.1 0.0% 5 0.6 0.6 0.6 0.8 0.1
2.|-- 192.168.100.1 0.0% 5 2.4 2.9 2.2 4.4 0.9
3.|-- ??? 100.0 5 0.0 0.0 0.0 0.0 0.0
4.|-- ??? 100.0 5 0.0 0.0 0.0 0.0 0.0
5.|-- ??? 100.0 5 0.0 0.0 0.0 0.0 0.0
6.|-- ??? 100.0 5 0.0 0.0 0.0 0.0 0.0
7.|-- ??? 100.0 5 0.0 0.0 0.0 0.0 0.0
8.|-- ??? 100.0 5 0.0 0.0 0.0 0.0 0.0
9.|-- 1.1.1.1 0.0% 5 3.8 4.2 3.6 5.4 0.7
Same story as the TCP traceroute earlier, and I’ve left it in specifically because it’s the output people panic over: six hops sitting at a flat 100.0% loss, and the destination on the last line at 0.0% loss and perfectly normal latency. This is not a six-hop outage. Read mtr’s Loss% column for any hop that isn’t the last one as “did this router bother to reply to my probe,” not “did traffic get through here.” Traffic gets through every one of those routers just fine, headed to hop 9; it’s their replies back to you, specifically, that aren’t showing up, usually rate-limiting again. The number that tells you whether the destination is actually having a bad day is the loss percentage on the final hop, full stop.
The RHEL detail that costs people an hour: unprivileged ping
ICMP sockets normally need root, and modern distros work around that with a kernel setting, net.ipv4.ping_group_range, that lets ordinary users send pings without sudo. Ubuntu ships this wide open. This particular RHEL 9.7 box does not:
ping -c2 1.1.1.1
ping: socket: Operation not permitted
sysctl net.ipv4.ping_group_range
net.ipv4.ping_group_range = 1 0
A range of 1 0 (start greater than end) means no group is allowed unprivileged pings at all, and it’s a setting hardened images and some container base images ship with intentionally. If you see “Operation not permitted” from plain ping on RHEL or in a container and sudo instantly fixes it, this is why, not a broken installation. To allow it properly for a group instead of reaching for sudo every time:
sudo sysctl -w net.ipv4.ping_group_range="0 2147483647"
# and to make it survive a reboot:
echo 'net.ipv4.ping_group_range = 0 2147483647' | sudo tee /etc/sysctl.d/99-ping.conf
mtr needs raw sockets the same way ping does, so on a box configured like this one, mtr needs sudo too:
sudo mtr -r -c 5 -n 1.1.1.1
Start: 2026-02-17T04:50:24+0500
HOST: my-machine Loss% Snt Last Avg Best Wrst StDev
1.|-- 172.21.0.1 0.0% 5 0.4 0.6 0.4 0.9 0.2
2.|-- 192.168.100.1 0.0% 5 2.7 2.5 2.2 2.7 0.2
3.|-- 10.15.234.230 0.0% 5 3.3 3.8 3.3 4.1 0.4
4.|-- 10.15.248.89 0.0% 5 4.2 4.2 3.6 5.8 0.9
5.|-- 10.15.248.177 0.0% 5 3.4 3.6 3.2 4.0 0.4
6.|-- 10.15.248.129 0.0% 5 3.6 4.1 3.6 4.5 0.4
7.|-- 192.168.146.98 0.0% 5 3.9 4.4 3.9 5.0 0.4
8.|-- 58.65.206.245 0.0% 5 6.3 5.1 4.2 6.3 0.8
9.|-- 1.1.1.1 0.0% 5 3.6 3.8 3.6 4.2 0.2
And mtr wasn’t installed on this RHEL box by default either, while traceroute and tracepath both were. The three tools have three different default-install stories across the two distros, and knowing that upfront saves a genuinely annoying five minutes mid-incident:
| Tool | Ubuntu 24.04 | RHEL 9 |
|---|---|---|
| ping | preinstalled | preinstalled |
| traceroute | apt install traceroute |
preinstalled |
| tracepath | preinstalled (part of iputils) | preinstalled (part of iputils) |
| mtr | apt install mtr-tiny |
dnf install mtr |
Putting them together: a real workflow
In order, this is genuinely how I work through “the connection to X is bad”:
- ping the destination. Reachable at all? What’s the baseline latency and is there any loss right now.
- ping the default gateway.
ip route | grep defaultto find it. If the gateway itself shows loss or high latency, the problem is your local network or your own machine, and everything past that point is noise. Check this before anything fancier. - traceroute to see the path, with
-nso it’s fast. Ignore asterisks in the middle unless the final hop also fails to answer; that’s the tell for a real break versus routers that just don’t reply to traceroute. - If the destination is unreachable but the second-to-last real hop answered fine, the problem sits between those two hops or at the destination itself, not in the network in between.
- If it’s intermittent rather than dead, switch to mtr in report mode and let it run for a few minutes (
-c 100or so) rather than the default handful of cycles. Occasional loss on one hop that doesn’t carry through to the hops after it is that router being busy, and it’s normal. Loss that starts at one hop and holds at every hop after it is a real problem starting there. - If everything above is clean but a specific service still hangs or times out while ping to the same host works fine, stop chasing the path and go looking at path MTU (
ping -M do) and the actual port withtraceroute -T -p <port>ormtr -T -P <port>, since ICMP reachability and a specific TCP port being reachable are two different questions with two different answers.
If step 3 or 4 points at something that resolves to the wrong place entirely rather than a hop that’s slow or missing, that’s usually not a routing problem at all, it’s DNS, and a BIND setup or dig is the next tool, not another traceroute. And once you’ve found a live host worth a closer look, ss and netstat pick up from there on what’s actually listening locally, and nmap for what’s reachable on the other end. If the addressing itself is what’s confusing (is this host even on the same subnet as me), IP addressing and subnetting is worth a read alongside this one.
Frequently asked questions
Why does traceroute show * * * in the middle but the destination still responds?
Nearly always ICMP rate-limiting or a firewall on those specific hops that drops what it doesn’t recognise. The path is working; those routers just aren’t replying to your probe. Trust the final hop.
Do I need root for ping, traceroute and mtr?
Plain ICMP ping usually doesn’t, thanks to net.ipv4.ping_group_range, though some hardened systems (this RHEL box included) disable that by default and need sudo or a sysctl change. traceroute’s default UDP mode doesn’t need root; ICMP and TCP modes (-I, -T) do. mtr needs raw sockets the same way ping does, so if plain ping needs sudo on your box, mtr will too.
What’s the difference between traceroute and mtr?
traceroute is a single pass: three probes per hop, once, then it’s done. mtr repeats continuously and keeps a running loss and latency average per hop, which is what actually catches intermittent problems a single traceroute run can miss entirely.
Why does mtr show 100% loss on hops that aren’t the problem?
Same cause as the traceroute asterisks: those routers aren’t answering your probes, but they’re still forwarding your actual traffic. Only sustained loss that appears at one hop and continues through every hop after it points at a real problem.
How do I check for an MTU problem specifically?
ping -M do -s <size>, working down from your interface’s MTU minus 28 bytes until it stops failing, or use tracepath, which finds the path MTU for you automatically and needs no special privileges.
Should I use UDP, ICMP or TCP probes?
Start with the default. Switch to TCP on the port you actually care about (-T -p 443 for HTTPS, for instance) when the default gives you nothing, since that’s the probe most likely to follow the exact path and firewall rules your real traffic does.