Monday, August 21, 2017

Policy based routing with SFC in DragonFlow


One of the coolest new Pike release features in Dragonflow is support for Service Function Chaining. In this post I'll give a short intro on the topic and share some details on how we implemented it, and what it's good for.

A quick intro

A network service function is a resource that (as the name suggests) provides a service, which could be an IDS, a Firewall, or even a cache server (or anything else that works on the network data path).

In a traditional network environment, packets are forwarded according to their destination, i.e. when Server A wants to send a packet to Server B, it puts Server B's address on packet destination field.  That way, all the switches between the servers know how to forward the packet correctly.




Now consider that you want to steer this traffic through an IDS and a Firewall. Server A will still put Server B as the destination for its packets.  
One popular way to accomplish this is to place A and B within different subnets. This will allow us to use a router to route the packets through our IDS and firewall.




However, such an approach complicates the network quite a bit, requiring each 2 communicating servers to be placed within separate subnets, and causing all their traffic to go through routers (slower and more expensive).  Moreover, all packets will be routed the same way, even if you want to only apply IDS on HTTP traffic.  This headache scales with the number of servers, and it can quickly become a configuration hell.


In the SDN world we should be able to do better


SFC introduces the concept of service chains. 

There are two aspects to a service chain:


Classification

What traffic should be served by the chain.  For example, outbound TCP connections with destination port 80 from subnet 10.0.0.0/16


Service path

Which service functions (and in what order) should be applied to the packet.  For example, a firewall, then IDS, then local HTTP cache

With this in mind, a forwarding element is enhanced to handle service function chains, so everything can be deployed in a more intuitive way:





SFC in OpenStack

OpenStack Neutron supports SFC through the networking-sfc extension. This extension provides a vendor-neutral API for defining service function chains. 

The basic model is composed of 4 object types:

PortChain

Represents the whole service function chain.  It is composed of FlowClassifiers, and PortPairGroups where the former specifies the subset of traffic for which this port chain applies, and the latter specifies what service functions need to be applied.

FlowClassifier

Specifies what packets should enter the specific chain.  The classification is done by matching against packet's fields.  Some of the fields that can be specified are:

  • Source/destination logical ports
  • IP(or IPv6) source and dest CIDRs
  • Protocol types and port numbers
  • L7 URLs

PortPairGroup

Represents a step in the service function chain.  The model aggregates all port pairs that can be used to perform this specific step.

PortPair

Represents a service function instance.  It specifies what port we need to forward our packet into to apply the service and what port the resulting packet will emerge at.


TCP/80 egress traffic of Server A will go through the port chain above. Blue arrow shows possible path of classified traffic, red shows path of not classified traffic.


What goes on the wire

We solved all our issues a few paragraphs above by adding a mysterious SFC forwarder element.  How does it make sure that packets traverse the correct path? 
Usually, packets that need to be serviced by a service function chain are encapsulated and a service header is added to the packet:




The service header is used to store information needed to steer the packet along the service chain (usually, what chain is performed, and how far along the chain are we). Two of the trending choices for service protocols are MPLS and NSH.

With this metadata on the packet, the forwarder can easily decide where packet should be sent next. The service function themselves will receive the packet with the service header and operate on the encapsulated packet.


A packet classifier at SFC forwarder. If the service function supports service headers, the packet is sent in encapsulated form (right). If the service function does on support service headers, a proxy must be used.

The left side of the above figure depicts service protocol unaware function, a function that expects ingress packets to be without any encapsulation. Dragonflow's forwarding element will act as proxy when the function is service unaware.

Dragonflow drivers

In Pike release we have added SFC drivers to Dragonflow, the drivers aim to implement the classification and forwarding elements. The initial version supports:
  • MPLS service chains (the only protocol supported by networking-sfc API)
  • both MPLS aware and unaware service functions

In Dragonflow, we manage our own integration bridge to provide various services in a distributed manner. We implemented service function chaining in a as such. Each Dragonflow controller is a fully capable SFC forwarding element, so a packet does not need to travel elsewhere, unless the service function itself is not present on the current node.

Take SFC for a spin

 Easiest way to get a working environment with Dragonflow + SFC is to deploy it in a devstack.  this is the local.conf I used to deploy it:


[[local|localrc]]
DATABASE_PASSWORD=password
RABBIT_PASSWORD=password
SERVICE_PASSWORD=password
SERVICE_TOKEN=password
ADMIN_PASSWORD=password

enable_plugin dragonflow https://github.com/openstack/dragonflow
enable_service q-svc
enable_service df-controller
enable_service df-redis
enable_service df-redis-server
enable_service df-metadata

disable_service n-net
disable_service q-l3
disable_service df-l3-agent
disable_service q-agt
disable_service q-dhcp

Q_ENABLE_DRAGONFLOW_LOCAL_CONTROLLER=True
DF_SELECTIVE_TOPO_DIST=False
DF_REDIS_PUBSUB=True
Q_USE_PROVIDERNET_FOR_PUBLIC=True
Q_FLOATING_ALLOCATION_POOL=start=172.24.4.10,end=172.24.4.200
PUBLIC_NETWORK_NAME=public
PUBLIC_NETWORK_GATEWAY=172.24.4.1

ENABLED_SERVICES+=,heat,h-api,h-api-cfn,h-api-cw,h-eng

ENABLE_DF_SFC=True
enable_plugin networking-sfc git://git.openstack.org/openstack/networking-sfc

IMAGE_URL_SITE="http://download.fedoraproject.org"
IMAGE_URL_PATH="/pub/fedora/linux/releases/25/CloudImages/x86_64/images/"
IMAGE_URL_FILE="Fedora-Cloud-Base-25-1.3.x86_64.qcow2"
IMAGE_URLS+=","$IMAGE_URL_SITE$IMAGE_URL_PATH$IMAGE_URL_FILE


Once stacking is done, Neutron will have networking-sfc extension enabled with Dragonflow drivers, and Dragonflow controller will have SFC apps loaded.

The above config also enables Heat, so the following stack template can be deployed: https://review.openstack.org/#/c/488987/

This stack brings up 2 VMs, one containing a UDP echo server and another containing a service function that intercepts return traffic and doctors it:






To be continued...

We have done some, but we certainly haven't done all, some of the things that can make Dragonflow's SFC even better:
  • Support for Network Service Header protocol - NSH is a new protocol designed specifically for service function chaining. It features fields designed for SFC and several kinds of metadata that can be used to pass information between forwarders.
  • Allow pluggable protocol drivers - While MPLS and NSH are great, many other protocols might fit the bill. We will make driver loading dynamic so anyone can implement their own protocol driver independently.
  •  Graph API - a new extension to networking-sfc's API. Chains are nice, but what if you want to have branching in the middle? The new Graph API allows specification of what chain to follow.
  • Move to Common Classification Framework - A Neutron wide effort to use uniform classification objects among different extensions.

No comments:

Post a Comment