# 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](#quick-start) to generate and run a topology. The [topology language overview](#topology-language) introduces the JSON format; the [JSON language reference](Mininet_DSL_Design.md) 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: ```text Node Interface Link Node Settings Service ``` ## Repository layout ```text 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: ```text Python Mininet module mn ovs-vsctl ip tc sysctl dnsmasq dhclient tcpdump ping ``` On Ubuntu, the corresponding packages are typically provided by: ```bash 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: ```bash 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](#complete-assignment-topology-example) as `topology.json` first, or use your own topology file. ### 1. Check the environment Run: ```bash python3 source/build_network.py --env-check ``` ### 2. Generate the intermediate Python only Run: ```bash python3 source/build_network.py topology.json --emit ``` This writes: ```text topology.generated.py ``` and stops without creating a Mininet network. A custom output path can be supplied: ```bash python3 source/build_network.py topology.json --emit gen-test.py ``` Generation does not require root privileges. ### 3. Generate and execute Run: ```bash sudo python3 source/build_network.py topology.json ``` The coordinator: ```text 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: ```text 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](Mininet_DSL_Design.md) 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](exercise/topology.json) 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: ```text 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: ```text 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: ```text 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: ```text 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: ```text 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`. The loader's convenience API, `load(path)`, performs both operations. 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](Mininet_DSL_Design.md) 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 `-eth`, 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: ```python 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](#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](#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: ```bash 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: ```bash python3 source/build_network.py --env-check ``` A generated topology can then be tested in Mininet with: ```bash 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 - [JSON language reference](Mininet_DSL_Design.md): complete field definitions, defaults, and semantic constraints. - [Assignment brief](Computer_Networks_II-Programming_task.pdf): required topology, experiments, and deliverables.