---
title: 'Reachability Tests'
source: 'https://academia.sh/en/courses/linux-network-administration/reachability-tests'
course: 'Linux Network Administration and Troubleshooting'
language: en
updated: '2026-08-17T18:10:00+00:00'
license: 'CC BY-SA 4.0'
---

# Reachability Tests

Reading echo and path-test tools correctly: no response from a remote address fits four separate candidate causes, three reachability tests plus a connection attempt together still cannot split two candidate pairs, and what splits them is reading the configuration.

The previous lesson measured the diagnosis order and showed that two tests in the layered
order's first steps looked weak: echo to the gateway eliminated only 2 candidates, echo to the
remote address 4. These tests are diagnosis's most commonly used tools, and their weakness is
not the real problem. The real problem is that they are **misread**.

The misreading runs in two directions. In the first direction, silence is assumed to say too
much: when no response arrives, the conclusion "the machine is down" or "there is no network" is
reached, even though more than one cause produces the same silence. In the second direction, a
response is assumed to say too much: when a response arrives, "reach is fine" is said, even
though the layer that responded is not the layer being reached. This lesson meets both
misreadings with numbers.

## The Echo Test's Two-Valued Response

An echo test sends a small request to an address and waits for a response in return. The mock
setup reduces this to a two-valued response: `response` or `no-response` (**SG6**). Latency,
loss rate, and response order are not measured in this lesson; what is measured is how many
candidate causes the silence **fits**.

There are two separate echo tests, and they look at different distances. **Gateway echo** asks
whether we can reach the local network at all; for a response, the interface being up and the
address being assigned is enough. **Remote echo** asks about the path all the way to the target;
for a response, the gateway also has to be correct and the route has to exist. The second test
requires everything the first one requires, plus two more conditions. This nesting directly
explains why the silence fits more than one cause.

Name resolution is kept out of these tests: echo is done with the address, not the name. An
echo done by name mixes two mechanisms into a single response and blurs the cause of the
silence by one more candidate. The protocol itself is the subject of the Computer Networks
curriculum and is not built here.

## Path Test: The Same Test Repeated at Increasing Distance

**Traceroute-style path testing** is the common name for tools that show the path to a target
along with its intermediate stops. What it does is repeat the same reachability question at
increasing distances: first one hop out, then two hops out. In the mock setup this corresponds
to running gateway echo and remote echo back to back (**SG7**); the intermediate stops'
individual behavior is not modeled separately.

A third reachability test is a **large-packet** attempt that is not allowed to fragment
(**SG8**). A path where small packets get through but large ones get lost looks sound to an
echo test. This split comes from echo sending a small request: the packet size a test uses
determines the fault it cannot see.

A fourth boundary needs to be written up front too: an echo response being deliberately withheld
is not in the candidate-cause set (**SG9**). A target can be configured not to respond to echo
at all, and in that case the silence corresponds to no fault on the list. This is the price of
the closed-world assumption: a cause outside the list gives the same response as a cause inside
it.

The most commonly misread part of path-test output is **an intermediate stop's silence**. No
response from a stop does not show that stop is broken; intermediate stops are not obligated to
respond to requests not addressed to them, and most treat it as low-priority work. The only
meaningful line in a path test is the **last** one: at what distance the path goes completely
silent. Empty lines in the middle eliminate no candidate, they only add to the line count. The
same mistake runs in reverse too: every stop responding while the target stays silent suggests
the path is sound and the fault sits at the target, but the return direction is a separate path,
and the outbound direction being sound does not prove the return is.

The mock setup's reduction to a two-valued response is not a convenience, it is a deliberate
boundary. A real echo test also says how many of three requests came back, and "3 lost out of
3" is not the same thing, diagnostically, as "1 lost out of 3": the first points to a
persistent fault, the second to an intermittent one. The mock setup does not cover this second
case; intermittent faults are the fourth lesson's subject. The point to hold onto here is this:
every reading that drops the loss rate and reduces the response to two values shows an
intermittent fault as either fully sound or fully broken.

```text
# example dump , has not been run
$ ping -c 3 <gateway>                    # one hop out
$ ping -c 3 <target-address>             # all the way to the target
$ tracepath <target-address>             # repetition at increasing distance
$ ping -c 3 -M do -s 1400 <target-address>  # attempt that disallows fragmentation
```

```python
HEALTHY = {"interface_up": True, "address_set": True, "gateway_correct": True,
           "route_present": True, "resolver_responds": True, "service_listening": True,
           "firewall_passes": True, "path_ok": True}
FAULT = {
    "interface-down":      {"interface_up": False, "address_set": False},
    "no-address":          {"address_set": False},
    "wrong-gateway":       {"gateway_correct": False},
    "missing-route":       {"route_present": False},
    "resolver-silent":     {"resolver_responds": False},
    "service-not-listening": {"service_listening": False},
    "firewall-dropping":   {"firewall_passes": False},
    "large-packet-lost":   {"path_ok": False},
}
CANDIDATES = tuple(FAULT)


def state(a=None):
    d = dict(HEALTHY)
    if a:
        d.update(FAULT[a])
    return d


def path(d):
    return (d["interface_up"] and d["address_set"]
            and d["gateway_correct"] and d["route_present"])


TEST = {                    # five of the common definition's eight used in this lesson
    "gateway-echo": lambda d: ("response" if d["interface_up"] and d["address_set"]
                               else "no-response"),
    "remote-echo": lambda d: "response" if path(d) else "no-response",
    "large-packet": lambda d: ("passes" if path(d) and d["path_ok"]
                               else "blocked"),
    "connection": lambda d: ("unreachable" if not path(d)
                             else "timeout" if not d["firewall_passes"]
                             else "refused" if not d["service_listening"]
                             else "established"),
    "route": lambda d: ("table-empty" if not d["address_set"]
                        else "complete" if d["route_present"] else "no-default"),
}
ECHO = ["gateway-echo", "remote-echo", "large-packet"]     # SG7: traceroute-style


def groups(kit, candidates=CANDIDATES):
    o = {}
    for a in candidates:
        o.setdefault(tuple(TEST[s](state(a)) for s in kit), []).append(a)
    return o


print("how many candidate causes a response fits")
for s in ECHO + ["connection"]:
    for response, fits in sorted(groups([s]).items()):
        print(f"  {s:14s} {response[0]:12s} {len(fits)}  {', '.join(sorted(fits))}")
print()
ORACLE = "wrong-gateway"
print("oracle:", ORACLE, "| tests applied in order")
remaining = CANDIDATES
for s in ECHO + ["connection", "route"]:
    response = TEST[s](state(ORACLE))
    new = tuple(a for a in remaining if TEST[s](state(a)) == response)
    print(f"  {s:14s} {response:12s} eliminated {len(remaining) - len(new)}  left {len(new)}"
          f"  {', '.join(sorted(new))}")
    remaining = new
print()
print("test kit                                       groups  unsplit  largest group")
for kit in (["gateway-echo"], ["remote-echo"], ["gateway-echo", "remote-echo"],
            ECHO, ["connection"], ECHO + ["connection"]):
    o = groups(kit)
    split = [v for v in o.values() if len(v) > 1]
    print(f"  {'+'.join(kit):45s} {len(o):4d} {len(split):12d}"
          f" {max(len(v) for v in o.values()):14d}")
print()
print("candidate set sweep: how many candidates 'remote-echo = no-response' fits")
kit = ["interface-down"]
for a in CANDIDATES:
    if a not in kit:
        kit.append(a)
        fits = [x for x in kit if TEST["remote-echo"](state(x)) == "no-response"]
        print(f"  candidate {len(kit)}  added {a:26s} fits {len(fits)}")
```

```
how many candidate causes a response fits
  gateway-echo   no-response  2  interface-down, no-address
  gateway-echo   response     6  firewall-dropping, large-packet-lost, missing-route, resolver-silent, service-not-listening, wrong-gateway
  remote-echo    no-response  4  interface-down, missing-route, no-address, wrong-gateway
  remote-echo    response     4  firewall-dropping, large-packet-lost, resolver-silent, service-not-listening
  large-packet   blocked      5  interface-down, large-packet-lost, missing-route, no-address, wrong-gateway
  large-packet   passes       3  firewall-dropping, resolver-silent, service-not-listening
  connection     established  2  large-packet-lost, resolver-silent
  connection     refused      1  service-not-listening
  connection     timeout      1  firewall-dropping
  connection     unreachable  4  interface-down, missing-route, no-address, wrong-gateway

oracle: wrong-gateway | tests applied in order
  gateway-echo   response     eliminated 2  left 6  firewall-dropping, large-packet-lost, missing-route, resolver-silent, service-not-listening, wrong-gateway
  remote-echo    no-response  eliminated 4  left 2  missing-route, wrong-gateway
  large-packet   blocked      eliminated 0  left 2  missing-route, wrong-gateway
  connection     unreachable  eliminated 0  left 2  missing-route, wrong-gateway
  route          complete     eliminated 1  left 1  wrong-gateway

test kit                                       groups  unsplit  largest group
  gateway-echo                                     2            2              6
  remote-echo                                      2            2              4
  gateway-echo+remote-echo                         3            3              4
  gateway-echo+remote-echo+large-packet            4            3              3
  connection                                       4            2              4
  gateway-echo+remote-echo+large-packet+connection    6            2              2

candidate set sweep: how many candidates 'remote-echo = no-response' fits
  candidate 2  added no-address                 fits 2
  candidate 3  added wrong-gateway              fits 3
  candidate 4  added missing-route              fits 4
  candidate 5  added resolver-silent            fits 4
  candidate 6  added service-not-listening      fits 4
  candidate 7  added firewall-dropping          fits 4
  candidate 8  added large-packet-lost          fits 4
```

## Oracle, Test, Eliminated Candidate

Three numbers sit side by side. **Oracle:** the real fault is `wrong-gateway`, that is, the
default gateway is set wrong. **Test:** remote echo returns `no-response`. **Eliminated
candidate:** this response eliminates **4** of eight candidates and leaves **4** behind.

The silence fitting four candidates is this lesson's first number. The sentence "no response
from the remote address" is on its own consistent with **all** of `interface-down`,
`no-address`, `wrong-gateway`, and `missing-route`. Reading the silence as "network's down"
lumps these four together as if they were one cause; fixing each of the four is different, and
picking the wrong fix does not work.

When gateway echo stays silent, the picture is narrower: only **2** candidates fit
(`interface-down`, `no-address`). A practical reading follows from this. **Nearby silence is
more informative than distant silence**, because it depends on fewer conditions. This is exactly
the path test's real function too: finding at what distance silence begins, to narrow the
number of candidates that fit.

## What a Response Does Not Say

The second misreading is sneakier. Echo returning a response eliminates four candidates but
leaves **4** behind: `resolver-silent`, `service-not-listening`, `firewall-dropping`, and
`large-packet-lost`. That is, the sentence "the machine is responding" does not mean the
service is reachable, and four separate faults are entirely consistent with that sentence.

What makes the difference is that silence and **being refused** are not the same thing. Two of
the connection test's four responses each pin down a single candidate: `refused` fits only
`service-not-listening`, `timeout` only `firewall-dropping`. Being refused is a response, and
the responding side also proves the path is sound; silence is ambiguous about the state of both
the path and the target. So what diagnosis looks for is a test that can **turn silence into a
response**.

The large-packet attempt splits off one of these four: in the `large-packet-lost` scenario, a
small echo gets through, a large packet does not. This is diagnosis's most commonly overlooked
signature, because it is the one place two tests contradict each other — one says sound, the
other broken, and both are correct.

The step-by-step table shows how this order runs. With the oracle at `wrong-gateway`, gateway
echo eliminates 2 candidates, remote echo eliminates 4; after that, the large-packet attempt
eliminates **0**, and so does the connection attempt. A test being powerful is not enough:
**re-eliminating an already-eliminated candidate is not elimination.** The connection attempt
eliminated seven candidates in the previous lesson; here it eliminates zero, because the tests
that ran before it had already made the same split.

## The Wall Reachability Tests Hit

The last table is the lesson's harshest result. Gateway echo alone splits the eight candidates
into 2 groups, and the largest group holds 6. Remote echo alone gives 2 groups, largest 4.
Together they produce 3 groups, and **all three groups hold more than one candidate**: these two
tests cannot pin down even a single candidate on their own.

Adding the large-packet attempt brings the group count to 4 and the largest group down to 3;
the number of unsplit groups is still **3**. Adding the connection attempt too produces 6
groups, and **2** unsplit pairs remain: `interface-down` with `no-address`, and `wrong-gateway`
with `missing-route`.

These two pairs stay unsplit no matter how many reachability tests are run. The reason is
structural: both members of each pair cut **the same path at the same place**, so every test
that looks at the path gives the same response. What splits them is not looking at the path, it
is **reading the configuration**. The table's last row does this: the routing-table test returns
`complete`, eliminates the last candidate between `wrong-gateway` and `missing-route`, and the
diagnosis drops to one candidate.

This is where the course's second claim shows its first face: **adding more tests does not add
elimination power.** Three reachability tests do not do what a single configuration reading
does. What is needed is not multiplying tests but **changing their kind**.

The reason these tools get used this much is hidden in the same table: they are cheap. An echo
test takes seconds, is a one-line command, changes nothing on the target, and its failure is
harmless. Cheapness makes a tool the first choice; **elimination power** is what makes it the
tool that finishes the diagnosis, and the two are not the same measure. The measurement counted
every test as one step (**SG5**), that is, decisions were counted, not seconds. Measured in
seconds, echo tests would look even more favorable, and it is exactly this appearance that
leads to dozens of repeated echo tests once diagnosis stalls. Repeating the same test eliminates
no new candidate; the information gained after the first run is fixed.

## Candidate Set Sweep

The sweep shows how much the number of candidates the silence fits depends on the set. As the
set grows from `interface-down` up to eight, the number of candidates fitting `remote-echo =
no-response` climbs 1, 2, 3, 4, and **stops at four**: the four candidates added after that sit
in an upper layer and produce a response to remote echo.

The table can also be read backward. Removing a candidate from the list is the same as reading
the rows upward: with `missing-route` removed, the candidates fitting the silence drop from 4
to 3; with `wrong-gateway` also removed, to 2. Removing always looks like a gain, because the
silence narrows; but if the removed candidate is the real fault, the diagnosis ends with zero
candidates, and this shows up nowhere in the table.

The result cuts both ways. If the candidate list is short, silence looks more informative — in a
three-candidate list, "no response" fits two candidates — but that information is bought at the
cost of causes left outside the list. As the candidate list grows, silence's discriminating
power **neither grows nor shrinks** past a certain point: as long as added candidates give a
different response, silence's meaning stays fixed. What determines a test's power is not the
candidate count, it is **which response the candidates fall into**.

## Summary

- No response from a remote address fits **4** separate candidate causes; no response from the
  gateway fits **2**. Nearby silence is more informative because it depends on fewer
  conditions.
- Echo returning a response also leaves **4** candidates standing; "the machine is responding"
  does not say the service is reachable.
- A path test is the same reachability question repeated at increasing distance; the
  large-packet attempt splits off the one fault a small echo cannot see.
- Three reachability tests together cannot pin down even a single candidate; adding the
  connection attempt leaves **2** unsplit pairs, and only reading the configuration splits
  them.
- Re-eliminating an already-eliminated candidate is not elimination: the connection attempt
  eliminates **0** candidates in this order; repeating the same test eliminates no new one
  either.
- Being refused is a response and pins down a single candidate; silence is ambiguous about both
  the path and the target.

## Next Step

When reachability tests hit a wall, the next step that comes to mind is usually the same one:
looking at the traffic itself. Packet capture does not show a summary of responses, it shows
every frame crossing the wire, and for that reason it is counted the "most detailed" tool. The
next lesson measures that expectation: it sets the number of lines a capture produces against
the number of candidate causes it eliminates, and compares elimination power per line against
the connection attempt's. That packet capture is a diagnosis tool, not a procedure for seizing
someone else's traffic, is also written there, along with its boundary.
