I deployed Ryu and ONOS controllers and programmed forwarding behaviour through a Python application and the northbound REST API. Centralizing logic in the controller replaced per-switch configuration with one programmable control point for the whole fabric.

Objective & Context

The controller is the brain of an SDN fabric. This lab writes a Ryu app that reacts to packet-in events and pushes flows, then drives ONOS through REST, demonstrating both event-driven and declarative control models on the topology from the SDN concepts lab.

Learning Objectives

  • Run a Ryu learning-switch application against OVS switches.
  • Push flows to ONOS through its REST API.
  • Verify managed devices and installed flows from the controller side.
  • Contrast event-driven (Ryu) and declarative (ONOS intent/REST) control models.
  • Explain packet-in handling and flow-mod installation.

Environment & Prerequisites

Prerequisites

  • Ryu and/or ONOS installed; Mininet with OVS switches.
  • Python 3 for the Ryu app; curl for ONOS REST.
  • OpenFlow 1.3 connectivity on port 6653.
  • Ryu and/or ONOS installed (Ryu via pip, ONOS as a distribution).
  • Mininet with OVS switches connected to the controller.
  • Python 3 for the Ryu application.
  • curl and jq for ONOS REST calls.
  • OpenFlow 1.3 connectivity on port 6653.

Step-by-Step Execution

1. Run a Ryu learning-switch app

ryu-manager ryu.app.simple_switch_13 --ofp-tcp-listen-port 6653

Expected output:

loading app ryu.app.simple_switch_13
loading app ryu.controller.ofp_handler
instantiating app ryu.app.simple_switch_13 of SimpleSwitch13
instantiating app ryu.controller.ofp_handler of OFPHandler

What this teaches: a Ryu app is an event loop: packet-in events arrive, and the app answers with flow-mods.

ryu-manager blocks in the foreground; the four lines above are its success signal. Ryu 4.x targets Python 3.6 to 3.10, so on a modern distribution use a virtualenv with Python 3.10 or expect dependency breakage. Connect a Mininet switch to port 6653 and watch packet-in handling begin in the logs.

2. Push a flow via the ONOS REST API

curl -u onos:rocks -H 'Content-Type: application/json' -X POST http://127.0.0.1:8181/onos/v1/flows/of:0000000000000001 -d @flow.json

Expected output:

{"flows":[{"id":"281475399122950","deviceId":"of:0000000000000001","state":"PENDING_ADD","life":0,"packets":0,"bytes":0,"lastSeen":0,"priority":40000,"timeout":0,"isPermanent":true,"tableId":"0","selector":{"criteria":[{"type":"IN_PORT","port":"1"}]},"treatment":{"instructions":[{"type":"OUTPUT","port":"2"}]}}]}

What this teaches: ONOS exposes the same forwarding intent as REST resources, so any automation can program the fabric.

File content (save as flow.json before posting):

{
  "flows": [
    {
      "priority": 40000,
      "timeout": 0,
      "isPermanent": true,
      "deviceId": "of:0000000000000001",
      "treatment": { "instructions": [ { "type": "OUTPUT", "port": "2" } ] },
      "selector": { "criteria": [ { "type": "IN_PORT", "port": "1" } ] }
    }
  ]
}

Without -H 'Content-Type: application/json', ONOS rejects the body. onos:rocks are the lab defaults; change them before any shared deployment. The deviceId must match a device ONOS actually manages (check GET /onos/v1/devices first), or the flow stays in PENDING_ADD forever.

3. Verify devices and flows are managed

curl -s -u onos:rocks http://127.0.0.1:8181/onos/v1/devices | jq '.devices[].id'

Expected output:

"of:0000000000000001"
"of:0000000000000002"

What this teaches: the controller's device and flow inventory is the source of truth for what the network is doing.

How It Works

A controller is an event-driven program sitting above the switches. When a switch sees a packet it cannot handle, it sends a packet-in to the controller; the controller's application logic decides what to do and replies with flow-mods that install forwarding entries. Ryu makes this explicit: you write Python handlers for OpenFlow events.

ONOS takes a more declarative, distributed approach: applications express intents (connectivity between endpoints, with constraints), and the framework compiles intents into flow rules across devices. The REST API exposes devices, links, hosts, flows, and intents as resources, which is what automation and orchestrators consume.

Both models converge on the same datapath: prioritized match-action entries in switch flow tables. The controller is the only writer that matters, so consistency, scale-out (clustering), and the behavior of switches during controller outages are the central design questions for any deployment.

Validation & Testing

Run pingall in Mininet and confirm the controller installs flows reactively (Ryu) or that REST-pushed flows appear in the switch (ONOS). Pass criteria: full reachability driven by controller-installed flows, visible in dump-flows.

Troubleshooting

SymptomLikely causeFix
Ryu starts but switches never appearSwitches pointing at the wrong controller IP/portConfirm ovs-vsctl get-controller and that ryu-manager listens on 6653
ONOS REST returns 401Wrong credentials or the onos:rocks default was changedCheck the karaf users configuration and test with curl -v
Flow POST returns 400Malformed flow JSON (bad device ID format or missing criteria)Validate the device ID exists via GET /devices and compare against a working example
Flows installed but traffic still floodsThe app also needs to handle the reverse direction or ARPThe simple switch app learns both directions; custom apps must install symmetric flows
Controller CPU spikes on ping sweepsEvery new flow triggers a packet-in (reactive storm)Add proactive flows for known traffic or rate-limit packet-ins at the switch
Advanced: Troubleshooting
  • App not receiving events: confirm switches connect on 6653 and speak OpenFlow 1.3.
  • ONOS REST 401: use the correct credentials and enable required apps (openflow, fwd).
  • Flows not applied: validate the JSON deviceId and selector/treatment fields.

Key Results

  • Centralized forwarding logic for a multi-switch fabric in one controller.
  • Programmed flows via both event-driven Python and declarative REST.
  • Achieved full fabric reachability driven entirely by controller flows.
  • Replaced per-switch config with a single programmable control point.

Key Takeaways

  • Ryu teaches the event model; ONOS teaches the intent and API model.
  • Both ultimately write the same OpenFlow entries into switch tables.
  • Verify from the controller inventory, not just from ping results.
  • Reactive control scales poorly under scan-like traffic; go proactive for known flows.

Practice Challenges

Challenge 1: Write a firewall app

Goal: Extend the Ryu learning switch to drop all traffic to one host while forwarding everything else.

Hint: Install a high-priority drop flow matching nw_dst of the target in the packet-in handler.

Challenge 2: Intent-based connectivity

Goal: Use the ONOS intent REST API to request host-to-host connectivity instead of raw flows.

Hint: POST to /onos/v1/intents with type HostToHostIntent and watch ONOS compile it to flows.