Define a routing table, enter a destination IP, and watch the router pick a route. The longest-prefix-match algorithm is animated step-by-step — see why one route wins and the others don't. Useful for CCNA prep, debugging real configs, or understanding "why is this packet going there?"
| Destination CIDR | Next-Hop | Interface | Metric |
|---|
When a router receives a packet, it walks every route in the routing table and asks: "Does this destination IP fall within this route's network?" If multiple routes match, the router picks the one with the longest prefix length — the most specific match wins.
If two routes have the same prefix length, the router uses other tiebreakers: administrative distance (route source preference), then metric. Real Cisco / Juniper boxes have ~10 levels of tiebreakers; this simulator uses the simplest model.
Say the table has these three routes:
0.0.0.0/0 → 192.168.1.1 (default route, "everything else") 10.0.0.0/8 → 10.0.0.1 (any 10.x.x.x) 10.5.0.0/16 → 10.5.0.1 (more specific - 10.5.x.x only)
For destination 10.5.10.55:
Three matches, but 10.5.0.0/16 wins because its prefix is longest. Traffic is sent to 10.5.0.1.
Routers select the most specific matching route, not the first. The snippets below implement longest-prefix-match against a small routing table and resolve which route wins for the destination 10.0.5.42.
import ipaddress
routes = [
("0.0.0.0/0", "192.0.2.1"),
("10.0.0.0/8", "10.1.1.1"),
("10.0.0.0/16", "10.1.2.1"),
("192.168.0.0/16", "192.168.1.1"),
]
dest = ipaddress.IPv4Address("10.0.5.42")
matches = []
for cidr, nh in routes:
net = ipaddress.IPv4Network(cidr)
if dest in net:
matches.append((net, nh))
# Longest prefix wins
matches.sort(key=lambda m: m[0].prefixlen, reverse=True)
winner = matches[0]
print(f"Destination: {dest}")
print("Routes considered:")
for net, nh in sorted(matches, key=lambda m: m[0].prefixlen):
print(f" {str(net)+'':<12}-> via {nh:<12} (match)")
print(f"Winner: {winner[0]} -> via {winner[1]}")package main
import (
"fmt"
"net"
"sort"
)
type route struct {
cidr string
nh string
ipnet *net.IPNet
}
func main() {
specs := []struct{ cidr, nh string }{
{"0.0.0.0/0", "192.0.2.1"},
{"10.0.0.0/8", "10.1.1.1"},
{"10.0.0.0/16", "10.1.2.1"},
{"192.168.0.0/16", "192.168.1.1"},
}
dest := net.ParseIP("10.0.5.42").To4()
matches := []route{}
for _, s := range specs {
_, n, _ := net.ParseCIDR(s.cidr)
if n.Contains(dest) {
matches = append(matches, route{s.cidr, s.nh, n})
}
}
sortAsc := make([]route, len(matches))
copy(sortAsc, matches)
sort.Slice(sortAsc, func(i, j int) bool {
pi, _ := sortAsc[i].ipnet.Mask.Size()
pj, _ := sortAsc[j].ipnet.Mask.Size()
return pi < pj
})
winner := matches[0]
for _, m := range matches[1:] {
pw, _ := winner.ipnet.Mask.Size()
pm, _ := m.ipnet.Mask.Size()
if pm > pw {
winner = m
}
}
fmt.Printf("Destination: %s\n", dest)
fmt.Println("Routes considered:")
for _, m := range sortAsc {
fmt.Printf(" %-12s-> via %-12s (match)\n", m.cidr, m.nh)
}
fmt.Printf("Winner: %s -> via %s\n", winner.cidr, winner.nh)
}function ip2int(s) {
return s.split('.').reduce((a, o) => (a << 8) + +o, 0) >>> 0;
}
function inNet(ip, cidr) {
const [base, plen] = cidr.split('/');
const p = +plen;
const mask = p === 0 ? 0 : (0xffffffff << (32 - p)) >>> 0;
return (ip2int(ip) & mask) === (ip2int(base) & mask);
}
function prefixOf(cidr) { return +cidr.split('/')[1]; }
const routes = [
['0.0.0.0/0', '192.0.2.1'],
['10.0.0.0/8', '10.1.1.1'],
['10.0.0.0/16', '10.1.2.1'],
['192.168.0.0/16', '192.168.1.1'],
];
const dest = '10.0.5.42';
const matches = routes.filter(([c]) => inNet(dest, c));
const asc = [...matches].sort((a, b) => prefixOf(a[0]) - prefixOf(b[0]));
const winner = matches.reduce((w, m) =>
prefixOf(m[0]) > prefixOf(w[0]) ? m : w);
console.log(`Destination: ${dest}`);
console.log('Routes considered:');
for (const [c, nh] of asc) {
console.log(` ${c.padEnd(12)}-> via ${nh.padEnd(12)} (match)`);
}
console.log(`Winner: ${winner[0]} -> via ${winner[1]}`);#!/usr/bin/env bash
# Longest-prefix-match against a small routing table.
ip2int() { local IFS=.; local -a o=($1)
echo $(( (o[0]<<24)|(o[1]<<16)|(o[2]<<8)|o[3] )); }
in_net() {
local ip=$1 cidr=$2
local base=${cidr%/*} plen=${cidr#*/}
local mask=0
if (( plen > 0 )); then mask=$(( (0xFFFFFFFF << (32 - plen)) & 0xFFFFFFFF )); fi
local i b
i=$(ip2int "$ip"); b=$(ip2int "$base")
[[ $(( i & mask )) -eq $(( b & mask )) ]]
}
DEST="10.0.5.42"
declare -a ROUTES=(
"0.0.0.0/0|192.0.2.1"
"10.0.0.0/8|10.1.1.1"
"10.0.0.0/16|10.1.2.1"
"192.168.0.0/16|192.168.1.1"
)
declare -a MATCHES=()
for r in "${ROUTES[@]}"; do
cidr=${r%|*}
if in_net "$DEST" "$cidr"; then MATCHES+=("$r"); fi
done
# Pick winner = longest prefix
winner_pfx=-1
winner=""
for r in "${MATCHES[@]}"; do
cidr=${r%|*}; pfx=${cidr#*/}
if (( pfx > winner_pfx )); then winner_pfx=$pfx; winner="$r"; fi
done
# Sort ascending for display
IFS=$'\n' SORTED=($(for r in "${MATCHES[@]}"; do
cidr=${r%|*}; printf "%02d|%s\n" "${cidr#*/}" "$r"
done | sort | cut -d'|' -f2-))
unset IFS
echo "Destination: $DEST"
echo "Routes considered:"
for r in "${SORTED[@]}"; do
cidr=${r%|*}; nh=${r#*|}
printf " %-12s-> via %-12s (match)\n" "$cidr" "$nh"
done
winner_cidr=${winner%|*}; winner_nh=${winner#*|}
printf "Winner: %s -> via %s\n" "$winner_cidr" "$winner_nh"import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class Lpm {
static long ip2int(String s) {
String[] p = s.split("\\.");
return ((Long.parseLong(p[0]) << 24)
| (Long.parseLong(p[1]) << 16)
| (Long.parseLong(p[2]) << 8)
| Long.parseLong(p[3])) & 0xFFFFFFFFL;
}
static boolean inNet(String ip, String cidr) {
String[] parts = cidr.split("/");
int plen = Integer.parseInt(parts[1]);
long mask = (plen == 0) ? 0L
: (0xFFFFFFFFL << (32 - plen)) & 0xFFFFFFFFL;
return (ip2int(ip) & mask) == (ip2int(parts[0]) & mask);
}
static int prefixOf(String cidr) {
return Integer.parseInt(cidr.split("/")[1]);
}
public static void main(String[] args) {
String[][] routes = {
{"0.0.0.0/0", "192.0.2.1"},
{"10.0.0.0/8", "10.1.1.1"},
{"10.0.0.0/16", "10.1.2.1"},
{"192.168.0.0/16", "192.168.1.1"},
};
String dest = "10.0.5.42";
List<String[]> matches = new ArrayList<>();
for (String[] r : routes) if (inNet(dest, r[0])) matches.add(r);
List<String[]> asc = new ArrayList<>(matches);
asc.sort(Comparator.comparingInt(r -> prefixOf(r[0])));
String[] winner = matches.get(0);
for (String[] m : matches) if (prefixOf(m[0]) > prefixOf(winner[0])) winner = m;
System.out.printf("Destination: %s%n", dest);
System.out.println("Routes considered:");
for (String[] m : asc) {
System.out.printf(" %-12s-> via %-12s (match)%n", m[0], m[1]);
}
System.out.printf("Winner: %s -> via %s%n", winner[0], winner[1]);
}
}#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
static uint32_t ip2int(const char *s) {
unsigned a, b, c, d;
sscanf(s, "%u.%u.%u.%u", &a, &b, &c, &d);
return (a << 24) | (b << 16) | (c << 8) | d;
}
typedef struct { const char *cidr; const char *nh; int prefix; } route_t;
static int parse_cidr(const char *cidr, uint32_t *base, int *plen) {
char ip[20]; int p;
if (sscanf(cidr, "%19[^/]/%d", ip, &p) != 2) return 0;
*base = ip2int(ip);
*plen = p;
return 1;
}
static int in_net(uint32_t ip, uint32_t base, int plen) {
uint32_t mask = (plen == 0) ? 0 : (uint32_t)(0xFFFFFFFFu << (32 - plen));
return (ip & mask) == (base & mask);
}
static int cmp_asc(const void *a, const void *b) {
return ((route_t*)a)->prefix - ((route_t*)b)->prefix;
}
int main(void) {
route_t routes[] = {
{"0.0.0.0/0", "192.0.2.1", 0},
{"10.0.0.0/8", "10.1.1.1", 0},
{"10.0.0.0/16", "10.1.2.1", 0},
{"192.168.0.0/16", "192.168.1.1", 0},
};
int n = sizeof(routes) / sizeof(routes[0]);
const char *dest = "10.0.5.42";
uint32_t destInt = ip2int(dest);
route_t matches[8]; int mc = 0;
for (int i = 0; i < n; i++) {
uint32_t b; int p;
if (!parse_cidr(routes[i].cidr, &b, &p)) continue;
routes[i].prefix = p;
if (in_net(destInt, b, p)) matches[mc++] = routes[i];
}
route_t asc[8];
memcpy(asc, matches, mc * sizeof(route_t));
qsort(asc, mc, sizeof(route_t), cmp_asc);
route_t winner = matches[0];
for (int i = 1; i < mc; i++)
if (matches[i].prefix > winner.prefix) winner = matches[i];
printf("Destination: %s\n", dest);
printf("Routes considered:\n");
for (int i = 0; i < mc; i++) {
printf(" %-12s-> via %-12s (match)\n", asc[i].cidr, asc[i].nh);
}
printf("Winner: %s -> via %s\n", winner.cidr, winner.nh);
return 0;
}Destination: 10.0.5.42 Routes considered: 0.0.0.0/0 -> via 192.0.2.1 (match) 10.0.0.0/8 -> via 10.1.1.1 (match) 10.0.0.0/16 -> via 10.1.2.1 (match) Winner: 10.0.0.0/16 -> via 10.1.2.1