Software-Defined Networking: Control and Data Plane Separation
I built working intuition for SDN by separating the control plane from the data plane on Mininet with Open vSwitch and a remote controller. Decoupling forwarding decisions from forwarding hardware enables programmable, centrally managed networks.
Objective & Context
Traditional switches couple control and forwarding in every box. SDN centralizes control logic and exposes it through northbound APIs to applications and southbound protocols (OpenFlow) to switches. This lab grounds those abstractions in a runnable topology.
Learning Objectives
- Explain control plane vs data plane separation in SDN.
- Launch a Mininet topology wired to a remote controller.
- Inspect the OpenFlow datapath of a managed switch.
- Confirm the controller connection from the switch side.
- Map northbound and southbound APIs to concrete components.
Environment & Prerequisites
Prerequisites
- Mininet with Open vSwitch; a controller (Ryu/ONOS) reachable.
- Python 3 for controller apps; OpenFlow 1.3 support.
- Basic switching/routing knowledge.
- Mininet with Open vSwitch installed.
- A controller (Ryu or ONOS) reachable at 127.0.0.1.
- Python 3 for controller applications.
- OpenFlow 1.3 support on the switches.
- Basic switching and routing knowledge.
Step-by-Step Execution
1. Launch a Mininet topology with a remote controller
sudo mn --topo tree,2 --controller remote,ip=127.0.0.1 --switch ovsk,protocols=OpenFlow13Expected output:
*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2 h3 h4
*** Adding switches:
s1 s2 s3
*** Adding links:
*** Configuring hosts
h1 h2 h3 h4
*** Starting controller
*** Starting 3 switches
s1 s2 s3 ...
*** Starting CLI:
mininet>
What this teaches: one command stands up an entire multi-switch fabric that a controller can program.
2. Inspect the switch's OpenFlow datapath
ovs-ofctl -O OpenFlow13 dump-flows s1Expected output:
cookie=0x0, duration=12.401s, table=0, n_packets=0, n_bytes=0, priority=0 actions=CONTROLLER:65535
What this teaches: with no controller logic, the switch holds no flows, which is the visible difference from autonomous switching.
3. Confirm controller connection
ovs-vsctl get-controller s1Expected output:
tcp:127.0.0.1:6653
What this teaches: the controller connection is a TCP session the switch maintains; SDN is concrete infrastructure, not magic.
The target format is tcp:IP:6653 (OpenFlow's IANA port). If the command prints nothing, no controller is configured on that bridge: check what the bridge thinks with ovs-vsctl get bridge s1 protocols, and confirm the TCP session is up with ss -tn | grep 6653.
How It Works
Traditional switches bundle two jobs: the control plane (deciding where packets go: STP, routing protocols, MAC learning) and the data plane (moving packets according to those decisions). SDN separates them: dumb-ish switches forward packets, and a logically centralized controller computes all forwarding behavior and programs it into the switches.
The controller talks southbound to switches, most famously with OpenFlow, installing flow entries that match packet fields and apply actions. Applications talk northbound to the controller through APIs (REST, intents, or event frameworks), expressing policy like 'isolate these tenants' without touching individual devices.
The payoff is programmability: the whole network becomes software-defined state that can be versioned, tested, and automated. The risk is centralization: the controller is a single point of logic, so production designs cluster it and keep the data plane forwarding during controller outages.
Validation & Testing
With the controller down, confirm switches have no proactive flows and traffic fails; bring it up and confirm reactive flows install. Pass criteria: forwarding behaviour visibly depends on the centralized controller.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Switches connect but hosts cannot ping | The controller app installs no flows (empty flow tables) | Dump flows; if empty, the controller logic is the problem, not the topology |
| Controller connection refused | Controller not listening on 6653 or bound to the wrong address | Check the controller logs and confirm the listening socket with ss -tlnp |
| Mininet topology starts but switches show no controller | protocols=OpenFlow13 mismatch with what the controller speaks | Align the OpenFlow version on both sides; mixed versions negotiate down or fail |
| Everything works until the controller restarts | Switches flushed flows on disconnect (default fail mode) | Set the fail mode to standalone or secure explicitly depending on the desired behavior |
| High packet-in rate overwhelms the controller | Too many table misses from broad or missing proactive flows | Install proactive flows for known traffic so only genuinely new flows hit the controller |
Advanced: Troubleshooting
- Switch not connecting: verify controller IP/port and matching OpenFlow version.
- No flows install: confirm the controller app is running and reachable on 6653.
- Mininet stale state: run
mn -cto clean before relaunch.
Key Results
- Demonstrated control/data plane separation on a 2-switch topology.
- Observed reactive flow installation driven entirely by the controller.
- Mapped northbound and southbound API roles to concrete components.
- Established the base topology reused in the OpenFlow and controller labs.
Key Takeaways
- SDN moves forwarding decisions to software; switches become programmable datapaths.
- Northbound APIs express intent, southbound protocols install mechanism.
- An empty flow table is the signature of a controller-driven network at rest.
- Design for controller failure: the data plane should survive losing its brain.
Practice Challenges
Challenge 1: Run controllerless
Goal: Start the same topology with no controller and observe how the switches behave in standalone mode.
Hint: Omit --controller and check whether the OVS bridges learn MACs on their own.
Challenge 2: Count packet-ins
Goal: Generate traffic to unknown destinations and count packet-in messages reaching the controller.
Hint: Watch the controller log while running ping to a nonexistent host from h1.