For my master thesis about Delftvisor I built an Openflow controller, both to do tests with and to famiarize myself with Openflow. This controller proactively routes installs rules routing the traffic. Ryu is a python framework to communicate with switches over openflow.
The code for the modules can be found at github. The easiest way to experiment and test with this controller or when developing your own controller is to use mininet to instantiate a virtual network and generate traffic to test with.
Modules
In Ryu a module subscribes to events, such as specific openflow messages being received. A module can also emit events if it wants to, the modules in my controller currently do not.
The simplest module is the init module, it listens to the datapath connect event and deletes previous flows from the datapath and installs two new flows. The first new flow forwards packet with a destination mac we haven’t learned yet to the flood group and the second flow sends packets with a source mac we haven’t learned yet to the controller so that mac addres can be learned.
The second module is the broadcast module.
This module subscribes to the datapath register/leave events and to the topology events.
When the topology changes an internal ryu module detects this and emits topology changed events.
The broadcast module calculates a minimum spanning tree based on the learned topology and updates group 0 to flood the packet over all ports in the minimum spanning tree.
Openflow already has a mechanism that a packet cannot be output over the port it came in on without using the special port IN_PORT
so flooding the packet this way is safe.
The last thing the broadcast rule does is install rules in the second flowtable that prevent the datapath from sending packets to the controller if the packet arrived over a port that is connected to another datapath.
This prevents the controller from receiving a packet to learn a source mac address for packets that are being forwarded.
The third and last module is the router module.
This module also subscribes to all datapath register/leave and topology events.
As an extra it also subscribes to the OFPPacketIn
event, this is the event triggered when a datapath sends a packet to the controller.
In the current setup does that only happen when a packet originates from a non-datapath connected port and has a source mac address that hasn’t been learned yet on this datapath.
When the module receives a packet in event it learns that the source mac address comes from the datapath, port combination from the packet in frame.
The module will then install a rule on that datapath to not forward new packets with that source mac address anymore, and will run a breadth first search over the discovered topology originating from that datapath.
After the breadth first search it will install a flow in each other datapath telling it over what port to forward packets with this destination mac address from now on.
If a host moves to another port on another datapath it will generate a packet in event again and the router module will update the flowrules to route to the new datapath, port combination.
Flow tables
Entry flow table, this flow table decides what port a packet should be send out of. If the switch doesn’t know an output port broadcast is using group table 0.
Priority | Amount | Module | Name | Match | Instructions |
---|---|---|---|---|---|
20 | 1 per learned local mac | router.py | Locally learned mac forwarding rules | eth.dst == learned mac | goto-table(1) AND write-action(output port) |
10 | 1 per learned foreign mac | router.py | Foreign learned mac forwarding rules | eth.dst == learned mac | goto-table(1) AND write-action(output port) |
0 | 1 | init.py | Table miss entry | everything | goto-table(1) AND write-action(group 0) |
Second flow table, this flow table looks up if the source ethernet address is known so the packet isn’t forwarded to the controller without containing new information. This ensures the controller only receives packets of which it doesn’t know the ethernet address. A switch also shouldn’t forward packets to the controller it received from other switches since the source port that the controller learns would be wrong.
Priority | Amount | Module | Name | Match | Instructions |
---|---|---|---|---|---|
20 | 1 per learned mac | router.py | Learned mac rules | eth.src == learned mac | No actions |
10 | 1 per connected switch | broadcast.py | Broadcasted forwarding rule | in port == other switch | No action |
0 | 1 | init.py | Table miss entry | everything | apply-action(output controller) |
Group table, this table only has 1 entry with the ports to forward on for broadcasting packets.
Identifier | Module | Name | Type | Buckets |
---|---|---|---|---|
0 | broadcast.py | Broadcast group | all | {Output port1, Output port2, …} |
Conclusion
This controller was sufficient for the tests that I needed to do for my thesis. The repository still receives some minor traffic and event got a pull request with a bug fix. Overall am I happy with the result.