Select Page

In this tutorial, we will be kicking off our new Faucet Technical Track by introducing the powerful Ryu-based platform Faucet. Not only that, but we will also cover running Faucet in a zero-cost virtual infrastructure using industry standard development tools such as Mininet and Python Virtualenv. There’s no reason not to get started developing using Faucet right away, even before purchasing your first OpenFlow switch!

Updated 25 October 2016 for Faucet v1.2

This article was original written for Faucet v1.1. Faucet recently came out with their v1.2 release with many major bug fixes and new features. There may be references in the screenshots to the v1.1 release, but I tried to update all the instructions (and removed caveats) where required. If you get stuck, please leave a comment at the end of this article.

Introduction to Faucet

Faucet is an extensible L2 platform for OpenFlow networks utilizing the Ryu OpenFlow controller. The Faucet platform also supports some L3 features, full VLAN learning and advanced ACL support, all out of the box!

When used as a base platform for advanced OpenFlow applications, you can squeeze every bit of functionality out of your switches with Faucet. With today’s hardware OpenFlow switches that support hundreds of thousands of wildcard match flows (and millions of exact match flows), the possibilities are endless. Imagine offloading most, if not all, of your firewall rules, ACLs, and even full IP blacklists right down to your top-of-rack switch, all executing at line-rate. Keep your east-to-west traffic safe with per-host internal ACLs and firewall rules without having to invest in expensive and slow x86 firewall appliances. Augment existing IDS/IPS systems to offload rules directly to the switch once threats (and non-threats) are identified, reducing the overall load on these systems, which allows for more compute intensive deep packet inspection or decreasing the size of your IDS/IPS cluster. When using Faucet as your base platform, these tasks become much easier.

Today’s hardware OpenFlow switch technology follows the official OpenFlow protocols without the developer having to remember which tables support which match conditions, as was the case with the incomplete abstraction layers of legacy switch platforms of yesteryear. Believe me, I know the pain of working with those older systems. However, since there are much better options out there that have real OpenFlow support, you don’t even need a hardware switch to get started on developing these applications. You can start writing your advanced OpenFlow applications on software switches, such as Open vSwitch, then be confident that your applications will work on compliant hardware switches with little to no modification. The cost to get started is virtually nil, especially if you already have a halfway decent workstation capable of running a VM or two. If you followed the previous Core OpenFlow Track, then you are already most of the way there.

So how does Faucet enable easier development of advanced OpenFlow applications? I see two main points.The first is that Faucet maintains a separation of concerns. It knows its place in the application stack and stays out of the way of other applications running on the same controller. Your own applications won’t have to deal with any L2 or VLAN learning; they can either interface with Faucet and let it deal with inserting ACLs in the proper tables or have your application manage its own then pass packets to the Faucet-controlled tables. Secondly, it gives an active example of a controller that can offload logic and state, reducing the load on the controller. This is similar to the Reimagined Simple Switch at the end of the last track, which was inspired heavily by Faucet.

Finally, Faucet has huge potential. On top of working with multiple applications and the ability to be a base platform for development, it can be extended with more security features over time. With continued development, it could support modern topologies like spine-leaf with ECMP and similar protocols.

Using a Virtual Infrastructure

In this tutorial, we will continue the theme of virtualization. In our past track, we set up a virtual workspace VM specifically for OpenFlow development. We followed this path to make the development of OpenFlow controller applications easier in a zero-cost workspace. No hardware switches are required, you don’t have to fight over development resources if working in a team, and this removes any barrier on developing real-world applications. With a little bit of work (already covered in the previous track), you can emulate a real datacenter topology. All that is left is the development itself.

Faucet is in Constant Development

As of this tutorial, Faucet just came out with their stable v1.1 release and that is what we will be using in this tutorial. However, you may be able to follow along using the latest development releases if you wish. Just note that Faucet is constantly changing and new features are being added all the time. In fact, I suggested, implemented, and submitted a feature for this article that will make our lives a bit easier in the long run (and help other developers, too!). The core developers are very friendly and I highly recommend checking out the Additional Resources section at the end of this article for links to developer blogs, mailing lists and related projects.

If you choose to use the development version, some of these instructions may not work in the future. If you run in to any problems, check out the stable v1.2 release and run through these instructions first.

Installing Faucet

Before we get started on installing Faucet, there are a few requirements that have to be met. For our new readers that haven’t completed the Core OpenFlow Track, I highly recommend giving that whirl before continuing. By the end, you will have developed an understanding of the virtual workspace in use, core OpenFlow principals, and developing with Ryu. This tutorial will assume you are using the virtual workspace developed while working through that track. If you are already proficient in OpenFlow and Ryu development, fulfill the Workspace Requirements. Otherwise, if you have been following along with the previous track, go ahead and skip to Introducing Python Virtual Environments.

Workspace Requirements

Follow this section if you do not want to go through the Core OpenFlow Track or if you wish to create a fresh workspace.

Hypervisor (Physical Workstation) Requirements

In order to make sure enough resources are allocated to both your VM and the Host that the VM runs on, it is import to make sure your workstation has an ample amount of resources. Here are the requirements for this track:

Minimal Requirements
  • VirtualBox 4.3 or equivalent
  • 64-bit, 4 CPU cores or 8 HTs with hardware virtualization support
  • 8GB RAM
  • 100GB free disk space
Recommended
  • VirtualBox 5.1 or equivalent
  • 64-bit, 8 CPU cores or 16 HTs with hardware virtualization support
  • 16GB RAM
  • 200GB free disk space

For reference, I use a modest home-built workstation with an AMD FX-8320 8-core CPU with 32GB RAM and a 1TB HDD running Ubuntu 14.04 LTS. Any modern workstation should more than match these requirements. If you are looking for a lower cost solution than a computer built as a workstation, I recommend getting a decent gaming rig without the high-end graphics card. For normal development, these PCs are more than adequate, are generally very stable when not overclocked, and are designed with maximum cooling in mind. Just make sure the RAM requirements are met. There is no need to invest more than $700-$800 on a base development workstation. As a plus side, the motherboards used in gaming rigs often contain an extra PCIe x16 slot that can be used for a multi-port network card down the road if you choose to emulate or test hardware switches.

The second thing to note is that the total RAM used by your running VMs should always be at least 4GB less than your host’s RAM. This ensures that the hypervisor OS (Linux or Windows) has at least 4GB to work with. Otherwise, excessive page swapping may occur.

Virtual Machine

The workspace for our tracks will require a Virtual Machine with 4GB of RAM (or more, if your workstation can handle it) running Ubuntu Desktop 16.04 64-bit. We recommend VirtualBox as the hypervisor, but VMware Workstation works just as well if you need nested virtualization (not normally needed). Also, make sure that the VM has access to at least 4 cores or 8 hyperthreads and at least 40GB of disk space. Don’t forget to install your hypervisor’s guest tools to make sure the proper drivers are used in the VM. The following requirements are for the VM itself and do not need to be installed on your workstation.  The commands listed in these requirements should be run in a terminal. Open a terminal by clicking the Dash button (top left Ubuntu logo), type terminal, and press Enter.

  • Any package updates for the OS should be installed You may want to reboot after this, especially if this is after a fresh install of Ubuntu.
  • A directory under your home directory in the VM at ~/ofworkspace where additional packages and software will be installed
  • Latest development version of Mininet checked out at ~/ofworkspace/mininet and installed globally – These instructions automatically install Mininet and all of its requirements (including Open vSwitch and other OpenFlow utilities).
  • Example Mininet Topologies extracted in ~/ofworkspace/mininet-topologies
  • Postman for exploring the Ryu REST API (see Interactive Ryu with Postman) – Technically not required for this article, but recommend for exploring the flows inserted by Faucet.
  • Atom for editing and exploring code (see Custom Mininet Topologies and Introducing Atom) – Technically not required for this article, but recommended for exploring Faucet’s code.

Introducing Python Virtual Environments

In aiding to keep our environment clean, we will use a Python Virtual Environment to keep all of Faucet’s libraries local without having to install them globally. I mentioned briefly in Creating a Development Workspace that Ryu could be installed inside a virtual environment using the virtualenv package. That is essentially what we will be doing here. When I wrote that article, the latest stable version of Ryu was 4.4 and Faucet requires 4.5. Different software may require different versions of the same library. Python virtual environments allow software to install the exact version of the libraries it is expecting all while keeping the global libraries untouched. This is especially useful when developing on many projects or as a non-privileged user (which is often a good idea for any development).

There are other methods for keeping clean environments. For example, Faucet suggests using Docker for production use and running the official test suite. Although Docker is great for production, Python virtual environments can use less resources, provide much quicker turn-around without having to rebuild a container and running it, and are much easier to use (and write tutorials for ;)). Docker does excel in providing pristine environments all the way up to the core libraries used on the Linux kernel, however, so it is very useful for deployment on multiple platforms. Check out Faucet’s Docker documentation for more information on Docker use. For this tutorial, however, we will stick with a virtual environment.

In a new terminal, run the following commands:

 

fve2-01

Now that the venv is activated, the prompt has changed to include (venv) at the front. This is to remind you that you are running commands within the virtual environment. Commands like python, pip, and other Python-related binaries will now be run in that environment. To leave the environment, just run the deactivate command, but don’t do this quite yet.

Caveats of Running in a Python Virtual Environment

The virtual environment changes some of the active environment variables, such as PATH and PYTHONPATH. Some commands may not work properly, especially commands that are meant to install packages globally. apt-get and apt may not work properly, for example. It is always a good idea to deactivate the current venv before running any admin commands. You can always reactivate the venv by running . ./venv/bin/activate in the venv’s parent directory. So if you run something and it doesn’t behave as expected, try running outside the venv.

Cloning Faucet and Installing in the Virtual Environment

Now let’s get to cloning and installing Faucet from the official repository. First, make sure you are in the venv by checking for the (venv) tag at the beginning of the prompt. If it isn’t, you will need to activate the virtual environment.

 

fve2-02

Clone Faucet v1.2 into the workspace.

 

fve2-03

Development Version

If you wish, you can checkout the latest development version of Faucet by removing the -b option:

Keep in mind that some of these instructions might not work if significant changes were made. Also check out the onfsdn fork, which is generally more stable:

Installation via pip

Faucet v1.2 now supports installation in a virtual environment without modifications if installing via pip. Rather than using a git clone, it can be installed like this:

Now run the Faucet install to get all the required libraries. This includes its own copy of Ryu at v4.7. This should complete successfully without any errors.

fve2-04

Finally, create the directory that Faucet will use to store its logs and configuration.

Since we are running in a virtual environment, the default configuration may not have copied over and probably doesn’t cover our basic topology test, so we will create a very simple Faucet configuration. Copy and paste the following into your terminal and press enter at the end:

fve2-05

To make sure Faucet is installed properly, run Faucet on Ryu in the venv.

Faucet Installed Location

If you installed Faucet using the pip install method, replace the path to faucet.py to the installed location. For example, instead of running the above command, use the following:

Make this change for any further examples that call for faucet.py to be loaded.

fve2-06

The output of Ryu should show that it loaded faucet.py as Faucet and sit waiting for a connection. Press Ctrl+C to shutdown Ryu.

If you’ve run into any errors, follow the Troubleshooting section and run the pip and ryu-manager commands again.

Running Faucet Unit Tests

This section is currently a work in progress.

It’s often a good idea to run tests that come with any software you compile or install manually. Faucet is no different. Running its tests will let you know if your environment is set up correctly or if there are any issues in the current code base. There are a few things to note about the current (8/2016) state of Faucet and running its tests:

  • Additional libraries will be required that are not normally required for Faucet to run.
  • There is a bug that causes inconsistent failures in some situations. Even though a workaround exists, the original test method should still be run to ensure the required components are installed.
  • The tests were not originally written to work inside a virtual environment, so the Faucet Mininet tests must be called in a certain way (see below).

If the Faucet Mininet tests fail to pass both using the direct call method (python ./faucet_mininet_test.py) and the Python Unittest method (python -m unittest), then run the Faucet tests using Docker by following the instructions in the README.docker.md in the project’s root. If the Docker tests pass, then the code-base is sound and something is not installed properly in your virtual environment. If the Docker tests fail, then most likely there is a bug with the current development version of Faucet. Follow the Troubleshooting section below in any case.

Troubleshooting

If you’ve run into any issue above, then Faucet might have changed significantly enough since this article was published that these commands do not work anymore. To remedy this, check out the version used when writing this article, which is v1_2:

Testing with our Datacenter Topology

At the end of the Core Technical Track, we tested our Reimagined Simple Switch with the Datacenter Mininet Topology we created earlier in Custom Mininet Topologies and Introducing Atom. Let’s use the same topology to test Faucet.

Downloadable Content – Faucet Examples

This includes the full configuration files used in this tutorial.

Download Faucet Examples v0.1.1

Installing the Datacenter Faucet Configuration

Faucet requires very specific configuration on the network topology in use. Before we can test the Datacenter Topology, we will need to install the appropriate Faucet configuration for this topology. Extract the Faucet Examples downloadable package into the ~/ofworkspace/. This will already be available if using the setup script.

Now that we have the example configuration files, copy the datacenter-4x4-faucet.yaml file to Faucet’s configuration directory as faucet.yaml:

fve-07

If you are not using the downloadable packages, you can copy the configuration from the Understanding the Faucet Configuration section below.

Running Faucet

Now that we have everything in place, it’s time to start Ryu with Faucet:

fve-08

In another terminal, run Mininet with the Datacenter Topology as we have in previous tutorials:

fve-09

Finally, in the Mininet console, use the pingall command to make sure all the hosts can ping each other.

fve-10

Understanding the Faucet Configuration

Faucet uses a YAML configuration file. YAML, or YAML Ain’t Markup Language, is an object notation language much like JSON (JavaScript Object Notation), but supports a much more readable format that supports comments like CSON (CoffeeScript Object Notation), which is used in the Atom editor we explored previously. As of this tutorial, there are two versions of the configuration structure used by Faucet. Version 1 only supported a single datapath (switch), so we will be sticking with version 2 as it supports multiple datapaths.

We will be using the Datacenter Topology configuration for Faucet as an example. YAML files usually start with the new document indicator (---), which is the first line in this file. Next, we specify that we intend to use version 2 of Faucet’s configuration structure.

Specifying Datapaths

Datapaths are specified in the dps property defined as an object in the configuration file. Each datapath itself is an object assigned as a property of dps that uses an internal id as the key, s1 in this case. The dp_id property is required and is usually set to the 64-bit Datapath ID in hexadecimal notation of the switch that it describes. There are other properties that can be specified, such as description below, that labels this switch as the Aggregate Switch of our topology. description is purely informational and is only used in logging.

Defining Interfaces

Interfaces are defined in the interfaces property of the datapath. Each interface in this example uses the numeric port ID of the defined interface as the key. The value of the interface property (such as the value of property dps.s1.interfaces['1']) is an object that supports various properties that define the interface. In this case, we specify a description and a list of tagged_vlans that are allowed to pass through this interface. There are also comments in this example that indicate the node connected to that port. In YAML, comments begin with # and include the content up to the end of the line.

As a side note, the interface property key could be something other than a number, but then port_id would have to be specifically set with the numeric port ID inside that interface object. Since we use only numbers as interface property keys, the port_id is implied from the key.

Interfaces with Native (Untagged) VLANs

An interface can have a native, or untagged, VLAN it is assigned to. Here we have a new definition of another switch (the ToR switch for rack 1 in this case). Ports 1-4 in this switch use a native_vlan of 10, which means any traffic received on this port will be automatically tagged as VLAN 10 and any traffic leaving this port on VLAN 10 will have the tag removed. In effect, this allows the traffic to and from a host attached to these ports to communicate in VLAN 10 without the host being configured for VLAN traffic. An interface can only have a single native_vlan. On port 5, we use the tagged_vlans list again as we may choose to allow multiple vlans across the switch-to-switch link.

Defining VLANs

Now that all the datapaths are defined, we should define the VLANs we used formally. This is done by using the vlans property of the Faucet configuration. Each VLAN is defined as a property with a key of the VLAN ID and a value of an object that defines the VLAN itself. In this case, we just specify the name of the VLAN. There are many other properties available, such as modifying routes, flood options and specifying the maximum number of learned hosts.

Names and IDs

As Chris Lorier pointed out on the Faucet mailing list, while the description property is purely informational, the logger will use the name property when defined. This property is automatically set to the configuration key for the object defined (ACL, VLAN, DP). If numeric (like I used in the above config file), it will also act as the internal ID for that object. You could write it the other way around and use the name as the key:

However, if using the name as the configuration key, all references to that object must use that name rather than the numeric ID. For example, anywhere that VLAN 10 is referenced, the reference would need to be changed to Test VLAN.

Experimenting with VLANs

Since Faucet supports VLAN separation, we can confirm this by modifying our configuration to use two different VLANs. Modify the configuration at ~/ofworkspace/venv/etc/ryu/faucet/faucet.yaml so the links between the aggregate switch and each ToR switch allows both VLANs 10 and 20. This can be done by changing the tagged_vlans property to [10, 20]:

For each ToR switch, make sure that port 5‘s tagged_vlans is also set to [10, 20] and change the native_vlan for hosts 3 and 4 to VLAN 20:

Finally, add the new VLAN to the VLAN definitions:

Although we recommend changing the configuration by hand, you could alternatively copy the datacenter-4x4-faucet-vlan.yaml file from faucet-examples to ~/ofworkspace/venv/etc/ryu/faucet/faucet.yaml as with the original configuration.

Testing the VLANs

With the above configuration, hosts 1 and 2 of each rack should be able to communicate with each other, but not with hosts 3 and 4 as they are not in the same VLAN (10) as hosts 1 and 2. The same is true for VLAN 20 where hosts 3 and 4 can communicate with each other, but not with hosts 1 and 2. As before, start Faucet (make sure you close any previously running instance of Ryu/Faucet or Mininet first):

fve-11

In a different terminal, start Mininet:

fve-12

Now run pingall in the Mininet console. This will take much longer before, as some hosts cannot contact each other (which is expected).

fve-13

As you can see, the first and second hosts of each rack can ping each other, and so can the third and fourth hosts, but these two groups cannot ping each other.

Adding Simple Firewall Rules with ACLs

This section is a work in progress.

Let’s go back to the original configuration before we made the VLAN modifications (datacenter-4x4-faucet.yaml at the beginning of the tutorial) and experiment a little bit with simple ACLs.

For our example, we will be applying three rules:

  • Drop all IPv4 traffic destined to h2r1 (IP 10.0.0.5)
  • Drop all TCP traffic to h3r1 on port 5001 (iperf’s server port)
  • Allow all other packets

To apply these rules, though, we need to add the acl_in property to each interface they should apply to. We aren’t going to worry about switch-to-switch traffic since we are going to have the ACL rules apply on the edges (ToR switches). The Aggregate switch configuration does not need to be modified:

For each interface connected to a host on the ToR switches, add the acl_in property with a value of 1, which is the identifier we will use for the ACL:

The VLANs section does not need to be modified:

And now we will add the ACLs themselves:

As before, we do recommend that you make the changes by hand to help with learning the configuration, but you can refer to the datacenter-4x4-faucet-acl.yaml file in the faucet-examples if needed.

Testing the ACLs

Close any existing Ryu/Faucet and Mininet instances, then start up Faucet and Mininet as before with the new configuration.

fve-14

In a separate terminal, start Mininet:

fve-15

For our first test, run pingall in the Mininet console. h2r1 should not be pingable or able to ping any other host.

fve-16

Now let’s check the TCP port-based rule by first running iperf h3r1 h1r1. This should pass as the iperf server is set up on h1r1.

fve-17

If we run iperf in the opposite direction with the server on h3r1, iperf should not be able to connect. After 15 seconds or so, press Ctrl+C to cancel the test.

fve-18

You can add any number of lists or rules in a list. For match conditions, see Ryu’s OFCtl library under def to_match. Please note that the combination of match conditions must follow the OpenFlow specification. For example, if you want to match on the TCP port number, you will also need to specify the IPv4 EtherType ID (0x0800) and the IP Protocol ID (6).

It is important to note that ACLs are processed on the port that receives the packet on the switch, not the port that the packet is destined to go. This means that for an ACL rule to be effective, it needs to be active on every port that receives untrusted traffic (or just run on all ports for east-west security). If we had an ACL active on a single port, those rules would only be active on packets received by that port, but not packets being sent out that port. In our example above, we have the acl_in property on every port that has a host attached, so the ACL rules will always be processed between hosts (on the edges), but not through the switches (the core). This is generally what you want in any case, since an aggregate switch is going to have to process a lot more traffic than the edge switches and, more importantly, the packet should have been checked before it even entered the network. ACLs between switches might be desireable on hybrid environments where legacy switches connect to OpenFlow islands or core.

Additional Resources

This tutorial would not be possible without the amazing support of the Faucet development community. I highly recommend checking out the following resources:

  • Josh Bailey’s FAUCET SDN Blog – A core developer of Faucet and has several articles on the developing and using Faucet. I found his articles quite enlightening, especially when I was trying to understand how Faucet worked for the first time.
  • ONF-SDN’s Faucet Repository – The official public-facing repository for Faucet, and the one I point to in this article. New issues and pull requests should be reported in this repository.
  • REANNZ’s Faucet Repository – The original repository for Faucet and still actively developed. Changes in this repository are merged with the official ONF-SDN fork often.
  • Faucet Development Mailing List – A privately shared mailing list, but free to join. It includes many conversations between the core developers and changelog notifications.

 

Share This