Select Page

One of the best ways to get a look at the current state of OpenFlow switches connected to Ryu is through the use of its REST API. With this API, it is possible to get all sorts of information about the switches while also manually installing new flows, groups, and meters. While not recommended for a production application, this provides valuable information during development and debugging. An API is only half the solution, though. You also need a consumer of that interface and I know of no better tool to use REST APIs than Postman. In this post, we’ll install Postman and configure it to connect to Ryu’s REST API. We’ll also observe the simple switch application presented in the last post and make on-the-fly modifications using nothing but the REST API.

We’ve also released an overview video for this article to make it easier to follow:

Installing Postman

Postman is an application built on Google Chrome and Chrome is required to run Postman. This doesn’t mean you have to use Chrome as your default browser or sign in with a Google account.

The following sections cover installing Chrome and Postman step-by-step assuming you are playing along with the virtual workspace covered in the last article. Feel free to expand each section you want to follow or skip this and go directly to Starting Mininet and Ryu.

Otherwise, follow the directions below or check out this video for a demonstration:

Installing Chrome

  1. Start Firefox and go to https://www.google.com/chrome/, then click the “Download now” button. ich-1
  2. You will be asked for the type of package to download. Select “64 bit .deb (For Debian/Ubuntu)” then click “Accept and install”. ich-2
  3. A download dialog will appear. It should have “Open with [ Software Install (default) ]” selected. Click “OK”. ich-3
  4. The Ubuntu Software application will load the package and give the option to Install. Click the “Install” button. You will also be asked to authenticate to install the package. When notified that installation is complete, close the Ubuntu Software window. ich-4
  5. Verify Chrome is installed by clicking tapping the Super key on the keyboard (usually has the Windows icon) or click on the top button on the Launcher to open the Dash. Type “chrome”. The Google Chrome application should appear. ich-5
    The progress of installing the package may incorrectly state that it is finished installing the package before installation is actually finished. If Chrome does not appear in the Applications result, close the Dash (hit Escape) and try again a moment later. If it still doesn’t show, reboot the VM.
  6. (Optional) Drag the Google Chrome icon from the Dash to the Launcher ich-6
  7. Start Chrome by clicking it in the Dash result or from the Launcher if you added it there. Choose the desired configuration in the first-run window that appears, then click “OK”.

Installing Postman

  1. Open Chrome and go to http://getpostman.com/. Click on the Chrome App button on the page to bring you to the Chrome Web Store. ipm-1
  2. You should be presented with the Postman entry in the Chrome Web Store. Click the “+ Add To Chrome” button. ipm-2
  3. An alert will appear explaining the permissions that Postman will have. Click “Add app”.
  4. Once installed, the Apps tab will appear with Postman added to the list. ipm-3
  5. (Optional) Postman will also be added to the list of installed applications in the Dash. Open the Dash (top icon in the Launcher) and search for “Postman”. Drag the Postman icon to the Launcher for ease of access. ipm-4
  6. Start Postman from Chrome, the Dash, or optionally the Launcher. On the first run, you will be asked to sign in or create an account. There are quite a few features available by signing in, but you may choose to skip this and instead click on “Skip this, go straight to the app”. ipm-5
If you want to see a list of all Chrome Apps installed in the future, open a new tab in Chrome and click the “Apps” bookmark in the top left of the page.

Starting Mininet and Ryu

Before we start experimenting with Postman, we first need to start up Mininet for the test network and Ryu with the simple switch example and the REST API.

First, let’s start Mininet with the same options as the last article. In a terminal, run:

Do not run pingall just yet, though.

In a second terminal, start Ryu, but this time also loading the REST API:

Your terminals should look like this:

smr-1

The ryu-manager command above demonstrates the power of Ryu’s multi-component design. You can have more than one controller application running at the same time and it is often useful to code your applications so they can run independently or cooperatively with other applications. In this example, we have the simple learning L2 switch application provided by ryu.app.simple_switch and the REST API provided by ryu.app.ofctl_rest. As demonstrated earlier, you can run the switch without the API and, as we will demonstrate later, we can use the API to override the functionality of the learning switch.

Not for Production Use

Like the ryu.app.simple_switch app, the ryu.app.ofctl_rest app and its API should not be used for production. There is no security mechanism preventing a ne’er-do-well from fiddling with the innards of your switch. As with any simple non-authenticated remote API, make sure to evaluate the security of your development environment.

Furthermore, this API depends on JavaScript Numbers, which are technically double-precision floating-point numbers and can only safely store an integer less than 53 bits long. This means that 32bit values and masks can be safely specified without converting it into a String, but 64-bit values and masks are limited to the lower-order 52 bits. Keep this in mind when working with 64-bit fields in Number form like DPIDs, cookies, various counters, and others. DPIDs are usually safe because they are rarely more than 48 bits long and then only for vendor-specific instance data. In a production environment, your custom controller applications would be written to use the controller’s libraries directly and generally don’t have to deal with number conversions. Now, time for more adventuring!

Using Postman

Now that we have our simple environment, let’s use Postman to query Ryu’s REST API.

These instructions assume a clean switch state. If you already ran commands in Mininet after starting it, close it with Ctrl+D or typing quit. Close Ryu with Ctrl+C, then start Mininet and Ryu again using the commands in the section above.

Start Postman and open a new tab. Keep the request type as GET. In the request URL, enter “http://localhost:8080/stats/switches”. Click “Send.” You should see something like this:

upm-1

If you are running Postman for the first time, you may see the helpful message about saving your request. This is a good idea, so let’s do that. Click the “Save” button. A new dialog will appear asking for the name and description of the request, along with the collection to add it to. Since we haven’t created a collection yet, we will also fill out the new Collection Name.

upm-2

Here, I entered “Get all switches” as the Name and write a description in Markdown format that provides a link to the documentation on the API endpoint. The new collection’s name is “Ryu API”. Now looking at the tab again, the header’s name changed to match the request with a dropdown showing the description entered. Further, “Ryu API” now shows under “Collections” (left bar). If we want to run this request again, just click “Get all switches” inside the collection. Feel free to add any other requests to your collection. For the purposes of this article, I’ll be providing the full request information. For more information about what endpoints are available, see the Ryu documentation on ryu.app.ofctl_rest.

upm-3

Peeking Into Ryu

Now let’s talk about the response itself. All responses to this API will be in JSON form. In this case, the response is an Array with a single value, 1.

The 1 is the DPID (Datapath ID) of the switch. On hardware switches, this will be a much larger number which is generally based on the switch’s internal MAC address along with vendor-specific information (such as a virtual switch ID). For simplicity, Mininet is using sequential DPIDs starting at 1.

DPID in Decimal Notation

The response from Ryu provides the DPIDs in decimal notation as opposed to the hexadecimal notation commonly used when dealing with MAC addresses and DPIDs. This is due to a limitation of the JavaScript Object Notation (JSON) format as integers can only be expressed in decimal. For Mininet, this is fine as the emulated switches have DPIDs starting at 1.

When writing OpenFlow entries, however, it is important to note that some reference material will show DPIDs, MAC addresses, masks, and other numbers in hexadecimal. For example, DPID 1 is normally presented as "00:00:00:00:00:00:00:01" to the user since it is a 64bit integer, or simplified to 0x1 in code or log messages. In practice, you will see much larger DPIDs on hardware switches so don’t be surprised if the DPID for your switch looks different than the response from Ryu. Most programming calculators (software or hardware) will allow you to easily convert decimal to hexadecimal and back if needed. Personally, I find the default calculator in Ubuntu in Programming mode to be more than adequate.

Finally, if you get to the point where you are writing a lot of Ryu tests, which are in JSON format, you might want to consider writing the test data in a more expressive format (especially one that supports comments and hexadecimal notation), then convert that data to JSON. I personally recommend CoffeeScript Object Notation (CSON) for this purpose and conversion utilities are readily available.

Switch Description

Now that we have the DPID of the only switch connected to Ryu, we can get some more information about it. To get the desc stats of the switch, request “GET http://localhost:8080/stats/desc/1”. You should get something like this back:

This time, we get an Object as the response. It contains a single item with the key of "1" and a value of a new object containing information about the switch with DPID 1. From this, we can tell that the switch is Open vSwitch 2.5.0.

Retrieving Flow Entries

Now, let’s get something a bit more interesting. Request “GET http://localhost:8080/stats/flow/1”, which retrieves all the flows in the switch specified, though the result may not be so interesting at first:

Here we have an Object like before, but an empty Array assigned to the DPID. This is because the simple switch application has had no reason to add any flows to the switch. Let’s change that by running pingall in the Mininet session.

Now send the request again.

OK, that’s a lot more information. This is a list of the 6 flows that were inserted by ryu.app.simple_switch, one for each ping attempted by the pingall command in Mininet. Exactly how the simple switch application determines what flow entries to write will be covered in a later article, but in its simplest form, it will write a flow entry for every combination of input port (in_port) and Ethernet destination address (dl_dst) as the match condition. The actions for the flow entry instruct the switch to send the packet to the appropriate port (OUTPUT:n).

Dissecting a Flow Entry

Let’s dissect the first flow entry above (the order may be different in your output):

The flow’s match condition. This flow will be active if the packet’s destination MAC address is 00:00:00:00:00:01 and arrived on port 2.

The flow’s priority. Matching flows are processed based on priority. Only the matching flow with the highest priority value has it’s actions processed. In this case, the priority is 32768, or 0x8000, which is the default priority for a flow (OFP_DEFAULT_PRIORITY). The match conditions combined with the priority constitute the unique identifier for a flow entry.

The ID of the table that the flow entry belongs to. Since we’ve been using the OpenFlow 1.0 version of the simple switch application so far, all of these flow entries are in table 0.

The flow’s actions. If no previous flow has matched and the packet matches the flow’s match conditions and has the highest priority of the matched flows, this packet will be output to port 1.

The idle_timeout determines the maximum amount of time after the most recent match or when the flow was added before removing the flow from the switch. The special value 0 specifies that the idle timeout is disabled for this flow. The hard_timeout specifies the maximum amount of time that the flow may exist before being removed, regardless of how recent it was active. Again, it is set to 0 so the hard timeout is disabled for this flow.

The duration_sec and duration_nsec specifies how long ago the flow was added in seconds plus nanoseconds respectively. In this case, the flow was active for 547.129 seconds when Ryu requested the flow entries from switch 1.

The packet_count and byte_count values represent how many times a packet matched this flow and the aggregate number of bytes from those packets, respectively.

The cookie value is set by the controller and can be used to filter flow entries. It is not used during the matching process, but is simply data that the controller can attach to the flow. The cookie value is 0, meaning that there is no cookie set.

Filtering Flow Entries

On switch with many flow entries, it is often convenient to filter the flows returned by Ryu. This uses the same API endpoint (http://localhost:8080/stats/flow/1), but instead uses POST rather than GET. Let’s perform a very simple query of finding all flows on the switch that have actions to output on port 2.

Open a new tab in Postman, select “POST” from the request methods dropdown (it defaults to “GET”), then enter “http://localhost:8080/stats/flow/1” as the URL.

For a POST call, we need to add data to send. For this call, we will be sending a JSON-formated string. Click “Body” under the URL, then select “raw” as the body input type. Where it shows the data type as “Text”, click that dropdown and select “JSON (application/json)”. This will allow postman to check your input for errors and automatically set the Content-Type header for the call.

Finally, for the body text, enter the following:

Your Postman tab should look like this:

upm-4

Click “Send” and scroll down to the response. It should look like this:

upm-5

Here is the data that is returned:

This is similar to the format returned from the GET request, but filtered to only show flows that output to port 2. These flows cover packets that are being sent to MAC “00:00:00:00:00:02” from ports 1 and 3.

Modifying Flow Entries Live

One of the more powerful features of Ryu’s REST API is the ability to insert flows on the fly. Imagine a case where one of the hosts on your network is compromised and is attacking the other hosts. A quick solution is to create a new flow that will disable communication from the compromised host until the situation is resolved. We can do this by adding a flow to the switch with a higher priority that will match on the incoming port.

Insert the New Flow

In this scenario, the host attached to port 3 is flooding traffic and we need to temporarily disable forwarding packets from that port. One possibility would be to set that port to administratively down, but for simplicity, we will use only flow entries similar to those already inserted by the simple switch application. Here is the flow we want to insert:

This flow will match on any packets arriving on port 3 and the flow has a higher priority than the flows added by simple switch. The action set is empty, so no action will be taken on the packet. Essentially, this is similar to a DROP rule in a firewall. As an added bonus, we are setting the cookie to 42 to make it easier to track later.

To insert this flow, we’ll need to POST it to the “/stats/flowentry/add” endpoint. Open a new tab in Postman, select POST and enter “http://localhost:8080/stats/flowentry/add” as the URL. As before with the flow filter API call, select “Body” under the URL, select the “raw” body type, then select “JSON (application/json)”. Then insert the above JSON data into the body of the request. It should look like this:

mfe-1

Click “Send” and you should get a status of “200 OK” (also in screenshot).

Verify the Flow was Added

Even though we got the “200 OK” message from the REST API, there is actually no guarantee that a flow was entered and active just from that request. It is important to verify the flow was added by requesting the flows from switch. Using the same API call above for retrieving flows matching certain conditions, use the following request body:

We should get back all the flows that match on packets arriving on port 3. There should be 3 flows: 2 from simple switch, and the flow we just added:

Looking through the returned flow entries, we can see the one we just added with an empty actions list, higher priority, and a cookie value of 42. Now let’s confirm that this flow works by running pingall in Mininet again:

This shows that host h1 can ping host h2 and vise versa, but neither can communicate with host h3, which is on port 3 of the switch, nor can host h3 communicate with any other host.

Cookies and OpenFlow Versions

For simplicity in demonstrating how the different development components work together, we’ve been using Open vSwitch in OpenFlow 1.0 mode. Although a cookie can be specified in OF1.0, it can’t be matched on for filtering or modifications. We will cover OpenFlow 1.3 and above in later articles. If we were using OF1.3 here, our query could be as simple as {"cookie": 42, "cookie_mask": 65535}. The additional step of reading through the queries returned could be skipped as we would only need to check if a single flow was returned based on the cookie.

Remove the New Flow

Let’s say that the issue on the host attached to port 3 of the switch was resolved and the blocking flow entry is no longer required. Now it is time to remove the flow and allow traffic to flow from port 3 again. Using the POST method on the “/stats/flowentry/delete_strict” endpoint, use the following request body:

The request should look something like this:

mfe-2

We are using the delete_strict endpoint for this demonstration as it is often a good idea to be as explicit as possible when deleting a flow. Alternatively, we could have used the delete endpoint instead and left out some fields like idle_timeout, but leaving out actions as well would cause several flows to be deleted as the match field would have matched more “specific” conditions, so long as one of those conditions is that the packet arrives on port 3.

Verify the Flow was Removed

As before, just because we get the OK from Ryu, it doesn’t mean the flow was actually deleted. Query the flows again as in the section “Verify the Flow was Added”. This time, you should see only two flows:

Now verify with another pingall in Mininet:

All hosts are able to communicate with host h3 again.

Examples and Exercises

There is a Postman collection available for download that includes all the API requests presented in this article. Each request in the collection includes tests to verify that the response was expected. To run the tests, import the collection then run through all the instructions in this article. If you get stuck on any request, try running the request from the collection and check that the parameters are correct on your own requests.

Inside OpenFlow Postman Collection

Download the Inside OpenFlow Postman Collection for free from our marketplace!

Run Collection Tests

To run the entire collection as a suite of tests, click the Details (>) button on the Inside OpenFlow collection, select “Article: Interactive Ryu with Postman”, then click the blue “Run” button. This will start the collection runner.

rct-1

In the Collection Runner window, the article’s request folder should already be selected. Click the blue “Start Test” button. All tests should pass.

rct-2

If some tests fail, try entering a delay in the Delay field of 100. This will give Ryu and Open vSwitch time to process the flow mods before verifying that the mods took place.

Experiment Yourself

With the ability to modify flows on the fly, try adding flows to deal with different scenarios. For example, what flow could you add to stop communication between two specific hosts? You can even start Ryu without the ryu.app.simple_switch application and enter all your flows manually. Try making a hub with just static flows. As a bonus, write the flows so it can accept any number of hosts and ports on the switch and adjust Mininet’s parameters accordingly.

Share This