The Router: Forwarding vs Routing
A packet leaves your laptop bound for a server on the other side of the planet. Nothing on your laptop
knows the whole route — it simply hands the packet to the first router it can reach and trusts the rest
of the network to sort it out. That packet will now pass through perhaps fifteen or twenty
routers, and at each one, in a few microseconds, a decision is made: which of
my outgoing links moves this packet one hop closer to its destination? Get that decision right, billions
of times a second, and the Internet works. Get it wrong and packets loop, pile up, or vanish.
This is the job of the network layer: host-to-host delivery of datagrams. The
transport layer
worries about which program gets the bytes; the network layer's single obsession is getting a
packet from this machine to that machine across a mesh of intermediate routers. And the
deepest idea on this page is that a router does two utterly different jobs that beginners
constantly merge into one — forwarding and routing. Separating them
cleanly is the key that unlocks everything else in the network layer.
Two speeds, two questions: the data plane and the control plane
Imagine a giant postal sorting office. On the floor, conveyor belts fling parcels into chutes: a worker
glances at each address label, reads a chart pinned to the wall, and drops the parcel down the matching
chute. That happens thousands of times a minute — fast, local, mechanical. Meanwhile, up in an office,
planners spend the afternoon redrawing that wall chart — deciding, given today's road closures
and truck schedules, which chute should feed which onward depot. Two activities, wildly different
timescales, and they meet only at the chart on the wall.
A router is exactly this. Its two jobs are:
-
Forwarding (the data plane) — the fast, local, per-packet action. A packet
arrives; the router reads its destination address, looks it up in the forwarding table,
and shoves the packet out the matching output port. This is the parcel worker reading the chart. It
must be brutally fast — line rate, often done in hardware — because it happens once per packet, and a
busy router sees hundreds of millions of packets a second. Forwarding never computes a route;
it only looks one up.
-
Routing (the control plane) — the slower, global, network-wide process that
computes the forwarding tables in the first place. Routers run
routing algorithms
among themselves, exchanging information about links and costs, and from that build the table each
router will consult. This is the planners redrawing the chart. It runs on a timescale of seconds to
minutes, not microseconds.
They meet at exactly one artefact: the forwarding table. Routing writes it;
forwarding reads it. This is the single most important sentence on the page — say it twice.
-
Forwarding is a local, per-packet action inside one router: move a
packet from an input port to the correct output port using the forwarding table. Timescale:
nanoseconds–microseconds.
-
Routing is a network-wide process across many routers: run
algorithms to compute what the forwarding tables should contain. Timescale: seconds–minutes.
-
They are coupled only through the forwarding table — routing produces it, forwarding consumes it.
Inside the box: the anatomy of a router
Open a real router and you find four functional pieces. Bytes flow left to right: in through an input
port, across a switching fabric, out through an output port — with queues buffering the crush wherever
packets arrive faster than they can leave.
-
Input ports terminate an incoming link and, crucially, perform the
forwarding-table lookup right there — deciding, at line rate, which output port each packet is
destined for. Doing the lookup at the input (rather than a central brain) is what lets a router keep up
with many high-speed links at once.
-
The switching fabric is the router's internal expressway — a crossbar, shared bus, or
interconnection network — that physically carries a packet from its input port to its chosen output
port.
-
Output ports buffer packets and transmit them onto the outgoing link.
-
Queues are the buffers that soak up bursts. When two input ports both want the same
output port at once, someone must wait — so packets queue. If a queue fills, the router
drops packets (this loss is the signal that
packet switching and TCP
congestion control are built around). Queuing is not a flaw; it is the shock absorber of a statistically
multiplexed network.
The lookup itself: longest-prefix match
Now zoom into that microsecond-long lookup. A forwarding table does not hold one row per possible
destination address — there are over four billion IPv4 addresses; no table could list them. Instead it
holds rows for address prefixes (blocks of addresses,
CIDR-style),
each pointing at an output link:
| Prefix | Output link |
| 200.23.16.0/20 | 0 |
| 200.23.16.0/23 | 1 |
| 200.23.24.0/24 | 2 |
| (default) 0.0.0.0/0 | 3 |
A destination address may match several prefixes at once. The rule that breaks the tie is
longest-prefix match: choose the matching prefix with the most leading bits
specified — the most specific route. An address in 200.23.16.0/23 also sits inside
200.23.16.0/20, but the /23 row is more specific, so it wins. The all-zeros
/0 default route matches everything and therefore only wins when nothing else does — it is
the fallback. Longest-prefix match is what makes hierarchical addressing and
route aggregation possible: a distant router can carry one coarse /20 entry for a
whole region while a nearby router carries the fine-grained exceptions.
The demo below is a forwarding-table lookup. It stores each prefix as a (network, mask-length)
pair, tests a destination against every row by masking off the host bits, and keeps the match with the
longest prefix. This is precisely the decision a router's input port makes, millions of times a second —
here in a few lines of pure bit arithmetic.
// A tiny forwarding table: [prefix base, prefix length, output link].
// We store the base as a 32-bit integer for clean bit masking.
function ip(a: number, b: number, c: number, d: number): number {
// >>> 0 forces an UNSIGNED 32-bit result (JS bit ops are signed).
return ((a << 24) | (b << 16) | (c << 8) | d) >>> 0;
}
function show(n: number): string {
return [n >>> 24, (n >>> 16) & 255, (n >>> 8) & 255, n & 255].join(".");
}
const table = [
{ base: ip(200, 23, 16, 0), len: 20, link: 0 },
{ base: ip(200, 23, 16, 0), len: 23, link: 1 },
{ base: ip(200, 23, 24, 0), len: 24, link: 2 },
{ base: ip(0, 0, 0, 0), len: 0, link: 3 }, // default route
];
// Longest-prefix match: keep the matching row with the MOST prefix bits.
function forward(dest: number): { link: number; len: number } {
let best = { link: -1, len: -1 };
for (const row of table) {
// Build a mask with `len` leading 1s. len === 0 -> mask 0 (matches all).
const mask = row.len === 0 ? 0 : (0xffffffff << (32 - row.len)) >>> 0;
if (((dest ^ row.base) & mask) === 0 && row.len > best.len) {
best = { link: row.link, len: row.len };
}
}
return best;
}
for (const d of [ip(200, 23, 16, 8), ip(200, 23, 24, 5), ip(200, 23, 20, 1), ip(8, 8, 8, 8)]) {
const r = forward(d);
console.log(`${show(d).padEnd(15)} -> link ${r.link} (matched /${r.len})`);
}
Read the output carefully: 200.23.16.8 matches both the /20 and the
/23, and the /23 wins by being longer; 200.23.20.1 falls outside
the /23 so the coarser /20 catches it; and 8.8.8.8 matches nothing
specific and drops through to the default route. That is longest-prefix match, live.
What the router reads: the IP datagram header
The address the router matches on lives in the packet's IP header — a compact block of
fields prepended to every datagram. You do not need every bit, but a handful matter enormously for how
forwarding behaves:
-
Source and destination IP addresses — the destination is what longest-prefix match
keys on. (Note: ordinary forwarding ignores the source — a packet is routed purely on where
it is going.)
-
TTL (time to live) — an 8-bit counter that every router decrements by one. If
it hits zero, the router discards the packet and sends back an error. TTL is the
Internet's seatbelt against routing loops: a packet caught circling forever is guaranteed to die within
255 hops instead of clogging the network eternally. (The
traceroute tool is a beautiful
hack on exactly this: send packets with TTL 1, 2, 3, … and collect the "TTL expired" complaints to map
out every router on the path.)
-
Protocol — an 8-bit number naming what's inside the payload (6 = TCP,
17 = UDP, 1 = ICMP). This is how the destination host demultiplexes the datagram up to the right
transport protocol — the network-layer echo of a port number.
-
Fragmentation fields (identification, flags, fragment offset) — if a datagram is too
big for a link's MTU (maximum transmission unit), it can be sliced into fragments that
are reassembled at the destination. These fields track which original datagram each fragment belongs to
and where it fits. (In IPv6, as you'll see, this job is pushed to the sender and the fields vanish.)
-
Header checksum — a lightweight integrity check over the header only. Because TTL
changes at every hop, the checksum must be recomputed at every router.
It looks stingy — surely you'd want to protect the payload too? But the network layer deliberately
checksums only its own header, for two reasons. First, speed: recomputing a checksum over a
1500-byte payload at every one of twenty hops would be enormously more work than over a 20-byte header,
and the header is the only part the router actually changes (via TTL). Second, layering: the
payload's integrity is the transport layer's business — TCP and UDP carry their own end-to-end
checksums that cover the data, checked once at the destination rather than at every router. It is a neat
example of each layer minding exactly its own correctness and trusting the others to mind theirs. IPv6
takes this logic to its conclusion and drops the header checksum entirely, leaning on
the link and transport layers to catch corruption.
This is the misconception that muddles half of all network-layer exams. People say "the router routes
the packet" and mean the everyday act of sending a packet onward — but that everyday act is
forwarding, not routing. Nail the distinction:
-
Forwarding = one router, one packet, right now: read the destination, consult the
table, pick the output port. Local and per-packet. It builds nothing; it only looks up.
-
Routing = many routers, over seconds, cooperatively computing the map — the
forwarding tables themselves. Global and occasional. No individual packet is being moved
when routing runs.
A one-line test you can apply: "Is a packet in flight being moved?" If yes, that's
forwarding. "Are routers exchanging information to figure out the paths?" If
yes, that's routing. One is the parcel worker reading the chart; the other is the
planners redrawing it. Confuse them and nothing else in this chapter will fit together.