Skip to content

Simulate an OpenFlow learning controller

Handle simulated Packet-In events, install and inspect learning-switch flows, then prove an IoT-isolation policy with priorities, counters, receiver results, and cleanup.

Cloud Clara: I want you to connect controller intent to table state, counters, receiver behavior, protected paths, and cleanup., your practice guide

Cloud Clara: I want you to connect controller intent to table state, counters, receiver behavior, protected paths, and cleanup.
Predict the reading, then compare it with the measurement.

Python 3 in your browser (JupyterLite)

Python · no install

Tier 2 · Web · paste-in setup · No account

Version tested: Python 3.12.7 / Pyodide 0.27.6 in JupyterLite 0.6.4; Chromium 148.0.7778.96; captureSource playwright:jupyterlite. Date: 2026-09-10.

Open the notebook in your browser and run each Python cell; no install or account is needed.

Three ways to run: use JupyterLite here with no install; run main.py locally from the downloadable lab folder; or open the same notebook in Google Colab.

Open in your browser (new tab)

Steps

Screens captured against JupyterLite Python 3.12.7 / Pyodide 0.27.6; Chromium 148.0.7778.96 on 2026-09-10; the tool may have moved on — the text steps are the contract.

  1. 1 Step 1

    Do
    In the JupyterLite notebook, run the Step 1 cell to inspect the simulated two-switch topology and initial table-miss rule.
    You will see
    Input: execute notebook step 1 with the record’s unchanged fixture, seed, and parameters. Observed: Simulated OpenFlow 1.3 topology Observed: sensor-1 --(port 1) s1-edge (port 3)-- s2-services -- telemetry Observed: laptop-1 --(port 2) s1-edge -- admin
    Why it matters
    Expected: STEP 1 topology switches=2 hosts=4 table-miss=CONTROLLER from this deterministic notebook step. Interpretation: The explicit table-miss establishes why an unknown packet reaches control logic instead of disappearing silently. Action: Run python3 main.py --step 1 to inspect the simulated two-switch topology and initial table-miss rule. Boundary: this local fixture is not a live device, network, or site measurement.
    JupyterLite notebook output for openflow-controller-sim step 1, captured after running the real Python cell.
    Step 1 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  2. 2 Step 2

    Do
    In the JupyterLite notebook, run the Step 2 cell to send the first frame to an unknown destination.
    You will see
    Input: execute notebook step 2 with the record’s unchanged fixture, seed, and parameters. Observed: First packet: unknown destination Observed: RX s1-edge port=1 src=02:00:00:00:00:01 dst=02:00:00:00:00:02 Observed: Table lookup: priority=0 match=* action=CONTROLLER
    Why it matters
    Expected: STEP 2 packet-in learned=02:00:00:00:00:01@1 action=FLOOD from this deterministic notebook step. Interpretation: The event shows which state can be learned safely from ingress and why no destination-specific rule exists yet. Action: Run python3 main.py --step 2 to send the first frame to an unknown destination. Boundary: this local fixture is not a live device, network, or site measurement.
    JupyterLite notebook output for openflow-controller-sim step 2, captured after running the real Python cell.
    Step 2 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  3. 3 Step 3

    Do
    In the JupyterLite notebook, run the Step 3 cell to deliver the reply and install forward and reverse learned flows.
    You will see
    Input: execute notebook step 3 with the record’s unchanged fixture, seed, and parameters. Observed: Reply packet: destination is now known Observed: RX s1-edge port=2 src=02:00:00:00:00:02 dst=02:00:00:00:00:01 Observed: Controller learns 02:00:00:00:00:02 -> port 2
    Why it matters
    Expected: STEP 3 learning-switch macs=2 installed=2 reply=OUTPUT:1 from this deterministic notebook step. Interpretation: Controller intent becomes reviewable switch state only after the matches, actions, and acknowledgement are visible. Action: Run python3 main.py --step 3 to deliver the reply and install forward and reverse learned flows. Boundary: this local fixture is not a live device, network, or site measurement.
    JupyterLite notebook output for openflow-controller-sim step 3, captured after running the real Python cell.
    Step 3 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  4. 4 Step 4

    Do
    In the JupyterLite notebook, run the Step 4 cell to print the resulting s1-edge flow table.
    You will see
    Input: execute notebook step 4 with the record’s unchanged fixture, seed, and parameters. Observed: Flow table: s1-edge Observed: prio match action packets Observed: 100 in_port=2,dst=02:00:00:00:00:01 OUTPUT:1 0
    Why it matters
    Expected: STEP 4 flow-table entries=3 learned=2 table-miss=1 from this deterministic notebook step. Interpretation: A table dump proves final ordering and preserves the fallback path for addresses the controller has not learned. Action: Run python3 main.py --step 4 to print the resulting s1-edge flow table. Boundary: this local fixture is not a live device, network, or site measurement.
    JupyterLite notebook output for openflow-controller-sim step 4, captured after running the real Python cell.
    Step 4 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  5. 5 Step 5

    Do
    In the JupyterLite notebook, run the Step 5 cell to repeat the forward packet after its rule is installed.
    You will see
    Input: execute notebook step 5 with the record’s unchanged fixture, seed, and parameters. Observed: Repeat packet: data-plane table hit Observed: RX s1-edge port=1 dst=02:00:00:00:00:02 Observed: Matched priority=100 match=in_port=1,dst=02:00:00:00:00:02
    Why it matters
    Expected: STEP 5 data-plane-hit action=OUTPUT:2 packets=1 packet-in=NO from this deterministic notebook step. Interpretation: The match, counter movement, controller absence, and receiver result together prove data-plane handling. Action: Run python3 main.py --step 5 to repeat the forward packet after its rule is installed. Boundary: this local fixture is not a live device, network, or site measurement.
    JupyterLite notebook output for openflow-controller-sim step 5, captured after running the real Python cell.
    Step 5 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  6. 6 Step 6

    Do
    In the JupyterLite notebook, run the Step 6 cell to install a higher-priority IoT-isolation policy.
    You will see
    Input: execute notebook step 6 with the record’s unchanged fixture, seed, and parameters. Observed: FLOW_MOD priority=300 match=src_group=iot,dst_group=admin action=DROP Observed: FLOW_MOD priority=200 match=src_group=iot,dst_group=telemetry action=OUTPUT:3 Observed: Policy rule: IoT devices may send telemetry but may not initiate admin traffic.
    Why it matters
    Expected: STEP 6 isolation-policy installed=2 deny-priority=300 allow-priority=200 from this deterministic notebook step. Interpretation: Narrow policy matches and explicit priorities keep a broad learning rule from bypassing the protected admin boundary. Action: Run python3 main.py --step 6 to install a higher-priority IoT-isolation policy. Boundary: this local fixture is not a live device, network, or site measurement.
    JupyterLite notebook output for openflow-controller-sim step 6, captured after running the real Python cell.
    Step 6 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  7. 7 Step 7

    Do
    In the JupyterLite notebook, run the Step 7 cell to test one allowed telemetry packet and one denied admin packet.
    You will see
    Input: execute notebook step 7 with the record’s unchanged fixture, seed, and parameters. Observed: packet A src_group=iot dst_group=telemetry Observed: packet A matched priority=200 action=OUTPUT:3 receiver=telemetry Observed: packet B src_group=iot dst_group=admin
    Why it matters
    Expected: STEP 7 policy-test telemetry=ALLOW admin=DENY counters=1,1 from this deterministic notebook step. Interpretation: Both positive and protected-path tests are needed to prove isolation without breaking the permitted IoT service. Action: Run python3 main.py --step 7 to test one allowed telemetry packet and one denied admin packet. Boundary: this local fixture is not a live device, network, or site measurement.
    JupyterLite notebook output for openflow-controller-sim step 7, captured after running the real Python cell.
    Step 7 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  8. 8 Step 8

    Do
    In the JupyterLite notebook, run the Step 8 cell to remove the temporary isolation rules and close the proof record.
    You will see
    Input: execute notebook step 8 with the record’s unchanged fixture, seed, and parameters. Observed: Before cleanup: entries=5 policy_entries=2 learned_entries=2 table-miss=1 Observed: FLOW_MOD DELETE owner=maintenance-window priorities=200,300 Observed: After cleanup: entries=3 policy_entries=0 learned_entries=2 table-miss=1
    Why it matters
    Expected: STEP 8 cleanup removed=2 remaining=3 proof=COMPLETE from this deterministic notebook step. Interpretation: Explicit cleanup prevents maintenance policy from becoming hidden permanent state and completes the protocol evidence chain. Action: Run python3 main.py --step 8 to remove the temporary isolation rules and close the proof record. Boundary: this local fixture is not a live device, network, or site measurement.
    JupyterLite notebook output for openflow-controller-sim step 8, captured after running the real Python cell.
    Step 8 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)

Chapter checks

These questions refer to the chapter’s examples. Use the return links to review their answers.

  1. During a maintenance window, a controller must steer gateway update downloads without catching telemetry, door-control events, or management access. Which first check keeps the OpenFlow-style rule change traceable?

    Return to the chapter’s knowledge check
  2. A controller sends a flow-mod to steer update traffic, and the switch accepts it. What proof is still needed before saying the service behavior worked?

    Return to the chapter’s knowledge check
  3. A controller sends two OpenFlow-style changes for a gateway maintenance window: remove an old broad update rule, then install a narrower rule with protected exceptions. Which proof closes the protocol boundary?

    Return to the chapter’s knowledge check

Caution

This deterministic simulator teaches rule ordering and evidence, not OpenFlow wire encoding, concurrency, switch fail modes, TLS, controller high availability, or production authorization. Use the optional Mininet commands only on a disposable Linux environment where you are permitted to use sudo.

Return to OpenFlow Protocol and Flow Tables · Browse Labs