Select Page

Some of Faucet’s little-advertised features can have quite a large positive impact for your network security. In this tutorial, we will be covering Faucet’s security features with a large emphasis on ACLs and how they can be used to better protect your network.

Updated 26 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. Specifically, the following issues were fixed:

  • The mirror: action now works when paired with an allow: action. This was partially an omission on my part.
  • Setting unicast_flood: false on a port now results in the expected behavior and no longer blocks broadcast traffic.

Special thanks to Josh Bailey and the Faucet team for helping me resolve some of the issues I ran in to when writing the original article.


Faucet ACLs provide a way to perform checks and specify actions on packets before they reach the L2 switching and L3 routing stages of the Faucet Pipeline. I’ll be focusing primarily on offloading firewall rules in this article since those are fairly easy to convert and highlight some of the features of Faucet ACLs.

From an implementation standpoint, ACLs are implemented in the ACL table of the Faucet Pipeline. Each port gets its own copy of ACL rules, each port can have a different ACL ruleset and ACL rulesets can be reused on multiple ports. These rules match on standard OpenFlow match conditions that are supported by Ryu’s OFCTL library for OpenFlow 1.3, and several actions. The ACL’s position in the Faucet packet pipeline and available actions are listed in following chart:

Faucet Packet Flow - ACL Security Actions

In order to demonstrate and teach the ACL pipeline, we will run through a live example. It’s highly recommended that you follow along with the example and experiment on the way.

Live Example

If you already setup Faucet using the directions in Faucet in a Virtual Infrastructure, all you need is a single config file to follow along with this example. Faucet and Mininet will be configured for a single switch with four ports, a single native VLAN, and a single ACL ruleset. The four hosts represent the following host or connection types:

  • h1 is an “unprotected” host representing connections to/from the untrusted WAN.
  • h2 is a “protected” host which runs a public service that any other host should be able to use.
  • h3 is a “protected” host which runs a private service that only h2 is allowed to use (such as a database or internal API server).
  • h4 is an “unprotected” host representing a trusted service, such as an external API or software update service. This is the only “unprotected” host that either h2 or h3 is allowed to connect to.

This configuration gives us a good range of examples that you might find in a datacenter. Please note, though, that this is still just an example and please review the Design Considerations before using a configuration like this in production. For the purposes of these examples, “protected” means a host that has explicit ACL rules assigned to it in order to restrict incoming and outgoing connections, while “unprotected” means a host that is not meant to be protected by ACLs.

Let’s start out with the Faucet configuration. Replace your working faucet.yaml file with the following:

Don’t worry if this is confusing at first. We will walk through this in detail later. Go ahead and start (or restart) Ryu with Faucet as described in Faucet in a Virtual Infrastructure:

Screenshot: Starting Ryu with Faucet ACL Security Rules

Keep Faucet running and open a new terminal for Mininet. Let’s get Mininet started with a simple topology of a single switch with four hosts:

Go ahead and run a pingall command to ensure Mininet is connected to Faucet.

Screenshot: Mininet Pingall with Faucet ACL Security Rules

To test to make sure the ACLs are applied properly, we will need to start an HTTP server on each host. Run the following commands on the mininet> prompt:

Screenshot: Starting Example HTTP Servers in Mininet to Test Faucet ACL Security Rules

Testing Open Ports

Now that we have services running on all hosts, let’s test to see if the ACLs follow the intended design. We should see the following results:

  • Connections between unprotected hosts
    • h1 -> h4, h4 -> h1 should pass since unprotected hosts have no ACLs restricting their traffic to other unprotected hosts
  • Connections from unprotected hosts to protected hosts
    • h1 -> h2, h4 -> h2 should pass since h2 is running a public service
    • h1 -> h3, h4 -> h3 should fail since h3 is running a private service where only h2 may connect
  • Connections between protected hosts
    • h2 -> h3 should pass since h3 is running a private service where only h2 may connect
    • h3 -> h2 should pass since h2 is running a public service
  • Connections from protected hosts to unprotected hosts
    • h2 -> h1, h3 -> h1 should fail because response traffic from unprotected hosts is denied from any port
    • h2 -> h4, h3 -> h4 should pass because response traffic from the trusted service port is allowed, but denied on any other service port on h4

We will get back to the last two and why only the response traffic might be blocked later. For now, let’s run wget on the Mininet console. For cases where the connection stalls (because traffic is blocked), press Ctrl+C to interrupt the wget command.

You may see log entries printed to the console from the HTTP servers running on each host. For passing tests, you should see an HTML response with a directory listing:

Screenshot: Testing HTTP Connection Through Faucet ACL Security Rules

For failing tests, you should see wget stall and can press Ctrl+C to stop the request before the long timeout:

Screenshot: Testing Disallowed Connection Through Faucet ACL Security Rules

Testing Unopened Ports

Next, let’s try accessing services on ports that are not explicitly opened by the ACL statements. First, run the following on the Mininet console to start some additional services:

This starts new HTTP servers on port 8080 on each host. For this case, the expected results are much simpler:

  • Connections between unprotected hosts
    • h1 -> h4, h4 -> h1 should pass as unprotected hosts should have no blocking ACL rules for receiving traffic.
  • All other combinations should fail since the ACL does not open up port 8080


The ACLs are processed between the VLAN and Ethernet Source tables. When an ACL rule matches, the listed actions determine whether the packet is allowed to continue to the Ethernet Source table, thereby continuing the learning and switching process, or dropping the packet. There are a couple other actions available, but for this example, we are only using the allow action.

Faucet Packet Flow - ACL Security Actions

This configuration differs in a number of ways from the original faucet.yaml file used in the Faucet in a Virtual Infrastructure tutorial. Here, there is only one switch and we added the acl_in property to the interfaces in the switch. This causes Faucet to load the specified ACL ID as flow entries in the ACL table for that specific port.

The ACL itself is fairly simple. The instructions can be listed simply:

  • Allow all ARP packets
  • Allow all ICMPv4 packets
  • Allow all to (h2) TCP port 80
  • Allow (h2) to (h3) TCP port 80
  • Allow all from (h2) TCP port 80 (response traffic)
  • Allow (h3) TCP port 80 to (h2) (response traffic)
  • Allow all to (h4) TCP port 80 (trusted unprotected service)
  • Allow all from (h4) TCP port 80 (reverse traffic)
  • Deny all to (h2)
  • Deny all to (h3)
  • Allow all from (h1-h4)

As you might be able to tell from the order of the list, Faucet ACLs are “first match wins”. Once the match conditions of any rule passes, the listed action is immediately taken and no further rules are processed. This also means that the order of the rules in an ACL are very important. On an implementation level, the ACL rule order is maintained by a decreasing flow entry priority. Let’s take a look at how these ACLs are translated into flow entries for the ACL table:

Table: Faucet ACL Security Flow Entry Listing

In this case, the action in the ACL in use is allow, which is translated either to a GOTO_TABLE: <eth_src> action if allow: 1 (continues processing) or an empty flow entry action if allow: is not set to 1 (drop). There are a couple more actions available, which will be covered later. For now, though, let’s cover some of the tests we’ve run and what flow entries were actually triggered. You can confirm that the flow entries were triggered by watching the packet counters through Ryu’s REST API (see Interactive Ryu with Postman).


When running Mininet’s pingall command, two protocols were in use: ARP and ICMPv4. These are listed at the top of the ACL to allow both of these protocols completely:

Before any host can ping another via ICMP, those hosts need to know the L2 addresses associated with their L3 addresses. For the example of h1 ping h2, h1 first sends an ARP request asking who owns h2‘s IP ( This first packet is flooded to all ports on the VLAN. During this process, that packet is matched by the first rule in the ACL (which matches on ARP’s EthType) and is allowed to continue through the Faucet pipeline. h2 responds in a similar fashion and now both hosts are aware of each other’s MAC addresses.

Table: Faucet ACL Security Flow Entry Listing Matching ARP Packet

Once the L2 addresses are known, h1 sends an ICMP Echo request, which matches the second rule in the ACL. That rule matches on the IP Protocol number for ICMP (0x01). It’s important to note that in order to match on any IP fields, the EtherType must also be specified for (in this case) IPv4 (0x0800). This is true later when matching on specific IP addresses. In any case, the second rule in the ACL allows the Echo request, and the reply from h2, to continue through the pipeline. This process is repeated for the 11 other combinations during the pingall.

More Specific Rules

The first two rules in the ACL are pretty permissive and really are just an example. It is entirely possible to restrict ICMP and even ARP requests to make sure non-public protected hosts do not receive these requests from untrusted sources. For example, we could have rules that only allow h3 to receive ARP and ICMP requests from h2 (click or tap to expand):

Screenshot: Testing Faucet ACL With Custom ARP and ICMP Rules

Of course, this would prevent h3 from receiving ARP or ICMP responses from any host other than h2. Ideally, more complex rules like this would be written by software aware of how the network security should be. It could then put in all the additional rules needed to have the network run smoothly while staying secure at the switch level.

Connections to Protected Hosts

Now that we’ve covered the easy example of ARP and ICMP requests, let’s get into connections to protected ports. It’s important to understand that for most protocols (and all TCP-based protocols), traffic must be able to pass both too and from the opened port. If we just checked on the destination port on the protected host, packets from the protected host would not be able to make it back to the host that originated the request.

Request to an Open Port

Table: Faucet ACL Security Flow Entry Listing Matching HTTP Request

When you ran the h1 wget -O - h2 command in Mininet, wget created an HTTP request from h1 to h2 on port 80. These packets to the open port (L4 TCP handshake SYN, handshake and data ACK, and closing FIN+ACK;  the L5+ HTTP GET request) matched the following rule in the ACL:

Response from an Open Port

Table: Faucet ACL Security Flow Entry Listing Matching HTTP Response

The packets responding to the HTTP request (and associated TCP handshake) match a very similar rule to the one that allowed packets to be received from h1 port 80 to h2. In this case, the HTTP server responds from port 80 and is sent to the ephemeral port on h1 used to initiate the connection:

Essentially, ipv4_dst and tcp_dst are renamed to ipv4_src and tcp_src respectively. Without this rule, h2 would not be allowed to send the response packets back to h1. Let’s take a look at the packets recorded from running the h1 wget -O - h2 example:

Screenshot: Wireshark Capture of HTTP Request Inside Mininet Through Faucet ACL Security Rules

In this case, wget initiated a connection from h1 on port 34172 (an ephemeral port) to h2‘s HTTP service on port 80. A standard TCP handshake occurs followed by the HTTP request itself. The HTTP service on h2 responds with several packets which are reassembled to form the HTTP response. Each of the response packets is acknowledged from h1 by an ACK packet. Finally, the HTTP service on h2 initiates a connecting closing handshake with a FIN+ACK, responded by h1 with another FIN+ACK, and then h2 responding with the last ACK signaling the connection as completely closed. Closing handshake can vary on platform, but is essentially the same.

Protection Pitfalls

These types of ACL rules should not be used as the only form of firewall protection for a host. See the Design Considerations later in the article as to why and to determine what is appropriate for your needs.

Connections from Protected Hosts

Just as service ports had to be explicitly opened for other hosts to communicate with services on the protected hosts, additional rules are required for the protected host to communicate with unprotected hosts. This is often the case when you are hosting services that need to communicate with a third-party API or to allow your hosts to get security updates from servers outside your network. This limitation generally arises with having to implement firewall rules without being able to check for TCP flags or using only a single ACL pass (chain) for UDP traffic. Future versions of Faucet may not have these limitations (see Drop Rules and ACL Ordering below), but it can still be a good idea to restrict where your hosts can connect to.

In our example configuration, protected hosts are allowed to connect to a service on port 80 of h4 ( Again, a reverse rule is required for responses from the unprotected host:

These rules are required before the protected host drop rules (see Drop Rules and ACL Ordering), but are otherwise the same as the rules for a protected host.

Drop Rules and ACL Ordering

The final set of rules ensures that any other unwanted traffic to our protected hosts are blocked on the protocols we’ve configured (in this case, IPv4).

These rules are also followed by an optional rule that allows unprotected hosts on the network. In our example, hosts h1 and h4 are unprotected and are able to communicate with each other freely and even the outside world if we had a router set up in Mininet:

This brings us to the order of the ACL rules. The drop rules for protected hosts will always match before the allow rule for the unprotected hosts because they are listed first. Essentially, these two sets of rules instruct Faucet to allow packets from any host in the LAN ( to any host except h2 and h3. Since OpenFlow is a first-match-wins system, the exclusions must be listed before masked match even if one might consider the masked match to be less “specific” than the exclusions. This might be confusing for those coming from configuring switches that base the winning action on the specificity of the match, where ordering might not otherwise be important.

All the rules before the drop rules allow packets to continue processing through the pipeline (have the same action) so their order in this case is not important. However, when using a wider range of actions (for example, static routing to a physical port) are used, the order can influence which match condition wins, especially if a packet matches multiple rules.

Finally, if you are wondering why these last three rules are required (blocking traffic to protected hosts, then allowing everything else), the last rule allows hosts outside your protected network to actually contact your public services. The rules could be rewritten on the subnet level to something like the following if the protected hosts need to communicate with the outside world:

ACLs in Depth

We’ve covered one way to protect your network. The ACLs provide much more than a simple firewall replacement. The firewall example is primarily meant to show some of the power and flexibility available in writing rules. It can also be used to mirror traffic to an IDS, statically route packets to specific ports while modifying the VLAN, and quite a bit more.

Assigning ACLs to Ports

In order for an ACL to be of any use, it has to be assigned to a port. As mentioned previously, ACLs are applied to packets arriving on the specified port, not leaving. That is why the interface property for applying an ACL is called acl_in.

A port can only be assigned a single ACL in Faucet v1.2, but many ACLs can be defined. Also, a single ACL can be applied to many ports. The flow entries generated from the ACL are specific to each port so each port will have its own copy of the ACL rules via flow entries with the in_port match condition specified.

To specify that a port should use a specific ACL, use the following format in the interfaces section:

To define an ACL, use the following format in the acls section:

Match Conditions

There are quite a few match conditions available for ACL rules. Any match condition supported by Ryu’s OFCTL Library for OpenFlow 1.3 is supported with the exception of in_port, which is always overridden based on the port that the ACL is assigned to. Here is a brief listing of the match conditions. Please note that match conditions also require switch support.

OpenFlow Required Match Fields

OpenFlow 1.3 specifies a list of match fields that all OpenFlow 1.3 compatible switches must implement and should be safe to use on any compliant switch. These are listed in the OpenFlow 1.3.5 specification document in Tables 11 and 12 and are converted to use Ryu’s match field representations:

  • in_port – integer – Ingress port. This is always overridden by Faucet as ACLs are per port, so this should not be used as a match condition in your rules.
  • eth_dst – masked MAC address – Ethernet destination address.
  • eth_src – masked MAC address – Ethernet source address.
  • eth_type – integer – Ethernet type of the Openflow packet payload, after VLAN tags.
  • ip_proto – integer – IPv4 or IPv6 protocol number. Requires eth_type: 0x0800 (IPv4) or eth_type: 0x86dd (IPv6) in match conditions.
  • ipv4_src – masked IPv4 address – IPv4 source address. Requires eth_type: 0x0800 in match conditions.
  • ipv4_dst – masked IPv4 address – IPv4 destination address. Requires eth_type: 0x0800 in match conditions.
  • ipv6_src – masked IPv6 address – IPv6 source address. Requires eth_type: 0x86dd in match conditions.
  • ipv6_dst – masked IPv6 address – IPv6 destination address. Requires eth_type: 0x86dd in match conditions.
  • tcp_src – integer – TCP source port. Requires ip_proto: 6 in match conditions (and its prerequisite eth_type match).
  • tcp_dst – integer – TCP destination port. Requires ip_proto: 6 in match conditions.
  • udp_src – integer – UDP source port. Requires ip_proto: 17 in match conditions.
  • udp_dst – integer – UDP destination port. Requires ip_proto: 17 in match conditions.

OpenFlow Optional Match Fields

These are the rest of the match fields that Ryu supports in OpenFlow 1.3, but are considered optional by the OpenFlow 1.3.5 specification. Make sure your switch supports these match conditions before using. These are compiled from OpenFlow Tables 11, 12, and 14 and converted to Ryu’s match field representations:

  • in_phy_port – integer – Switch physical input port.
  • metadata – masked integer – Metadata passed between tables.
  • vlan_vid – masked integer – VLAN-ID from 802.1Q header. Note: if defined in decimal, the OFPVID_PRESENT bit (0x1000) is automatically set. If defined in hexadecimal, the OFPVID_PRESENT bit is NOT automatically set.
  • vlan_pcp – integer – VLAN-PCP from 802.1Q header. Requires vlan_vid match condition to be set.
  • ip_dscp – integer – Diff Serv Code Point (DSCP). Part of the IPv4 ToS field or the IPv6 Traffic Class field. Requires eth_type: 0x0800 or eth_type: 0x86dd in match conditions.
  • ip_ecn – integer – ECN bits of the IP header. Part of the IPv4 ToS field or the IPv6 Traffic Class field. Requires eth_type: 0x0800 or eth_type: 0x86dd in match conditions.
  • sctp_src – integer – SCTP source port. Requires ip_proto132 (SCTP) in match conditions.
  • sctp_dst – integer – SCTP destination port. Requires ip_proto: 132 in match conditions.
  • icmpv4_type – integer – ICMP type. Requires ip_proto: 1 (ICMP) in match conditions.
  • icmpv4_code – integer – ICMP code.Requires ip_proto: 1 in match conditions.
  • arp_op – integer – ARP opcode. Requires eth_type0x0806 (ARP) in match conditions.
  • arp_spa – masked IPv4 address – Source IPv4 address in ARP payload. Requires eth_type0x0806 in match conditions.
  • arp_tpa – masked IPv4 address – Target IPv4 address in ARP payload. Requires eth_type0x0806 in match conditions.
  • arp_sha – masked MAC address – Source MAC address in ARP payload. Requires eth_type0x0806 in match conditions.
  • arp_tha – masked MAC address – Target MAC address in ARP payload. Requires eth_type0x0806 in match conditions.
  • ipv6_flabel – integer – IPv6 flow label. Requires eth_type: 0x86dd in match conditions.
  • icmpv6_type – integer – ICMPv6 type. Requires ip_proto: 58 (ICMPv6) in match conditions.
  • icmpv6_code – integer – ICMPv6 code. Requires ip_proto: 58 in match conditions.
  • ipv6_nd_target – masked IPv6 address – Target address in an IPv6 Neighbor Discovery message. Requires icmpv6_type: 135 (Neighbor Solicitation) or icmpv6_type: 136 (Neighbor Advertisement) in match conditions.
  • ipv6_nd_sll – masked MAC address – Source MAC address option in an IPv6 ND message. Requires icmpv6_type: 135 in match conditions.
  • ipv6_nd_tll – masked MAC address – Target MAC address option in an IPv6 ND message. Requires icmpv6_type: 136 in match conditions.
  • mpls_label – integer – LABEL in the first MPLS shim header. Requires eth_type: 0x8847 (MPLS unicast) or eth_type: 0x8848 (MPLS multicast) in match conditions.
  • mpls_tc – integer – TC in the first MPLS shim header. Requires eth_type: 0x8847 or eth_type: 0x8848 in match conditions.
  • mpls_bos – integer – BoS bit (Bottom of Stack) in the first MPLS shim header. Requires eth_type: 0x8847 or eth_type: 0x8848 in match conditions.
  • pbb_isid – masked integer – The I-SID in the first PBB service instance tag. Requires eth_type: 0x88e7 in match conditions.
  • tunnel_id – masked integer – Metadata associated with a logical port.
  • ipv6_exthdr – masked integer – IPv6 Extension Header pseudo-field. Requires eth_type: 0x86dd in the match conditions.

Legacy Fields Converted by Ryu

There are legacy fields keys that still work and automatically translated in Ryu, but are deprecated and should not be used. They are listed here only for reference:

  • dl_dst – Same as eth_dst
  • dl_src – Same as eth_src
  • dl_type – Same as eth_type
  • dl_vlan – Same as vlan_vid
  • nw_src – Same as ipv4_src or arp_spa depending on context
  • nw_dst – Same as ipv4_dst or arp_tpa depending on context
  • nw_proto – Same as ip_proto
  • tp_src – Same as tcp_src or udp_src depending on context
  • tp_dst – Same as tcp_dst or udp_dst depending on context

Ryu Match Values

The following define how to construct each of the field match types:

  • integer – A decimal or hexadecimal number. Examples: 42, 0x0800
  • masked integer – Can be a single integer, or two integers separated by a forward slash (/). The second integer represents the mask, if specified. Otherwise, the mask is set to all ones for an exact match. Examples: 0x0202 (same as 0x0202/0xffff), 128/32
  • masked MAC address – Standard MAC address notation. Examples: 00:00:00:00:00:01 (same as 00:00:00:00:00:01/ff:ff:ff:ff:ff:ff), 33:33:00:00:00:00/ff:ff:00:00:00:00
  • masked IPv4 address – Standard IPv4 address notation with a subnet mask or arbitrary bitmask. Examples:,,,
  • masked IPv6 address – Standard IPv6 address notation with a subnet mask or arbitrary bitmask. Examples: 2013:da8:215:8f2:aa22:66ff:fe4c:9c3c, ::ffff:, a:b:c:d:e:f:1:2/96 (converted to a:b:c:d:e:f::/ffff:ffff:ffff:ffff:ffff:ffff::), a:b:c:d:e:f:1:2/a:b:c:d:e:f:1:2, ::/0, ::/128

Specifying Match Conditions

Match conditions are added per rule and at the same level as the actions: key, like so:

Although it isn’t required, it is often a good idea to comment your rules, especially for fields like eth_type and ip_proto so it is easier to understand what the rule is matching.


There are a few actions available for matched rules. So far, we’ve used the allow: action, which determines whether or not the packet continues through the Faucet pipeline. Essentially, two values are used for the allow: action: 0 to drop the packet, and 1 to allow the packet to continue through the pipeline.

Another common action that you might use is the output: action. It allows you to specify the port: that the packet will be output on the switch, the dl_dst: to change the eth_dst of the packet, and vlan_vid: to modify the VLAN tag with the specified ID. Both the dl_dst: and vlan_vid: fields are optional with the only required field being port:. Here are a couple examples:

In this case, the first rule routes traffic for a specific IP to output on a certain port that leads to an imaginary IPS inspection port. The second rule routes all traffic in a subnet to a specific port while modifying the VLAN tag with ID 101. Please note that the output: action will immediately output the packet and prevent further processing in the Faucet pipeline.

Deprecated Field Name

Please note that although the output: action uses the dl_dst: field to represent the Ethernet destination address, the use of dl_dst is deprecated and should not be used anywhere else in the configuration as this is a holdover from OpenFlow 1.0 and was removed in later versions. Always use eth_dst in match conditions instead. This may be fixed in a later release of Faucet.

Finally, there is the mirror: action. This action will instruct the packet to be mirrored on the specified port and continue processing on the Faucet pipeline. Note that because the ACLs are “first match wins”, this action will stop the packet from matching any other ACL rule and will instead immediately process the mirror action. If you want the packet to continue processing through the pipeline as well, make sure to also provide the allow: 1 action alongside the mirror: action. In the current development version of Faucet, the allow: 1 action is implicit when using a mirror: action, so a future release may not require specifying the allow: action in that case.

Other Security Features

Faucet’s ACLs provide a powerful tool for network security, though it isn’t the only security feature Faucet provides.

VLAN Max Hosts

A VLAN can be configured to allow allow a certain number of hosts to be learned to help mitigate L2 DoS attacks. This can be configured by setting the max_hosts: field in the VLAN’s configuration:

Hosts will be allowed to be learned again after old host entries expire bringing the number of hosts under the specified maximum, and the hard timeout for the blocking flow entry is reached.

Prevent Unicast Floods (VLAN and Port)

If data on a network is very sensitive, it can sometimes be a good idea to prevent unicast packets from flooding in case the switch hasn’t learned the destination’s port or the host entry has expired. This is an option on both the VLAN and Interface (Port) configuration:

Periodic Traffic Required

When disabling unicast flooding, Faucet (and the switch) need to see periodic traffic from the hosts on the VLAN. This timeout is currently hardcoded to 5 minutes, so make sure the host sends packets more often than that. Otherwise, the host cache and flow entries will timeout and the host will become unlearned causing the host to no longer receive unicast traffic. The only exception is if the host is on a port configured to use the Permanent Learn feature (see next section).

This feature is also quite useful with the permanent learn option below.

Permanent Learn (Port)

A port can be configured to permanently learn hosts, meaning that they will never expire or timeout and future spoofing attempts will not change the MAC-to-port association. This is primarily used as an anti-spoofing measure and is suggested for critical servers or services known to be on a specific port. Note that because the flow entries never expire for permanently learned hosts, updating the learned hosts after physically moving the host’s connection to a different port will require a controller restart. Here is a configuration example:

Also, if there is a possibility that the port configured with permanent learning could receive traffic from multiple MAC addresses (such as a host being compromised and used for an L2 DoS attack), it is recommended to set the max_hosts: option on the VLANs in use by this port. Otherwise, the Ethernet Source and Ethernet Destination tables on the switch could reach their maximum number of flow entries, preventing any new learning to occur across the entire switch.


Now that we’ve covered the specifics on what Faucet supports, let’s look at some ways that existing firewall rules can be converted into Faucet ACLs.

Replacing ufw and Other Simple Firewalls

If you are an Ubuntu user, you may be familiar with ufw or Uncomplicated FireWall. It allows writing simple firewall rules that are automatically converted into iptable rules. We’ll cover a few ufw examples and how they translate to Faucet.

Let’s take a look at a simple ruleset for a web server:

This (very) simple example opens up SSH and HTTP access to the host that ufw is configured on. If we were to enable ufw with this configuration, it would automatically set up iptables with these rules and several others to make sure traffic can move smoothly. We will take a look at iptables in the next section. For now, let’s look at a direct conversion from ufw rules.

Because the ACL rules will have to apply on any ingress traffic on the switch to be effective, just specifying the port won’t be effective. Let’s expand the ufw rules to include the destination IP address, which we need for writing our ACL rules. In this case, the host we are protecting has the address Also, not specifying a protocol in ufw enables the rule for both UDP and TCP traffic. The expanded ACL rules would be:

In short, opening a port on a protected host requires two ACL rules per port/protocol combination. The first required rule is the rule that allows packets to be received by the host on the specific port and protocol (TCP or UDP) and the second rule allows the host’s response. Here are the specific rules for allowing traffic on port 80 using TCP:

Finally, any time a host needs to be protected, a drop rule needs to be added near the end of the ACL:

To also make sure no other traffic leaves the protected host the second drop rule is needed as well:

As mentioned before, in order for firewall rules to work within Faucet’s single ACL table, outgoing traffic (such as a response to an HTTP request) must be explicitly added, hence the two ACL rules for the same port. All the allow: 1 (allow) rules need to be grouped before the allow: 0 (drop) rules. This also means that even if outgoing traffic from one protected host is implicitly dropped with the final drop rule, it can still send traffic to other protected hosts on their open ports. There are additional rules that you can add to ensure that outgoing traffic behaves properly, but this can make the number of rules required much higher.

All in all, if you are using simple UFW rules, it is pretty easy to create offload ACL rules for Faucet. As stated before, until further firewall-specific features are added to Faucet (and depending on your security policy), it is a good idea to keep a firewall running on the host even though most disallowed traffic should not reach it.

Replacing or Offloading iptables Chains

In the last section, we talked about converting simple ufw rules. ufw actually creates several iptables chains and iptables-style rules are much similar to Faucet ACLs if the features used are supported by Faucet and the switch. So while not all iptables rules can be converted, the ones that can are fairly easy to implement in ACLs. Even some of the rules that can’t be directly converted can still be at least partially offloaded to the switch. The main thing to keep in mind is that there is essentially one “chain” in Faucet’s ACL and it is applied for all traffic entering the switch at the input port with that ACL defined for it.

Since anything more complicated than what we covered in the section on converting ufw section will require iptables extensions, I’ll list several commonly used extensions here and the appropriate match conditions for a Faucet ACL rule to do the same. This is by no means a complete list and there are probably more clever ways to convert some of them, but this should be a good start.


The addrtype iptables module generally checks for common address types by specifying a name like UNSPEC or LOCAL. Here are some direct translations where available.

  • UNSPEC – Unspecified Address
  • UNICAST – Unicast address
  • MULTICAST – Multicast Address

There are several other match conditions that can be translated from this extension. Please leave a comment if you would like to see more.

icmp (IPv4)

The --icmp-type option can easily be translated by using the icmpv4_type: and icmpv4_code: match fields. Here are some examples:

Checking for the ICMP Code is also possible:

Check out the IANA ICMP Parameters document on ICMP types and codes.

icmp6 (IPv6)

As above, the --icmpv6-type parameter can be translated to use the icmpv6_type: and icmpv6_code: match fields.

And ICMPv6 codes can also be matched:

With this information, most common firewall rules should be easily convertable. If you would like to see additional examples, let us know in the comments below.

Beyond iptables: L2 Firewall Rules

Most firewalls, including firewalls configured using iptables run at an L3/L4 level, but sometimes you need to get a bit closer to the wire. Faucet’s ACL match conditions allow for checking against specific MAC addresses, including masked matches. This allows rules that can be applied to the host NIC’s vendor or even determining if a host is a virtual machine if a common address policy is used. And since L2 connections are essentially stateless, nearly any rule can be converted from Linux’s ebtables filtering (the L2 counterpart to iptables) to a Faucet ACL so long as the action is to allow or deny the packet. Here are some examples:

The --in-interface equivalent would be to create a dedicated ACL and assign that ACL to the specific in-port of the switch. The --out-interface cannot be matched as the ACLs are processed before the destination (L2 Switching) is known.

Design Considerations

Knowing what Faucet can do and how to configure it is nice, but knowing when to use these features can make or break your network security. I’ll try to give some additional context here on when you should use these security features and when not to.

Stateless vs Stateful Firewalls

Different firewalls handle traffic filtering in different ways. Two of the primary classes of firewalls are stateless and stateful and both have advantages and disadvantages. Stateful firewalls primarily keep state about open connections in order to reduce CPU load that packet filtering causes. In the case of a firewall, a connection attempt is first checked against the rules configured for that firewall. Once the connection is made and found to be allowed, identifying information is stored in a quick lookup table so future packets in that connection do not need to be checked against the firewall rules again. This is especially useful when using masked matching rules which require significant CPU cycles to process while checking against the connection cache would require much less resources. Most stateful firewalls are run on general purpose CPUs.

Stateless firewalls, on the other hand, check every packet in a stream against the firewall rules. Depending on implementation for a CPU-based stateless firewall, these firewalls can be faster than a stateful firewall, but often at the expense of connection tracking features such as bandwidth monitoring and metering. In both cases, most CPU-based firewalls require a lot of resources for high-bandwidth connections and are not always guaranteed to run at line rate. Because of this, these firewalls generally only protect north-to-south traffic in a datacenter leaving east-to-west traffic unprotected. There are several solutions to this, of course, including leaving all the protection to a firewall service running on each host, using a dedicated firewall appliance on each rack, or complex Network Function Virtualization (NFV) services. In all of these cases, a lot of CPU cycles (and power) is used somewhere in the network to provide this protection. Thankfully, many of the checks that firewalls perform are now implemented in hardware and available on just about any modern OpenFlow switch, reducing the power and physical space requirements or freeing up those resources for other services.

What we demonstrated in this article is that common firewall rules can be at least partially offloaded on to the top-of-rack switch. With the right software and OF compliant hardware, this can replace the need for dedicated x86 firewall appliances or add protection on the network where none existed. When using hardware OpenFlow top-of-rack switches, these OF-based firewall rules run at line-rate and take no additional room in the rack. In many cases, this protection can be added without upgrading your existing OpenFlow switches, essentially providing a free service. All that is needed is to configure your OpenFlow controller.

OpenFlow-based firewalls implemented today in Faucet would be completely stateless, though it is possible to add connection tracking abilities in Faucet or other controllers. In any case, it is possible to reduce the load on existing firewalls even if an OF-based firewall can’t provide all the protection your network needs right now (see the next section on limitations). A hybrid solution could be to use the ToR switch to filter most of the traffic and have the remaining traffic filtered by a stateful firewall on the protected host. This would allow connection tracking features for allowed services through the stateful firewall while dropping traffic for blocked services before they even hit the host.

ACLs as Front-Line Protection to Host Firewalls

You might have noticed one glaring security issue with the reverse rules used in this article. They do not check the TCP flags to make sure that connections are not initiated from an opened port like port 80 on h2. This means that if h2 is compromised, the HTTP service could be closed and a malicious program could initiate connections from port 80 to any protected host on the network. This is due to a limitation in Faucet and OpenFlow 1.3, which is the only version Faucet v1.2 supports. This can be mitigated in the future by Faucet using Ryu’s libraries for newer versions of OpenFlow (such as OF1.4, which implements TCP flag matching) and switches that support it.

This also demonstrates that ACLs should not be the only form of protection in your network (at least, not yet), especially if east-to-west security is critical. It does a great job of offloading more general firewall rules from the hosts to the switch itself at line rate, especially for east-to-west traffic that would otherwise be very CPU intensive on the hosts themselves. However, it does not entirely replace the need for firewalls on the hosts. Think of these ACLs as a first line of defense if using for firewall offloading. As Faucet and OpenFlow evolve, more and more security tasks can be offloaded completely to the switch. For example, when Faucet supports future versions of OpenFlow, the ability to check TCP flags could make the protection complete enough for some security policies, removing the need for host firewalls and perhaps even existing dedicated firewall appliances.

As it stands right now, using Faucet ACLs can provide a lot more protection in a datacenter where east-to-west traffic is not otherwise restricted. It all depends on your security policy and how much protection you need. You can even add as many ACL entries as you want without a performance hit on any of your hosts or the network so long as your switch supports the number of flow entries required. Since modern OpenFlow switches support millions of exact matches and hundreds of thousands of masked matches, that’s a lot of ACL entries!


We’ve covered a lot of information on how you might better protect your network using Faucet and OpenFlow through Faucet’s ACLs and other protection mechanisms. These features and possibilities will only get better as development continues on Faucet and as network engineers better understand OpenFlow and SDN. Additionally, Faucet’s development is powered by people like you. If you would like more information on how to contribute to Faucet’s development or suggest new features, please check out Faucet’s GitHub page. If you would like to see more content on how to use Faucet, please let us know what you would like to see below!

Share This