A hands-on lab that uses NetLab and Containerlab with FRRouting (FRR) containers to demonstrate IS-IS multi-area routing, DIS election, pseudo-nodes, suboptimal inter-area routing, route leaking, and redistribution of external routes — all in a single reproducible topology.
graph TB
subgraph A1["Area 49.0001 · LAN — DIS election"]
direction TB
br1["br1 · L1/L2"]
br2["br2 · L1/L2"]
LAN1(["LAN · DIS election"])
r1["r1 · L1"]
r2["r2 · L1"]
stub_r1[/"10.1.1.0/24"/]
stub_r2[/"10.1.2.0/24"/]
br1 -. "P2P metric 20 ⚠️" .- r1
br1 -. "P2P metric 20 ⚠️" .- r2
br2 --- LAN1
LAN1 --- r1
LAN1 --- r2
r1 --- stub_r1
r2 --- stub_r2
end
subgraph A3["Area 49.0003 · P2P"]
direction TB
br4["br4 · L1/L2"]
r5["r5 · L1"]
stub_r5[/"10.3.1.0/24"/]
ext[/"ext · 192.168.100.0/24"/]
br4 --- r5
r5 --- stub_r5
r5 -. "redistribute connected ↑" .- ext
end
subgraph A2["Area 49.0002 · P2P"]
direction TB
br3["br3 · L1/L2"]
r3["r3 · L1"]
r4["r4 · L1"]
stub_r3[/"10.2.1.0/24"/]
stub_r4[/"10.2.2.0/24"/]
br3 --- r3
br3 --- r4
r3 --- stub_r3
r4 --- stub_r4
end
br1 == "L2 · metric 5" === br4
br2 == "L2 · metric 10" === br3
br3 == "L2 · metric 10" === br4
⚠️ Suboptimal routing highlighted:r1/r2preferbr2as their L1 default gateway (lower L1 cost —br2is reachable via the LAN at metric 10) but the shortest end-to-end path to Area 3 goes viabr1(P2P metric 20 + direct L2 link metric 5 = 25, vs 10+10+10=30 viabr2). See the Suboptimal Inter-Area Routing section for details and the Route Leaking section for the fix.
Note: In IS-IS there is no concept of an ABR (Area Border Router — that is OSPF terminology). The routers participating in both L1 and L2 are simply called L1/L2 routers or border routers (
br1–br4).
| Router | IS-IS Level | Area | Attached stub network |
|---|---|---|---|
r1 |
L1 only | 49.0001 | 10.1.1.0/24 |
r2 |
L1 only | 49.0001 | 10.1.2.0/24 |
br1 |
L1/L2 border | 49.0001 ↔ L2 | — |
br2 |
L1/L2 border | 49.0001 ↔ L2 | — |
r3 |
L1 only | 49.0002 | 10.2.1.0/24 |
r4 |
L1 only | 49.0002 | 10.2.2.0/24 |
br3 |
L1/L2 border | 49.0002 ↔ L2 | — |
r5 |
L1 only | 49.0003 | 10.3.1.0/24 |
br4 |
L1/L2 border | 49.0003 ↔ L2 | — |
ext |
no IS-IS | — (external host) | 192.168.100.0/24 (redistributed from r5) |
Note on IP addresses: NetLab auto-assigns addresses from its default pools. Loopbacks use
10.0.0.x/32and transit links use172.16.x.x. Runnetlab upand inspectnetlab.ymlfor the exact assigned addresses.
A Level-1 router knows only about routers and prefixes within its own area. It builds an intra-area L1 LSDB from:
- Type 1 LSPs (router LSPs) — one per router in the area, describing its links and reachable prefixes.
- Type 2 LSPs (pseudo-node LSPs) — generated by the DIS on multi-access LAN segments (see DIS Election).
For destinations outside its area, an L1 router installs a default route pointing to the nearest L1/L2 router (the one with the lowest L1 metric). This is the root cause of suboptimal inter-area routing — the L1 router picks its exit point based only on the L1 metric to the border router, with no visibility into the inter-area topology beyond it.
# Show the L1 LSDB on r1 — contains only LSPs from Area 49.0001
netlab connect r1 -- vtysh -c "show isis database"
# Show detailed L1 LSDB entries
netlab connect r1 -- vtysh -c "show isis database detail"The L2 LSDB is shared among all L1/L2 routers across every area. It describes the full inter-area topology — every area's prefixes, all L2 links, and their metrics. L2 forms the IS-IS backbone — unlike OSPF, there is no mandatory "area 0"; the backbone is simply the set of L2 adjacencies between L1/L2 border routers.
On an L1/L2 router (br1–br4) you can inspect both databases side by side:
# Show the L2 LSDB on br1 — contains LSPs from ALL areas
netlab connect br1 -- vtysh -c "show isis database level-2"
# Compare with the L1 LSDB (Area 49.0001 entries only)
netlab connect br1 -- vtysh -c "show isis database level-1"An L1/L2 router participates in both levels simultaneously. It maintains:
- an L1 LSDB for its local area, and
- an L2 LSDB shared with all other L1/L2 routers.
It advertises a default route into its local area so that pure L1 routers can reach destinations in other areas. It also leaks (summarises) the L1 prefixes of its area into the L2 topology so that other areas can reach them.
On a multi-access LAN segment (e.g., the Area 1 LAN in this lab), IS-IS elects a Designated IS (DIS).
- Election rule: The router with the highest configured priority wins. If priorities are equal, the highest SNPA (MAC address) wins. There is no concept of a Backup DIS — the election is pre-emptive.
- Role of the DIS: The DIS generates a pseudo-node LSP (Type 2 LSP) that logically represents the LAN segment as a virtual node in the link-state database. All routers on the LAN appear connected to this pseudo-node rather than to each other, which reduces the number of adjacencies that need to be tracked.
- Flooding: On a LAN, IS-IS still forms full adjacencies between every pair of routers (each router synchronises its LSDB with the DIS), but LSA flooding is coordinated by the DIS.
You can observe DIS election on the Area 1 LAN (r1, r2, br2). br1 is connected to r1 and r2 via separate P2P links instead, which is why its metric can be differentiated (see Suboptimal Inter-Area Routing). Areas 2 and 3 use point-to-point links — no DIS election occurs on P2P interfaces.
# Show IS-IS adjacencies on r1 (expect neighbours r2 and br2 on the LAN, plus br1 on P2P)
netlab connect r1 -- vtysh -c "show isis neighbor"
# Show IS-IS database on r1 — look for a pseudo-node LSP (Type 2)
netlab connect r1 -- vtysh -c "show isis database detail"This lab deliberately demonstrates a classic IS-IS pitfall.
Why IS-IS LAN metrics cannot differentiate border routers
On a multi-access LAN, each router's SPF cost to any other router on that LAN equals the router's own outgoing interface metric plus the pseudo-node's cost back to the neighbour — which IS-IS always sets to zero. Concretely, if r1's LAN interface metric is 10, r1 sees every LAN neighbour at cost 10, regardless of what metric br1 or br2 configure on their own LAN interfaces. Setting a higher metric on br1's LAN port therefore has no effect on r1's default-route selection.
The correct solution is to move br1 off the shared LAN and connect it to r1 and r2 via dedicated P2P links. On a P2P link the metric on r1's side is the exact SPF cost r1 will use to reach br1, so differentiation works as expected.
The Area 1 topology has two L1/L2 border routers with deliberately different effective costs as seen from r1/r2:
| Router | How r1/r2 reach it | Effective L1 cost | L2 path to Area 3 (49.0003) | Total cost |
|---|---|---|---|---|
br2 |
via LAN (default metric 10) | 10 ← preferred | br2 → br3 → br4 |
10 + 10 + 10 = 30 |
br1 |
via P2P (metric 20) | 20 (higher, less preferred) | br1 → br4 (direct) |
20 + 5 = 25 ✅ |
Because r1 and r2 see a lower L1 cost to br2, they install a default route via br2. Traffic destined for 10.3.1.0/24 (Area 3) therefore travels the longer path:
r1 → br2 → br3 → br4 → r5 (total IS-IS cost: 30)
The optimal path is:
r1 → br1 → br4 → r5 (total IS-IS cost: 25)
r1 cannot discover this because it has no L2 LSDB — it only knows the L1 metric to its nearest border router.
# Verify the suboptimal default route on r1 — next-hop should be br2
netlab connect r1 -- vtysh -c "show ip route isis"
# Traceroute from r1 to the Area 3 stub network to observe the longer path
netlab connect r1 -- vtysh -c "show ip route 10.3.1.0/24"Route leaking (also called L2-to-L1 redistribution) pushes specific L2 prefixes back into an L1 area as L1 routes. Once r1 and r2 have a specific L1 route to 10.3.1.0/24 via br1, they can compare its total cost against the same prefix via br2 and select the optimal path.
# On r1: only a default route exists — no specific route to Area 3
netlab connect r1 -- vtysh -c "show ip route isis"
# Expected output includes: i*L1 0.0.0.0/0 via <br2-ip>
# Trace the actual path to 10.3.1.1
netlab connect r1 -- traceroute 10.3.1.1
# Expected: r1 → br2 → br3 → br4 → r5 (suboptimal)Connect to br1 and enter the following FRR configuration:
netlab connect br1 vtyshconfigure terminal
!-- Prefix-list matching the Area 3 stub network
ip prefix-list AREA3_PREFIXES seq 5 permit 10.3.1.0/24
!-- Route-map that permits matched prefixes
route-map LEAK_AREA3 permit 10
match ip address prefix-list AREA3_PREFIXES
!
!-- Inside the IS-IS process: leak matching L2 routes into L1
router isis Gandalf
redistribute isis level-2 into level-1 route-map LEAK_AREA3
!
end
write memory
# On r1: a specific L1 route to 10.3.1.0/24 should now appear via br1
netlab connect r1 -- vtysh -c "show ip route 10.3.1.0/24"
# Expected: i L1 10.3.1.0/24 via <br1-ip> [115/25]
# Trace the path again — should now go via br1 (shorter)
netlab connect r1 -- traceroute 10.3.1.1
# Expected: r1 → br1 → br4 → r5 (optimal)The leaked specific route is preferred over the default route due to longest prefix match — a /24 beats 0.0.0.0/0. Traffic to 10.3.1.0/24 will now follow the optimal path through br1.
IS-IS, like OSPF, is a link-state IGP — it only knows about routers and prefixes that are explicitly part of the IS-IS domain. Any network attached to a router that is not configured as an IS-IS passive interface, and not reachable via another IS-IS router, will be invisible to the rest of the domain.
In this lab the Linux host ext is connected to r5 via the 192.168.100.0/24 subnet. ext does not run IS-IS (or any routing protocol). Until redistribution is configured on r5, no other router in the lab can reach 192.168.100.0/24 — it simply does not appear in any LSDB.
Redistribution solves this: r5 imports the connected prefix into IS-IS and advertises it in its own LSP, making the external network reachable from every area.
- Connecting IS-IS to a non-IS-IS network (e.g., a server segment, a management network, or a legacy subnet).
- Importing prefixes from another routing protocol (e.g.,
redistribute bgporredistribute static). - Making a directly attached network reachable without turning it into a passive IS-IS interface (useful when you do not want IS-IS hello packets on that interface).
# On r1: verify that 192.168.100.0/24 is NOT present
netlab connect r1 -- vtysh -c "show ip route 192.168.100.0/24"
# Expected: % Network not in table
# On r5: the prefix IS in the routing table as a connected route
netlab connect r5 -- vtysh -c "show ip route connected"
# Expected: C 192.168.100.0/24 is directly connected, <interface>
# But it is absent from the IS-IS LSDB
netlab connect r5 -- vtysh -c "show isis database detail"
# 192.168.100.0/24 will NOT appear in r5's LSPConnect to r5 and enter the following FRR configuration:
netlab connect r5 vtyshconfigure terminal
!-- (Optional) restrict redistribution to the specific external prefix only
ip prefix-list EXT_NETWORKS seq 5 permit 192.168.100.0/24
route-map REDIST_CONNECTED permit 10
match ip address prefix-list EXT_NETWORKS
!
!-- Redistribute connected routes into IS-IS (Level-1, since r5 is L1 only)
router isis Gandalf
redistribute connected route-map REDIST_CONNECTED
!
end
write memory
Note:
r5is a Level-1 router, so the redistributed prefix is announced in the L1 LSDB of Area 49.0003.br4(the L1/L2 border router) will automatically promote it into the L2 LSDB, making it reachable from all other areas.
# On r5: the LSP now includes 192.168.100.0/24
netlab connect r5 -- vtysh -c "show isis database detail"
# Look for: IP Reachability: 192.168.100.0/24
# On r1 (different area): the external prefix should now be reachable
netlab connect r1 -- vtysh -c "show ip route 192.168.100.0/24"
# Expected: i L2 192.168.100.0/24 via <br1-or-br2-ip> [115/...]
# Connectivity test from r1 to the external host
netlab connect r1 -- ping 192.168.100.1 -c 5Redistribution vs. passive interface: A passive IS-IS interface also advertises a connected prefix into IS-IS, but it still sends IS-IS hello packets on the interface (and can form adjacencies if another IS-IS router is present). Redistribution via
redistribute connectedmakes the prefix visible in IS-IS without enabling IS-IS on that interface at all — which is the correct choice for a host-facing segment like the one towardext.
Tip: You can skip local setup entirely by launching the lab in GitHub Codespaces — all dependencies are pre-installed in the dev container.
Follow the official installation guide: 👉 https://netlab.tools/install/
git clone https://github.com/severindellsperger/netlab-isis-lab.git
cd netlab-isis-labClick the button below to open this lab in a pre-configured cloud environment — no local installation required:
Once the Codespace is ready, run netlab up in the terminal to start the lab.
netlab upnetlab up will:
- Parse
topology.ymland auto-assign IP addresses and IS-IS parameters. - Generate Containerlab and FRR configuration files.
- Start all containers via Containerlab.
- Deploy the generated FRR configuration to every container.
After a few seconds, IS-IS adjacencies should form and the routing tables converge.
# Show IS-IS neighbours on br1
netlab connect br1 -- vtysh -c "show isis neighbor"
# Show IS-IS database (LSDB) on r1 — note the pseudo-node LSPs
netlab connect r1 -- vtysh -c "show isis database"
# Show the routing table on r1 (expect a default route via br2)
netlab connect r1 -- vtysh -c "show ip route isis"
# Ping a host in the Area 3 customer network from r1
netlab connect r1 -- ping 10.3.1.1 count 5You can also open an interactive vtysh shell on any device:
netlab connect r1 vtyshOr connect directly via Docker (container names follow the pattern clab-<lab-name>-<node>; run docker ps to confirm the exact names for your environment):
docker exec -it clab-netlab-isis-lab-r1 vtyshnetlab downnetlab down destroys all containers and removes generated configuration files, leaving the repository in a clean state.
This lab is provided as-is for educational purposes.