Software Defined
Networking (SDN) is a set of technologies for allowing greater control of how networks
operate. Rather than a fairly static network that can only be controlled by
proprietary vendor specific protocols, with sometimes limited visibility into
the internals of layer 2 devices like switches, SDN allows for experimentation
in optimizing and configuring how the network functions. Additionally, SDN can
be controlled using commodity server hardware, which can add to the
practicality and cost savings. This flexibility is network design is in part
accomplished by separating the switch’s control plane from the data plane.
Having this new level of control can be of great benefit to security engineers,
and we will cover some potential use cases for SDN and information security.
With this flexibility comes potential pitfalls as well, the logic in the code
that runs the controller may have faults of its own that could be taken
advantage of by attackers, or a given rule may have unintended side effects
because of bugs in its implementation. This paper will cover a few potential
possibilities, both negative and positive, of how SDN can be related to
security. As OpenFlow seems to be the prevalent SDN architecture in research, this
paper will focus on it.

Software Defined Networking
vs. the Autonomous Systems model

Most traditional
networks follow the Autonomous Systems (AS) model. In the AS model things are hierarchical,
and its routing design has been compared to the post office, with set paths and
delegation of who routes to whom on a fairly static basis based on simple
communication protocols between the neighboring components. Each part of the
network does its job, with little “awareness” of the other equipment’s duties. While
the AS model is very scalable, it is not necessarily flexible. Let us say a
given network node has been moved from one section of the network to another,
it may take some time for the new location to be figured out and adjusted for
by traditional Autonomous Systems, and some interruption of service is to be
expected. In cloud architectures where servers are expected to be spun up and
down as demand changes, perhaps even moved from data center to data center, and
still be transparent to the end user, greater levels of control are needed for
controlling the path of network communications. Another common use case for SDN
is mobile data to devices such as cell phones and tablets. Mobile devices
frequently change their location, but data flows to the ever-changing endpoints
have to be managed in a reliable manner. One intent of SDN is to allow for this
sort of flexibility and control.

Standards vs. Roll Your Own

In a way,
network administrators and security engineers have had some of the power of SDN
in the past. If desired, and the hardware in place had the functionality to
support it, administrators could script something to detect changes and
automate configuration adjustments on the network. The author of this paper has
implemented similar things to this in the past to manage Cisco ASAs using the Python
scripting language and the Pexpect library. The problem with some of these
homebrew solutions is that they are not very standardized, may be specific to
one vendor’s hardware, and may be incomprehensible to someone else trying to
follow the collections of scripts, detection probes and improvised ways of making
changes. APIs and standardized systems like OpenFlow can potentially help with
this by allowing for a common interface amongst different switch vendors and
allowing network admins who inherit a network from a predecessor a better
understanding of what is being done on the controller side of a network.

OpenFlow, Oversimplified

Most security
practitioners the author has conversed with have never heard of OpenFlow or
Software Defined Networking. This small section of the paper will be somewhat
of a massive oversimplification of how OpenFlow works. We hope Software Defined
Networking experts will excuse the diversion, but we would like to relay some
of the concepts and core ideas of SDN to those who are unfamiliar with the
concept so that they might better understand the model before we continue on to
the more security related concepts. At its core, OpenFlow lets a network
administrator define rules on a switch that allow them specify “if these
conditions apply, send the packet out this port on the switch”. There are many variables
that can be matched on, and some OpenFlow frameworks (like POX) have their own
short-hands and extensions, but in OpenFlow version 1.3 [1] a controller can
set a switch to make matches on at least the following required fields from the
OpenFlow spec:

Field

Description

OXM_OF_IN_PORT

Ingress
port. This may be a physical or switch-defined logical port.

OXM_OF_ETH_DST

Ethernet
source address. Can use arbitrary bitmask.

OXM_OF_ETH_SRC

Ethernet
destination address. Can use arbitrary bitmask.

OXM_OF_ETH_TYPE

Ethernet
type of the OpenFlow packet payload, after VLAN tags.

OXM_OF_IP_PROTO

IPv4
or IPv6 protocol number.

OXM_OF_IPV4_SRC

IPv4
source address. Can use subnet mask or arbitrary bitmask.

OXM_OF_IPV4_DST

IPv4
destination address. Can use subnet mask or arbitrary bitmask.

OXM_OF_IPV6_SRC

IPv6
source address. Can use subnet mask or arbitrary bitmask.

OXM_OF_IPV6_DST

IPv6
destination address. Can use subnet mask or arbitrary bitmask.

OXM_OF_TCP_SRC

TCP
source port

OXM_OF_TCP_DST

TCP
destination port

OXM_OF_UDP_SRC

UDP
source port

OXM_OF_UDP_DST

UDP
destination port

The administrator
can set rules so that if certain matches are found, different actions can be
taken. The most common are: Forward the packet out a given port or ports, encapsulate
the packet and forward to the controller so a decision can be made about it,
drop the packet, or just send the packet to the normal switch processing
pipeline. Commonly, if the packet matches no rules in the flow table, the
packet can be sent to the controller and the controller can decide what to do
with it. The controller can then send new flow rules to the switch to tell it
how to handle those sorts of packets in the future. This forwarding to the
controller for instruction does cause a slow down in performance initially, but
once a flow rule has been placed in the TCAM (Ternary Content-Addressable Memory)
future packets should be sent on with little delay. Statistics about the switch
can also be collected via the OpenFlow protocol which may be of interest to a
security engineer.

Potential uses and potential
pitfalls

The flexibility
of Software Defined Networks/OpenFlow allows for a security engineer to try
things they have not been able to easily do before. If they have an idea for
how to rearrange data paths on the network to get better visibility on the network,
or how to detect and shape possibly malicious traffic on the network, they can
now experiment with these ideas. Of course, flexibility of control also allows
for mistakes and unintended outcomes in configuration. For a comparison most
security practitioners would recognize, consider stacks in memory and SQL
backbends. Stacks and SQL are not necessarily insecure technologies in and of
themselves, but badly designed applications that use them can bring with them
vulnerabilities like buffer overflows and SQL Injection. Is it possible to find
similar implementation issues in how people might configure OpenFlow? Currently
looking for logic flaws or security possibilities in OpenFlow/SDN
implementations would be a very niche endeavor, but over time, as more
equipment supports it and more networks utilize it, it could become quite an
interesting security topic for researchers.

With greater
control of the network though standardized means, a security engineer has new
possibilities and pitfalls to face. If a researcher wants to experiment with
ways to mitigate against common layer 2 attacks like ARP poisoning, they now
have the hooks into the networking equipment to do it. An idea in a similar
vein is ELK [2], which is intended to cut back on unneeded ARP congestion by
having a more centrally managed ARP table for the network. As a proof of
concept the author has put together a simple implementation of a switch that is
hardened again ARP poisoning as a proof of concept for this paper. The
Anti-Arp-Poisoning switch demo is implemented in POX [2], a Python based
framework for creating OpenFlow controllers, and is based on the GPLed POX code
from the OpenFlow Tutorial page at OpenFlow.org [4]. The Anti-Arp-Poisoning
switch actually had to be opened up somewhat from the switch made in the original
OpenFlow tutorial to allow for more flexibility. As the tutorial switch was
written originally, flows did not time out so the first MAC address to seize a
set of identifiers would always have them. This is not very flexible if the
same IP has to be reassigned later to a different MAC address, or moved to a
different port on the switch. In the Anti-Arp-Poisoning switch POX
implementation OpenFlow’s idle time option for flows is used, along with a
table in memory to track IP to MAC address mappings. If a device continues to
use the same MAC address and IP within the idle timeout period, the switch
continues to consider it as having the rights to use those setting. If an
attempt is seen to map a MAC address to an IP that is already taken according
to the IP to MAC address table, the switch can detect this and take counter
measures. These countermeasures could be anything from the shunning of connections
from the (perceived) spoofing MAC address, forwarding to a switch port that has
an IDS on it, scanning the host for malware using tools on the controller box
initiated by the Python/POX script, or just simply alerting the controller’s
admin. For the demo, the Anti-Arp-Poisoning switch simply alerts the console
and sets up a shun flow to port 100 (unused) that takes twice as long to time
out as a normal flow. If the controller receives a message that a given flow
has timed out because it was idle for too long, the IP to MAC address mapping
in the table for that IP is removed so that the IP is free to be used by another
device attached to the switch. Of course, there are potential race conditions
where an unintended MAC address could claim an IP first, but the logic of the
switch makes this hard for ongoing connections that do not idle out. As mentioned,
shunning of a perceived attacker is possible, but this should be done cautiously.
Spoofed packets and bad logic in the controller’s code could lead to legitimate
network devices being shunned and cause potential Denial of Service (DoS)
problems to occur.

ARP Poisoning
protection is just one possibility, and is used only as an example that’s
relatively easy to demonstrate in code. If a network administrator wants to
control how switch ports are mirrored to Intrusion Detection Systems, Intrusion
Prevention Systems or Data Loss Prevention Systems, OpenFlow can potentially be
used to implement this sort of functionality as well. Some work has already
been done in controlling flows to these sorts of monitoring devices in the form
of CloudwWatcher [3]. There is also the possibility of having systems that seem
to be behaving oddly automatically quarantined via OpenFlow, or perhaps placed
in a sandboxed honeynet for further study of what the host is doing.

We have covered
some of the positive possibilities of SDN for network security, so let us
consider some of the potential negative ramifications of bad implementations as
well. As an example of an unintended consequence of badly implemented SDN
control, let us consider an application that can control an OpenFlow switch. Because
of how OpenFlow works, this would likely be a piece of shim software on the
application’s client host that communicates to the controller, which then
communicates to the switch. With OpenFlow and flow tables, a firewall of sorts
can be made out of an OpenFlow enabled switch by telling it to drop or pass
packets depending on which ports and protocols are in use. A dynamic firewall based
on OpenFlow messages could be seen as a little like a SOHO (Small Office/Home
Office) router with UPnP (Universal Plug and Play) support, but with hopefully more
authentication (depending on the implementation). Let us continue to use UPnP
as a comparison technology to explain how things could potential go wrong
security wise. Many SOHO wireless routers seem to leave UPnP on, which allows a
client to set up a port-forwarding rule on the NAT device dynamically. The
problem here is that a piece of malicious code, or a malicious user, may open
up ports for things that are not wanted by the network administrator, like ports
for back doors and file sharing. For example, BitTorrent may be slow at someone’s
local coffee shop without forwarding a port for ratio reasons, and the user at
the coffee shop likely doesn't have the admin password to set up a static port
forward on the wireless router’s interface directly, but if UPnP is enabled the
user can open the port anyway using the UPnP protocol. Can a similar situation
appear in SDN? It depends greatly on the intended users.

With the subject
of intended users comes the subject of authentication, and OpenFlow has a
fairly simple model. Communication between a switch and the controller happens
over TCP on port 6633 (canonical, this could be configured differently). The
option is there for using TLS (Transport Layer Security) and mutual
authentication via certs on both the switch and the controller. However, based
on what the author has read from the documents of one controller implementer, it
seems that many switch vendors do not support the TLS options yet, so the
control channel would be in the clear. Without TLS, what security features are
in place to keep just anyone from configuring an OpenFlow enabled switch? The
author’s understanding is the switch starts the communications, so it has to
know the IP/host name of the controller. An important question for security is
how is this configured? If it's by host name an attacker could think of doing
some DNS shenanigans to have the host name map to their IP, thus gaining
control of the switches. Or the attacker may knock out the real controller, and
if the attacker is on the same LAN, take its IP for themselves. TLS with signed
certs on each switch and controller would prevent these attacks, though having
the communications between the controller and the switches be on its own
channel/out of band would also help.

Some potential
pitfalls also exist depending on how the Software Defined Networking system is
configured, where it is allowed to be configured from, and who is allowed to do
the configuring. As a mental exercise, let’s say an application is given the
power to decide on its own network path via some extensions/shims that
communicate with an OpenFlow controller. This could be usefully in many
situations. For example, let’s say the data is of an immediate nature, like
Voice over IP. An application like VoIP may choose a path that is not as reliable
but has lower latency and a lot of bandwidth. If the data is important from a
confidentiality standpoint, but time is not of the essence, then a slower set
of links routed around the more exposed sections of the network could be
preferred. However, a user/application being able to choose the network path
data takes could lead to potential problems depending on how authentication and
repudiation are handled. As a comparison from history, IP4 has source routing
options that allow for the host to specify the path of the packet though
routers on the Internet. Let’s say Mallory wants to talk to Bob while pretending
she is Alice, and use a connection based protocol. Normally spoofing IP packets
is easy (Scapy, Nemesis, HPing are common tools for this task) if the attacker
does not care if they get a response back. Mallory sending a packet with
Alice's IP is not a problem using raw sockets, but when Bob gets the message he
will reply in a way that is routed to Alice’s host, not Mallory’s, so Mallory never
sees the return traffic and is blind as to what to send next (Sometimes not so
blind as with vulnerable services like the R tools suite). If Mallory controls
a router in the path however, and uses source routing to specify the path for
the connection to return though, she can see the returning traffic and keep
spoofing a session-based connection as Alice. The author wonders if something
similar can be done with applications that use OpenFlow to choose their own path.
For security reasons many modern Internet routers are configure to drop source
routed packets to avoid these sorts of spoofing attacks. Can there be a modern
equivalent?

XSP [4] (eXtensible Session Protocol) is a framework that leverages OpenFlow to allow an
application to adjust its own path through the network. The author’s understanding
is that XSP has mitigations in place to thwart these sorts of attacks, but what
if someone designs a similar system and is not as careful? There are plenty of
implementation details that could have security consequences, and as with
cryptographic implementations, even seemingly minor details can matter. A few
potential security related questions include: Is each application responsible
for having credentials to make calls for path changes, or is the authentication
built into the OS and any application the user is running has to make an OS
call to use the SDN features?
How and where are these credentials stored? If each application has to be
configured with credentials separately it would be a burden to the user, but if
the credentials are handled by the OS and all applications use them, the author
can also envision some unintended consequences happening as well if the
credentials can be obtained by malicious applications and used in unanticipated
ways. Much of the security impact of SDN aware applications may depend on the
intended users. Giving the keys to the kingdom to control network switches to
an application only network administrators themselves can access is not so bad,
access by end users could be catastrophic.

Conclusion

This paper has
attempted to spur an interest in Software Defined Networking amongst security
practitioners. We have tried to provide a few ideas, and some practical
examples, of how SDN can be both used and misused. Many of the ideas put forth
in this paper are just conceptual for the time being, but we hope it inspires
others to research these topics in more depth. We highly recommend going to the
OpenFlow tutorial site [4], downloading the VM, and working though the projects
just to get a feel for the possibilities. After that, experiment with how
OpenFlow/Software Define Networking can be used in securing your network
environment.

Thanks to my
professor/advisor Martin Swany for feedback on this paper, and Brent Salisbury
for answering some of my OpenFlow/SDN questions.

# ARP Poison Resistant Switch Demo# Created by Adrian Crenshaw# Heavily based on switch/hub demo code by James McCauley# Copyright 2012 James McCauley## This file is part of POX.## POX is free software: you can redistribute it and/or modify# it under the terms of the GNU General Public License as published by# the Free Software Foundation, either version 3 of the License, or# (at your option) any later version.## POX is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the# GNU General Public License for more details.## You should have received a copy of the GNU General Public License# along with POX. If not, see <http://www.gnu.org/licenses/>.frompox.coreimportcoreimportpox.openflow.libopenflow_01asoffrompox.lib.recocoimportTimerfrompox.lib.utilimportdpidToStrfrompox.lib.addressesimportIPAddr,EthAddrlog=core.getLogger()#The variable below are timer setting. #With this number, itemes are set to timeout on idle, and timer set to run, ever 10 sec.TimerQuantumDuration=10classAntiARPPoisonSwitch(object):"""
A AntiARPPoisonSwitch object is created for each switch that connects.
A Connection object for that switch is passed to the __init__ function.
"""def__init__(self,connection):# Keep track of the connection to the switch so that we can# send it messages!self.connection=connection# This binds our PacketIn event listenerconnection.addListeners(self)# Use this table to keep track of which ethernet address is on# which switch port (keys are MACs, values are ports).self.mac_to_port={}self.ip_to_mac={}self.ip_to_mac_time_track={}Timer(TimerQuantumDuration,self.Clean_Tables,recurring=True)defsend_packet(self,buffer_id,raw_data,out_port,in_port):"""
Sends a packet out of the specified switch port.
If buffer_id is a valid buffer on the switch, use that. Otherwise,
send the raw data in raw_data.
The "in_port" is the port number that packet arrived on. Use
OFPP_NONE if you're generating this packet.
"""msg=of.ofp_packet_out()msg.in_port=in_portifbuffer_id!=-1andbuffer_idisnotNone:# We got a buffer ID from the switch; use thatmsg.buffer_id=buffer_idelse:# No buffer ID from switch -- we got the raw dataifraw_dataisNone:# No raw_data specified -- nothing to send!returnmsg.data=raw_data# Add an action to send to the specified portaction=of.ofp_action_output(port=out_port)msg.actions.append(action)# Send message to switchself.connection.send(msg)#Checks to see if a IP to MAC mapping is already there.defarp_spoof_detected(self,packet,packet_in):ipsrc=self.int_of_ip(packet,packet_in)arp=packet.find('arp')ifarpisnotNone:macsrc=str(arp.hwsrc)ip=packet.find('ipv4')ifipisnotNone:macsrc=str(packet.src)ifipsrcinself.ip_to_mac:ifself.ip_to_mac[ipsrc]!=macsrc:returnTrue#If it seems to be a new IP/MAC mapping, add it to are table, but make it timeout#for network flexibility. Also, new packets restart the clock for IP/MAC time outs.#Let's note the IP to MAC address mapping.self.ip_to_mac[ipsrc]=macsrcreturnFalse#General function for checking if something looks like ARP poisoningdefcheck_arp_spoof(self,packet,packet_in):ifself.arp_spoof_detected(packet,packet_in):#Here we can put code that runs if ARP Poisoning is detected. In this case I am just alerting.arp=packet.find('arp')ifarpisnotNone:tempipstring=str(self.ip_to_mac[arp.protosrc.toUnsignedN()])print"Dup IP to MAC!!! "+str(arp.protosrc)+" is already taken by "+ \
tempipstring+". "+str(arp.hwsrc)+" may be spoofing!"#I need an index for my dictonary I can also use as an OpenFlow cookie, so I make the IP an integer defint_of_ip(self,packet,packet_in):arp=packet.find('arp')ifarpisnotNone:ipsrc=arp.protosrc.toUnsignedN()ip=packet.find('ipv4')ifipisnotNone:ipsrc=ip.srcip.toUnsignedN()returnipsrcdefact_like_switch(self,packet,packet_in):# Learn the port for the source MACself.mac_to_port[str(packet.src)]=packet_in.in_portself.check_arp_spoof(packet,packet_in)ifself.arp_spoof_detected(packet,packet_in):log.debug("Installing drop flow...")# Maybe the log statement should have source/destination/port?msg=of.ofp_flow_mod()# Set fields to match received packet sourcemsg.match=of.ofp_match(dl_src=packet.src)#We don't want the flow to last forever, so we set it to time out#Make the blocked, potential ARP poisoner wait twice as long as other flows to time outmsg.idle_timeout=TimerQuantumDuration*2#Output to nowhere, or in this case port 100 that is unused.msg.actions.append(of.ofp_action_output(port=100))self.connection.send(msg)elif(str(packet.dst)inself.mac_to_port):log.debug('MAC '+str(packet.src)+' on port '+str(packet_in.in_port))# Send packet out the associated portself.send_packet(packet_in.buffer_id,packet_in.data,self.mac_to_port[str(packet.dst)],packet_in.in_port)log.debug("Installing flow...")# Maybe the log statement should have source/destination/port?msg=of.ofp_flow_mod(cookie=self.int_of_ip(packet,packet_in),flags=of.OFPFF_SEND_FLOW_REM)# Set fields to match received packet sourcemsg.match.dl_dst=packet.dst#Uncomment line below to lock on more fields#msg.match = of.ofp_match.from_packet(packet)#We don't want the flow to last forever, so we set it to time outmsg.idle_timeout=TimerQuantumDurationmsg.actions.append(of.ofp_action_output(port=self.mac_to_port[str(packet.dst)]))self.connection.send(msg)else:# Flood the packet out everything but the input port# This part looks familiar, right?self.send_packet(packet_in.buffer_id,packet_in.data,of.OFPP_FLOOD,packet_in.in_port)def_handle_PacketIn(self,event):"""
Handles packet in messages from the switch.
"""packet=event.parsed# This is the parsed packet data.ifnotpacket.parsed:log.warning("Ignoring incomplete packet")returnpacket_in=event.ofp# The actual ofp_packet_in message.self.act_like_switch(packet,packet_in)#If the flow times out, we assume the IP to MAC address mapping is not longer in usedef_handle_FlowRemoved(self,event):log.debug("Flow removed on switch %s",dpidToStr(event.dpid))#If I get message back that a flowhas timed out/been remove, I also remove it from the IP/MAC tableifevent.ofp.cookieinself.ip_to_mac:delself.ip_to_mac[event.ofp.cookie]#Just used for debugging, lets us show out IP/MAC tabledefClean_Tables(self):print"Show Table"printself.ip_to_macdeflaunch():"""
Starts the component
"""defstart_switch(event):log.debug("Controlling %s"%(event.connection,))AntiARPPoisonSwitch(event.connection)core.openflow.addListenerByName("ConnectionUp",start_switch)