What is OpenFlow?
If we tried to explain what OpenFlow is, a possible definition would be: OpenFlow is a protocol for controlling and interacting with forwarding behaviors of switches. It allows us to dynamically control the behavior of the switches in our network. Many SDN (software defined network) and Open Source projects use OpenFlow or support it as a plugin, such as OpenStack Neutron and OpenDaylight.
But It’s hard to grasp what it is, what it solves and how it works only using this brief description. In order to truly understand what is OpenFlow, we need to start from the beginning, before SDN era.
Note: I’ll not deep dive into SDN as this post is not about ‘what is SDN’, but I’ll cover it quickly since it’s crucial for the understanding of why OpenFlow is needed and what it is used for.
Before talks about software defined network started, a traditional network would look something like this:
You can see four switches while the control plane and data plan are local to the devices.
A control plane considered to be the brain of the device. It’s responsible for all the data plane activities, which are not related to the end-user data packets. Such activities are creating/managing local routing tables and setting packet handling policies.
The data plane is how packets are forwarded through the device. For example, a packet arriving the first device, needs to know how to continue its journey to the destination (e.g. through which port to exit).
So in a traditional network each device has its own “brain” (control plane) and a data plane. There is no single device who can “see” the entire network. From an administration point of view, you would need to connect to every device in your system (in the drawing, it’s only two, but in reality it can reach thousands of devices) and configure each one of them, in some cases, manually.
To move traffic, each device would need to communicate with other devices to get an updated view on the network topology and choose the best way to forward the packet.
Beginning of a new era – SDN
One of the main concepts SDN introduced, was to centralize all the control planes into one device which is called the ‘controller’. The controller has the visibility of all the network and this visibility allows it to make smart decisions on how to forward packets. So the controller can send instructions to the different switches, to tell them what to do with incoming packets (e.g. drop, forward through port X).
It is also called “SDN controller.”
Note: regarding SDN, this is really just the tip of the iceberg. To learn more about SDN, I recommend to watch some of the videos I gathered right here.
Why we need OpenFlow?
So we understand why we need or more importantly why we want one centralized controller, but how will this controller communicate with all the switches? the answer is OpenFlow.
OpenFlow has several roles in this new structure:
- It makes sure to create a logical representation of the switches in our network so the controller can process this information.
- It allows the controller to communicate with the switches in a secure manner.
- It defines the flow tables and flow entries to change the behavior of a switch and define how packets are handled (dropped, forwarded, stripped, etc.)
Let’s have a look at OpenFlow switch components:
We’ll cover each one of them in more detail.
Flow Tables and Flow entries
An OpenFlow switch holds one or more flow tables.
When a packet arrives to the switch it processed by the first table. If the packet doesn’t match any flow entry, it’s called a table miss. The packet can then either be dropped or passed to another table. It depends on configuration.
Each flow table consists of flow entries. Three important components of a flow entry:
- Match Fields – these fields used to match against a packet. Some of the fields: VLAN id, MAC src address, MAC dest address, IP src address, IP dest address, switch port and a lot more fields that can be found in OpenFlow specification.
- Priority – what is the priority of the flow entry. When a packet matches several flow entries, the entry with the highest priority is used.
- Instructions – what should be done in case of a match. Instructions can change:
- The Packet itself – remove headers, change headers ( set VLAN header, change TTL, push MPLS header)
- Processing – move packet to table X
- Flow – output packet through port 5, drop packet
It can be one of them or a combination (e.g. change the packet, and move it to table X)
There are additional fields such as counters and flags, but Match fields and priority are what makes a flow entry a unique one.
Group table is like a special flow table. It consists of group entries which include action buckets and an identifier.
The identifier is an 32bit integer to uniquely identify the group table and the action bucket is a set of actions to execute.
Multiple flow entries can point to the same group entry. So you can have 500 flow entries pointing to one group entry with the action of “send out through port 4”. Once you change the group entry to “send out through port 10”, it changes the behavior for 500 flow entries in one effective command.
Secure Channel ( aka OpenFlow Channel)
This component used for communication between the controller and the OpenFlow switch. It uses the OpenFlow protocol for the communication. The communication can be periodic health messages, status information, flow modification, packet-in, packet-out. By default it will use TCP for the communication, but can also use TLS for encrypted communication.
Example: Access a web server
To better understand how it looks when a OpenFlow switch is used, we’ll use an example of a flow in which we are reaching out to a web server (Host 3) from Host 1.
Notes about the flow:
- In step 2, the lack of match between the packet, and an entry flow is also called a table miss
- In step 3 if the packet-in referencing just some of the packet headers, then it probably also includes a buffer id. The buffer id means that the original packet is entirely buffered in the switch and the buffer id can be then used by the controller to instruct the switch on what to do with the packet this buffer id belongs to.
- In step 4 it’s the same idea. It can use the entire packet, or reference it with the buffer id.
- Note that the controller might choose to send a flow modification message to instruct the switch to install a new entry flow so similar packets in the future would be handled the same way and they won’t need to redirect the packets to the controller.
Now let’s review the reply flow from Host 3 to Host 1
Notes about the flow:
- The entry flow installed in the previous drawing/flow is not the same one as for this flow, since then it was Host 1 -> Host 3 and this drawing is about Host 3 -> Host 1, this is why once again, the packet encapsulated and sent to the controller.
- In step 3 the buffer id will be different for the same reason the packet sent to the controller – not the same flow.
- In step 4, as in the previous drawing, the controller might send a flow modification to install flow entry for future similar packets
After a packet sent from Host 1 to Host 3 and a reply received from Host 3 by Host 1, any future communication can be done using the flow entries installed by the controller. There is no need to send more packets to the controller unless there is a new type of communication.
You don’t even need to contact the controller if the flows are inserted before the communication starts. We’ll see in a second how to insert new flow entries by ourselves.
Open vSwitch and OpenFlow
Many solutions/products today use Open vSwitch together with OpenFlow. Take for example OpenStack and OpenDaylight.
In order to understand the network flows and to be able to troubleshoot issues in such environments, you need to be familiar with Open vSwitch and OpenFlow related commands. Open vSwitch already covered in previous posts. In the next following lines, I’m going to cover ovs-ofctl specifically.
ovs-ofctl is how you manage, operate and monitor OpenFlow switches.
First command you want to be familiar with is ‘ovs-ofctl show <switch_name>’. It will print information on the switch and its ports in addition to limits such as number of tables and number of buffers.
Let’s see an example from a TripleO setup
> ovs-ofctl show br-ctlplane OFPT_FEATURES_REPLY (xid=0x2): dpid:0000fa163e9839ce n_tables:254, n_buffers:256 capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst 1(eth1): addr:fa:16:3e:98:39:ce config: 0 state: 0 speed: 0 Mbps now, 0 Mbps max 2(phy-br-ctlplane): addr:22:62:b2:72:3b:d6 config: 0 state: 0 speed: 0 Mbps now, 0 Mbps max LOCAL(br-ctlplane): addr:fa:16:3e:98:39:ce config: 0 state: 0 speed: 0 Mbps now, 0 Mbps max OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0
It gives us a lot of information such as switch capabilities (note that it supports FLOW_STATS), actions and ports. We have 2 ports, the same you’ll see if using ‘ovs-vsctl show’ but here the information is a little bit different (MAC address, config, state and the speed of each port). The third one is the switch itself.
Let’s add our first flow.
> ovs-vsctl add-br my_switch > ovs-ofctl add-flow my_switch action=normal
We added one entry flow without any match fields and with the action ‘normal’. The ‘normal’ means our switch will act as a traditional layer 2 switch.
Now let’s add a flow with some match fields. Let’s say that we every incoming packet through port 1, to send it through port 2.
> ovs-ofctl add-flow my_switch priority=400, in_port=1,actions=output:2
We added a new flow. This flow has a priority of 400, so if a packet matched several flow entries, with lower priorities, it will be processed by the one we added. We also set a match field ‘in_port=1’ which means packets that arrived through port 1. The action is to output the packet through port 2 (‘actions=output:2’). To verify it actually works, I recommend to test it by sending something to port 1 and see it go through port 2 by looking on the counter or “sniffing” the network with tcpdump.
Now let’s try layer 2 matching
> ovs-ofctl add-flow my_switch dl_src=01:00:00:01:00:00,dl_dst=02:00:00:02:00:00,actions=output:3
We used dl_src to match on the source MAC address and dl_dst to match on destination MAC address. So everything coming from 01:00:00:01:00:00 will be sent to 02:00:00:02:00:00 through port 2.
To remove all the flows
> ovs-ofctl del-flows my_switch
You can specify a specific flow you want to remove, right after the name of a switch.
Prints all the flow entries in the specified switch tables. Let’s again add a simple flow (after we removed everything) and see how it looks in our switch
> ovs-ofctl add-flow my_switch action=normal > ovs-ofctl dump-flows my_switch NXST_FLOW reply (xid=0x4): cookie=0x0, duration=556.040s, table=0, n_packets=0, n_bytes=0, idle_age=556, priority=0 actions=NORMAL cookie=0x0, duration=499.348s, table=0, n_packets=0, n_bytes=0, idle_age=499, actions=NORMAL
You can see two flow entries. One was there by default when the switch created (the with idle_age=556). The second is the one we added earlier.
For each flow entry you can see the duration of the time it’s there. To which table number if belongs (table=0) how many packets matched the entry flow (0), the idle_age which represents the age of the entire flow without being matched to any packet, the priority (if specified. we haven’t specified one when adding the flow) and the actions.
That looks pretty empty and boring for a new switch, right? let’s look on the switch TripleO created (specifically br-ctlplane)
> ovs-ofctl dump-flows br-ctlplane NXST_FLOW reply (xid=0x4): cookie=0x8e67bd412a95a3fb, duration=5662.610s, table=0, n_packets=61, n_bytes=20971, idle_age=5083, priority=4,in_port=2,dl_vlan=1 actions=strip_vlan,NORMAL cookie=0x8e67bd412a95a3fb, duration=5679.407s, table=0, n_packets=3, n_bytes=258, idle_age=5662, priority=2,in_port=2 actions=drop cookie=0x8e67bd412a95a3fb, duration=5679.641s, table=0, n_packets=7779256, n_bytes=30272629666, idle_age=1, priority=0 actions=NORMAL
Three flow entries, still one table, but look how many packets matched each flow, especially the third one (779256). We can also see new actions. For example, first entry which includes now “strip vlan”, so whenever a packet hits this entry, its vlan header will be stripped off. For the second flow entry, we have ‘drop’ which means that when a packet matches this entry, it will be dropped.
Finally, I really recommend to read the OpenFlow specification. It’s great and has very detailed information on OpenFlow.
Here is the specification for 1.5.1. Make sure to check if a newer version is available.