477 lines
14 KiB
Markdown
477 lines
14 KiB
Markdown
# 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`.
|
|
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 `<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:
|
|
|
|
```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.
|
|
|