Computer Networks II — Declarative Mininet Topology Builder

This repository implements the programming assignment for Computer Networks II using a small declarative JSON topology language and a staged Python build pipeline.

The JSON describes the desired network rather than the exact Mininet commands used to create it.

The implementation loads that description, parses it into typed Python objects, validates its semantics, resolves all abstract interface selections into a concrete execution plan, renders executable Mininet Python, and then runs that generated artifact.

Start with Quick start to generate and run a topology. The topology language overview introduces the JSON format; the JSON language reference defines its fields, defaults, and semantic constraints.

Project goals

The main goals of the implementation are:

  • describe Mininet topologies declaratively in JSON,
  • keep topology intent separate from Mininet implementation details,
  • use typed Python objects throughout the processing pipeline,
  • detect grammar and semantic errors before changing the network,
  • generate an inspectable intermediate Python artifact,
  • execute exactly that generated artifact for normal runs,
  • keep runtime experiments separate from topology construction,
  • clean up services and Mininet state safely after the CLI exits.

The topology language is intentionally small.

Its core semantic entities are:

Node
Interface
Link
Node Settings
Service

Repository layout

source/
    build_network.py       CLI coordinator and environment checks
    topology_loader.py     JSON loading
    topology_parser.py     Strict parsing and typed semantic model
    topology_validator.py  Semantic and cross-reference checks
    topology_plan.py       Fully resolved execution plan
    topology_renderer.py   Executable Mininet Python generation
report/                    Assignment report workspace
exercise/                  Assignment execution files
Mininet_DSL_Design.md      Mininet JSON domain specific language rules and semantics
README.md                  Repository's main documentation file

Save a topology source as topology.json. Normal execution writes topology.generated.py beside that source file.

Requirements

The implementation targets a Linux environment capable of running Mininet.

The current runtime and experiment dependencies are:

Python Mininet module
mn
ovs-vsctl
ip
tc
sysctl
dnsmasq
dhclient
tcpdump
ping

On Ubuntu, the corresponding packages are typically provided by:

sudo apt install \
    mininet \
    openvswitch-switch \
    iproute2 \
    procps \
    dnsmasq-base \
    isc-dhcp-client \
    tcpdump \
    iputils-ping

The exact environment can be checked without modifying the system:

python3 source/build_network.py --env-check

The check reports each required module or executable as OK or MISSING.

Quick start

Run these commands from the repository root. Save the complete assignment example as topology.json first, or use your own topology file.

1. Check the environment

Run:

python3 source/build_network.py --env-check

2. Generate the intermediate Python only

Run:

python3 source/build_network.py topology.json --emit

This writes:

topology.generated.py

and stops without creating a Mininet network.

A custom output path can be supplied:

python3 source/build_network.py topology.json --emit gen-test.py

Generation does not require root privileges.

3. Generate and execute

Run:

sudo python3 source/build_network.py topology.json

The coordinator:

loads
parses
validates
plans
renders
writes topology.generated.py
executes topology.generated.py

Network execution requires root privileges because Mininet creates namespaces, virtual interfaces, Open vSwitch state, and other kernel networking objects.

4. Exit the Mininet CLI

When finished, leave the CLI with:

mininet> exit

The generated script then stops the service processes it started and calls net.stop().

Topology language

The JSON describes nodes, their interfaces, links, and services. Nodes may be hosts, Layer-2 switches, or routers; interfaces use static, DHCP, or no addressing. Links connect interfaces, and services describe processes such as a DHCP server.

Use the JSON language reference for field definitions, defaults, interface selection rules, and semantic constraints. The following example provides a complete starting point for the assignment.

Complete assignment topology example

The example here describes the required two-subnet topology.

Configuration versus runtime state

The topology source describes configuration intent.

It does not record all runtime state.

Examples include:

JSON declaration                 Runtime result

static address          ----->   configured IPv4 address
static gateway          ----->   configured default route
DHCP addressing         ----->   dynamically learned address
DHCP service settings   ----->   dynamically advertised options
links                    ----->   Mininet/veth interfaces
services                 ----->   running processes

DHCP leases, ARP entries, packet counters, and packet captures are runtime state.

They do not belong in the topology grammar.

Runtime experiments

After the generated topology enters the Mininet CLI, the assignment experiments can be performed with tools such as:

dhclient
tcpdump
ip neigh
ping

These commands operate on the running network and may create transient runtime state.

They do not modify the declarative topology source.

DHCP capture preparation

DHCP clients are intentionally not started by the generated network.

The assignment requires packet capture to begin before the DHCP exchange is triggered manually.

A clean client-side DHCP experiment can use a temporary lease and PID file:

mininet> h1 rm -f /tmp/h1-dhclient.leases /tmp/h1-dhclient.pid
mininet> h1 dhclient -v \
    -lf /tmp/h1-dhclient.leases \
    -pf /tmp/h1-dhclient.pid \
    h1-eth0

A successful exchange should show the normal DORA sequence:

DHCPDISCOVER
DHCPOFFER
DHCPREQUEST
DHCPACK

The DHCP server lease file generated by this project is reset before the service starts, so stale server-side leases from earlier topology runs do not affect a new experiment.

Architecture

The implementation follows a compiler-like pipeline:

topology.json
    → load → parse → validate → plan → render
    → write generated Python → execute → Mininet CLI → cleanup

The language describes network concepts that map to Mininet and Linux primitives: nodes, interfaces, links, network-stack settings, and services. Each stage has a separate responsibility so it can be tested independently. The generated Python is the only executable Mininet backend.

Loading and parsing

topology_loader.py provides load_topology(path), which reads a JSON file into a raw dictionary. topology_parser.py provides parse_topology(raw), which converts that dictionary into a typed Topology. Neither stage depends on Mininet.

The semantic model uses dataclasses for Topology, Node, Interface, Link, LinkEndpoint, Service, and their structured settings. Enums represent node types, addressing modes, service types, and selectors. Interfaces and settings are composed into their owning entities.

Nodes, links, and services remain ordered lists during parsing. Keeping nodes in a list preserves duplicate declarations for diagnostics; converting them immediately to a name-indexed dictionary could hide duplicates. Node.interfaces uses Optional[Dict[str, Interface]]: None preserves an omitted interface set, while {} preserves an explicitly empty set.

Validation

topology_validator.py exposes validate(topology). It checks semantic and cross-reference constraints after parsing has checked field names, representation, and value types. See the language reference for those rules.

The validator collects ValidationIssue(location, message) objects and raises one TopologyValidationError containing all issues discovered in the pass. It does not print diagnostics or create Mininet objects. Validation checks whether automatic interface allocation is possible; planning performs the concrete allocation.

Planning

topology_plan.py exposes plan(topology) and produces an immutable ExecutionPlan from a validated topology. It does not import Mininet, execute commands, or render Python.

The plan contains ordered tuples of node, link, interface, sysctl, and service operations. Its types include PlannedNode, PlannedLinkEndpoint, PlannedLink, PlannedLinkSettings, PlannedInterface, PlannedSysctl, PlannedService, and PlannedDHCPServerSettings. No unresolved auto or all selectors remain.

The planner reserves all explicit link-interface references before allocating auto selections from fixed pools in declaration order. Dynamic pools receive synthetic logical identifiers auto0, auto1, and so on. Concrete interface names follow <node>-eth<N>, starting at zero per node in link and endpoint traversal order. For example, (r0, lan) may resolve to r0-eth0.

Mininet creates interfaces through links. Planning therefore rejects configured interfaces or service bindings that have no corresponding link, while allowing unused empty interface declarations.

Switches are planned with Open vSwitch failMode="standalone" to operate as Layer-2 learning switches without an OpenFlow controller. Hosts and routers are created with net.addHost(..., ip=None). Router settings generate sysctls only when explicitly supplied. rp_filter operations cover all, default, and each materialized router interface inside the router namespace.

Rendering and runtime lifecycle

topology_renderer.py exposes render_python(plan). It serializes the resolved plan without allocating interfaces or validating language semantics. The resulting standalone Python program is preserved for inspection.

The generated program:

  1. Creates nodes and links, then builds and starts Mininet.
  2. Configures interfaces, static default routes, and namespace-scoped sysctls.
  3. Starts services and enters the Mininet CLI.
  4. Terminates tracked service processes and calls net.stop() in a finally block.

When a link has traffic-control settings, the renderer emits TCLink. Bandwidth maps to bw, loss to loss, and delay and jitter to millisecond strings. For example:

net.addLink(
    nodes["r0"],
    nodes["s1"],
    intfName1="r0-eth0",
    intfName2="s1-eth1",
    cls=TCLink,
    delay="1.5ms",
)

Interface configuration uses Linux commands such as ip addr add, ip route replace, and sysctl -w inside the appropriate node namespace.

DHCP services run through dnsmasq, with DNS disabled and a 12-hour lease time. These are backend choices, not configurable JSON fields. Each service uses indexed lease, PID, and log paths under /tmp. The generated script removes its previous lease and PID files before startup and tracks the background PID for cleanup. DHCP clients are started manually after packet capture begins, as described in Runtime experiments.

Coordination and error reporting

build_network.py coordinates the stages through compile_topology(path), which returns the execution plan and rendered source. The coordinator writes the generated artifact beside the input using the .generated.py suffix unless --emit PATH selects a different destination.

--emit stops after writing, without requiring Mininet or root privileges. Normal execution writes the same artifact, checks dependencies, and runs it with the coordinator's Python interpreter. Network execution requires root privileges. --env-check reports dependencies and exits without compiling a topology. See Quick start for commands.

Core stages raise typed exceptions; the command-line layer reports them.

Stage Exception
Loading TopologyLoadError
Parsing TopologyParseError
Validation TopologyValidationError
Planning TopologyPlanError
Artifact writing or process launch BuildNetworkError

The coordinator returns a failing status for reported errors and propagates the exit status of the generated program.

Self-tests

The five core pipeline modules contain lightweight __main__ smoke tests. The coordinator's __main__ invokes its command-line interface instead.

They exercise each module's public interface.

They can be run directly:

python3 source/topology_loader.py
python3 source/topology_parser.py
python3 source/topology_validator.py
python3 source/topology_plan.py
python3 source/topology_renderer.py

These smoke tests do not require Mininet, root privileges, or network changes.

The environment can be checked separately with:

python3 source/build_network.py --env-check

A generated topology can then be tested in Mininet with:

sudo python3 source/build_network.py topology.json

Current scope and extension points

The current language supports:

  • hosts,
  • Layer-2 learning switches,
  • routers,
  • static IPv4 addressing,
  • DHCP client intent,
  • one optional static default gateway per node,
  • logical interface tags,
  • automatic interface allocation,
  • link bandwidth, delay, loss, and jitter,
  • DHCP server services,
  • router forwarding and reverse-path-filter settings.

Extending the language requires corresponding parser, validator, planner, and renderer support. Potential extensions include:

  • additional node types,
  • additional services,
  • richer interface properties,
  • richer link behavior,
  • more general routing constructs.

The project currently supports IPv4 network configuration for the assignment.

Further documentation

S
Description
AUTH's THMMY "Computer Networks II" course assignments.
Readme MIT
1 MiB
Languages
Python 60.5%
TeX 39.5%