<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Open-Source Routing and Network Simulation</title>
	<atom:link href="https://brianlinkletter.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://brianlinkletter.com/</link>
	<description>Descriptions and evaluations of open-source projects related to network emulation and simulation</description>
	<lastBuildDate>Sat, 28 Mar 2026 13:33:43 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2014/09/favicon-144.png?fit=32%2C32&#038;ssl=1</url>
	<title>Open-Source Routing and Network Simulation</title>
	<link>https://brianlinkletter.com/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">39593494</site>	<item>
		<title>Building IRRd and bgpq4 Docker Containers for Network Labs</title>
		<link>https://brianlinkletter.com/2026/03/building-irrd-and-bgpq4-docker-containers-for-network-labs/</link>
					<comments>https://brianlinkletter.com/2026/03/building-irrd-and-bgpq4-docker-containers-for-network-labs/#respond</comments>
		
		<dc:creator><![CDATA[Brian Linkletter]]></dc:creator>
		<pubDate>Sun, 22 Mar 2026 20:15:29 +0000</pubDate>
				<category><![CDATA[Learning IP]]></category>
		<category><![CDATA[Network Simulation]]></category>
		<category><![CDATA[Open Source Networking]]></category>
		<category><![CDATA[BGP]]></category>
		<guid isPermaLink="false">https://opensourcenetworksimulators.com/?p=6035</guid>

					<description><![CDATA[<p>In this post, I walk through the process of building reusable IRRd and bgpq4 container images that can be dropped into any network emulator that supports Docker containers, such as Containerlab, GNS3, or Kathará.</p>
<p>The post <a href="https://brianlinkletter.com/2026/03/building-irrd-and-bgpq4-docker-containers-for-network-labs/">Building IRRd and bgpq4 Docker Containers for Network Labs</a> appeared first on <a href="https://brianlinkletter.com">Open-Source Routing and Network Simulation</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-jetpack-markdown"><p>To demonstrate basic BGP security practices in a network emulator in a way that emulates real-world conditions, researchers need to emulate an <a href="https://www.arin.net/resources/manage/irr/?utm_source=chatgpt.com#submitting-routing-information">Internet Routing Registry (IRR)</a> database server running software like <a href="https://irrd.readthedocs.io">IRRd</a> and a network management workstation running software utilities like <a href="https://github.com/bgp/bgpq4">bgpq4</a>. These tools enable the centralized registration of prefix information and the <a href="https://bgpfilterguide.nlnog.net/">generation of prefix filter lists</a> from that information. I was not able to find ready-to-use container images that support either of these functions, so I created them.</p>
<p>In this post, I walk through the process of building reusable container images that can be dropped into any network emulator that supports Docker containers, such as <a href="https://containerlab.dev/">Containerlab</a>, <a href="https://www.gns3.com/">GNS3</a>, or <a href="https://www.kathara.org/">Kathará</a>, etc. I also show how I published the containers in a public repository.</p>
<h3>IRRd and bgpq4</h3>
<p><a href="https://irrd.readthedocs.io">IRRd (Internet Routing Registry daemon) version 4</a> is a widely used software program for maintaining and serving IRR data in the <a href="https://www.ripe.net/manage-ips-and-asns/db/rpsl/">RPSL format</a>. To experience IRRd, you can directly interact with the IRRd user interface at <a href="https://rr.ntt.net/ui/">ntt.net</a>, which is a tier-1 global IP backbone provider, and it also powers industry-standard registries like <a href="https://www.radb.net/">RADB</a>.</p>
<p><a href="https://github.com/bgp/bgpq4">Bgpq4</a> is a command-line tool used by network engineers to query an IRR server and generate BGP prefix-list configurations for most router platforms.</p>
<h4>Design decisions</h4>
<p>The will build the IRRd container on top of a <a href="https://hub.docker.com/layers/library/debian/trixie-slim/">lightweight Debian image</a> will bundle PostgreSQL, Redis, and IRRd in a single “all-in-one” image. This is not the recommended pattern for production, but we want the IRRd to appear to be one “node” in a network emulation lab. And, we want to make it available as a single image, with no Docker Compose file required.</p>
<p>I will also build the bgpq4 container on top of a lightweight Debian image that will include bgpq4 and common network utilities. This container will act as a network management workstation.</p>
<h3>Building the IRRd container image</h3>
<p>The IRRd image requires four files: a Dockerfile, an entrypoint script, an IRRd configuration file, and a base RPSL data file. Create them, as described below, and place them all in a project directory (I used the drectory name <em>irrd-lab</em>).</p>
<pre><code>$ mkdir irrd-lab
$ cd irrd-lab
</code></pre>
<h4>The IRRd Dockerfile</h4>
<p>The IRRd Dockerfile will build the Docker image that combines IRRd, PostgreSQL, and Redis into one container.</p>
<p>Create a Dockerfile named <em>Dockerfile.irrd</em>:</p>
<pre><code>$ vi Dockerfile.irrd
</code></pre>
<p>Copy and paste the following text into the file:</p>
<pre><code class="language-dockerfile"># All-in-one IRRd lab container

FROM python:3.14-slim-trixie

ENV DEBIAN_FRONTEND=noninteractive 

# Install PostgreSQL, Redis, and build dependencies for IRRd
RUN apt-get update &amp;&amp; apt-get install -y --no-install-recommends \
        postgresql \
        redis-server \
        gnupg \
        iproute2 \
        net-tools \
        netcat-openbsd \
        procps \
        curl \
        gcc \
        libpq-dev \
        python3-dev \
        rustc \
        cargo \
        apache2-utils \
    &amp;&amp; pip install --no-cache-dir irrd \
    &amp;&amp; apt-get purge -y gcc python3-dev rustc cargo \
    &amp;&amp; apt-get autoremove -y \
    &amp;&amp; rm -rf /var/lib/apt/lists/*

# Create directories for IRRd
RUN mkdir -p /var/log/irrd /var/run/irrd /etc/irrd /var/lib/irrd/gnupg \
    &amp;&amp; chmod 700 /var/lib/irrd/gnupg

# Make PostgreSQL binaries available on PATH
ENV PATH=&quot;/usr/lib/postgresql/17/bin:${PATH}&quot;

# Copy configuration and entrypoint
COPY irrd.yaml /etc/irrd.yaml
RUN mkdir -p /etc/irrd/data
COPY lab-irr-base.rpsl /etc/irrd/data/lab-irr-base.rpsl
RUN ln -sf /etc/irrd/data/lab-irr-base.rpsl /etc/irrd/lab-irr-base.rpsl
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

# (Optional) Bind-mount host directory with RPSL files to /etc/irrd/data
# VOLUME [&quot;/etc/irrd/data&quot;]

EXPOSE 43
EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=5s --start-period=45s --retries=3 \
    CMD sh -ec &quot;pg_isready -q &amp;&amp; redis-cli ping | grep -q PONG &amp;&amp; nc -z 127.0.0.1 43&quot;

ENTRYPOINT [&quot;/entrypoint.sh&quot;]
</code></pre>
<p>Save the file.</p>
<p>The Dockerfile, above, starts from <code>python:3.14-slim-trixie</code>. The <a href="https://irrd.readthedocs.io/en/stable/admins/deployment/">IRRd deployment documentation</a> states that IRRd requires PostgreSQL and Redis so the Dockerfile installs PostgreSQL and Redis from Debian packages, then installs IRRd from PyPI. It also installs some helpful utilities. The Dockerfile then temporarily installs build dependencies <em>gcc</em> and <em>rustc</em>, and removes them after the IRRd build is completed to keep the image smaller.</p>
<p>The Dockerfile then creates directories for IRRd and adds the PostgreSQL application to the system path. It also copies the configuration files and startup script into the container.</p>
<p>The Dockerfile also includes a <em>HEALTHCHECK</em> instruction. Docker will periodically run it and flag the container unhealthy if it fails.</p>
<p>Every time the container starts, it initializes a new database and loads the base RPSL data so no persistent volumes are needed.</p>
<h4>The entrypoint script</h4>
<p>The entrypoint script is the startup script for the container. It starts each service in order, waits for dependencies, then runs IRRd in the foreground.</p>
<p>Because the database and Redis live in the same container, the script also bootstraps the <code>irrd</code> database, loads the base RPSL data and creates a Web UI admin account. Waiting for each dependency avoids race conditions during startup.</p>
<p>Create the <em>entrypoint.sh</em> script:</p>
<pre><code>$ vi entrypoint.sh
</code></pre>
<p>Copy and paste the following text into the file:</p>
<pre><code class="language-bash">#!/bin/bash
set -e

echo &quot;=== IRRd Lab Container Starting ===&quot;

if [ ! -x &quot;/usr/lib/postgresql/17/bin/initdb&quot; ]; then
    echo &quot;ERROR: expected PostgreSQL binaries at /usr/lib/postgresql/17/bin, but initdb was not found or is not executable.&quot;
    exit 127
fi
mkdir -p /var/log/irrd
chown postgres:postgres /var/log/irrd

# ------------------------------------------------------------------
# Start PostgreSQL
# ------------------------------------------------------------------
echo &quot;Starting PostgreSQL...&quot;

# Initialize the database cluster if it doesn't exist yet
if [ ! -f &quot;/var/lib/postgresql/data/PG_VERSION&quot; ]; then
    install -d -o postgres -g postgres -m 0700 &quot;/var/lib/postgresql/data&quot;
    su - postgres -c &quot;/usr/lib/postgresql/17/bin/initdb -D /var/lib/postgresql/data&quot;
fi

# Tune PostgreSQL for minimal lab use
cat &gt;&gt; &quot;/var/lib/postgresql/data/postgresql.conf&quot; &lt;&lt;EOF
random_page_cost = 1.0
work_mem = 50MB
max_connections = 30
listen_addresses = 'localhost'
EOF

# Allow local connections without a password
cat &gt; &quot;/var/lib/postgresql/data/pg_hba.conf&quot; &lt;&lt;EOF
local   all   all                 trust
host    all   all   127.0.0.1/32  trust
host    all   all   ::1/128       trust
EOF

su - postgres -c &quot;/usr/lib/postgresql/17/bin/pg_ctl -D /var/lib/postgresql/data -l /var/log/irrd/postgresql.log start&quot;

# Wait for PostgreSQL to be ready
echo &quot;Waiting for PostgreSQL to accept connections...&quot;
for i in $(seq 1 30); do
    if su - postgres -c &quot;/usr/lib/postgresql/17/bin/pg_isready -q&quot; 2&gt;/dev/null; then
        break
    fi
    sleep 1
done

# Create the IRRd database and pgcrypto extension
echo &quot;Creating IRRd database...&quot;
su - postgres -c &quot;/usr/lib/postgresql/17/bin/psql -tc \&quot;SELECT 1 FROM pg_database WHERE datname='irrd'\&quot; | grep -q 1&quot; || \
    su - postgres -c &quot;/usr/lib/postgresql/17/bin/createdb irrd&quot;
su - postgres -c &quot;/usr/lib/postgresql/17/bin/psql -d irrd -c 'CREATE EXTENSION IF NOT EXISTS pgcrypto;'&quot;

# ------------------------------------------------------------------
# Start Redis (no persistence, low memory)
# ------------------------------------------------------------------
echo &quot;Starting Redis...&quot;
redis-server \
    --daemonize yes \
    --save &quot;&quot; \
    --appendonly no \
    --maxmemory 64mb \
    --logfile /var/log/irrd/redis.log

# Wait for Redis to be ready
for i in $(seq 1 15); do
    if redis-cli ping 2&gt;/dev/null | grep -q PONG; then
        break
    fi
    sleep 1
done

# ------------------------------------------------------------------
# Build IRRd database tables
# ------------------------------------------------------------------
echo &quot;Building IRRd database tables...&quot;
irrd_database_upgrade --config /etc/irrd.yaml

# Load base RPSL data
irrd_load_database --config /etc/irrd.yaml --source LABRIR /etc/irrd/lab-irr-base.rpsl
echo &quot;RPSL data loaded.&quot;


# ------------------------------------------------------------------
# Bootstrap a hard-coded Web UI admin user (because no e-mail available)
# ------------------------------------------------------------------
echo &quot;Creating IRRd Web UI admin user: test@irrtest.com&quot;
WEBUI_PASSWORD_HASH=&quot;$(htpasswd -bnBC 12 &quot;&quot; &quot;mypassword&quot; | tr -d ':\n')&quot;

su - postgres -c &quot;/usr/lib/postgresql/17/bin/psql -d irrd -v ON_ERROR_STOP=1 &lt;&lt;'SQL'
INSERT INTO auth_user (email, name, password, active, override)
VALUES ('test@irrtest.com', 'Lab Administrator', '$WEBUI_PASSWORD_HASH', true, true)
ON CONFLICT (email)
DO UPDATE SET
    name = EXCLUDED.name,
    password = EXCLUDED.password,
    active = EXCLUDED.active,
    override = EXCLUDED.override,
    updated = now();
SQL&quot;

echo &quot;Web UI user created/updated: test@irrtest.com (override=true)&quot;

# ------------------------------------------------------------------
# Start IRRd in the foreground
# ------------------------------------------------------------------
echo &quot;Starting IRRd...&quot;
echo &quot;=== IRRd Lab Container Ready ===&quot;
exec irrd --config /etc/irrd.yaml --foreground
</code></pre>
<p>Save the file.</p>
<p>As you can see from the entrypoint.sh file, the server startup sequence is: PostgreSQL → Redis → Schema creation → RPSL data load → Web user creation → Run IRRd in foreground. The code comments explain each step.</p>
<blockquote>
<p><strong>Note:</strong> The <a href="https://irrd.readthedocs.io/en/stable/admins/deployment/#postgresql-configuration">IRRd deployment docs</a> recommend creating a dedicated <em>irrd</em> database role with a password and explicit grants. But, to keep things simple in this lab container, the entrypoint script connects to the database as the <em>postgres</em> superuser without authentication. This is OK in a lab because our PostgreSQL database is local and disposable.</p>
</blockquote>
<h4>The IRRd configuration file</h4>
<p>See the <a href="https://irrd.readthedocs.io/en/stable/admins/configuration/">full IRRd configuration reference</a> for all IRRd configuration options. A lab instance needs very few features so our IRRd file is very simple. The <em>irrd.taml</em> file below covers the basic database/Redis URLs, server listeners, authentication overrides and source definitions that are relevant to our simple setup.</p>
<p>Create the <em>irrd.yaml</em> file:</p>
<pre><code>$ vi irrd.yaml
</code></pre>
<p>Copy and paste the following text to the <em>irrd.yaml</em> file:</p>
<pre><code class="language-yaml">irrd:
    database_url: 'postgresql://postgres@localhost/irrd'
    redis_url: 'redis://localhost'
    piddir: /var/run/irrd/
    user: postgres
    group: postgres

    server:
        http:
            interface: '0.0.0.0'
            port: 8080
            url: 'http://localhost:8080/'
            workers: 1
        whois:
            interface: '0.0.0.0'
            port: 43
            max_connections: 5

    auth:
        gnupg_keyring: /var/lib/irrd/gnupg/
        override_password: '$1$test$4PzmqjLUwdD2j1Otz/LSw.' # password: mypassword
        set_creation:
            COMMON:
                prefix_required: false
                autnum_authentication: disabled

    email:
        from: 'test@irrtest.com'
        footer: ''
        smtp: 'localhost'

    log:
        level: INFO

    rpki:
        roa_source: null
    
    compatibility:
        inetnum_search_disabled: true

    sources_default:
        - LABRIR

    sources:
        LABRIR:
            authoritative: true
            keep_journal: true
            authoritative_non_strict_mode_dangerous: true
</code></pre>
<p>Then, save the file.</p>
<p>The key IRRd settings are:</p>
<ul>
<li>Both <code>server.http.interface</code> and <code>server.whois.interface</code> bind to <code>0.0.0.0</code> so other lab nodes can reach the IRR server.</li>
<li><code>server.whois.max_connections: 5</code> and <code>server.http.workers: 1</code>, to keep memory usage low (each WHOIS connection uses ~200 MB).</li>
<li><code>rpki.roa_source: null</code> disables RPKI integration. We don’t need it in this filtering lab, and I will cover RPKI in a future post.</li>
<li><code>sources.LABRIR</code> is configured as authoritative with <code>authoritative_non_strict_mode_dangerous: true</code> to relax RPSL validation for lab objects.</li>
<li><code>auth.override_password</code> is the MD5 hash of “mypassword”, used for bypassing authentication when loading data.</li>
</ul>
<h4>The base RPSL data file</h4>
<p>IRRd needs at least a <em>mntner</em> (maintainer) object to function as an authoritative source. This file seeds the IRRd database with a maintainer and an administrative contact.</p>
<p>Create the <em>lab-irr-base.rpsl</em> file:</p>
<pre><code>$ vi lab-irr-base.rpsl
</code></pre>
<p>Then, copy and paste the following text into the file:</p>
<pre><code>mntner:         LAB-MNT
descr:          Lab maintainer for all objects; password is &quot;mypassword&quot;
admin-c:        LAB-ADMIN
upd-to:         test@irrtest.com
auth:           MD5-PW $1$test$4PzmqjLUwdD2j1Otz/LSw.
mnt-by:         LAB-MNT
source:         LABRIR

person:         Lab Administrator
address:        Lab Network
phone:          +1-555-0100
nic-hdl:        LAB-ADMIN
mnt-by:         LAB-MNT
source:         LABRIR

</code></pre>
<p>Ensure that there us a blank line at the end of the file. RPSL requires that every object end with a blank line, including the last object in the file.</p>
<p>Save the file.</p>
<p>In the file above, the objects use <em>LAB-MNT</em> as their maintainer with the MD5 hash of “mypassword”, matching the override password in <em>irrd.yaml</em>. The <em>source: LABRIR</em> value must exactly match the source name in the IRRd configuration. When you use the container in a lab, you will load additional route, aut-num, and as-set objects describing your lab’s routing policy.</p>
<h4>Building the IRRd image</h4>
<p>Build the Docker image:</p>
<pre><code>$ docker build -t irrd-lab -f Dockerfile.irrd .
</code></pre>
<p>This may take several minutes because it compiles some of its dependencies.</p>
<h4>Checking container health</h4>
<p>After building it, test the container by checking its health. First, run the new image in a container tagged <em>irrd-test</em>:</p>
<pre><code>$ docker run -d -p 8080:8080 --name irrd-test irrd-lab:latest
</code></pre>
<p>Run the command below to see the <em>healthcheck</em> status, which verifies PostgreSQL, Redis, and IRRd’s WHOIS listener are all running. If you used the same docker build command, above, the container name will be <em>irrd-test</em>:</p>
<pre><code>$ docker inspect --format '{{json .State.Health}}' irrd-test
</code></pre>
<p>A brief <code>starting</code> state or a few failed probes during initialization is normal. Eventually, you should see output similar to teh folloing:</p>
<pre><code>{&quot;Status&quot;:&quot;healthy&quot;,&quot;FailingStreak&quot;:0,&quot;Log&quot;:[{&quot;Start&quot;:&quot;2026-03-22T10:40:52.405173463-04:00&quot;,&quot;End&quot;:&quot;2026-03-22T10:40:52.460239963-04:00&quot;,&quot;ExitCode&quot;:1,&quot;Output&quot;:&quot;&quot;},{&quot;Start&quot;:&quot;2026-03-22T10:40:57.461480257-04:00&quot;,&quot;End&quot;:&quot;2026-03-22T10:40:57.51754926-04:00&quot;,&quot;ExitCode&quot;:0,&quot;Output&quot;:&quot;Connection to 127.0.0.1 43 port [tcp/whois] succeeded!\n&quot;},{&quot;Start&quot;:&quot;2026-03-22T10:41:27.518192933-04:00&quot;,&quot;End&quot;:&quot;2026-03-22T10:41:27.617904019-04:00&quot;,&quot;ExitCode&quot;:0,&quot;Output&quot;:&quot;Connection to 127.0.0.1 43 port [tcp/whois] succeeded!\n&quot;}]}
</code></pre>
<h4>Test the IRRd server</h4>
<p>To check that the IRRd server is running, access the server’s web UI in a web browser. Go to <code>https://localhost:8080</code>. You should see the IRRd page as shown below:</p>
<p><img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/03/IRRd-010.png?w=1200&#038;ssl=1" alt=""></p>
<p>Try to login with userid: <code>test@irrtest.com</code> and password <code>mypassword</code>. If the login works, then the server is running properly.</p>
<h4>Destroy the IRRd container</h4>
<p>Later, we will test the IRRd container in a small network with the bgpq4 container. So, we need to stop and destroy the current container.</p>
<pre><code>$ docker destroy irrd-test
$ docker rm irrd-test
</code></pre>
<h3>Build the <em>bgpq4</em> utility container image</h3>
<p>The <em>bgpq4</em> utility container image is a lightweight Debian image that provides bgpq4 and common network tools.</p>
<p>Create <em>Dockerfile.bgpq4</em>:</p>
<pre><code>$ vi Dockerfile.bgpq4
</code></pre>
<p>Copy and paste the following text into the file:</p>
<pre><code class="language-dockerfile"># Linux container with bgpq4 and other utilities

FROM debian:trixie-slim

RUN apt-get update &amp;&amp; \
    apt-get install -y --no-install-recommends \
        bgpq4 \
        curl \
        iproute2 \
        traceroute \
        net-tools \
        netbase \
        netcat-openbsd \
        iputils-ping \
        whois \
    &amp;&amp; rm -rf /var/lib/apt/lists/*

CMD [&quot;sleep&quot;, &quot;infinity&quot;]
</code></pre>
<p>Save the file.</p>
<p>The bgpq4 Dockerfile uses the <code>sleep infinity</code> command so the bgpq4 container stays running in a lab topology, just like a real network management workstation would.</p>
<p>Build the image:</p>
<pre><code class="language-bash">$ cd irrd-lab
$ docker build -t bgpq4-utils -f Dockerfile.bgpq4 .
</code></pre>
<p>To test it properly, the bgpq4 container needs to be in a network and must be able to reach an IRRd server. We’ll set that up in the following section.</p>
<h3>Testing the containers together</h3>
<p>Verify the containers work together using plain Docker (no network emulator required).</p>
<h4>Start the containers</h4>
<p>Create a Docker network and start both containers:</p>
<pre><code class="language-bash">$ docker network create irr-test-net
$ docker run -d -p 8080:8080 --name irrd-test --network irr-test-net irrd-lab
$ docker run -d --name bgpq4-test --network irr-test-net bgpq4-utils
</code></pre>
<p>Wait for the IRRd container to finish initializing (15–30 seconds). Then, verify it is healthy:</p>
<pre><code class="language-bash">$ docker inspect --format '{{json .State.Health}}' irrd-test
</code></pre>
<h4>Verify the WHOIS service</h4>
<p>Query the IRRd server from the bgpq4 container:</p>
<pre><code class="language-bash">$ docker exec bgpq4-test sh -c 'echo &quot;-i origin AS100&quot; | nc irrd-test 43'
</code></pre>
<p>This query returns no results because only the base RPSL data is loaded (no route objects) — the important thing is that the connection succeeds. Verify the maintainer object is present:</p>
<pre><code class="language-bash">$ docker exec bgpq4-test sh -c 'echo &quot;LAB-MNT&quot; | nc irrd-test 43'
</code></pre>
<p>This should return the <code>LAB-MNT</code> maintainer object like the following output:</p>
<pre><code>mntner:         LAB-MNT
descr:          Lab maintainer for all objects; password is &quot;mypassword&quot;
admin-c:        LAB-ADMIN
upd-to:         test@irrtest.com
auth:           MD5-PW DummyValue  # Filtered for security
mnt-by:         LAB-MNT
source:         LABRIR
last-modified:  2026-03-22T15:18:21Z
</code></pre>
<p>This shows that the IRRd container and the bgpq4 container can interoperate over a Docker network (a Linux bridge). I will show how these containers may be used in more complex networks in a future post.</p>
<h4>Clean up the test</h4>
<pre><code class="language-bash">$ docker stop irrd-test bgpq4-test
$ docker rm irrd-test bgpq4-test
$ docker network rm irr-test-net
</code></pre>
<h3>Publishing the images</h3>
<p>Publish the images we created to a container registry so they will be easy to access and use. I show the procedure for <a href="https://hub.docker.com/">Docker Hub</a> below; the process is similar for <a href="https://ghcr.io/">GitHub Container Registry</a>.</p>
<h4>Tag and push to Docker Hub</h4>
<p>Tag the images with a specific version.</p>
<p>Replace <code>yourusername</code> with your Docker Hub account name:</p>
<pre><code class="language-bash">$ docker tag irrd-lab yourusername/irrd-lab:0.1
$ docker tag bgpq4-utils yourusername/bgpq4-utils:0.1
</code></pre>
<p>Push the tagged versions to Docker Hub:</p>
<pre><code class="language-bash">$ docker login
$ docker push yourusername/irrd-lab:0.1
$ docker push yourusername/bgpq4-utils:0.1
</code></pre>
<p>Now, anyone can use these images in a network emulation lab by pulling them from Docker Hub.</p>
<h3>Conclusion</h3>
<p>I built two Docker container images for network labs: an all-in-one IRRd server (WHOIS on port 43, web UI on port 8080) and a lightweight bgpq4 utility container. Both are self-contained and disposable, and are compatible with any Docker-capable network emulator. In a future post, I will use them to demonstrate a complete BGP prefix filtering lab.</p>
</div>



<p class=""></p>
<p>The post <a href="https://brianlinkletter.com/2026/03/building-irrd-and-bgpq4-docker-containers-for-network-labs/">Building IRRd and bgpq4 Docker Containers for Network Labs</a> appeared first on <a href="https://brianlinkletter.com">Open-Source Routing and Network Simulation</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://brianlinkletter.com/2026/03/building-irrd-and-bgpq4-docker-containers-for-network-labs/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">6035</post-id>	</item>
		<item>
		<title>Containerlab Network Emulator v0.73 Review</title>
		<link>https://brianlinkletter.com/2026/03/containerlab-network-emulator-v0-73-review/</link>
		
		<dc:creator><![CDATA[Brian Linkletter]]></dc:creator>
		<pubDate>Wed, 04 Mar 2026 03:29:36 +0000</pubDate>
				<category><![CDATA[Network Simulation]]></category>
		<category><![CDATA[Open Source Networking]]></category>
		<category><![CDATA[Containerlab]]></category>
		<category><![CDATA[emulation]]></category>
		<guid isPermaLink="false">https://opensourcenetworksimulators.com/?p=6024</guid>

					<description><![CDATA[<p>Five years after I first reviewed it, Containerlab has matured into a polished, developer-friendly platform with a rich ecosystem of tools built around it. In this post, I will revisit Containerlab and cover what has changed since my original review.</p>
<p>The post <a href="https://brianlinkletter.com/2026/03/containerlab-network-emulator-v0-73-review/">Containerlab Network Emulator v0.73 Review</a> appeared first on <a href="https://brianlinkletter.com">Open-Source Routing and Network Simulation</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-jetpack-markdown"><p>I first <a href="https://www.brianlinkletter.com/2021/05/use-containerlab-to-emulate-open-source-routers/">reviewed Containerlab in 2021</a> when it was a promising but relatively new container-based network emulator. Five years later, <a href="https://containerlab.dev">Containerlab</a> has matured into a polished, developer-friendly platform with a rich ecosystem of tools around it. In this post, I will revisit Containerlab and cover what has changed since my original review.</p>
<p><img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/03/vscode-clab-070-splash-scaled.png?w=1200&#038;ssl=1" alt=""></p>
<h3>What is Containerlab?</h3>
<p><a href="https://containerlab.dev">Containerlab</a> is an open-source, container-based network emulation platform that lets you build, run, and tear down realistic network topologies using simple, declarative YAML files. It uses lightweight containers and Linux networking to interconnect routers, switches, hosts, and tools into reproducible labs that behave like real networks. It also supports virtual machines, so it can run many commercial router images.</p>
<p>Containerlab integrates cleanly with automation tools such as Ansible, Nornir, and CI/CD pipelines, and emphasizes “lab-as-code” workflows. It is well suited for network automation development, validating designs and configuration changes, and learning about complex scenarios like BGP, EVPN, or data-center fabrics on a single workstation or server.</p>
<p>The project was originally developed by <a href="https://www.nokia.com/">Nokia</a> engineers and is hosted on <a href="https://github.com/srl-labs/containerlab">GitHub</a>. It has grown into one of the most widely-used open-source network lab platforms, with an active community on <a href="https://discord.gg/vAyddtaEV9">Discord</a> and a <a href="https://containerlab.dev">dedicated documentation site</a>.</p>
<h3>What’s Changed Since 2021</h3>
<p>Back in 2021, Containerlab was a command-line tool that required <code>sudo</code> for every operation. It required the user to create shell scripts to configure nodes at startup. Users also needed to be very familiar with Docker commands and Linux networking commands to get the most out of the tool.</p>
<p>Today, Containerlab offers rootless operation, declarative node configuration, a full-featured VSCode extension with a graphical topology editor, multiple graph export formats, integrated packet capture, and a thriving community lab catalog. This post will walk through the most significant improvements and show you how the Containerlab experience has changed over the past five years.</p>
<p>Here are the most important changes to Containerlab over the past five years that will affect your day-to-day experience.</p>
<h4>MacOS and Windows Support</h4>
<p>Containerlab now officially supports <a href="https://containerlab.dev/macos/">macOS</a> (both ARM and Intel) and <a href="https://containerlab.dev/windows/">Windows via WSL2</a>. In 2021, the tool was Linux-only. Linux remains the primary platform, but the ability to run labs on a Mac or Windows machine broadens Containerlab’s reach to more users.</p>
<h4>Sudo-less Operation</h4>
<p>In 2021, every Containerlab command required <code>sudo</code>.</p>
<p>Containerlab now supports <a href="https://containerlab.dev/rn/0.63/#sudo-less-containerlab">sudo-less operation</a>. The installer sets up a <code>clab_admins</code> Unix group and the installer provides instructions to add your username to the group. Users in this group can run all Containerlab commands without <code>sudo</code>.</p>
<h4>Declarative Node Configuration with <em>exec:</em></h4>
<p>In my 2021 review, configuring lab nodes required either logging into each container and manually running commands after deployment or writing shell scripts to run configuration commands. For example, to assign an IP address to a PC, I had to run the following commands in the CLI or in a shell script.</p>
<pre><code>$ sudo docker exec -it clab-frrlab-PC1 /bin/ash
# ip link set eth1 up
# ip addr add 192.168.11.2/24 dev eth1
# ip route add 192.168.0.0/16 via 192.168.11.1 dev eth1
# exit
$
</code></pre>
<p>Containerlab <a href="https://containerlab.dev/rn/0.43/#execute-on-host">now supports an <code>exec:</code> key</a> in the topology file that runs commands inside each container at startup. This means you can configure IP addresses, routes, and other settings declaratively, without separate scripts.</p>
<pre><code class="language-yaml">topology:
  nodes:
    PC1:
      kind: linux
      image: wbitt/network-multitool:3.22.2
      exec:
        - ip link set eth1 up
        - ip addr add 192.168.11.2/24 dev eth1
        - ip route add 192.168.0.0/16 via 192.168.11.1 dev eth1
</code></pre>
<p>This improves the “lab-as-code” workflow because the entire lab state, such as topology, wiring, and node configuration, lives in a single YAML file (plus any router configuration files you bind-mount).</p>
<h4>Imperative Node Configuration with <code>exec</code> CLI command</h4>
<p>The <a href="https://containerlab.dev/cmd/exec/"><em>clab exec</em> CLI command</a> allows a user to execute a command inside one or more nodes in a lab emulation scenario. In 2021, users had to use the <em>docker exec</em> CLI command to run commands in lab nodes, and they still can if they want to.</p>
<p>The <em>clab exec</em> command is similar to <em>docker exec</em>, but it allows a user to run the same command across multiple lab nodes in the lab topology. Users can use command flags to specify the nodes upon which to execute the command, so it’s useful for running the same command on a group of nodes.</p>
<h4>Bridge Node Type</h4>
<p>Containerlab <a href="https://containerlab.dev/rn/0.43/#execute-on-host">added the <code>kind: bridge</code> node type</a>, which creates a Linux bridge that acts as a shared Ethernet segment. This is useful for simulating scenarios like Internet Exchange Points (IXPs) where multiple routers peer over a common LAN.</p>
<h4>Integrated Link Impairment Commands</h4>
<p>In 2021, users who wanted to cause impairments on network links had to use Linux IP networking commands. Today, Containerlab offers integrated CLI commands that enable users to emulate <a href="https://containerlab.dev/manual/impairments/">network link impairments</a>.</p>
<p>The new <code>clab tool netem</code> CLI command enables users to set link speed, corruption, delay, jitter, and loss.</p>
<h4>Lab Examples Shipped with the Install</h4>
<p>When you install Containerlab, it copies a set of ready-made lab examples to <em>/etc/containerlab/lab-examples/</em>. More example labs available today, compared to 2021. These labs cover a variety of topologies and vendor combinations, from simple FRR labs to multi-vendor data center fabrics.</p>
<p>The lab examples make it easy to explore different network scenarios and often serve as a good starting point for writing your own topology files.</p>
<h4>Expanded Device Support</h4>
<p>The list of <a href="https://containerlab.dev/manual/kinds/">supported network operating systems</a> has grown significantly since 2021. Containerlab now has first-class <code>kind:</code> support for dozens of platforms, including:</p>
<ul>
<li><em>Nokia</em>: SR Linux, SR OS (SR-SIM and vSIM)</li>
<li><em>Arista</em>: cEOS, vEOS</li>
<li><em>Cisco</em>: XRd, XRv9k, XRv, CSR1000v, Nexus 9000v, 8000, c8000v, IOL, VIOS, ASAv, FTDv, SD-WAN, Catalyst 9000v</li>
<li><em>Juniper</em>: cRPD, vMX, vQFX, vSRX, vJunos, cJunos</li>
<li><em>Others</em>: Cumulus VX, SONiC, MikroTik RouterOS, VyOS, OpenWRT, Ostinato, and more</li>
</ul>
<p>When a device has first-class <code>kind:</code> support, Containerlab handles vendor-specific startup requirements (interface naming, management network setup, license handling, etc.) automatically. This is a big improvement over the generic <code>kind: linux</code> approach, which requires users to handle those details themselves.</p>
<h5>Open-Source Router Support</h5>
<p>For open-source routers, Containerlab still operates the same way it did in 2021. Users of open-source routing software like <a href="https://frrouting.org/">FRR</a> still have to manage some setup commands, themselves. Containerlab still provides the <code>kind: linux</code> node type, which enables users to run open-source routing stacks like FRR, <a href="https://bird.network.cz/">BIRD</a>, <a href="https://osrg.github.io/gobgp/">GoBGP</a>, and <a href="https://www.openbgpd.org/">OpenBGPD</a> in any Linux container.</p>
<p>The good news is that the new <code>exec:</code> command in the topology file, makes it easier to set up and configure open-source routers based on Linux containers.</p>
<h4>Improved Visualization Export</h4>
<p>The <code>clab graph</code> CLI command has been improved and now supports multiple output formats: HTML, draw.io, Mermaid, Graphviz.</p>
<h4>The Containerlab VSCode Extension</h4>
<p>The most visible addition to the Containerlab ecosystem is the <a href="https://containerlab.dev/manual/vsc-extension/">VSCode extension</a>, which transforms VSCode into a full-featured Containerlab IDE. This extension was developed by the Containerlab community to complement Containerlab’s CLI workflow with a graphical interface.</p>
<p><img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/03/vscode-clab-010.png?w=1200&#038;ssl=1" alt="VSCode Containerlab extension tree view"></p>
<p>The extension adds a Containerlab icon to the VSCode activity bar. Clicking it opens an explorer panel with three sections:</p>
<ul>
<li><em>Running labs</em>: Shows all deployed labs on the system. You can expand each lab to see its containers, and expand containers to see their interfaces — including IP addresses, MAC addresses, and MTU values.</li>
<li><em>Undeployed local labs</em>: Discovers all <code>*.clab.yml</code> files in your workspace so you can deploy them with a click.</li>
<li><em>Help &amp; Feedback</em>: Quick links to documentation and community resources.</li>
</ul>
<h5>Graphical Topology Editor</h5>
<p>The standout feature of the Containerlab VSCode extension is a graphical topology creator and viewer built directly into VSCode. The graphical viewer lets you create topologies visually. The lab topology file updates in real-time as you make changes. Creating a split-screen view of the graphical editor alongside the YAML file is a powerful way to design topologies.</p>
<p>When a lab is running, it displays the live topology with deployed node status. You can right-click on nodes to SSH into them, open a container shell, or view container logs, all without leaving VSCode.</p>
<p><img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/03/vscode-clab-050-edit-graph.png?w=1200&#038;ssl=1" alt="VSCode TopoViewer topology editor"></p>
<h5>Deploy, Destroy, and Manage Labs from VSCode</h5>
<p>The extension provides multiple ways to manage lab lifecycle without opening a terminal.</p>
<ul>
<li><em>Deploy/Destroy buttons</em>: When a <code>.clab.yml</code> file is open, deploy and graph buttons appear in the editor title bar.</li>
<li><em>Command palette</em>: Press <code>Ctrl+Shift+P</code> and search for Containerlab commands (deploy, redeploy, destroy, graph)</li>
<li><em>Context menus</em>: Right-click on a lab nodes in the tree view in the side panel to access all operations</li>
</ul>
<h5>Integrated Packet Capture</h5>
<p>In 2021, capturing packets required a multi-step process involving <code>ip netns exec</code>, <code>tcpdump</code>, and piping to Wireshark:</p>
<p>The VSCode extension now provides integrated packet capture. Wireshark runs in a container and streams its GUI directly into a VSCode tab via VNC. No local Wireshark installation is needed. You can start a capture by right-clicking any interface in the tree view and selecting “Start capture.” Capture files can be saved to disk from within the Wireshark session.</p>
<p><img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/03/vscode-clab-060-edgeshark.png?w=1200&#038;ssl=1" alt="Wireshark capture from interface"></p>
<h5>Draw.io Integration</h5>
<p>The extension can generate <a href="https://draw.io/">draw.io</a> diagrams from your topology. The diagram opens inside a Draw.io editor in VSCode where you can further edit it and save it as a file. This is useful for documentation and presentations.</p>
<h5>Installing the Extension</h5>
<p>To install the extension, open the Extensions tab in VSCode and search for “Containerlab”, or visit the <a href="https://marketplace.visualstudio.com/items?itemName=srl-labs.vscode-containerlab">Visual Studio Marketplace</a>. The extension is free and open-source.</p>
<h4>Other Ecosystem Improvements</h4>
<p>Several other community contributions have improved the Containerlab experience.</p>
<h5>Community Lab Catalog</h5>
<p>The Containerlab community maintains a catalog of ready-made lab topologies at <a href="https://clabs.netdevops.me/">clabs.netdevops.me</a>. These community-contributed labs cover a wide range of scenarios — from basic routing protocols to complex multi-vendor data center fabrics. You can deploy many of these labs directly from the catalog, which makes it easy to explore unfamiliar technologies without building a topology from scratch.</p>
<h5>Clabernetes: Containerlab on Kubernetes</h5>
<p><a href="https://containerlab.dev/manual/clabernetes/">Clabernetes</a> is a newer project that lets you run Containerlab topologies on Kubernetes clusters. This enables users to scale large labs across multiple nodes in a cluster and run labs in cloud environments. While this is beyond what most individual users need, it shows how far the Containerlab ecosystem has grown.</p>
<h3>Install Containerlab</h3>
<p>The Containerlab project offers <a href="https://containerlab.dev/install/">multiple install methods</a>. I chose to install Containerlab from its package repository.</p>
<h4>Prerequisites</h4>
<p>To install and run Containerlab, you need a Linux host — this can be bare metal, a virtual machine, or Windows Subsystem for Linux (WSL2). Ensure you have at least 4 cores or vCPUs and 8 GB of RAM. The exact resources you need depend on the lab you intend to run; small labs with a few FRR containers will run fine with less, but commercial router images can be more demanding.</p>
<p>Containerlab’s main dependency is <a href="https://docs.docker.com/engine/install/">Docker</a>. Install Docker and verify it is running before you proceed:</p>
<pre><code>$ sudo systemctl is-active docker
</code></pre>
<p>You should see the following output:</p>
<pre><code>active
</code></pre>
<p>If Docker is not installed, refer to the <a href="https://docs.docker.com/engine/install/">Docker installation guide</a> for your distribution.</p>
<p>After installing Docker, add your user to the <em>docker</em> group so you can run Docker commands without <code>sudo</code>:</p>
<pre><code>$ sudo usermod -aG docker $USER
$ newgrp docker
</code></pre>
<h5>Install Containerlab</h5>
<p>The Containerlab project offers <a href="https://containerlab.dev/install/">multiple install methods</a>. I chose to install it from a package:</p>
<pre><code>$ echo &quot;deb [trusted=yes] https://netdevops.fury.site/apt/ /&quot; | \
  sudo tee -a /etc/apt/sources.list.d/netdevops.list
$ sudo apt update
$ sudo apt install containerlab
</code></pre>
<p>After the installer finishes, add your username to the <em>clab_admins</em> group.</p>
<pre><code>$ sudo usermod -aG clab_admins $USER
$ newgrp clab_admins
</code></pre>
<p>Restart your computer. Then, check the version to ensure the binary is on your path:</p>
<pre><code>$ clab version
</code></pre>
<p>You should see the following output:</p>
<pre><code>  ____ ___  _   _ _____  _    ___ _   _ _____ ____  _       _     
 / ___/ _ \| \ | |_   _|/ \  |_ _| \ | | ____|  _ \| | __ _| |__  
| |  | | | |  \| | | | / _ \  | ||  \| |  _| | |_) | |/ _` | '_ \ 
| |__| |_| | |\  | | |/ ___ \ | || |\  | |___|  _ &lt;| | (_| | |_) |
 \____\___/|_| \_| |_/_/   \_\___|_| \_|_____|_| \_\_|\__,_|_.__/ 

    version: 0.73.0
     commit: 611350001
       date: 2026-02-08T13:22:45Z
     source: https://github.com/srl-labs/containerlab
 rel. notes: https://containerlab.dev/rn/0.73/
</code></pre>
<h5>Install the VSCode extension (optional)</h5>
<p>If you use VSCode, I recommend installing the Containerlab extension. Open the Extensions tab in VSCode, search for “Containerlab”, and install the extension published by <em>SR Labs</em>. You can also install it from the <a href="https://marketplace.visualstudio.com/items?itemName=srl-labs.vscode-containerlab">Visual Studio Marketplace</a>.</p>
<h3>Deploy and Test a Sample Lab</h3>
<p>With Containerlab and Docker installed, let’s deploy a sample lab to verify that everything works. We will use the <em>frr01</em> example lab that ships with Containerlab and run some tests.</p>
<h4>Deploy the <em>frr01</em> lab</h4>
<p>When you installed Containerlab, it copied a set of ready-made lab examples to the directory <em>/etc/containerlab/lab-examples/</em>. The <a href="https://containerlab.dev/lab-examples/frr01/"><em>frr01</em> lab</a> is a small topology with three FRR routers arranged in a triangle, each with a “PC” (a lightweight Linux container) attached to it.</p>
<p>Navigate to the lab directory and deploy:</p>
<pre><code>$ cd /etc/containerlab/lab-examples/frr01
$ clab deploy
</code></pre>
<p>You will see output indicating that container images are being pulled (on the first run), containers started, links created, and configurations applied. When the deployment finishes, Containerlab prints a summary table:</p>
<pre><code>╭────────────────────┬─────────────────────────────────┬─────────┬───────────────────╮
│        Name        │            Kind/Image           │  State  │   IPv4/6 Address  │
├────────────────────┼─────────────────────────────────┼─────────┼───────────────────┤
│ clab-frr01-PC1     │ linux                           │ running │ 172.20.20.5       │
│                    │ wbitt/network-multitool:3.22.1  │         │ 3fff:172:20:20::5 │
├────────────────────┼─────────────────────────────────┼─────────┼───────────────────┤
│ clab-frr01-PC2     │ linux                           │ running │ 172.20.20.6       │
│                    │ wbitt/network-multitool:3.22.1  │         │ 3fff:172:20:20::6 │
├────────────────────┼─────────────────────────────────┼─────────┼───────────────────┤
│ clab-frr01-PC3     │ linux                           │ running │ 172.20.20.4       │
│                    │ wbitt/network-multitool:3.22.1  │         │ 3fff:172:20:20::4 │
├────────────────────┼─────────────────────────────────┼─────────┼───────────────────┤
│ clab-frr01-router1 │ linux                           │ running │ 172.20.20.3       │
│                    │ quay.io/frrouting/frr:10.5.1    │         │ 3fff:172:20:20::3 │
├────────────────────┼─────────────────────────────────┼─────────┼───────────────────┤
│ clab-frr01-router2 │ linux                           │ running │ 172.20.20.2       │
│                    │ quay.io/frrouting/frr:10.5.1    │         │ 3fff:172:20:20::2 │
├────────────────────┼─────────────────────────────────┼─────────┼───────────────────┤
│ clab-frr01-router3 │ linux                           │ running │ 172.20.20.7       │
│                    │ quay.io/frrouting/frr:10.5.1    │         │ 3fff:172:20:20::7 │
╰────────────────────┴─────────────────────────────────┴─────────┴───────────────────╯
</code></pre>
<p>All six containers are running. The routers use the FRR image and the PCs use the <em>wbitt/network-multitool</em> image, a lightweight Alpine-based container with common network tools.</p>
<h4>Test connectivity</h4>
<p>The frr01 lab comes pre-configured with OSPF routing, so all PCs should be able to reach each other through the router mesh. Run a quick ping from PC1 to PC3 to verify:</p>
<pre><code>$ clab exec --label clab-node-name=PC1 --cmd &quot;ping -c 3 192.168.13.2&quot;
20:42:55 INFO Executed command node=clab-frr01-PC1 command=&quot;ping -c 3 192.168.13.2&quot;
  stdout=
  │ PING 192.168.13.2 (192.168.13.2) 56(84) bytes of data.
  │ 64 bytes from 192.168.13.2: icmp_seq=1 ttl=62 time=0.059 ms
  │ 64 bytes from 192.168.13.2: icmp_seq=2 ttl=62 time=0.045 ms
  │ 64 bytes from 192.168.13.2: icmp_seq=3 ttl=62 time=0.040 ms
  │ 
  │ --- 192.168.13.2 ping statistics ---
  │ 3 packets transmitted, 3 received, 0% packet loss, time 2061ms
  │ rtt min/avg/max/mdev = 0.040/0.048/0.059/0.008 ms
</code></pre>
<p><strong>Note:</strong> We use <code>clab exec</code> to run the ping command because the FRR and multitool containers are based on Alpine Linux and do not include an SSH server. For containers that support SSH, Containerlab provides a built-in <code>ssh</code> command.</p>
<p>The pings succeed, which confirms that Containerlab deployed the containers, created the virtual links between them, and applied the routing configuration correctly.</p>
<h5>Test OSPF Adjacencies</h5>
<p>The <code>clab exec</code> CLI command can run commands on multiple nodes, and you can select the nodes using labels. For example, the <a href="https://raw.githubusercontent.com/srl-labs/containerlab/refs/heads/main/lab-examples/frr01/frr01.clab.yml"><em>frr01.clab.yml</em> topology file</a> organizes nodes into two groups: <em>routers</em> and <em>hosts</em>.</p>
<p>To show the OSPF neighbour status on all routers using one command, run the following:</p>
<pre><code>$ clab exec --label clab-node-group=routers --cmd &quot;vtysh -c 'show ip ospf neighbor'&quot;
22:11:26 INFO Executed command node=clab-frr01-router1 command=&quot;vtysh -c show ip ospf neighbor&quot;
  stdout=
  │ 
  │ Neighbor ID     Pri State           Up Time         Dead Time Address         Interface                        RXmtL RqstL DBsmL
  │ 10.10.10.2        1 Full/DR         1h29m27s          32.842s 192.168.1.2     eth1:192.168.1.1                     0     0     0
  │ 10.10.10.3        1 Full/DR         1h29m27s          32.842s 192.168.2.2     eth2:192.168.2.1                     0     0     0
  │ 

22:11:26 INFO Executed command node=clab-frr01-router2 command=&quot;vtysh -c show ip ospf neighbor&quot;
  stdout=
  │ 
  │ Neighbor ID     Pri State           Up Time         Dead Time Address         Interface                        RXmtL RqstL DBsmL
  │ 10.10.10.1        1 Full/Backup     1h29m27s          32.932s 192.168.1.1     eth1:192.168.1.2                     0     0     0
  │ 10.10.10.3        1 Full/DR         1h29m22s          32.932s 192.168.3.2     eth2:192.168.3.1                     0     0     0
  │ 

22:11:26 INFO Executed command node=clab-frr01-router3 command=&quot;vtysh -c show ip ospf neighbor&quot;
  stdout=
  │ 
  │ Neighbor ID     Pri State           Up Time         Dead Time Address         Interface                        RXmtL RqstL DBsmL
  │ 10.10.10.1        1 Full/Backup     1h29m27s          32.885s 192.168.2.1     eth1:192.168.2.2                     0     0     0
  │ 10.10.10.2        1 Full/Backup     1h29m22s          32.886s 192.168.3.1     eth2:192.168.3.2                     0     0     0
  │ 
</code></pre>
<h4>Visualize the topology with <em>containerlab graph</em></h4>
<p>Now let’s try the improved <code>graph</code> command. While still in the <em>frr01</em> lab directory, run:</p>
<pre><code>$ clab graph
</code></pre>
<p>Containerlab starts a local web server on port 50080. Open your browser and navigate to <code>http://localhost:50080</code>. You will see an interactive topology diagram showing the six nodes and the links between them. The graph displays node names, link endpoints, and — because the lab is running — live data such as management IP addresses.</p>
<p><img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/03/clab-graph-scaled.png?w=1200&#038;ssl=1" alt="containerlab graph web view"></p>
<p>Press <code>Ctrl+C</code> in the terminal to stop the graph web server when you are done.</p>
<p>You can also export the topology to other formats without starting a web server. For example, generate a <a href="https://graphviz.org/doc/info/lang.html">dot file</a> for use in <a href="https://graphviz.org/">Graphviz</a>:</p>
<pre><code>$ clab graph --dot
</code></pre>
<p>Or generate a <a href="https://mermaid.ai/open-source/">Mermaid diagram</a> for use in Markdown documentation:</p>
<pre><code>$ clab graph --mermaid
</code></pre>
<h4>Visualize the topology in VSCode</h4>
<p>If you installed the Containerlab VSCode extension, you can visualize the topology directly inside your editor.</p>
<p>Start VSCode and open the folder containing the lab topology file. The Containerlab extension will automatically detect a running lab.</p>
<h5>TopoViewer</h5>
<p>Double-click on the <code>frr01.clab.yml</code> topology file in VSCode. This opens the <a href="https://github.com/asadarafat/topoViewer">TopoViewer</a> panel with an interactive diagram of the running lab. You can right-click on nodes to SSH into them or open a container shell.</p>
<p><img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/03/vscode-clab-090-topo-graph-scaled.png?w=1200&#038;ssl=1" alt="VSCode TopoViewer showing the frr01 lab"></p>
<h5>Draw.io view</h5>
<p>You can also use Draw.io, if you prefer.</p>
<p>In the Containerlab sidebar panel, right-click on the running lab and hover over <em>Graph Lab (draw.io)</em>. Choose a layout mode (horizontal, vertical, or interactive) and a Draw.io diagram opens inside VSCode.</p>
<p><img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/03/vscode-clab-080-drawio-graph-scaled.png?w=1200&#038;ssl=1" alt="VSCode Draw.io showing the frr01 lab"></p>
<p>You can save the Draw.io graph as a file and import it into other programs that support that format.</p>
<h4>Tear down the lab</h4>
<p>When you are finished exploring, destroy the lab to clean up all containers and virtual links:</p>
<pre><code>$ clab destroy
</code></pre>
<p>You should see output confirming that each container has been removed. At this point, you have confirmed that Containerlab, Docker networking, visualization tools, and your user permissions are all working correctly.</p>
<h4>Perform more network emulation experiments</h4>
<p>At this point, you have a running lab that you may use for learning or validation. You can add configurations to any node and experiment with IP networking functionality.</p>
<p>Some experiments you might try are:</p>
<ul>
<li>Verify the routing table from the PCs</li>
<li>Watch OSPF routes on the routers</li>
<li>Break a link and observe OSPF convergence</li>
<li>Add delay, jitter, or loss to links and test the impact</li>
<li>Capture packets on a link</li>
<li>…and more</li>
</ul>
<h3>Conclusion</h3>
<p>Five years after my original review, Containerlab has matured from a promising CLI tool into a polished, developer-friendly network lab platform with a rich ecosystem surrounding it.</p>
<p>The most visible change is the tooling that now surrounds Containerlab. The VSCode extension turns your editor into a full lab management environment. You can design topologies graphically with TopoViewer, deploy and destroy labs with keyboard shortcuts, and generate diagrams for documentation. Features like Wireshark integration, the community lab catalog at <a href="https://clabs.netdevops.me/">clabs.netdevops.me</a>, and Clabernetes for Kubernetes deployment round out an ecosystem that did not exist five years ago.</p>
<p>If you have not tried Containerlab recently, or if you have never tried it at all, I encourage you to install it and deploy one of the bundled example labs.</p>
</div>
<p>The post <a href="https://brianlinkletter.com/2026/03/containerlab-network-emulator-v0-73-review/">Containerlab Network Emulator v0.73 Review</a> appeared first on <a href="https://brianlinkletter.com">Open-Source Routing and Network Simulation</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">6024</post-id>	</item>
		<item>
		<title>Recreating a Real-World BGP Hijack with the Kathará Network Emulator</title>
		<link>https://brianlinkletter.com/2026/02/bgp-hijack-with-kathara-network-emulator/</link>
		
		<dc:creator><![CDATA[Brian Linkletter]]></dc:creator>
		<pubDate>Mon, 16 Feb 2026 15:09:01 +0000</pubDate>
				<category><![CDATA[Learning IP]]></category>
		<category><![CDATA[Network Simulation]]></category>
		<category><![CDATA[Open Source Networking]]></category>
		<category><![CDATA[BGP]]></category>
		<category><![CDATA[emulation]]></category>
		<category><![CDATA[Kathara]]></category>
		<guid isPermaLink="false">https://opensourcenetworksimulators.com/?p=5969</guid>

					<description><![CDATA[<p>Use the Kathará network emulator to recreate one of the most famous BGP hijacking incidents in Internet history, the 2008 YouTube hijack. By building a small network topology and simulating a similar attack, you will learn both the fundamentals of Kathará and to gain hands-on experience with BGP security concepts.</p>
<p>The post <a href="https://brianlinkletter.com/2026/02/bgp-hijack-with-kathara-network-emulator/">Recreating a Real-World BGP Hijack with the Kathará Network Emulator</a> appeared first on <a href="https://brianlinkletter.com">Open-Source Routing and Network Simulation</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-jetpack-markdown"><p><a href="https://www.kathara.org/">Kathará</a> is a container-based network emulator developed by researchers at Roma Tre University in Italy as a modern successor to the <a href="https://brianlinkletter.com/tag/netkit/">Netkit network emulator</a>. Coincidentally, Roma Tre University is also the <a href="https://massimocandela.com/bgplay-doc/Kathar%C3%A1">same organization that developed BGPlay</a>, a tool used to investigate <a href="https://www.catchpoint.com/blog/what-is-bgp">BGP</a> incidents.</p>
<p>Kathará uses Docker containers to emulate network devices. This approach enables users to create complex network topologies comprised of dozens of routers on a modest laptop. Kathará uses simple text-based configuration files that are easy to version-control and share. It’s open source, actively maintained, and runs on Linux, Windows, and MacOS.</p>
<p>In this tutorial, I will use the <em>Kathará</em> network emulator to recreate one of the most famous BGP hijacking incidents in Internet history, the <a href="https://www.ripe.net/publications/news/industry-developments/youtube-hijacking-a-ripe-ncc-ris-case-study">2008 YouTube hijack</a>. By building a small network topology and simulating a similar attack, we will learn both the fundamentals of Kathará and to gain hands-on experience with BGP security concepts.</p>
<h3>Install Kathará</h3>
<p>First, we will install the Kathará network emulator and test it by setting up a basic lab environment.</p>
<h4>Install Docker</h4>
<p>Kathará uses Docker as its container runtime. Install Docker on your Linux system using the <a href="https://docs.docker.com/engine/install/">official Docker installation guide</a>.</p>
<p>After that, add your user to the <code>docker</code> group so you can run containers without <code>sudo</code>:</p>
<pre><code>$ sudo usermod -aG docker $USER
</code></pre>
<p>Log out and log back in for the group change to take effect. Verify Docker is working:</p>
<pre><code>$ docker run hello-world
</code></pre>
<p>You should see a message confirming Docker is installed correctly.</p>
<h4>Install Kathará</h4>
<p>The <a href="https://github.com/KatharaFramework/Kathara/wiki/Linux">install instructions on the Kathará wiki</a> <strong>are outdated</strong>[^1] and do not work in Ubuntu 24.04 because they use the deprecated <em>apt-key</em> command. You can skip the <em>apt-key</em> command. Instead, follow the <a href="https://launchpad.net/~katharaframework/+archive/ubuntu/kathara">Kathara instructions on the Launchpad platform</a> to add the Kathará PPA to Ubuntu, then install Kathará.</p>
<p>I summarize the modified install commands, below:</p>
<pre><code>$ sudo add-apt-repository ppa:katharaframework/kathara
$ sudo apt update
$ sudo apt install kathara
</code></pre>
<p>Verify the installation:</p>
<pre><code>$ kathara --version
</code></pre>
<p>You should see output showing the Kathará version, which was 3.8.0 when I wrote this post.</p>
<h4>Verify Your Setup</h4>
<p>Run a the <em>check</em> command to proactively <a href="https://hub.docker.com/r/kathara/base">download the Kathará base container</a> and the <a href="https://hub.docker.com/r/kathara/katharanp/tags">Kathará network plugin container</a>, and validate that Kathará can communicate with Docker:</p>
<pre><code class="language-text">$ kathara check
</code></pre>
<p>With your environment ready, we can move on to configuring Kathará.</p>
<h4>Set the Terminal Emulator</h4>
<p>Run the <em>kathara settings</em> command and set the terminal emulator used by emulated devices to be the <em>Gnome Terminal</em>. Alternatively, you could install xterm, because it is the default used by Kathará.</p>
<pre><code class="language-text">$ kathara settings
</code></pre>
<p>In the menu that appears, select <em>5</em>, for <em>Choose terminal</em>:</p>
<pre><code class="language-text">  ╔═════════════════════════════════════════════════════════════════════════╗
  ║                                                                         ║
  ║                            Kathara Settings                             ║
  ║                                                                         ║
  ╠═════════════════════════════════════════════════════════════════════════╣
  ║                                                                         ║
  ║                      Choose the option to change.                       ║
  ║                                                                         ║
  ╠═════════════════════════════════════════════════════════════════════════╣
  ║                                                                         ║
  ║    1 - Choose default manager                                           ║
  ║    2 - Choose default image                                             ║
  ║    3 - Automatically open terminals on startup                          ║
  ║    4 - Choose device shell to be used                                   ║
  ║    5 - Choose terminal emulator to be used                              ║
  ║    6 - Choose Kathara prefixes                                          ║
  ║    7 - Choose logging level to be used                                  ║
  ║    8 - Print Startup Logs on device startup                             ║
  ║    9 - Enable IPv6                                                      ║
  ║   10 - Choose Docker Network Plugin version                             ║
  ║   11 - Automatically mount /hosthome on startup                         ║
  ║   12 - Automatically mount /shared on startup                           ║
  ║   13 - Docker Image Update Policy                                       ║
  ║   14 - Enable Shared Collision Domains                                  ║
  ║   15 - Configure a remote Docker connection                             ║
  ║   16 - Exit                                                             ║
  ║                                                                         ║
  ║                                                                         ║
  ╚═════════════════════════════════════════════════════════════════════════╝
  &gt;&gt; 5

</code></pre>
<p>Then, choose <em>2</em>, to select <em>gnome-terminal</em>:</p>
<pre><code class="language-text">  ╔═════════════════════════════════════════════════════════════════════════╗
  ║                                                                         ║
  ║                   Choose terminal emulator to be used                   ║
  ║                                                                         ║
  ║                         Current: /usr/bin/xterm                         ║
  ║                                                                         ║
  ╠═════════════════════════════════════════════════════════════════════════╣
  ║                                                                         ║
  ║     Terminal emulator application to be used for device terminals.      ║
  ║   **The application must be correctly installed in the host system!**   ║
  ║                      Default is `/usr/bin/xterm`.                       ║
  ║                                                                         ║
  ╠═════════════════════════════════════════════════════════════════════════╣
  ║                                                                         ║
  ║    1 - /usr/bin/xterm                                                   ║
  ║    2 - /usr/bin/gnome-terminal                                          ║
  ║    3 - TMUX                                                             ║
  ║    4 - Choose another terminal emulator                                 ║
  ║    5 - Return to Kathara Settings                                       ║
  ║                                                                         ║
  ║                                                                         ║
  ╚═════════════════════════════════════════════════════════════════════════╝
  &gt;&gt; 2
</code></pre>
<p>Then select <em>16</em> to <em>Exit</em>.</p>
<h4>Pull the FRR Docker Image</h4>
<p>We’ll use the <a href="https://hub.docker.com/r/kathara/frr"><em>kathara/frr</em> Docker image</a>, which includes FRRouting, an open-source routing suite that supports BGP, OSPF, and other protocols. Pull it now to save time later:</p>
<pre><code>$ docker pull kathara/frr
</code></pre>
<h4>Test a very simple lab</h4>
<p>To verify that Kathará is working, and to get an initial view of how Kathara labs are created and nodes are configured, let’s create a minimal lab with two FRR routers connected to each other and test connectivity between them.</p>
<p>First, create a directory for your test lab:</p>
<pre><code>$ mkdir -p ~/Kathara/kathara-test
$ cd ~/Kathara/kathara-test
</code></pre>
<p>Create the <em>lab.conf</em> file that defines two routers connected by a shared network segment:</p>
<pre><code>$ cat &gt; lab.conf &lt;&lt; 'EOF'
LAB_NAME=&quot;Simple Two-Router Test&quot;
r1[0]=&quot;link1&quot;
r1[image]=&quot;kathara/frr&quot;
r2[0]=&quot;link1&quot;
r2[image]=&quot;kathara/frr&quot;
EOF
</code></pre>
<p>Create startup scripts to assign IP addresses to each router:</p>
<pre><code>$ cat &gt; r1.startup &lt;&lt; 'EOF'
ip addr add 10.0.0.1/24 dev eth0
ip link set eth0 up
EOF
</code></pre>
<pre><code>$ cat &gt; r2.startup &lt;&lt; 'EOF'
ip addr add 10.0.0.2/24 dev eth0
ip link set eth0 up
EOF
</code></pre>
<p>The lab directory should now contain:</p>
<pre><code>kathara-test/
├── lab.conf
├── r1.startup
└── r2.startup
</code></pre>
<p>Start the lab:</p>
<pre><code>$ kathara lstart
</code></pre>
<p>Kathara reads the <em>lab.conf</em> file and the two <em>startup</em> files to set up and configure the lab. You should see output indicating both routers have started:</p>
<pre><code>┌─────────────────────────────────────────────────────────────────────────────────────┐
│                              Starting Network Scenario                              │
└─────────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ Name: Simple Two-Router Test                                                        │
└─────────────────────────────────────────────────────────────────────────────────────┘
[Deploying collision domains]   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1/1
[Deploying devices]   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2/2
</code></pre>
<p>And, you should see two new terminal windows. Each one is connected to one of the routers in the lab. By default, Kathara starts terminals with the Bash shell.</p>
<p>Inside the r1 container’s terminal window, ping r2’s IP address:</p>
<pre><code>root@r1:/# ping -c2 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.995 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=1.08 ms

--- 10.0.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.995/1.038/1.081/0.043 ms
root@r1:/# 
</code></pre>
<p>We demonstrated that a simple lab with two routers works as expected. Clean up the lab:</p>
<pre><code>$ kathara lclean
</code></pre>
<p>This removes all containers and networks created for the lab.</p>
<pre><code>┌─────────────────────────────────────────────────────────────────────────────────────┐
│                              Stopping Network Scenario                              │
└─────────────────────────────────────────────────────────────────────────────────────┘
[Deleting devices]   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2/2
[Deleting collision domains]   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1/1
</code></pre>
<p>With Kathará working correctly, we’re ready to build more complex topologies.</p>
<h3>Understanding BGP Hijacks and Route Leaks</h3>
<p><a href="https://manrs.org/2020/09/what-is-bgp-prefix-hijacking-part-1/">BGP hijacks</a> and <a href="https://datatracker.ietf.org/doc/html/rfc7908">route leaks</a> are among the most significant threats to Internet routing security. <a href="https://www.kentik.com/blog/a-brief-history-of-the-internets-biggest-bgp-incidents/">Understanding how these incidents occur</a> helps network engineers implement proper safeguards. By recreating these scenarios in a safe, isolated lab environment, one can observe exactly how prefix hijacks propagate through a network and experiment with mitigation techniques without affecting real infrastructure.</p>
<h4>Origin Hijack</h4>
<p>An origin hijack occurs when an AS announces a prefix it doesn’t legitimately own. The hijacking AS claims to be the origin of the route, essentially saying “I own this IP address range” when it doesn’t.</p>
<p>For example, if AS400 legitimately owns the prefix 40.40.0.0/16, an origin hijack would occur if AS300 started announcing that same prefix as if it originated there. Routers receiving both announcements would choose between them based on BGP path selection rules. Depending on network topology, some parts of the Internet might start sending traffic for 40.40.0.0/16 toward AS300 instead of the legitimate owner, AS400.</p>
<h4>More-Specific Prefix Hijack</h4>
<p>More-specific prefix hijack is a variant of origin hijacking. Instead of announcing the same prefix as the victim, the attacker announces a more-specific, or longer, prefix that falls within the victim’s address space.</p>
<p>BGP routers always prefer more-specific routes. If AS400 announces 40.40.0.0/16 and an attacker announces 40.40.0.0/17 and 40.40.128.0/17, the attacker’s more-specific /17 routes will be preferred everywhere in the Internet, completely overriding the legitimate /16 announcement.</p>
<h4>Route Leak</h4>
<p>A route leak occurs when an AS violates the expected routing policies by redistributing routes in ways that break the traditional customer-provider-peer relationships on the Internet. Unlike origin hijacks or more-specific prefix hijacks, the origin AS information remains correct. The problem is that the route is propagated where it shouldn’t be.</p>
<p>Route leaks are almost always accidental and are usually caused by misconfiguration, but their effects can be severe. When a small customer AS suddenly appears to offer a shortcut to major networks, traffic can flood through infrastructure that was never designed to handle it.</p>
<h3>The 2008 Pakistan YouTube Hijack</h3>
<p>The incident we’ll recreate is the famous YouTube hijacking that occurred on February 24, 2008, when Pakistan Telecom (<a href="https://www.peeringdb.com/asn/17557">AS17557</a>) accidentally blocked YouTube globally. This event has become a textbook example of how BGP vulnerabilities can have worldwide impact and is <a href="https://www.ripe.net/publications/news/industry-developments/youtube-hijacking-a-ripe-ncc-ris-case-study">well-documented by RIPE NCC</a> and other organizations.</p>
<p>The key players were:</p>
<ul>
<li><em>YouTube (AS36561)</em>: The victim, legitimately announcing prefix 208.65.152.0/22</li>
<li><em>Pakistan Telecom (AS17557)</em>: The hijacker, who announced the more-specific prefix 208.65.153.0/24</li>
<li><em>PCCW (AS3491)</em>: Pakistan Telecom’s upstream provider who propagated the hijack</li>
<li><em>Various upstream providers</em>: Who received and further propagated the bogus route</li>
</ul>
<h4>What Happened</h4>
<p>The Pakistan government ordered local ISPs to block access to YouTube due to content the government deemed offensive. Pakistan Telecom attempted to comply by creating a blackhole route for a more specific prefix that was part of YouTube’s allocated IP address space. However, instead of keeping this route prefix internal to their own network, they accidentally announced it to the global Internet through their upstream provider PCCW (AS3491).</p>
<p>YouTube was announcing their address space as a /22 prefix (208.65.152.0/22), which covers IP addresses from 208.65.152.0 to 208.65.155.255. Pakistan Telecom announced a /24 prefix (208.65.153.0/24), which is a subset of YouTube’s range.</p>
<p>BGP’s “longest prefix match” rule means routers always prefer more-specific routes. A /24 prefix is more specific than a /22, so routers receiving both announcements would send traffic for 208.65.153.0/24 toward Pakistan Telecom where it was blackholed. This affected global YouTube traffic because PCCW propagated the announcement to their peers and transit providers, who propagated the more-specific prefixes worldwide.</p>
<p>YouTube responded to the attack by announcing even-more-specific prefixes 208.65.153.128/25 and 208.65.153.0/25. Routers that received these announcements would have preferred the more-specific prefix and sent the traffic to YouTube. Eventually, PCCW properly resolved the problem by filtering the offending route.</p>
<p>This incident represents a straightforward BGP hijack with no other complications like AS-path manipulation. It demonstrates both the attack and the mitigation so it is a good BGP security example to study in a lab.</p>
<h3>Kathará BGP Lab Setup</h3>
<p>When you start a lab, Kathará reads the lab configuration files in the lab folder, creates Docker containers for each device, and sets up virtual network interfaces to connect them. The <a href="https://www.kathara.org/man-pages/kathara.1.html">Kathará man pages</a> provide information about how to use the command line interface and the <a href="https://www.kathara.org/man-pages/kathara-lab.conf.5.html">Kathará Lab man pages</a> provide information about building lab configuration files.</p>
<p>The <em>kathara/frr</em> image we pulled earlier contains <a href="https://frrouting.org/">FRRouting</a>, which enables us to create fully-featured routers capable of running BGP, OSPF, IS-IS, and other routing protocols. Kathará also provides base images for simple hosts, and you can use other <a href="https://hub.docker.com/u/kathara/">Kathará device images</a> or any other Docker image that suits your needs.</p>
<h4>What We’ll Build</h4>
<p>In our lab, we’ll create a simplified 4-AS topology that will help us emulate this incident:</p>
<pre><code>                     +----------+
                     | upstream |
                     |  AS400   |
                     +----+-----+
                          |
                          | 10.0.3.0/24
                          |
                     +----+-----+
                     | transit  |
                     |  AS300   |
                     +----+-----+
                    /            \
       10.0.1.0/24 /              \ 10.0.2.0/24
                  /                \
           +-----+----+       +-----+-----+
           | youtube  |       | pakistan  |
           |  AS100   |       |  AS200    |
           +----------+       +-----------+
             (Victim)          (Hijacker)
</code></pre>
<p>In this topology:</p>
<ul>
<li><em>AS100 (YouTube)</em> is the victim, legitimately announcing 100.100.0.0/22</li>
<li><em>AS200 (Pakistan Telecom)</em> is the hijacker, who will announce a more-specific 100.100.0.0/24</li>
<li><em>AS300 (Transit/PCCW)</em> is the transit provider connecting both ASes to the Internet</li>
<li><em>AS400 (Upstream)</em> represents upstream providers who observe the hijack propagation</li>
</ul>
<p>We’ll first configure this topology during normal BGP operations where only AS100 announces its prefix. Then we’ll introduce the hijack by having AS200 announce a more-specific prefix, and observe how it overrides the legitimate route. Finally, we’ll demonstrate how AS100 can counter the hijack using even more-specific announcements.</p>
<h4>Lab Structure</h4>
<p>As you saw in our simple test, above, a Kathará lab is simply a directory containing configuration files that describe your network topology and configuration. The lab file and directory structure is straightforward. The <em>lab.conf</em> file is mandatory. Other files or folders are optional.</p>
<table>
<thead>
<tr>
<th>File/Directory</th>
<th>Purpose</th>
</tr>
</thead>
<tbody>
<tr>
<td><em>lab.conf</em></td>
<td>Main configuration file that defines devices and network connections</td>
</tr>
<tr>
<td><em>lab.dep</em></td>
<td>Optional network scenario dependencies defines the order in which devices start</td>
</tr>
<tr>
<td><em>lab.ext</em></td>
<td>Optional direct connections between virtual lab devices and physical host interfaces</td>
</tr>
<tr>
<td><em>&lt;device&gt;/</em></td>
<td>Directory for each device containing configuration files (optional)</td>
</tr>
<tr>
<td><em>&lt;device&gt;.startup</em></td>
<td>Shell script that runs when a device starts (optional)</td>
</tr>
</tbody>
</table>
<h4>Lab Configuration File Syntax</h4>
<p>The best way to learn the lab configuration file syntax is to <a href="https://github.com/KatharaFramework/Kathara-Labs">look at the example labs</a> provided by the Kathará team.</p>
<p>If you want to see all options that are available, the Kathará team provides man-pages documentation for the syntax of each lab configuration file. See the links below:</p>
<ul>
<li><em><a href="https://www.kathara.org/man-pages/kathara-lab.conf.5.html">lab.conf</a></em>: mandatory configuration file syntax manual</li>
<li><em><a href="https://www.kathara.org/man-pages/kathara-lab.dep.5.html">lab.dep</a></em>: optional configuration file syntax manual</li>
<li><em><a href="https://www.kathara.org/man-pages/kathara-lab.ext.5.html">lab.ext</a></em>:  optional configuration file syntax manual</li>
</ul>
<h4>Device Directories and Lab Structure</h4>
<p>For each device in your lab, you will usually create a directory with the same name. Files in this directory are copied into the container’s filesystem when the lab starts. This is how you provide configuration files to your devices.</p>
<p>In this lab example, you will create a directory structure like the one shown below:</p>
<pre><code>bgp-youtube-hijack/
├── lab.conf
├── youtube/
│   └── etc/
│       └── frr/
│           ├── daemons
│           └── frr.conf
├── youtube.startup
├── pakistan/
│   └── etc/
│       └── frr/
│           ├── daemons
│           └── frr.conf
├── pakistan.startup
├── transit/
│   └── etc/
│       └── frr/
│           ├── daemons
│           └── frr.conf
├── transit.startup
├── upstream/
│   └── etc/
│       └── frr/
│           ├── daemons
│           └── frr.conf
└── upstream.startup
</code></pre>
<p>The contents of <code>youtube/etc/frr/</code> will appear at <code>/etc/frr/</code> inside the <em>youtube</em> container. This allows you to pre-configure FRR’s routing daemons with the required BGP settings.</p>
<h4>BGP Lab Config File</h4>
<p>To emulate a small network in which we can “replay” the famous YouTube BGP Hijack incident, we create a <em>lab.conf</em> file that creates four routers connected as shown in the diagram above.</p>
<p>First, create the lab directory</p>
<pre><code>$ mkdir -p ~/Kathara/bgp-youtube-hijack
$ cd ~/Kathara/bgp-youtube-hijack
</code></pre>
<p>Create the <em>lab.conf</em> file in your favorite editor and enter the following configuration information:</p>
<pre><code># ~/bgp-youtube-hijack/lab.conf

LAB_NAME=&quot;YouTube BGP Hijack 2008&quot; 
LAB_DESCRIPTION=&quot;This lab recreates the famous February 24, 2008 incident where Pakistan Telecom (AS17557) hijacked YouTube's traffic by announcing a more-specific prefix within YouTube's allocated IP address space.&quot;
LAB_VERSION=1.0

# YouTube (Victim) - AS100
youtube[0]=&quot;link_youtube_transit&quot;
youtube[image]=&quot;kathara/frr&quot;

# Pakistan Telecom (Hijacker) - AS200
pakistan[0]=&quot;link_pakistan_transit&quot;
pakistan[image]=&quot;kathara/frr&quot;

# Transit/PCCW - AS300
transit[0]=&quot;link_youtube_transit&quot;
transit[1]=&quot;link_pakistan_transit&quot;
transit[2]=&quot;link_transit_upstream&quot;
transit[image]=&quot;kathara/frr&quot;

# Upstream Observer - AS400
upstream[0]=&quot;link_transit_upstream&quot;
upstream[image]=&quot;kathara/frr&quot;
</code></pre>
<p>The syntax follows a simple pattern: <code>device[interface]=&quot;collision_domain&quot;</code>, where “collision domain” is simply the name you assign a network segment. All devices with interfaces on the same network segment can communicate directly with each other.</p>
<p>The <code>device[image]</code> property specifies which Docker image to use for each device. In this example, we are using the <a href="https://hub.docker.com/r/kathara/frr">kathara/frr</a> image for all routers, but you could use different images like <a href="https://hub.docker.com/r/kathara/bird">kathara/bird</a> or <a href="https://containerlab.dev/manual/kinds/vyosnetworks_vyos/#">vyos</a>, if you wish.</p>
<h4>IP Addressing Scheme</h4>
<p>Before we configure the routers, we must first establish the IP addressing plan:</p>
<table>
<thead>
<tr>
<th>Link</th>
<th>Network</th>
<th>Device</th>
<th>Interface</th>
<th>IP Address</th>
</tr>
</thead>
<tbody>
<tr>
<td>YouTube to Transit</td>
<td>10.0.1.0/30</td>
<td>youtube</td>
<td>eth0</td>
<td>10.0.1.1/30</td>
</tr>
<tr>
<td></td>
<td></td>
<td>transit</td>
<td>eth0</td>
<td>10.0.1.2/30</td>
</tr>
<tr>
<td>Pakistan to Transit</td>
<td>10.0.2.0/30</td>
<td>pakistan</td>
<td>eth0</td>
<td>10.0.2.1/30</td>
</tr>
<tr>
<td></td>
<td></td>
<td>transit</td>
<td>eth1</td>
<td>10.0.2.2/30</td>
</tr>
<tr>
<td>Transit to Upstream</td>
<td>10.0.3.0/30</td>
<td>transit</td>
<td>eth2</td>
<td>10.0.3.1/30</td>
</tr>
<tr>
<td></td>
<td></td>
<td>upstream</td>
<td>eth0</td>
<td>10.0.3.2/30</td>
</tr>
</tbody>
</table>
<h4>Loopback addresses</h4>
<p>Each AS router will have one or two loopback addresses that are reachable within its advertised prefix. The loopback addresses and announced prefixes for this hijack scenario will be:</p>
<table>
<thead>
<tr>
<th>AS</th>
<th>Role</th>
<th>Loopback</th>
<th>Announced Prefix</th>
</tr>
</thead>
<tbody>
<tr>
<td>AS100 (YouTube)</td>
<td>Victim</td>
<td>100.100.0.1/32&lt;br&gt;100.100.1.1/32</td>
<td>100.100.0.0/22 (the /22 aggregate)</td>
</tr>
<tr>
<td>AS200 (Pakistan)</td>
<td>Hijacker</td>
<td>200.200.0.1/32</td>
<td>100.100.0.0/24 (hijacked more-specific)</td>
</tr>
<tr>
<td>AS300 (Transit)</td>
<td>PCCW</td>
<td>30.30.0.1/32</td>
<td>30.30.0.0/16</td>
</tr>
<tr>
<td>AS400 (Upstream)</td>
<td>Observer</td>
<td>40.40.0.1/32</td>
<td>40.40.0.0/16</td>
</tr>
</tbody>
</table>
<p>Notice that YouTube announces 100.100.0.0/22 while Pakistan will announce 100.100.0.0/24, which is a more-specific prefix within YouTube’s range, after we trigger the hijack.</p>
<h4>FRR Daemons Configuration</h4>
<p>Each router needs a <em>daemons</em> file to tell FRR which routing protocols to enable. Create the directory structure and daemons file for each device. The content is identical for all routers:</p>
<pre><code>$ mkdir -p youtube/etc/frr
$ mkdir -p pakistan/etc/frr
$ mkdir -p transit/etc/frr
$ mkdir -p upstream/etc/frr
</code></pre>
<p>Create the <em>daemons</em> file for each router. Here’s the content, which is the same for all four:</p>
<p>For the YouTube router, create the <a href="https://docs.frrouting.org/en/latest/setup.html#daemons-configuration-file"><em>daemons</em> file</a>. In this lab scenario, the <em>/etc/frr/daemons</em> file copied to each router will contain the following configuration:</p>
<pre><code># /etc/frr/daemons
zebra=yes
bgpd=yes
vtysh_enable=yes
zebra_options=&quot;  -A 127.0.0.1 -s 90000000&quot;
bgpd_options=&quot;   -A 127.0.0.1&quot;
</code></pre>
<p>Save this content to:</p>
<ul>
<li><em>youtube/etc/frr/daemons</em></li>
<li><em>pakistan/etc/frr/daemons</em></li>
<li><em>transit/etc/frr/daemons</em></li>
<li><em>upstream/etc/frr/daemons</em></li>
</ul>
<h4>frr.conf Files</h4>
<p>Each router is pre-configured with an <a href="https://docs.frrouting.org/en/latest/basic.html#integrated-config-file"><em>frr.conf</em> file</a>.</p>
<h5>YouTube Configuration (AS100) — The Victim</h5>
<p>The following FRR configuration file sets up BGP with AS number 100, representing YouTube’s AS36561, peers with Transit at 10.0.1.2 (AS300), and announces the 100.100.0.0/16 prefix, representing YouTube’s 208.65.152.0/22.</p>
<p>Create <em>youtube/etc/frr/frr.conf</em>:</p>
<pre><code>frr version 9.1
frr defaults traditional
hostname youtube
log syslog informational
no ipv6 forwarding
service integrated-vtysh-config
!
interface eth0
 ip address 10.0.1.1/24
 description Link to Transit
exit
!
interface lo
 ip address 100.100.0.1/32
 ip address 100.100.1.1/32
 description Loopbacks - within announced /22 prefix
exit
!
router bgp 100
 bgp router-id 100.100.0.1
 no bgp ebgp-requires-policy
 neighbor 10.0.1.2 remote-as 300
 neighbor 10.0.1.2 description Transit-PCCW
 address-family ipv4 unicast
  network 100.100.0.0/22
  neighbor 10.0.1.2 activate
 exit-address-family
exit
!
ip route 100.100.0.0/22 blackhole
!
end
</code></pre>
<p>This lab scenario, YouTube is the legitimate owner of the 100.100.0.0/22 prefix.</p>
<h5>Pakistan Telecom Configuration (AS200) — The Hijacker</h5>
<p>Initially, Pakistan Telecom will NOT announce the hijacked prefix. We’ll add that later to demonstrate the hijack. For now, it only announces its own legitimate prefix.</p>
<p>Create <em>pakistan/etc/frr/frr.conf</em>:</p>
<pre><code>frr version 9.1
frr defaults traditional
hostname pakistan
log syslog informational
no ipv6 forwarding
service integrated-vtysh-config
!
interface eth0
 ip address 10.0.2.2/24
 description Link to Transit
exit
!
interface lo
 ip address 200.200.0.1/32
 description Loopback - within announced /16 prefix
exit
!
router bgp 200
 bgp router-id 200.200.0.1
 no bgp ebgp-requires-policy
 neighbor 10.0.2.1 remote-as 300
 neighbor 10.0.2.1 description Transit-PCCW
 address-family ipv4 unicast
  network 200.200.0.0/16
  neighbor 10.0.2.1 activate
 exit-address-family
exit
!
ip route 200.200.0.0/16 blackhole
!
end
</code></pre>
<h5>Transit/PCCW Configuration (AS300)</h5>
<p>Transit represents PCCW (AS3491), the provider that connected both YouTube and Pakistan Telecom and inadvertently propagated the hijack. It peers with all three other ASes and provides transit.</p>
<p>Create <em>transit/etc/frr/frr.conf</em>:</p>
<pre><code>frr version 9.1
frr defaults traditional
hostname transit
log syslog informational
no ipv6 forwarding
service integrated-vtysh-config
!
interface eth0
 ip address 10.0.1.2/24
 description Link to YouTube
exit
!
interface eth1
 ip address 10.0.2.1/24
 description Link to Pakistan
exit
!
interface eth2
 ip address 10.0.3.1/24
 description Link to Upstream
exit
!
interface lo
 ip address 30.30.0.1/32
 description Loopback - within announced /16 prefix
exit
!
router bgp 300
 bgp router-id 30.30.0.1
 no bgp ebgp-requires-policy
 !
 neighbor 10.0.1.1 remote-as 100
 neighbor 10.0.1.1 description YouTube
 !
 neighbor 10.0.2.2 remote-as 200
 neighbor 10.0.2.2 description Pakistan-Telecom
 !
 neighbor 10.0.3.2 remote-as 400
 neighbor 10.0.3.2 description Upstream
 !
 address-family ipv4 unicast
  network 30.30.0.0/16
  neighbor 10.0.1.1 activate
  neighbor 10.0.2.2 activate
  neighbor 10.0.3.2 activate
 exit-address-family
exit
!
ip route 30.30.0.0/16 blackhole
!
end
</code></pre>
<p>Note that Transit has <strong>no outbound filtering</strong>—it simply propagates all routes it learns. This is the configuration weakness that allowed the real-world hijack to spread globally.</p>
<h5>Upstream Configuration (AS400)</h5>
<p>Upstream represents the broader Internet—a provider that will receive and observe routes from Transit. This is where we’ll see the hijack propagate.</p>
<p>Create <em>upstream/etc/frr/frr.conf</em>:</p>
<pre><code>frr version 9.1
frr defaults traditional
hostname upstream
log syslog informational
no ipv6 forwarding
service integrated-vtysh-config
!
interface eth0
 ip address 10.0.3.2/24
 description Link to Transit
exit
!
interface lo
 ip address 40.40.0.1/32
 description Loopback - within announced /16 prefix
exit
!
router bgp 400
 bgp router-id 40.40.0.1
 no bgp ebgp-requires-policy
 neighbor 10.0.3.1 remote-as 300
 neighbor 10.0.3.1 description Transit-PCCW
 address-family ipv4 unicast
  network 40.40.0.0/16
  neighbor 10.0.3.1 activate
 exit-address-family
exit
!
ip route 40.40.0.0/16 blackhole
!
end
</code></pre>
<h4>Startup Files</h4>
<p>The <code>&lt;device&gt;.startup</code> file is a shell script that runs inside the container when it starts. This is where you configure network interfaces, start services, or run any initialization commands.</p>
<h5>How Kathará Creates Ethernet Interfaces</h5>
<p>Before each node’s startup script runs, Kathará creates network interfaces on each node based on the <em>lab.conf</em> file. So, to avoid conflicts or weird behaviour, we should not create interfaces in the device startup files or by copying network interface configuration files into the node container.</p>
<p>When the <code>kathara lstart</code> command is run, Kathará creates the Docker container for a node, creates virtual ethernet interfaces like <em>eth0</em>, <em>eth1</em>, etc. and attaches them to the container, connects each interface to the specified collision domain (virtual network bridge), and then runs the node’s startup script.</p>
<p>The interfaces exist and are in “DOWN” state when your startup script begins.</p>
<h5>Interface Naming Convention</h5>
<p>The Ethernet device numbering convention matches the numbers defines in the <em>lab.conf</em> file. For example, the <em>Transit</em> router’s interfaces would be mapped from teh configuration file to the container as follows:</p>
<ul>
<li>transit[0] = eth0</li>
<li>transit[1] = eth1</li>
<li>transit[2] = eth2</li>
</ul>
<h5>youtube.startup</h5>
<p>Create the file <em>youtube.startup</em> and add the following startup commands:</p>
<pre><code>#!/bin/bash

ip link set eth0 up

/etc/init.d/frr start
</code></pre>
<p>Save the file in the lab directory, <em>~/bgp-youtube-hijack/</em>.</p>
<p>The startup file executes after the device directory contents are copied into the container, so you can reference any configuration files you’ve provided.</p>
<h5>pakistan.startup</h5>
<p>Create <em>pakistan.startup</em>:</p>
<pre><code>#!/bin/bash

ip link set eth0 up

/etc/init.d/frr start
</code></pre>
<h5>transit.startup</h5>
<p>Create <em>transit.startup</em>:</p>
<pre><code>#!/bin/bash

ip link set eth0 up
ip link set eth1 up
ip link set eth2 up

/etc/init.d/frr start
</code></pre>
<h5>upstream.startup</h5>
<p>Create <em>upstream.startup</em>:</p>
<pre><code>#!/bin/bash

ip link set eth0 up

/etc/init.d/frr start
</code></pre>
<h4>Ready to start</h4>
<p>Now we have a Kathará lab directory that contains a <em>lab.conf</em> file, router startup files, and sub-directories containing the configuration files for each router. We are ready to use the Kathará command-line interface.</p>
<h3>Kathará Command Line Interface</h3>
<p>Kathará provides a simple set of commands to manage your labs. Here are the ones you’ll use most often:</p>
<h4>Starting the Lab</h4>
<p>To start a lab, navigate to the lab directory and run:</p>
<pre><code>$ kathara lstart
</code></pre>
<p>Kathará reads <em>lab.conf</em>, creates the necessary containers and networks, and executes each device’s startup script. You’ll see output as each device comes online:</p>
<pre><code>┌─────────────────────────────────────────────────────────────────────────────────────┐
│                              Starting Network Scenario                              │
└─────────────────────────────────────────────────────────────────────────────────────┘
[Deploying collision domains]   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3/3
[Deploying devices]   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4/4
</code></pre>
<p>You will also see four terminal windows appear. each one is connected to a different router in the lab.</p>
<h4>Connecting to a Device</h4>
<p>If you closed a router’s terminal window, you can open another terminal session on a running device. Open a terminal window and enter the command:</p>
<pre><code>$ kathara connect router1
</code></pre>
<p>This drops you into a shell inside the router container where you can run commands, check routing tables, or configure the device interactively. To exit, type <code>exit</code> or press Ctrl+D.</p>
<h4>Checking Lab Status</h4>
<p>To see information about a running lab:</p>
<pre><code>$ kathara linfo
</code></pre>
<p>This displays details about all running devices, their interfaces, and which collision domains they’re connected to.</p>
<pre><code>┌──────────────────────┬──────────┬─────────────────────┬─────────┬────────────────────┬──────┬───────────┬─────────────────────┬─────────────┬───────────────────┬──────────────────────┐
│ NETWORK SCENARIO ID  │ NAME     │ USER                │ STATUS  │ IMAGE              │ PIDS │ CPU USAGE │ MEM USAGE           │ MEM PERCENT │ NET USAGE         │ INTERFACES           │
╞══════════════════════╪══════════╪═════════════════════╪═════════╪════════════════════╪══════╪═══════════╪═════════════════════╪═════════════╪═══════════════════╪══════════════════════╡
│ GBhCCLpizE6xM9yUZKj… │ pakistan │ blinklet-hlipcf4s0… │ running │ kathara/frr:latest │ 17   │ 0.02%     │ 22.77 MB / 62.55 GB │ 0.04 %      │ 1.77 KB / 1.66 KB │ 0:link_pakistan_tra… │
├──────────────────────┼──────────┼─────────────────────┼─────────┼────────────────────┼──────┼───────────┼─────────────────────┼─────────────┼───────────────────┼──────────────────────┤
│ GBhCCLpizE6xM9yUZKj… │ youtube  │ blinklet-hlipcf4s0… │ running │ kathara/frr:latest │ 17   │ 0.02%     │ 22.75 MB / 62.55 GB │ 0.04 %      │ 1.77 KB / 1.66 KB │ 0:link_youtube_tran… │
├──────────────────────┼──────────┼─────────────────────┼─────────┼────────────────────┼──────┼───────────┼─────────────────────┼─────────────┼───────────────────┼──────────────────────┤
│ GBhCCLpizE6xM9yUZKj… │ upstream │ blinklet-hlipcf4s0… │ running │ kathara/frr:latest │ 17   │ 0.03%     │ 22.81 MB / 62.55 GB │ 0.04 %      │ 1.77 KB / 1.66 KB │ 0:link_transit_upst… │
├──────────────────────┼──────────┼─────────────────────┼─────────┼────────────────────┼──────┼───────────┼─────────────────────┼─────────────┼───────────────────┼──────────────────────┤
│ GBhCCLpizE6xM9yUZKj… │ transit  │ blinklet-hlipcf4s0… │ running │ kathara/frr:latest │ 17   │ 0.02%     │ 24.87 MB / 62.55 GB │ 0.04 %      │ 5.25 KB / 5.04 KB │ 0:link_youtube_tran… │
│                      │          │                     │         │                    │      │           │                     │             │                   │ 1:link_pakistan_tra… │
│                      │          │                     │         │                    │      │           │                     │             │                   │ 2:link_transit_upst… │
└──────────────────────┴──────────┴─────────────────────┴─────────┴────────────────────┴──────┴───────────┴─────────────────────┴─────────────┴───────────────────┴──────────────────────┘
</code></pre>
<h4>Stopping and Cleaning Up</h4>
<p>When you’re done with a lab, clean up all containers and networks:</p>
<pre><code>$ kathara lclean
</code></pre>
<p>This stops and removes all containers created for the lab. Always run this before starting a modified version of your lab to ensure you’re working with a fresh environment.</p>
<p>With these fundamentals covered, you now understand how Kathará structures labs and how to interact with them.</p>
<p>In the next sections, we’ll use the virtual routers that have been created and interconnected by Kathará, as described in the <em>lab.cong</em> file and the router configuration files, to explore how the 2008 YouTube Hijack occurred and how it was mitigated.</p>
<h3>Discover the Normal BGP State</h3>
<p>First, we will establish the “normal” state of the lab. On each router, verify that the BGP sessions are established and YouTube’s prefix is being announced correctly to “upstream” systems.</p>
<h4>Check Upstream (Observer)</h4>
<p>We’ll start with Upstream since this is where we’ll observe the hijack’s effect. Connect and check the BGP table on the router named <em>upstream</em>. A terminal window connected to <em>upstream</em> should already be open on your desktop. If not, use Kathará’s <em>connect</em> command:</p>
<pre><code>$ kathara connect upstream
</code></pre>
<p>The, start the FRR CLI, <em>vtysh</em>, and check BGP status:</p>
<pre><code>root@upstream:/# vtysh
upstream# show ip bgp summary
</code></pre>
<p>You should see output showing the BGP session with Transit (AS300) is established:</p>
<pre><code>IPv4 Unicast Summary (VRF default):
BGP router identifier 40.40.40.1, local AS number 400 vrf-id 0
BGP table version 4
RIB entries 7, using 672 bytes of memory
Peers 1, using 20 KiB of memory

Neighbor        V         AS   MsgRcvd   MsgSent   TblVer  InQ OutQ  Up/Down State/PfxRcd   PfxSnt Desc
10.0.3.1        4        300        11        12        4    0    0 00:04:42            3        4 Transit-PCCW

Total number of neighbors 1
</code></pre>
<p>Check the BGP route table to see the routes:</p>
<pre><code>upstream# show ip bgp
</code></pre>
<p>This is the crucial view. See below that:</p>
<ul>
<li>100.100.0.0/22 is YouTube’s legitimate prefix, learned via AS path “300 100” (Transit → YouTube)</li>
<li>200.200.0.0/16 is Pakistan Telecom’s own prefix</li>
<li>30.30.0.0/16 is Transit’s prefix</li>
</ul>
<pre><code>BGP table version is 4, local router ID is 40.40.40.1, vrf id 0
Default local pref 100, local AS 400
Status codes:  s suppressed, d damped, h history, * valid, &gt; best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, &lt; announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

    Network          Next Hop            Metric LocPrf Weight Path
 *&gt; 30.30.0.0/16     10.0.3.1                 0             0 300 i
 *&gt; 40.40.0.0/16     0.0.0.0                  0         32768 i
 *&gt; 100.100.0.0/22   10.0.3.1                               0 300 100 i
 *&gt; 200.200.0.0/16   10.0.3.1                               0 300 200 i

Displayed  4 routes and 4 total paths
</code></pre>
<p>There is no more-specific /24 route for YouTube’s address space yet. This is the normal, pre-hijack state.</p>
<h4>Check Transit</h4>
<p>Verify <em>Transit</em> is properly peering with all neighbors. Use the terminal window connected to the <em>transit</em> router:</p>
<pre><code>root@transit:/# vtysh
transit# show ip bgp summary
</code></pre>
<p>We see <em>Transit</em> BGP has three peers, as expected. Sessions are established with YouTube (AS100), Pakistan (AS200), and Upstream (AS400):</p>
<pre><code>IPv4 Unicast Summary (VRF default):
BGP router identifier 30.30.30.1, local AS number 300 vrf-id 0
BGP table version 4
RIB entries 7, using 672 bytes of memory
Peers 3, using 60 KiB of memory

Neighbor        V         AS   MsgRcvd   MsgSent   TblVer  InQ OutQ  Up/Down State/PfxRcd   PfxSnt Desc
10.0.1.1        4        100        15        15        4    0    0 00:08:59            1        4 YouTube
10.0.2.2        4        200        15        15        4    0    0 00:08:59            1        4 Pakistan-Telecom
10.0.3.2        4        400        15        15        4    0    0 00:08:59            1        4 Upstream

Total number of neighbors 3
</code></pre>
<p>Check the BGP route table to see the routes:</p>
<pre><code>upstream# show ip bgp
</code></pre>
<p>See below that <em>Transit</em> is receiving:</p>
<ul>
<li>1 prefix from Upstream (AS400): its 40.40.0.0/16</li>
<li>1 prefix from YouTube (AS100): the 100.100.0.0/22</li>
<li>1 prefix from Pakistan (AS200): its legitimate 200.200.0.0/16</li>
</ul>
<pre><code>BGP table version is 4, local router ID is 30.30.30.1, vrf id 0
Default local pref 100, local AS 300
Status codes:  s suppressed, d damped, h history, * valid, &gt; best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, &lt; announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

    Network          Next Hop            Metric LocPrf Weight Path
 *&gt; 30.30.0.0/16     0.0.0.0                  0         32768 i
 *&gt; 40.40.0.0/16     10.0.3.2                 0             0 400 i
 *&gt; 100.100.0.0/22   10.0.1.1                 0             0 100 i
 *&gt; 200.200.0.0/16   10.0.2.2                 0             0 200 i

Displayed  4 routes and 4 total paths
</code></pre>
<h4>Check YouTube</h4>
<p>Verify <em>YouTube</em> is properly peering with all neighbors.</p>
<pre><code>root@youtube:/# vtysh
youtube# show ip bgp summary
</code></pre>
<p>We see <em>YouTube</em> BGP has one BGP peer, as expected. It has established a BGP session with <em>Transit</em> (AS300):</p>
<pre><code>IPv4 Unicast Summary (VRF default):
BGP router identifier 100.100.100.1, local AS number 100 vrf-id 0
BGP table version 4
RIB entries 7, using 672 bytes of memory
Peers 1, using 20 KiB of memory

Neighbor        V         AS   MsgRcvd   MsgSent   TblVer  InQ OutQ  Up/Down State/PfxRcd   PfxSnt Desc
10.0.1.2        4        300        40        41        4    0    0 00:33:25            3        4 Transit-PCCW

Total number of neighbors 1
</code></pre>
<p>Verify <em>YouTube</em> is announcing its prefix correctly:</p>
<pre><code>root@youtube:/# vtysh
youtube# show ip bgp
</code></pre>
<p>As shown below, YouTube sees:</p>
<ul>
<li>Its own 100.100.0.0/22 (locally originated)</li>
<li>Routes to other ASes via Transit</li>
</ul>
<pre><code>BGP table version is 4, local router ID is 100.100.100.1, vrf id 0
Default local pref 100, local AS 100
Status codes:  s suppressed, d damped, h history, * valid, &gt; best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, &lt; announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*&gt; 30.30.0.0/16     10.0.1.2                 0             0 300 i
*&gt; 40.40.0.0/16     10.0.1.2                               0 300 400 i
*&gt; 100.100.0.0/22   0.0.0.0                  0         32768 i
*&gt; 200.200.0.0/16   10.0.1.2                               0 300 200 i

Displayed  4 routes and 4 total paths
</code></pre>
<h4>Check Pakistan</h4>
<p>Verify <em>Pakistan</em> is properly peering with all neighbors.</p>
<pre><code>root@transit:/# vtysh
transit# show ip bgp summary
</code></pre>
<p>We see <em>Pakistan</em> BGP has established a BGP peering session with <em>Transit</em>:</p>
<pre><code>IPv4 Unicast Summary (VRF default):
BGP router identifier 200.200.200.1, local AS number 200 vrf-id 0
BGP table version 4
RIB entries 7, using 672 bytes of memory
Peers 1, using 20 KiB of memory

Neighbor        V         AS   MsgRcvd   MsgSent   TblVer  InQ OutQ  Up/Down State/PfxRcd   PfxSnt Desc
10.0.2.1        4        300        35        36        4    0    0 00:28:18            3        4 Transit-PCCW

Total number of neighbors 1
</code></pre>
<p>Check the BGP route table to see the routes:</p>
<pre><code>pakistan# show ip bgp
</code></pre>
<p>See below that <em>Pakistan</em> is receiving:</p>
<ul>
<li>1 prefix from Transit (AS300): its 30.30.0.0/16</li>
<li>1 prefix from Upstream (AS400): its 40.40.0.0/16</li>
<li>1 prefix from YouTube (AS100): the 100.100.0.0/22</li>
</ul>
<pre><code>BGP table version is 4, local router ID is 200.200.200.1, vrf id 0
Default local pref 100, local AS 200
Status codes:  s suppressed, d damped, h history, * valid, &gt; best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, &lt; announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

    Network          Next Hop            Metric LocPrf Weight Path
 *&gt; 30.30.0.0/16     10.0.2.1                 0             0 300 i
 *&gt; 40.40.0.0/16     10.0.2.1                               0 300 400 i
 *&gt; 100.100.0.0/22   10.0.2.1                               0 300 100 i
 *&gt; 200.200.0.0/16   0.0.0.0                  0         32768 i

Displayed  4 routes and 4 total paths
</code></pre>
<h4>Verifying Connectivity</h4>
<p>Let’s test connectivity to YouTube’s address space. From <em>Upstream’s</em> loopback interface, ping <em>YouTube’s</em> loopback address <em>100.100.0.1</em>:</p>
<pre><code>root@upstream:/# ping -I 40.40.0.1 -c 3 100.100.0.1
PING 100.100.0.1 (100.100.0.1) from 40.40.0.1 : 56(84) bytes of data.
64 bytes from 100.100.0.1: icmp_seq=1 ttl=63 time=1.70 ms
64 bytes from 100.100.0.1: icmp_seq=2 ttl=63 time=2.06 ms
64 bytes from 100.100.0.1: icmp_seq=3 ttl=63 time=1.80 ms

--- 100.100.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 1.704/1.853/2.058/0.149 ms
</code></pre>
<p>Traffic from Upstream, which represents traffic from Internet users in this lab example, reaches YouTube correctly. This is the normal, pre-hijack state.</p>
<p>Use the <code>traceroute</code> command to check the path that the traffic between <em>Upstream</em> and <em>Youtube</em> follows:</p>
<pre><code>root@upstream:/# traceroute -s 40.40.0.1 100.100.0.1
traceroute to 100.100.0.1 (100.100.0.1), 30 hops max, 60 byte packets
 1  10.0.3.1 (10.0.3.1)  0.730 ms  1.239 ms  1.827 ms
 2  100.100.0.1 (100.100.0.1)  2.428 ms  3.025 ms  3.541 ms
</code></pre>
<p>At this point, we have a fully functional 4-AS BGP topology with normal routing:</p>
<p>In the next section, we’ll introduce Pakistan Telecom’s more-specific /24 hijack and observe how it overrides YouTube’s legitimate route.</p>
<h3>Recreating the Prefix Hijack</h3>
<p>Now we will introduce the more-specific prefix announcement that hijacks YouTube’s traffic, recreating the 2008 Pakistan incident. This demonstrates how BGP’s longest-prefix-match rule can be exploited, even unintentionally.</p>
<h4>The Before State</h4>
<p>Before making changes, let’s confirm YouTube’s route is the only one for the 100.100.x.x address space. In the Upstream terminal window:</p>
<pre><code>root@upstream:/# vtysh
upstream# show ip bgp 100.100.0.1
</code></pre>
<p>When we query BGP for the specific IP 100.100.0.1, we see that it matches the /22 prefix owned by YouTube (AS100). The AS path “300 100” shows the route comes via Transit (AS300) from YouTube (AS100).</p>
<pre><code>BGP routing table entry for 100.100.0.0/22, version 3
Paths: (1 available, best #1, table default)
  Advertised to non peer-group peers:
  10.0.3.1
  300 100
    10.0.3.1 from 10.0.3.1 (30.30.0.1)
      Origin IGP, valid, external, best (First path received)
      Last update: Sat Jan 31 16:31:16 2026
</code></pre>
<h4>Starting the Hijack</h4>
<p>Now we’ll simulate Pakistan Telecom announcing the more-specific prefix. In the Pakistan terminal window:</p>
<pre><code>root@pakistan:/# vtysh
pakistan# configure terminal
</code></pre>
<p>First, we need to create a blackhole route for the prefix we’re about to announce, just like Pakistan Telecom did when trying to block YouTube:</p>
<pre><code>pakistan(config)# ip route 100.100.0.0/24 blackhole
</code></pre>
<p>Now, add the more-specific prefix to BGP:</p>
<pre><code>pakistan(config)# router bgp 200
pakistan(config-router)# address-family ipv4 unicast
pakistan(config-router-af)# network 100.100.0.0/24
pakistan(config-router-af)# exit
pakistan(config-router)# exit
pakistan(config)# exit
</code></pre>
<p>Verify the new route is in Pakistan’s BGP table:</p>
<pre><code>pakistan# show ip bgp
</code></pre>
<p>In the output below, notice that there are two routes for YouTube’s address space:</p>
<ul>
<li><em>100.100.0.0/22</em> is YouTube’s legitimate announcement (learned via Transit)</li>
<li><em>100.100.0.0/24</em> is Our hijacked announcement (locally originated)</li>
</ul>
<pre><code>BGP table version is 5, local router ID is 200.200.0.1, vrf id 0
Default local pref 100, local AS 200
Status codes:  s suppressed, d damped, h history, * valid, &gt; best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, &lt; announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

    Network          Next Hop            Metric LocPrf Weight Path
 *&gt; 30.30.0.0/16     10.0.2.1                 0             0 300 i
 *&gt; 40.40.0.0/16     10.0.2.1                               0 300 400 i
 *&gt; 100.100.0.0/22   10.0.2.1                               0 300 100 i
 *&gt; 100.100.0.0/24   0.0.0.0                  0         32768 i
 *&gt; 200.200.0.0/16   0.0.0.0                  0         32768 i

Displayed  5 routes and 5 total paths
</code></pre>
<p>The hijack is now being advertised to Transit.</p>
<p>In our lab YouTube announces <em>100.100.0.0/22</em>, which covers 100.100.0.0 &#8211; 100.100.3.255, and Pakistan is now announcing <em>100.100.0.0/24</em>, which covers 100.100.0.0 &#8211; 100.100.0.255.</p>
<p>The /24 is more specific than the /22, so routers will prefer it for any traffic destined to addresses in the 100.100.0.0/24 range.</p>
<h4>Observing the Hijack Propagation</h4>
<p>Let’s see how the hijack propagates through the network. Go to the Transit terminal window and enter the following commands.</p>
<pre><code>root@transit:/# vtysh
transit# show ip bgp
</code></pre>
<p>You can see that the more specific route has reached the Transit AS router.</p>
<pre><code>BGP table version is 5, local router ID is 30.30.0.1, vrf id 0
Default local pref 100, local AS 300
Status codes:  s suppressed, d damped, h history, * valid, &gt; best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, &lt; announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

    Network          Next Hop            Metric LocPrf Weight Path
 *&gt; 30.30.0.0/16     0.0.0.0                  0         32768 i
 *&gt; 40.40.0.0/16     10.0.3.2                 0             0 400 i
 *&gt; 100.100.0.0/22   10.0.1.1                 0             0 100 i
 *&gt; 100.100.0.0/24   10.0.2.2                 0             0 200 i
 *&gt; 200.200.0.0/16   10.0.2.2                 0             0 200 i

Displayed  5 routes and 5 total paths
</code></pre>
<p>It now has two routes covering YouTube’s address space:</p>
<ul>
<li>100.100.0.0/22 from YouTube (AS100) via 10.0.1.1</li>
<li>100.100.0.0/24 from Pakistan (AS200) via 10.0.2.1</li>
</ul>
<p>Transit will forward the hijacked route to its other neighbors, including Upstream.</p>
<p>Check Upstream, which is the observer that represents the broader Internet. Go to the Upstream terminal window and enter the following commands.</p>
<pre><code>root@upstream:/# vtysh
upstream# show ip bgp
</code></pre>
<p>You can see that the more specific route has reached the Upstream AS router, from which we will assume it propagates to all other routers in the Internet.</p>
<pre><code>BGP table version is 6, local router ID is 40.40.40.1, vrf id 0
Default local pref 100, local AS 400
Status codes:  s suppressed, d damped, h history, * valid, &gt; best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, &lt; announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*&gt; 30.30.0.0/16     10.0.3.1                 0             0 300 i
*&gt; 40.40.0.0/16     0.0.0.0                  0         32768 i
*&gt; 100.100.0.0/22   10.0.3.1                               0 300 100 i
*&gt; 100.100.0.0/24   10.0.3.1                               0 300 200 i
*&gt; 200.200.0.0/16   10.0.3.1                               0 300 200 i

Displayed  5 routes and 5 total paths
</code></pre>
<p>Notice the two routes:</p>
<ul>
<li>100.100.0.0/22 with AS path “300 100” (Transit → YouTube), which is the legitimate route</li>
<li>100.100.0.0/24 with AS path “300 200” (Transit → Pakistan), which is the hijacked route</li>
</ul>
<p>In the real incident, Pakistan Telecom was trying to block YouTube internally by creating a blackhole route. The problem was that this route was accidentally announced to their transit provider PCCW, who propagated it globally.</p>
<h4>Understanding the Impact</h4>
<p>Now let’s see what happens when Upstream tries to reach an IP address in the hijacked range:</p>
<pre><code>upstream# show ip bgp 100.100.0.1
</code></pre>
<pre><code>BGP routing table entry for 100.100.0.0/24, version 5
Paths: (1 available, best #1, table default)
  Advertised to non peer-group peers:
  10.0.3.1
  300 200
    10.0.3.1 from 10.0.3.1 (30.30.0.1)
      Origin IGP, valid, external, best (First path received)
      Last update: Sat Jan 31 17:38:36 2026
</code></pre>
<p>This is the hijack in action! When querying for IP 100.100.0.1:</p>
<ul>
<li>Before the hijack, this command matched the /22 route to YouTube and we saw that it originated from AS100 (Youtube)</li>
<li>After the hijack, it matches the /24 route to Pakistan and we see it originates from AS200 (Pakistan)</li>
</ul>
<p>The more-specific /24 prefix “wins” due to BGP’s longest-prefix-match rule. Traffic for 100.100.0.x is now being sent toward Pakistan instead of YouTube.</p>
<p>Let’s verify with a traceroute. Exit vtysh first:</p>
<pre><code>upstream# exit
</code></pre>
<p>Try to ping the YouTube loopback address and see that you cannot reach YouTube at 100.100.0.1 from the Upstream router:</p>
<pre><code>root@upstream:/# ping -I 40.40.0.1 -c 3 100.100.0.1
PING 100.100.0.1 (100.100.0.1) from 40.40.0.1 : 56(84) bytes of data.

--- 100.100.0.1 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2056ms
</code></pre>
<p>Next, trace the path to an address in the hijacked range:</p>
<pre><code>root@upstream:/# traceroute -s 40.40.0.1 100.100.0.1
traceroute to 100.100.0.1 (100.100.0.1), 30 hops max, 60 byte packets
 1  10.0.3.1 (10.0.3.1)  1.592 ms  2.020 ms  2.429 ms
 2  * * *
 3  * * *
</code></pre>
<p>The traceroute shows:</p>
<ul>
<li>First hop: Transit (10.0.3.1)</li>
<li>Second hop: Nothing shows because Pakistan is blackholing the traffic.</li>
</ul>
<p>This is exactly what happened in 2008: YouTube traffic flowed toward Pakistan where it was dropped, causing a global YouTube outage.</p>
<p>Compare this to the other YouTube address 100.100.1.1, which is outside the hijacked /24 but still within YouTube’s /22:</p>
<pre><code>root@upstream:/# traceroute -s 40.40.0.1 100.100.1.1
traceroute to 100.100.1.1 (100.100.1.1), 30 hops max, 60 byte packets
 1  10.0.3.1 (10.0.3.1)  0.895 ms  1.173 ms  1.573 ms
 2  100.100.1.1 (100.100.1.1)  1.762 ms  2.185 ms  2.586 ms
</code></pre>
<p>Traffic to 100.100.1.1 still reaches YouTube (via Transit) because that address is covered by the /22 but NOT by the hijacked /24.</p>
<h4>YouTube’s Response</h4>
<p>In the real incident, YouTube responded by announcing their own more-specific prefixes to compete with Pakistan’s hijack. Let’s simulate this defense.</p>
<p>Go to the YouTube terminal window and enter the following commands:</p>
<pre><code>root@youtube:/# vtysh
youtube# configure terminal
</code></pre>
<p>YouTube will announce two /25 prefixes which are more specific than Pakistan’s /24 and which, together, cover the name address range as the /24 that Pakistan announced:</p>
<pre><code>youtube(config)# ip route 100.100.0.0/25 blackhole
youtube(config)# ip route 100.100.0.128/25 blackhole
youtube(config)# router bgp 100
youtube(config-router)# address-family ipv4 unicast
youtube(config-router-af)# network 100.100.0.0/25
youtube(config-router-af)# network 100.100.0.128/25
youtube(config-router-af)# exit
youtube(config-router)# exit
youtube(config)# exit
</code></pre>
<p>Verify YouTube is now announcing the counter-attack routes:</p>
<pre><code>youtube# show ip bgp
</code></pre>
<pre><code>BGP table version is 7, local router ID is 100.100.0.1, vrf id 0
Default local pref 100, local AS 100
Status codes:  s suppressed, d damped, h history, * valid, &gt; best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, &lt; announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

    Network          Next Hop            Metric LocPrf Weight Path
 *&gt; 30.30.0.0/16     10.0.1.2                 0             0 300 i
 *&gt; 40.40.0.0/16     10.0.1.2                               0 300 400 i
 *&gt; 100.100.0.0/22   0.0.0.0                  0         32768 i
 *&gt; 100.100.0.0/24   10.0.1.2                               0 300 200 i
 *&gt; 100.100.0.0/25   0.0.0.0                  0         32768 i
 *&gt; 100.100.0.128/25 0.0.0.0                  0         32768 i
 *&gt; 200.200.0.0/16   10.0.1.2                               0 300 200 i

Displayed  7 routes and 7 total paths
</code></pre>
<p>YouTube is now announcing /25s that will override Pakistan’s /24 prefix</p>
<p>Check Upstream to see the effect. Go to the Upstream terminal window and enter the following commands:</p>
<pre><code>root@upstream:/# vtysh
upstream# show ip bgp
</code></pre>
<p>This will show there are three routes covering the 100.100.0.0/24 range:</p>
<ul>
<li>100.100.0.0/24 from Pakistan (AS200)</li>
<li>100.100.0.0/25 from YouTube (AS100), which is more specific</li>
<li>100.100.0.128/25 from YouTube (AS100), which is more specific</li>
</ul>
<pre><code>BGP table version is 7, local router ID is 40.40.0.1, vrf id 0
Default local pref 100, local AS 400
Status codes:  s suppressed, d damped, h history, * valid, &gt; best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, &lt; announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

    Network          Next Hop            Metric LocPrf Weight Path
 *&gt; 30.30.0.0/16     10.0.3.1                 0             0 300 i
 *&gt; 40.40.0.0/16     0.0.0.0                  0         32768 i
 *&gt; 100.100.0.0/22   10.0.3.1                               0 300 100 i
 *&gt; 100.100.0.0/24   10.0.3.1                               0 300 200 i
 *&gt; 100.100.0.0/25   10.0.3.1                               0 300 100 i
 *&gt; 100.100.0.128/25 10.0.3.1                               0 300 100 i
 *&gt; 200.200.0.0/16   10.0.3.1                               0 300 200 i

Displayed  7 routes and 7 total paths
</code></pre>
<p>Let’s verify Upstream traffic now goes to YouTube:</p>
<pre><code>upstream# show ip bgp 100.100.0.1
</code></pre>
<p>This will show that the Upstream router now will send traffic addressed to 100.100.0.1 to YouTube (AS100) instead of to Pakistan (AS200).</p>
<pre><code>upstream# show ip bgp 100.100.0.1
BGP routing table entry for 100.100.0.0/25, version 6
Paths: (1 available, best #1, table default)
  Advertised to non peer-group peers:
  10.0.3.1
  300 100
    10.0.3.1 from 10.0.3.1 (30.30.0.1)
      Origin IGP, valid, external, best (First path received)
      Last update: Sat Jan 31 19:41:08 2026
</code></pre>
<p>YouTube has reclaimed control. The /25 routes from YouTube (AS100) override Pakistan’s /24 route. Traffic for 100.100.0.1 now correctly flows to YouTube.</p>
<h4>Cleanup</h4>
<p>When you’re finished experimenting with the hijack scenario clean up the lab:</p>
<pre><code>$ kathara lclean
┌──────────────────────────────────────────────────────────────────────────────┐
│                          Stopping Network Scenario                           │
└──────────────────────────────────────────────────────────────────────────────┘
[Deleting devices]   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4/4
[Deleting collision domains]   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3/3
</code></pre>
<h4>Summary: The Attack and Defense</h4>
<p>This exercise demonstrated the lifecycle of the Pakistan YouTube hijack.</p>
<p>The key lesson is that in the BGP “longest prefix match” game, the most specific route wins. However, while that was possible in 2008, that’s a game that attackers and defenders a less able to play in modern networks due to the use of modern BGP security practices.</p>
<h3>Modern BGP Security Practices</h3>
<p>Now that you’ve seen how a prefix hijack works, let’s discuss the techniques network operators use to prevent them. The Pakistan YouTube incident highlighted critical weaknesses in BGP that the industry has been working to address ever since.</p>
<p>Pakistan Telecom’s /24 BGP Hijack and YouTube’s emergency /25 de-aggregation response worked in 2008 because prefix filtering and origin validation were weak. Today, strict prefix-length filtering and the widespread use of RPKI mean that such more-specific announcements would often be rejected.</p>
<h4>Prefix Filtering with Prefix Lists</h4>
<p>The most fundamental protection against prefix hijacks is explicit prefix filtering. Transit providers can configure their routers to only accept announcements for prefixes the customer is authorized to announce. To accomplish this, they need to maintain a comprehensive list of valid customer prefixes.</p>
<p>Internet providers maintain prefix filter lists using tools like <em><a href="https://github.com/bgp/bgpq4">bgpq4</a></em> that automatically generate prefix lists from data stored in Internet Routing Registry (IRR) databases like <a href="https://www.radb.net/">RADB</a>, <a href="https://www.ripe.net/">RIPE</a>, and <a href="https://www.arin.net/">ARIN</a>.</p>
<h4>RPKI and Route Origin Validation</h4>
<p><a href="https://www.arin.net/resources/manage/rpki/">Resource Public Key Infrastructure (RPKI)</a> represents a fundamental shift in BGP security. Instead of relying on trusted databases or manual coordination, RPKI provides cryptographic proof of route authorization. RPKI would have prevented the Pakistan YouTube hijack, if RPKI had existed in 2008.</p>
<p><a href="https://rpki-monitor.antd.nist.gov/">RPKI adoption</a> has grown dramatically since the Pakistan YouTube incident. Major networks and tier-1 providers now validate routes and drop RPKI-invalid announcements.</p>
<h4>BGPsec</h4>
<p><a href="https://www.bgpsec.net/">BGPsec</a> extends RPKI to provide path validation, cryptographically signing each AS hop in the path. While RPKI validates the origin AS, BGPsec validates the entire path, detecting route leaks and path manipulation attacks. However, BGPsec faces significant deployment challenges because every AS in the path must support BGPsec.</p>
<h3>Conclusion</h3>
<p>In this post, I’ve walked you through using Kathará to recreate the 2008 Pakistan YouTube BGP prefix hijack. We’ve learned how to use Kathará to gain hands-on experience with how prefix hijacks occur and propagate, and how to detect and counter them.</p>
<p>The beauty of network emulation, using tools like Kathará, is that you can experiment freely without risking production infrastructure. If you want to continue exploring BGP scenarios with Kathará, the project maintains an <a href="https://github.com/KatharaFramework/Kathara-Labs">excellent collection of ready-to-use labs</a>:</p>
<h4>Further Reading</h4>
<p>To get more information about BGP security, I recommend these resources:</p>
<ul>
<li><a href="https://www.ripe.net/publications/news/industry-developments/youtube-hijacking-a-ripe-ncc-ris-case-study">RIPE NCC Case Study: Pakistan YouTube Hijack</a>: Detailed analysis of the 2008 YouTube incident with routing data</li>
<li><a href="https://csrc.nist.gov/pubs/sp/800/189/r1/ipd">NIST BGP Security Guidelines (SP 800-189)</a>: Comprehensive guidance on securing BGP infrastructure</li>
<li><a href="https://www.manrs.org/">MANRS (Mutually Agreed Norms for Routing Security)</a>: Industry initiative promoting routing security best practices</li>
<li><a href="https://blog.cloudflare.com/tag/bgp/">Cloudflare’s BGP Security Blog</a>: Accessible explanations of BGP incidents and mitigations</li>
<li><a href="https://www.arin.net/resources/manage/rpki/">RPKI Documentation at ARIN, RIPE, or APNIC</a>: Guides for creating and managing RPKI data</li>
</ul>
<p>[^1] https://github.com/KatharaFramework/Kathara/wiki/Linux</p>
</div>



<p class=""></p>
<p>The post <a href="https://brianlinkletter.com/2026/02/bgp-hijack-with-kathara-network-emulator/">Recreating a Real-World BGP Hijack with the Kathará Network Emulator</a> appeared first on <a href="https://brianlinkletter.com">Open-Source Routing and Network Simulation</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">5969</post-id>	</item>
		<item>
		<title>Open-source network simulators and emulators in 2026</title>
		<link>https://brianlinkletter.com/2026/02/open-source-simulator-emulator-in-2026/</link>
		
		<dc:creator><![CDATA[Brian Linkletter]]></dc:creator>
		<pubDate>Sun, 01 Feb 2026 13:09:00 +0000</pubDate>
				<category><![CDATA[Network Simulation]]></category>
		<category><![CDATA[cloonix]]></category>
		<category><![CDATA[Containerlab]]></category>
		<category><![CDATA[CORE]]></category>
		<category><![CDATA[GNS3]]></category>
		<category><![CDATA[IMUNES]]></category>
		<category><![CDATA[Kathara]]></category>
		<category><![CDATA[mininet]]></category>
		<category><![CDATA[Netlab]]></category>
		<category><![CDATA[ns-3]]></category>
		<category><![CDATA[OMnet]]></category>
		<guid isPermaLink="false">https://opensourcenetworksimulators.com/?p=5930</guid>

					<description><![CDATA[<p>In this annual update of my list of open-source network simulators and emulators, I check each projects's activity level, verify the well-maintained projects, identify new projects that deserve attention, and consider projects that may be fading away.</p>
<p>The post <a href="https://brianlinkletter.com/2026/02/open-source-simulator-emulator-in-2026/">Open-source network simulators and emulators in 2026</a> appeared first on <a href="https://brianlinkletter.com">Open-Source Routing and Network Simulation</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-jetpack-markdown"><p>In this annual update of my list of open-source network simulators and emulators, I check each project’s development activity, verify the well-maintained projects, identify new projects that deserve attention, and consider projects that may be fading away.</p>
<h2>Top Projects</h2>
<p>Two open-source network emulation projects stand out in 2026, due to their popularity, functionality, and development velocity.</p>
<p><strong><a href="https://containerlab.dev/">Containerlab</a></strong> continues its impressive development pace, and seems to have cemented its position as a leading network emulation tool for developers. New features added in 2024 and 2025 include VM snapshot/restore functionality, expanded device support, a system for running labs on Kubernetes clusters, and improved container network configuration.</p>
<p><strong><a href="https://www.gns3.com/">GNS3</a></strong> continues steady development and remains a leading network emulation tool for engineers. While the 2.x release is still the default download, the GNS3 team released a 3.0 version that implemented major architectural changes in GNS3. GNS3 continues to be the most popular GUI-based open-source network emulator.</p>
</div>



<div class="wp-block-jetpack-markdown"><h2>Maintained Projects</h2>
<p>Many other open-source network simulation and emulation projects continue to be well maintained in 2026 and may be used with confidence. Each project’s documentation is usable, the developers are responsive to issues and contributions, and the user community is engaged.</p>
<p><strong><a href="https://www.nsnam.org/">ns-3</a></strong> continues to be the premier open-source discrete-event network simulator. Recent additions include new Zigbee support and improved Wi-Fi simulation helpers.</p>
<p><strong><a href="https://omnetpp.org/">OMNeT++</a></strong> shipped its latest release in November, 2025. New features included an AI notebook tool for analysis and Time-Sensitive Networking (TSN) tutorials.</p>
<p><strong><a href="https://shadow.github.io/">Shadow</a></strong> has evolved into a general-purpose discrete-event network simulator that directly executes real application code. The developer is also re-writing some Shadow components in Rust. Shadow is well maintained, and the development team responds to GitHub issues and pull requests in a timely manner.</p>
<p><strong><a href="https://clownix.net/">Cloonix</a></strong> continued active development in 2025 with multiple releases. This emulator provides a solid alternative for those preferring VM-based network emulation. The developer is looking for more users to excercise the emulator’s features.</p>
<p><strong><a href="https://coreemu.github.io/core/">CORE</a></strong> is well-maintained with a new release arriving in May, 2025. The latest updates improved Docker support and added Podman nodes with Compose support.</p>
<p><strong><a href="https://github.com/imunes/imunes/">IMUNES</a></strong> delivered it’s latest release in January 2025 and the project has seen new commits as recently as December 2025. IMUNES added FreeBSD 15 support and improved Docker support for Linux.</p>
<p><strong><a href="https://netlab.tools/">NetLab</a></strong> received many updates through 2025, continuing to bring infrastructure-as-code concepts to networking labs. It is suitable for learners and practitioners alike, offering both curated example scenarios and the flexibility to define custom topologies.</p>
<p><strong><a href="https://github.com/openconfig/kne">OpenConfig-KNE</a></strong> is actively maintained and its latest release was delivered in August, 2025. Recent updates include security fixes. The developers are evolving the tool into a general-purpose Kubernetes Network Emulation (KNE) tool for quickly setting up network topologies of containers running various device OSes.</p>
<p><strong><a href="https://www.lernsoftware-filius.de/Herunterladen">Filius</a></strong>, an educational network simulator, continued development in 2025, delivering bug fixes and minor enhancements. No major features were added in the past two years. This German-language tool remains an excellent choice for teaching networking concepts to students.</p>
<p><strong><a href="https://docs.contiki-ng.org/en/develop/doc/tutorials/Running-Contiki-NG-in-Cooja.html">Cooja</a></strong> remains part of the <a href="https://www.contiki-ng.org/">Contiki-NG</a> IoT operating system project and has received minor updates over the past two years.</p>
<p><strong><a href="https://github.com/GUVWAF/Meshtasticator">Meshtasticator</a></strong> continues to receive maintenance updates for Meshtastic mesh network simulation.</p>
<p><strong><a href="https://www.kathara.org/">Kathara</a></strong> has, over the past two years, focused on improving usability, the Python API, and Docker/Kubernetes workflows. The project also introduced a new lab checker tool.</p>
<p><strong><a href="https://nps.edu/web/c3o/labtainers">Labtainers</a></strong> continued active development over the past two years and its latest release was in December, 2025. This Docker-based cybersecurity lab environment now includes over 50 lab exercises.</p>
</div>



<div class="wp-block-jetpack-markdown"><h2>Resurrected Projects and New Projects</h2>
<p>Some projects that I had previously considered to be retired have showed signs of life in 2026. I also found some interesting new (or new-to-me) projects worth watching.</p>
<p><strong><a href="https://teaching.csse.uwa.edu.au/units/CITS3002/cnet/index.php">cnet</a></strong> had previously removed its downloads from the web, but now they are back. Cnet is updated and is now available on Apple M-series processors running macOS Sonoma 14. It also runs on Linux systems. You can get the Linux installer from the University of Western Australia by running the command:</p>
</div>



<pre class="wp-block-code"><code>$ curl -s -o cnet-Linux-x86_64 https://teaching.csse.uwa.edu.au/units/CITS3002/cnet/downloads/cnet-Linux-x86_64</code></pre>



<div class="wp-block-jetpack-markdown"><p><strong><a href="https://www.marionnet.org/site/index.php/en/">Marrionet</a></strong> received a few bug fixes in May 2025. I had previously removed it from my list due to inactivity. I will not add it back to the list, yet, but I will keep an eye on this project in case it becomes active again.</p>
<p><strong><a href="https://github.com/named-data/mini-ndn">Mini-NDN</a></strong> has been available for over ten years and I am adding it to my list now because Mininet is fading away and this project, along with Mininet-WiFi are forks of Mininet with continued development. Mini-NDN is a lightweight networking emulation tool that enables testing, experimentation, and research on <a href="https://named-data.net/">Named Data Networks (NDN)</a>.</p>
<p><strong><a href="https://mininet-wifi.github.io/">Mininet-WiFi</a></strong> is also a well-established project that I have written about before. I used to categorize it under “Mininet” but I promoted to its own line in my list in 2026. Again, it’s because it is a more active fork of Mininet. In addition to base Mininet functionality, it offers some very interesting WiFi network emulation features.</p>
<p><strong><a href="https://github.com/IPoAC-SMT/DONE">DONE (Docker Orchestrator for Network Emulation)</a></strong> is a simple network emulator, inspired by the IMUNES project. It is inspired by IMUNES but is its own unique creation. The developers say they aim to improve the user experience while reducing software dependencies to the bare minimum. It’s relatively new, and has seen a few updates since it was released. I won’t add it to my list, yet, but I will keep an eye out to see if it develops further.</p>
<h2>Projects Showing Signs of Decline</h2>
<p>Other projects from my list have showed significant decline in development activity and user engagement over the past several years.</p>
<h3>On Watch</h3>
<p>The following projects are fading away due to significantly lower involvement from developers and/or users. I probably should remove them from my list, but I am not ready to give up on them.</p>
<p><strong><a href="https://www.eve-ng.net/index.php/community/">EVE-NG Community Edition</a></strong> last received an update in July, 2024. The EVE-NG development team’s focus remains almost exclusively on the commercial EVE-PRO version. In my opinion, users who require active development and open-source should consider alternatives like GNS3 or Containerlab.</p>
<p><strong><a href="https://mininet.org/">Mininet</a></strong> remains stable at version 2.3.0, last released in February 2021. It continues to work well for SDN development and education, but active development has stopped. GitHub issues are no longer responded to and pull requests are piling up with no action from the maintainers. Also, <a href="https://mailman.stanford.edu/pipermail/mininet-discuss/2025-May/008806.html">no one is responding to questions</a> on the Mininet mailing list. However, Mininet remains popular with users, based on the level of interest in Mininet posts in my blog, and it is the base for forked projects like <a href="https://mininet-wifi.github.io/">Mininet-WiFi</a>, <a href="https://github.com/named-data/mini-ndn">Mini-NDN</a>, and <a href="https://containernet.github.io/">Containernet</a>. I will keep an eye on all these projects and consider replacing Mininet in my list with one of its forks.</p>
<p><strong><a href="https://gitlab.com/v-a/nemu/-/wikis/home">NEmu</a></strong> virtual networking environment implemented some bug fixes or minor improvements in the <a href="https://gitlab.com/v-a/nemu/">NEmu GitLab repo</a> in 2025, but has published no releases since 2023.</p>
<p><strong><a href="http://cupcarbon.com">CupCarbon</a></strong>, an open source internet of things (IoT) and wireless sensor network (WSN) simulator, has not been updated in 4 years. It seems like further development is no longer open source and has been <a href="https://github.com/bounceur/CupCarbon/issues/31">moved to a closed-source version of the product</a>, re-named <a href="http://cupcarbon.com/CupCarbon-Tutorials_7.html">CupCarbon-Klines</a>. The new version is an “Agentic AI and Digital Twin IoT Simulation Platform”. While I investigate the licensing story for this project, I’ll keep this on my list because CupCarbon-Klines remains free-to-use and seems interesting.</p>
<h3>Removed from my Lists</h3>
<p>I removed the following projects from my list of tracked projects in 2026. They appear to me to be no longer maintained.</p>
<p><strong><a href="https://github.com/vrnetlab/vrnetlab">vrnetlab</a></strong>, the original at <em>github.com/vrnetlab/vrnetlab</em>, has not received commits in over two years and has no published releases. Users should use the <a href="https://github.com/srl-labs/vrnetlab">srl-labs/vrnetlab</a> fork, which is actively maintained by Containerlab.and supports the use of VMs in Containerlab. I intend to remove the original vrnetlab project from my list of active projects and will track it as part of Containerlab.</p>
<p><strong><a href="http://lnst-project.org/">Linux Network Stack Test (LNST)</a></strong> appears to be dormant. The project has no GitHub releases and the <a href="https://lists.fedorahosted.org/archives/list/lnst-developers@lists.fedorahosted.org/">LNST mailing list</a> shows zero activity.</p>
<p><strong><a href="http://web.dit.upm.es/vnxwiki/index.php/Main_Page">VNX</a></strong> has not received updates since September 2020. While the software still works, users should consider more actively maintained alternatives like Containerlab or Kathara for new projects.</p>
<p><strong><a href="https://malkiah.github.io/NetworkSimulator/">Educational Network Simulator</a></strong> has stopped development. The <a href="https://malkiah.github.io/NetworkSimulator/simulator01.html">hosted web app</a> is not working well and the <a href="https://github.com/malkiah/NetworkSimulator">code</a> has not been updated in a long time.</p>
<p><strong><a href="https://netsim.erinn.io/">CS4G Netsim</a></strong> was a student project that ended. The <a href="https://netsim.erinn.io/">hosted web app</a> continues to run for educational purposes. The <a href="https://github.com/errorinn/netsim"><em>NetSim</em> source code</a> remains on GitHub.</p>
<p><strong><a href="https://github.com/tinynetwork/tinet">tinet</a></strong> has not been updated in several years and the developers are not responding to pull requests. I think this project is retired.</p>
<h3>Removed for Other Reasons</h3>
<p>I removed a couple of projects from my list because I decided to tighten the focus of my blog to emulators/simulators that can run on a personal computer.</p>
<p><strong><a href="https://www.northeastern.edu/colosseum/">Colosseum</a></strong> continues as a research platform at Northeastern University. I will remove this from my list because I am tightening the focus of this list to emulators/simulators that can run on a personal computer.</p>
<p><strong><a href="https://github.com/roVer-HM/crownet">CrowNet</a></strong> development activity remains low, with only minor updates. I will move this to the “Tools” category because it is an auxiliary tool for simulating crowds and I am tightening the focus of this list to network emulators/simulators.</p>
<h2>Conclusion</h2>
<p>The 2024-2025 period saw continued growth in container-based network emulation tools, with Containerlab leading the way.</p>
<p>Traditional VM-based emulators like GNS3 remain popular for their graphical interfaces and broad hardware vendor support.</p>
<p>For software developers and network engineers learning automation in 2026, I recommend starting with Containerlab for its excellent documentation and active community. For those requiring GUI-based tools, I believe that GNS3 is the best choice in 2026.</p>
</div>



<p class=""></p>
<p>The post <a href="https://brianlinkletter.com/2026/02/open-source-simulator-emulator-in-2026/">Open-source network simulators and emulators in 2026</a> appeared first on <a href="https://brianlinkletter.com">Open-Source Routing and Network Simulation</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">5930</post-id>	</item>
		<item>
		<title>Cloonix Network Emulator Updated to Version 53</title>
		<link>https://brianlinkletter.com/2026/01/cloonix-network-emulator-updated-to-version-53/</link>
					<comments>https://brianlinkletter.com/2026/01/cloonix-network-emulator-updated-to-version-53/#comments</comments>
		
		<dc:creator><![CDATA[Brian Linkletter]]></dc:creator>
		<pubDate>Fri, 02 Jan 2026 14:34:43 +0000</pubDate>
				<category><![CDATA[Network Simulation]]></category>
		<category><![CDATA[Open Source Networking]]></category>
		<category><![CDATA[cloonix]]></category>
		<category><![CDATA[emulation]]></category>
		<category><![CDATA[simulation]]></category>
		<category><![CDATA[virtualization]]></category>
		<guid isPermaLink="false">https://brianlinkletter.com/?p=5874</guid>

					<description><![CDATA[<p>The Cloonix network emulator continues to be actively developed. In version 53, it provides container support, a rootless installation option, experimental support for WiFi emulation, and a simplified user experience.</p>
<p>The post <a href="https://brianlinkletter.com/2026/01/cloonix-network-emulator-updated-to-version-53/">Cloonix Network Emulator Updated to Version 53</a> appeared first on <a href="https://brianlinkletter.com">Open-Source Routing and Network Simulation</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>The <a href="http://clownix.net/">Cloonix network emulator</a> continues to be actively developed in 2026. In version 53, it provides container support, a rootless installation option, experimental support for WiFi emulation, and a simplified user experience.</p>
<div style="text-align: center;">
    <img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/01/00-splash.png?w=1200&#038;ssl=1" alt="" />
</div>
<p>In this post, I will review Cloonix v53 and walk through some hands-on exercises with the new features.</p>
<h2>What&#8217;s New in Cloonix v53</h2>
<p>The <a href="http://clownix.net/">Cloonix network emulator</a> has evolved significantly since I <a href="https://brianlinkletter.com/tag/cloonix/">last wrote about it</a> in 2017.</p>
<p>I list the major changes, since I last looked at Cloonix, below:</p>
<h3>Container Support</h3>
<p>One of the most significant additions is native container support, using <a href="https://github.com/containers/crun">crun</a>. Cloonix feature support for containers is, however, limited. For example, one cannot save a container&#8217;s filesystem and containers do not support Spice connections so they can&#8217;t run a desktop environment.</p>
<p>Cloonix containers start faster and use fewer resources than KVM virtual machines, or even Docker containers, making them ideal for scenarios where you need many lightweight nodes. I imagine containers in Cloonix will be used to create many clients in a network simulation scenario.</p>
<p>Cloonix continues to support virtual machines using qemu-kvm, and you can mix containers and KVM VMs in the same Cloonix topology.</p>
<h3>Rootless Installation Option</h3>
<p>Cloonix v53 introduces a &#8220;cloonix-toy&#8221; installation that runs entirely in user space without requiring root privileges. This is particularly useful for quick evaluations without modifying the host system or for using Cloonix on systems where you don&#8217;t have administrator access.</p>
<p>The rootless &#8220;cloonix-toy&#8221; version uses a container to isolate Cloonix from the host system while still providing full functionality.</p>
<p>In this post, I cover the full installation. I&#8217;ll cover the rootless &#8220;cloonix-toy&#8221; version in a future post.</p>
<h3>WiFi Emulation</h3>
<p>Cloonix v53 adds WiFi emulation support on qemu-kvm virtual machines using <a href="https://github.com/Raizo62/vwifi">vwifi</a>. This allows you to create wireless network scenarios in Cloonix. WiFi emulation is an experimental feature in Cloonix v53.</p>
<h3>Web Interface</h3>
<p>Cloonix now includes a web interface built with <a href="https://github.com/novnc/noVNC">noVNC</a> that allows you to access the Cloonix GUI from a web browser. This is useful for remote access or when running Cloonix on a headless server.</p>
<h2>Installing Cloonix</h2>
<p>Before installing Cloonix, ensure your system meets the requirements. The following procedures were tested on my laptop running Ubuntu 24.04:</p>
<h3>Hardware Virtualization</h3>
<p>Cloonix uses KVM for virtual machines. Check that your CPU supports hardware virtualization:</p>
<pre><code class="language-bash">$ egrep -c '(vmx|svm)' /proc/cpuinfo
</code></pre>
<p>If the result is greater than zero, your CPU supports virtualization. If not, you may need to enable it in your BIOS settings.</p>
<h3>Install virtualization tools</h3>
<p>Cloonix uses <em>qemu-kvm</em> and other virtualization tools that are not installed by default in the Ubuntu Desktop system. Install virtualization tools with the commands:</p>
<pre><code class="language-bash">$ sudo apt update
$ sudo apt install -y qemu-kvm \
                      libvirt-daemon-system \
                      libvirt-clients \
                      bridge-utils \
                      virt-manager
</code></pre>
<p>Installing the virtualization tools should also set up your PC to support the virtualization features required by Cloonix. If virtualization is not enabled, read the Cloonix documentation to see the steps required to <a href="https://clownix.net/doc_stored/build-53/singlehtml/index.html#host-system-required-customisation">enable virtualization for your Linux PC</a>.</p>
<h3>Install the <em>mac80211_hwsim</em> kernel module</h3>
<p>The <em>mac80211_hwsim</em> is used in WiFi simulation scenarios. Check that the <em>mac80211_hwsim</em> kernel module is installed:</p>
<pre><code class="language-bash">$ lsmod | grep mac80211_hwsim
</code></pre>
<p>If it is not installed, install it with the command:</p>
<pre><code class="language-bash">$ sudo modprobe mac80211_hwsim
</code></pre>
<h3>Cloonix Full Installation (Recommended)</h3>
<p>The full installation procedure is shown below:</p>
<pre><code class="language-bash">$ cd
$ wget http://clownix.net/downloads/cloonix-53/cloonix-bundle-53.tar.gz
$ tar xvf cloonix-bundle-53.tar.gz
$ rm cloonix-bundle-53.tar.gz
$ cd cloonix-bundle-53
$ sudo ./install_cloonfs
</code></pre>
<p>This installs Cloonix to the following directories:</p>
<ul>
<li><code>/usr/bin/cloonix_*</code> — User interface scripts</li>
<li><code>/usr/libexec/cloonix</code> — Binaries and libraries</li>
<li><code>/var/lib/cloonix</code> — Storage for VM images</li>
</ul>
<p>Removing Cloonix from your system is easy; you just delete the directories that the Cloonix install script created. See the Cloonix documentation for more details about <a href="https://clownix.net/doc_stored/build-53/singlehtml/index.html#howto-erase-cloonix-from-host">uninstalling Cloonix</a>.</p>
<h3>Guest filesystems</h3>
<p>Before you can use Cloonix, you need to have filesystems installed in the <em>/var/lib/cloonix/bulk</em> directory.</p>
<p>Download guest filesystem images in KVM and container formats. Cloonix offers different systems in their website&#8217;s <a href="https://clownix.net/downloads/cloonix-53/bulk/">downloads page for Cloonix v53</a>. I chose to use the images based on Debian 13 &#8220;Trixie&#8221;.</p>
<pre><code class="language-bash">$ cd /var/lib/cloonix/bulk
$ wget http://clownix.net/downloads/cloonix-53/bulk/trixie.qcow2.gz
$ gunzip trixie.qcow2.gz
$ wget https://clownix.net/downloads/cloonix-53/bulk/trixie_gnome.qcow2.gz
$ gunzip trixie_gnome.qcow2.gz
$ wget http://clownix.net/downloads/cloonix-53/bulk/trixie.zip
$ wget http://clownix.net/downloads/cloonix-53/bulk/trixie.tar.gz
$ tar zxvf trixie.tar.gz
$ rm trixie.tar.gz
</code></pre>
<p>Cloonix filesystems are based on standard Linux distributions, with <em><a href="https://wiki.qemu.org/Features/GuestAgent">qemu-guest-agent</a></em> and <em>cloonix-agent</em> pre-installed.</p>
<h3>Fix AppArmor settings (Ubuntu)</h3>
<p>I run Ubuntu 24.04 and ran into an <a href="https://clownix.net/doc_stored/build-53/html/doc/system.html#qemu-guest-agent">AppArmor problem with Cloonix</a> because Ubuntu restricts unprivileged user namespaces by default.</p>
<p>To see if you have the same problem, just run the help command:</p>
<pre><code class="language-bash">$ cloonix_cli nemo help
</code></pre>
<p>You should see a help screen that displays a list of Cloonix commands. If nothing happens, you probably have a problem with AppArmor and Cloonix.</p>
<p>To verify if AppArmor is a problem, check the kernel ring buffer for &#8220;denied&#8221; appaarmor operations:</p>
<pre><code class="language-bash">$ sudo dmesg | grep -i denied
</code></pre>
<p>If you see messages like the following, then you have the same problem I had:</p>
<pre><code class="language-text">[   54.172165] audit: type=1400 audit(1767126460.057:244): apparmor="DENIED" operation="capable" class="cap" profile="unprivileged_userns" pid=5282 comm="cloonix-hide-di" capability=21  capname="sys_admin"
</code></pre>
<p>The AppArmor issue with Cloonix on Ubuntu 24.04 relates to how Cloonix uses unprivileged user namespaces for container isolation. Starting with Ubuntu 23.10 and continuing in 24.04, Ubuntu added a new AppArmor restriction specifically targeting unprivileged user namespaces. This was a security hardening measure because unprivileged user namespaces have historically been a source of privilege escalation vulnerabilities.</p>
<p>To fix this, edit the <code>/etc/sysctl.conf</code> file:</p>
<pre><code class="language-bash">$ sudo nano /etc/sysctl.conf
</code></pre>
<p>Add the following two lines to the end of the file:</p>
<pre>
<code>kernel.unprivileged_userns_clone=1
kernel.apparmor_restrict_unprivileged_userns=0</code></pre>
<p>Save the file. Then, apply the changes:</p>
<pre><code class="language-bash">$ sudo sysctl -p
</code></pre>
<p>These settings reduce some security restrictions on your system. Unprivileged user namespaces can potentially be exploited in container escape or privilege escalation attacks. If you&#8217;re running Cloonix on a dedicated lab machine or VM, the security trade-off may be acceptable.</p>
<h2>Your First Cloonix v53 Network</h2>
<p>To test that the basics of Cloonix are working, let&#8217;s create a simple network with two virtual machines connected by a LAN.</p>
<h3>Start Cloonix</h3>
<p>Start the Cloonix server and GUI:</p>
<pre><code class="language-bash">$ cloonix_net nemo
</code></pre>
<p>Remember, you always have to start the Cloonix server before anything else. The &#8220;nemo&#8221; tag is the name of the Cloonix server. The Cloonix server names and parameters were defined in the <a href="https://clownix.net/doc_stored/build-53/singlehtml/index.html#cloonix-configuration">Cloonix configuration file</a>, which is installed in your system in the file <em>/usr/libexec/cloonix/cloonfs/etc/cloonix.cfg</em>.</p>
<p>Then, you can start the Cloonix GUI or issue Cloonix CLI commands. In this exercise, let&#8217;s start the GUI:</p>
<pre><code class="language-bash">$ cloonix_gui nemo
</code></pre>
<p>If you see an error message stating that something <em>Failed to load module &#8220;canberra-gtk-module&#8221;</em>, you can ignore it. It is warning about a module used to generate sound and I don&#8217;t feel like messing with the audio on my laptop.</p>
<h3>Create Virtual Machines</h3>
<p>Before creating VMs, configure the VM settings by right-clicking and selecting <em>kvm_config</em>.</p>
<div style="text-align: center;">
    <img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/01/01-KVM-config1.png?w=1200&#038;ssl=1" alt="Alt text" style="width: 450px;">
</div>
<p>The KVM config you set will be used to create the next virtual machine. If you need virtual mnachines with different configurations, set the config first, then start the virtual machine.</p>
<p>In this example, I will create two VMs with the same configurations, shown below:</p>
<ul>
<li><strong>VM name</strong>: Leave as default or customize. I set it to &#8220;VM&#8221;.</li>
<li><strong>RAM</strong>: 4000 MB (adjust based on your system)</li>
<li><strong>CPU</strong>: 2</li>
<li><strong>eth</strong>: <code>sss</code> (three spyable interfaces that can interact with Wireshark)</li>
</ul>
<p>The config window will look like the screenshot below:</p>
<div style="text-align: center;">
    <img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/01/01-KVM-config2.png?w=1200&#038;ssl=1" alt="Alt text" style="width: 300px;">
</div>
<p>Click <em>OK</em> to accept the configuration.</p>
<p>In the Cloonix GUI, right-click on the canvas to open the menu. Select the <em>kvm</em> radio button show the KVM options and select the filesystem <em>trixie.qcow2</em> to start a new virtual machine.</p>
<div style="text-align: center;">
    <img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/01/01-KVM1.png?w=1200&#038;ssl=1" alt="Alt text" style="width: 450px;">
</div>
<p>A new VM will appear on the canvas. Repeat the process again to create a second VM.</p>
<div style="text-align: center;">
    <img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/01/02-KVM1-started.png?w=1200&#038;ssl=1" alt="Alt text" style="width: 450px;">
</div>
<blockquote><p>
  <strong>Note:</strong> I noticed that if I changed the name to anything besides &#8220;Cloon&#8221;, Cloonix stops automatically numbering VMs (&#8220;Cloon1&#8221;, &#8220;Cloon2&#8221;, etc.). I had to open the KVM config panel before creating each new VM so I could give it a unique name.
</p></blockquote>
<h3>Create a LAN</h3>
<p>Right-click on the canvas and select <em>lan</em> to create a LAN.</p>
<div style="text-align: center;">
    <img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/01/03-KVM-lan.png?w=1200&#038;ssl=1" alt="Alt text" style="width: 450px;">
</div>
<p>A small circle representing the LAN switch will appear. It is labeled <em>lan01</em>.</p>
<div style="text-align: center;">
    <img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/01/04-KVM-lan2.png?w=1200&#038;ssl=1" alt="Alt text" style="width: 450px;">
</div>
<h3>Connect VMs to the LAN</h3>
<p>Double-click on the LAN object <em>lan01</em>. It will turn purple, indicating it&#8217;s selected for connection. Then click on the <em>eth1</em> port (the small circle labeled &#8220;1&#8221;) on the VM named <em>VM</em>. A line will connect the VM&#8217;s <em>eth1</em> interface to the LAN.</p>
<p>Repeat the process. Double-click on the LAN. Click on <em>VM2&#8217;s eth1</em> port.</p>
<p>Now both VMs are connected to the same LAN.</p>
<div style="text-align: center;">
    <img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/01/05-KVM-lan-connected.png?w=1200&#038;ssl=1" alt="Alt text" style="width: 450px;">
</div>
<h3>Configure IP Addresses</h3>
<p>Double-click on <em>VM</em> to open a terminal window. Configure its network interface:</p>
<pre><code class="language-text">VM# ip addr add 10.0.0.1/24 dev eth1
VM# ip link set eth1 up
</code></pre>
<p>Double-click on <em>VM2</em> and configure it:</p>
<pre><code class="language-text">VM2# ip addr add 10.0.0.2/24 dev eth1
VM2# ip link set eth1 up
</code></pre>
<h3>Test Connectivity</h3>
<p>From <em>VM</em>, ping <em>VM2</em>:</p>
<pre><code class="language-text">VM# ping -c 3 10.0.0.2
</code></pre>
<p>You should see output similar to the screenshot below:</p>
<div style="text-align: center;">
    <img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/01/06-KVM-lan-config.png?w=1200&#038;ssl=1" alt="Alt text">
</div>
<p>Congratulations! You&#8217;ve created your first Cloonix v53 network.</p>
<p>You can close both terminal windows by typing <code>exit</code> in each.</p>
<h3>SSH to VMs</h3>
<p>The terminal windows that open up when you double-click the VM lack some terminal features, like copy-and-paste via keyboard shortcuts. For a better user experience, I suggest that you run Cloonix CLI commands to SSH into any node running on a Cloonix server.</p>
<p>For example, in my PC&#8217;s terminal application, I can type the Cloonix CLI command to SSH into <em>VM2</em>:</p>
<pre><code class="language-text">$ cloonix_ssh nemo VM2
</code></pre>
<p>This enables me to interact with <em>VM2</em> in my PC&#8217;s terminal application.</p>
<h2>Using Containers in Cloonix</h2>
<p>To create a Cloonix node based on a container, you must first choose which container type you will use. The &#8220;zip&#8221; containers are very lightweight but, currently, do not have the option to install packages or save a persistent filesystem. The &#8220;cvm&#8221; containers are a bit heavier weight, but still lighter and faster than Docker containers, and they allow packages to be installed. In this example, I chose to use the &#8220;zip&#8221; container type.</p>
<p>Right-click on the Cloonix GUI canvas and click on the <em>zip</em> radio button to activate the container options for crun zip containers. Then, select <em>zip config</em> to configure the next container.</p>
<div style="text-align: center;">
    <img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/01/10-Container-config.png?w=1200&#038;ssl=1" alt="Alt text" style="width: 450px;">
</div>
<p>Configure the container. Choose a name for the node that will be created, the number of interfaces, etc.</p>
<div style="text-align: center;">
    <img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/01/11-Container-config-1.png?w=1200&#038;ssl=1" alt="Alt text" style="width: 300px;">
</div>
<p>Then, select the <em>trixie.zip</em> file system to create your container.</p>
<div style="text-align: center;">
    <img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/01/11-Container-create.png?w=1200&#038;ssl=1" alt="Alt text" style="width: 450px;">
</div>
<p>Now you have added a node based on a container to your topology. Cloonix shows you it&#8217;s a container by rendering it as a smaller circle on the canvas.</p>
<h3>Connect the Container</h3>
<p>Connect the container to the same LAN as your VMs:</p>
<ol>
<li>Double-click on the existing LAN, <em>lan01</em>.</li>
<li>Click on the container <em>Cnt1</em>&#8216;s <em>eth1</em> port.</li>
</ol>
<p>Now you should see all three nodes connected to the same LAN:</p>
<div style="text-align: center;">
    <img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/01/13-Container-connect.png?w=1200&#038;ssl=1" alt="Alt text" style="width: 450px;">
</div>
<h3>Access the Container</h3>
<p>Double-click on the container to open a shell, or run the following command in your terminal app:</p>
<pre><code class="language-bash">$ cloonix_ssh nemo Cnt1
</code></pre>
<p>Configure <em>Cnt1</em>&#8216;s network:</p>
<pre><code class="language-text"># ip addr add 10.0.0.3/24 dev eth1
# ip link set eth1 up
</code></pre>
<p>You now have a mixed environment with both KVM VMs and containers on the same network. Test that the connection work with the command:</p>
<pre><code class="language-txt"># ping -c 3 10.0.0.1
</code></pre>
<p>You should see output like:</p>
<pre><code class="language-txt">PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.508 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.319 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=0.827 ms

--- 10.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2046ms
rtt min/avg/max/mdev = 0.319/0.551/0.827/0.209 ms
Cnt1#
</code></pre>
<p>Exit the SSH session with the <code>exit</code> command.</p>
<h2>Using Wireshark to Capture Traffic</h2>
<p>Cloonix includes a customized version of Wireshark for packet capture and analysis.</p>
<h3>Enable Packet Capture</h3>
<p>In the Cloonix GUI, look at the <em>eth1</em> port on one of your nodes. If it&#8217;s green, it&#8217;s &#8220;spyable&#8221; and you can capture packets on it.</p>
<p>Double-click on the green <em>eth1</em> port on <em>Cnt1</em>. A Wireshark window will open and begin capturing packets.</p>
<h3>Generate Traffic</h3>
<p>From <em>VM2</em>&#8216;s terminal, ping <em>Cnt1</em>:</p>
<pre><code class="language-txt">VM2# ping -c 5 10.0.0.1
</code></pre>
<p>Watch the Wireshark window — you&#8217;ll see the ICMP echo request and reply packets.</p>
<div style="text-align: center;">
    <img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/01/15-wireshark-1.png?w=1200&#038;ssl=1" alt="Alt text" style="width: 650px;">
</div>
<h3>Save the Capture</h3>
<p>The customized Wireshark in Cloonix v53 is a simplified version. For full analysis capabilities, save the <code>.pcap</code> file and open it with a complete Wireshark installation on your host.</p>
<p>First, stop the capture. In Wireshark&#8217;s menu, go to <em>Capture</em> &#8211;> <em>Stop</em>. Then, save the .pcap file.<br />
Go to <em>File</em> &#8211;> <em>Save As</em> and choose a location.</p>
<h2>Adding Internet Access with NAT</h2>
<p>Cloonix provides a variety of filesystems for use in the Cloonix network simulator. In version 53, these root filesystem only have the most basic software packages installed and will not support advanced network configuration.</p>
<p>To create a network simulation that runs real-world networking software, we need to install new software on the root filesystems we will use in Cloonix. In this example, we chose to start with the Debian Trixie filesystem and we will install some new packages on it as a demonstration.</p>
<p>Let&#8217;s connect our network to the Internet using Cloonix&#8217;s NAT component.</p>
<h3>Create and connect NAT Object</h3>
<p>To connect a Cloonix node to a NAT object so it can communicate with the host system and with the Internet, follow the steps below:</p>
<ul>
<li>Right-click on the Cloonix GUI canvas and select <em>nat</em>. A NAT object will appear.</li>
<li>Right-click and select <em>lan</em> to create a new LAN (e.g., lan02).</li>
<li>Double-click on the new LAN to select it</li>
<li>Click on the NAT object to connect it</li>
<li>Double-click on the LAN again</li>
<li>Click on an unused port on <em>VM1</em> (e.g., <em>eth0</em>)</li>
</ul>
<p>You should see that the Cloonix canvas now looks similar to the screenshot below:</p>
<div style="text-align: center;">
    <img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/brianlinkletter.com/wp-content/uploads/2026/01/20-nat-create-1.png?w=1200&#038;ssl=1" alt="Alt text" style="width: 450px;">
</div>
<h3>Configure the VM for NAT</h3>
<p>On <em>VM1</em>, configure the interface connected to the NAT and request an IP address via DHCP. The Cloonix v53 documentation says to use the <code>dhclient</code> command but that utility is not installed in the root Trixie filesystem that comes with Cloonix. So, let&#8217;s set up DHCP manually on <em>VM1</em>.</p>
<p>Create a file named after the interface, e.g.:</p>
<pre><code class="language-text">VM1# nano /etc/network/interfaces.d/eth0
</code></pre>
<p>In the <em>eth0</em> file, add the following lines:</p>
<pre><code class="language-txt">auto eth0
iface eth0 inet dhcp
</code></pre>
<p>Save the file, then bring up the interface:</p>
<pre><code class="language-text">VM1# ifup eth0
</code></pre>
<p>The VM <em>VM1</em> should successfully connect with the DHCP server running on the NAT and request an IP address. In my case, it was assigned the IP address, <code>172.17.0.10/24</code>.</p>
<h3>Test Internet Connectivity</h3>
<p>Try to ping an external host:</p>
<pre><code class="language-text">VM# ping -c 3 google.com
</code></pre>
<p>If your host has Internet connectivity, the ping should succeed. In my case, pings failed but I still had Internet connectivity. The NAT object seems to be corrupting ICMP packets. Pinging the NAT interface at <code>172.17.0.2</code> will not work, and using Wireshark to check the packets on the <em>eth1</em> interface shows that the ICMP replies from <code>172.17.0.2</code> have invalid checksums.</p>
<p>This is a minor issue, because the actual functionality that we need still works. I can download repository info from the Internet and, after installing it, I can use <em>curl</em> to get web page information from the Internet.</p>
<h4>Get repository updates</h4>
<p>I tried running the <em>apt</em> command to update the repository information but the Trixie root file system that comes with Cloonix has only a local mirror configured and, since I am not running a local Debian mirror, it does not work. I had to edit the <em>/etc/apt/sources.list</em> file and add in the Debian repos.</p>
<pre><code class="language-txt">VM1# nano /etc/apt/sources.list
</code></pre>
<p>Delete the line that already exists in the file and add in the following line:</p>
<pre><code class="language-text">deb http://deb.debian.org/debian trixie main contrib non-free
</code></pre>
<p>Save the file, then update the repository:</p>
<pre><code class="language-txt">VM1# apt clean
VM1# apt update
</code></pre>
<p>Install <em>curl</em> on the VM <em>VM1</em>:</p>
<pre><code class="language-text">VM1# apt install curl
</code></pre>
<p>Now, you can use <em>curl</em> to test other Internet connectivity, like downloading a web page:</p>
<pre><code class="language-text">VM1# curl https://www.example.com
&lt;!doctype html&gt;&lt;html lang="en"&gt;&lt;head&gt;&lt;title&gt;Example Domain&lt;/title&gt;&lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;&lt;style&gt;body{background:#eee;width:60vw;margin:15vh auto;font-family:system-ui,sans-serif}h1{font-size:1.5em}div{opacity:0.8}a:link,a:visited{color:#348}&lt;/style&gt;&lt;body&gt;&lt;div&gt;&lt;h1&gt;Example Domain&lt;/h1&gt;&lt;p&gt;This domain is for use in documentation examples without needing permission. Avoid use in operations.&lt;p&gt;&lt;a href="https://iana.org/domains/example"&gt;Learn more&lt;/a&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;
</code></pre>
<h3>Configuring containers to connect to NAT</h3>
<p>Containers can be connected to the NAT object, the same as VMs. However, in Cloonix v53, both the <em>cvm</em> and <em>zip</em> versions of the Trixie filesystem are designed to be extremely minimal. They do not support DHCP.</p>
<p>Manually set up an IP address and default route:</p>
<pre><code class="language-txt">Cnt1# ip addr add 172.17.0.50/24 dev eth0
Cnt1# ip route add default via 172.17.0.2 dev eth0
</code></pre>
<p>Then, configure the nameserver. Edit the <em>/etc/resolv.conf</em> file:</p>
<pre><code class="language-txt">Cnt1# vi /etc/resolv.conf
</code></pre>
<p>Replace the nameserver with:</p>
<pre><code class="language-txt">nameserver 172.17.0.3
</code></pre>
<p>Save the file.</p>
<p>It is not possible to install packages on the <em>zip</em>-type containers. But, if you use the <em>cvm</em>-type containers, you can install packages. To enable the apt repository, edit the <em>/etc/apt/sources.list</em> file:</p>
<pre><code class="language-txt">Cvm2# vi /etc/apt/sources.list
</code></pre>
<p>Delete the existing line, and replace it with:</p>
<pre><code class="language-txt">deb http://deb.debian.org/debian trixie main contrib non-free
</code></pre>
<h2>Using the Command-Line Interface</h2>
<p>The <code>cloonix_cli</code> command allows you to automate topology creation with scripts. See the Cloonix documentation for CLI commands. You can do everything in the CLI that you can do in the GUI.</p>
<p>I&#8217;ll cover more about the Cloonix v53 CLI in a future post.</p>
<h3>Stop Cloonix</h3>
<p>When finished with a simulation, stop the Cloonix server:</p>
<pre><code class="language-bash">$ cloonix_cli nemo kil
</code></pre>
<p>This terminates all VMs, containers, and cleans up resources.</p>
<h2>Running a Demo Script</h2>
<p>Cloonix includes demo scripts that create pre-configured network scenarios.</p>
<p>Demo scripts are available on the Cloonix web site. They make good examples of how to build a script that creates a customized simulation scenario.</p>
<p>Let&#8217;s get the demo scripts so we can use them to play with Cloonix:</p>
<pre><code class="language-bash">$ cd
$ cd cloonix-bundle-53
$ mkdir demos
$ cd demos
$ wget https://clownix.net/downloads/cloonix-53/demos/eth_cvm.sh
$ wget https://clownix.net/downloads/cloonix-53/demos/eth_kvm.sh
$ wget https://clownix.net/downloads/cloonix-53/demos/eth_zip.sh
$ wget https://clownix.net/downloads/cloonix-53/demos/wpa_cvm.sh
$ wget https://clownix.net/downloads/cloonix-53/demos/wpa_kvm.sh
$ wget https://clownix.net/downloads/cloonix-53/demos/wpa_zip.sh
$ chmod +x *
</code></pre>
<p>To run a demo script, run the following commands:</p>
<pre><code class="language-bash">$ cd
$ cd cloonix-bundle-53/demos
$ ./eth_kvm.sh
</code></pre>
<h2>Conclusion</h2>
<p>Cloonix v53 represents the continued evolution of the Cloonix network emulator. The addition of container support and WiFi emulation show the potential to significantly expands its capabilities, as these features are improved in future releases. The simplified installation process and improved user interface make Cloonix more accessible than ever.</p>
<p>Cloonix is actively developed and it continues to be a valuable tool in the network emulation landscape.</p>
<h2>Links</h2>
<ul>
<li><a href="http://clownix.net/">Cloonix Website</a></li>
<li><a href="https://clownix.net/doc_stored/build-53/singlehtml/index.html">Cloonix Documentation (v53)</a></li>
<li><a href="https://github.com/clownix/cloonix">Cloonix on GitHub</a></li>
<li><a href="https://brianlinkletter.com/tag/cloonix/">My Previous Cloonix Posts</a></li>
</ul>
<p>The post <a href="https://brianlinkletter.com/2026/01/cloonix-network-emulator-updated-to-version-53/">Cloonix Network Emulator Updated to Version 53</a> appeared first on <a href="https://brianlinkletter.com">Open-Source Routing and Network Simulation</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://brianlinkletter.com/2026/01/cloonix-network-emulator-updated-to-version-53/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">5874</post-id>	</item>
	</channel>
</rss>
