Policy based forwarding application

In this blog, I will cover a policy based forwarding application that I wrote in Python.

Use case:

There are many scenarios where regular routing decisions do not suffice and customers want more fine grained control over paths their traffic takes. This could be because of security reasons, better link utilization, vpn concerns etc. Following scenarios are covered in the application.

  • Traffic type(Either cos based or L4 based)
  • Time of the day
  • Bandwidth based

Following are some notes regarding the application.

  • The application uses the Python sdk library that I covered in previous blog.
  • Networkx python library is used to find the list of all paths between 2 nodes. The input to the library is the list of nodes and edges in a given topology.
# Put nodes and edges into a graph
graph = nx.Graph()
for node in all_nodes:
  graph.add_node(node['node']['id'])
for edge in all_edges:
  e = (edge['edge']['headNodeConnector']['node']['id'], edge['edge']['tailNodeConnector']['node']['id'])
  graph.add_edge(*e)
# Find all paths
all_paths=[]
all_paths = nx.all_simple_paths(graph, src_node_id, dest_node_id)
  • User inputs source node, destination node and the type of policy needed.
  • For cos based policy, flows are programmed based on the cos value and the different paths chosen by networkx library between the 2 nodes.
  • For L4 based policy, flows are programmed based on tcp/udp type.
  • For policy based on time of day, different paths are chosen based on time of day.
  • For policy based on bandwidth, if bandwidth on a particular path exceeds a threshold, backup path is chosen.

Topology and Results:

This application was tested with Mininet. Following topology was used.

custom_top1

Following Mininet command was used to create the topology.

sudo mn --custom ~/Downloads/mininet/custom/multi_path.py --topo=mytopo --pre ~/Downloads/mininet/pre/host3_diff_subnet.py --host=rt --link=tc --mac --controller=remote,ip=192.168.56.101,port=6633

Following are some characteristics of the topology.

  • node1<->node2 has a bandwidth of 10mbps, node1<->node3<->node2 has a bandwidth of 100mbps.
  • Hosts are in different subnet and the specific host address is mentioned in host3_diff_subnet.py script and can be found here.
  • Custom topology python script is specified in multi_path.py script and the complete script can be found here.

Important notes:

  • After the topology is discovered by controller, it is needed to change the operation mode to Proactive forwarding. This is needed because the controller has a default gateway and it forwards the traffic for unlearnt traffic and we want to disable the behavior. In reactive forwarding mode, all traffic which does not have a matching flow is forwarded to the controller. In proactive forwarding mode, all traffic which does not have a matching flow is dropped in the node. In proactive forwarding mode, control traffic like arp and lldp is still forwarded to the controller and flows are installed for these as soon as we select proactive mode.
  • Disable simpleforwarding application from controller by finding the bundle number and stopping it. This is necessary since we are doing custom forwarding in this application.

Traffic type cos based policy:

When we choose cos based policy, following flows get installed in the node.

{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:01'}, u'installInHw': u'true', u'name': u'flow0', u'etherType': u'2048', u'actions': [u'OUTPUT=3', u'SET_DL_DST=00:00:00:00:00:01'], u'nwDst': u'10.0.1.1', u'priority': u'500'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:02'}, u'installInHw': u'true', u'name': u'flow1', u'etherType': u'2048', u'actions': [u'OUTPUT=3', u'SET_DL_DST=00:00:00:00:00:02'], u'nwDst': u'10.0.2.1', u'priority': u'500'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:01'}, u'installInHw': u'true', u'name': u'flow2', u'etherType': u'2048', u'actions': [u'OUTPUT=2', u''], u'nwDst': u'10.0.2.1', u'priority': u'500', u'tosBits': u'1'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:03'}, u'installInHw': u'true', u'name': u'flow3', u'etherType': u'2048', u'actions': [u'OUTPUT=1', u''], u'nwDst': u'10.0.2.1', u'priority': u'500', u'tosBits': u'1'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:02'}, u'installInHw': u'true', u'name': u'flow4', u'etherType': u'2048', u'actions': [u'OUTPUT=2', u''], u'nwDst': u'10.0.1.1', u'priority': u'500', u'tosBits': u'1'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:03'}, u'installInHw': u'true', u'name': u'flow5', u'etherType': u'2048', u'actions': [u'OUTPUT=2', u''], u'nwDst': u'10.0.1.1', u'priority': u'500', u'tosBits': u'1'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:01'}, u'installInHw': u'true', u'name': u'flow6', u'etherType': u'2048', u'actions': [u'OUTPUT=1', u''], u'nwDst': u'10.0.2.1', u'priority': u'500', u'tosBits': u'0'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:02'}, u'installInHw': u'true', u'name': u'flow7', u'etherType': u'2048', u'actions': [u'OUTPUT=1', u''], u'nwDst': u'10.0.1.1', u'priority': u'500', u'tosBits': u'0'}
  • The first 2 flows are for the nodes node1 to reach the host h1 and node2 to reach h2. Here mac address is changed.
  • The next 4 flows are for node1<->node3<->node2 with matching cos 1.
  • The next 2 flows are for node1<->node2 with matching cos 0.
  • To verify the behavior, I did a ping with -Q option with different cos values and checked packet statistics and wireshark output to make sure that the correct  paths are chosen.

Traffic type L4 based policy:

When we choose L4 based policy, following flows get installed in the node.

{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:01'}, u'installInHw': u'true', u'name': u'flow0', u'etherType': u'2048', u'actions': [u'OUTPUT=3', u'SET_DL_DST=00:00:00:00:00:01'], u'nwDst': u'10.0.1.1', u'priority': u'500'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:02'}, u'installInHw': u'true', u'name': u'flow1', u'etherType': u'2048', u'actions': [u'OUTPUT=3', u'SET_DL_DST=00:00:00:00:00:02'], u'nwDst': u'10.0.2.1', u'priority': u'500'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:01'}, u'installInHw': u'true', u'name': u'flow2', u'etherType': u'2048', u'actions': [u'OUTPUT=2', u''], u'nwDst': u'10.0.2.1', u'priority': u'500', u'protocol': u'17'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:03'}, u'installInHw': u'true', u'name': u'flow3', u'etherType': u'2048', u'actions': [u'OUTPUT=1', u''], u'nwDst': u'10.0.2.1', u'priority': u'500', u'protocol': u'17'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:02'}, u'installInHw': u'true', u'name': u'flow4', u'etherType': u'2048', u'actions': [u'OUTPUT=2', u''], u'nwDst': u'10.0.1.1', u'priority': u'500', u'protocol': u'17'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:03'}, u'installInHw': u'true', u'name': u'flow5', u'etherType': u'2048', u'actions': [u'OUTPUT=2', u''], u'nwDst': u'10.0.1.1', u'priority': u'500', u'protocol': u'17'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:01'}, u'installInHw': u'true', u'name': u'flow6', u'etherType': u'2048', u'actions': [u'OUTPUT=1', u''], u'nwDst': u'10.0.2.1', u'priority': u'500', u'protocol': u'6'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:02'}, u'installInHw': u'true', u'name': u'flow7', u'etherType': u'2048', u'actions': [u'OUTPUT=1', u''], u'nwDst': u'10.0.1.1', u'priority': u'500', u'protocol': u'6'}
  • The first 2 flows are for the nodes node1 to reach the host h1 and node2 to reach h2. Here mac address is changed.
  • The next 4 flows are for node1<->node3<->node2 with L4 type 17(tcp).
  • The next 2 flows are for node1<->node2 with matching L4 type 6(udp).
  • To verify the behavior, I did a iperf and iperfudp test and checked packet statistics and wireshark output to make sure that the correct  paths are chosen. iperf uses tcp and iperfudp uses udp.

Time of day policy:

In this, flows get programmed dynamically based on time of day.

At time instance 1, following flows are present:

{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:01'}, u'installInHw': u'true', u'name': u'flow0', u'etherType': u'2048', u'actions': [u'OUTPUT=3', u'SET_DL_DST=00:00:00:00:00:01'], u'nwDst': u'10.0.1.1', u'priority': u'500'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:02'}, u'installInHw': u'true', u'name': u'flow1', u'etherType': u'2048', u'actions': [u'OUTPUT=3', u'SET_DL_DST=00:00:00:00:00:02'], u'nwDst': u'10.0.2.1', u'priority': u'500'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:01'}, u'installInHw': u'true', u'name': u'flow2', u'etherType': u'2048', u'actions': [u'OUTPUT=2', u''], u'nwDst': u'10.0.2.1', u'priority': u'500'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:03'}, u'installInHw': u'true', u'name': u'flow3', u'etherType': u'2048', u'actions': [u'OUTPUT=1', u''], u'nwDst': u'10.0.2.1', u'priority': u'500'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:02'}, u'installInHw': u'true', u'name': u'flow4', u'etherType': u'2048', u'actions': [u'OUTPUT=2', u''], u'nwDst': u'10.0.1.1', u'priority': u'500'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:03'}, u'installInHw': u'true', u'name': u'flow5', u'etherType': u'2048', u'actions': [u'OUTPUT=2', u''], u'nwDst': u'10.0.1.1', u'priority': u'500'}

At time instance 2, following flows are present:

{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:01'}, u'installInHw': u'true', u'name': u'flow0', u'etherType': u'2048', u'actions': [u'OUTPUT=3', u'SET_DL_DST=00:00:00:00:00:01'], u'nwDst': u'10.0.1.1', u'priority': u'500'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:02'}, u'installInHw': u'true', u'name': u'flow1', u'etherType': u'2048', u'actions': [u'OUTPUT=3', u'SET_DL_DST=00:00:00:00:00:02'], u'nwDst': u'10.0.2.1', u'priority': u'500'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:01'}, u'installInHw': u'true', u'name': u'flow2', u'etherType': u'2048', u'actions': [u'OUTPUT=1', u''], u'nwDst': u'10.0.2.1', u'priority': u'500'}
{u'node': {u'type': u'OF', u'id': u'00:00:00:00:00:00:00:02'}, u'installInHw': u'true', u'name': u'flow3', u'etherType': u'2048', u'actions': [u'OUTPUT=1', u''], u'nwDst': u'10.0.1.1', u'priority': u'500'}
  • At time instance t1, following path node1<->node3<->node2 is chosen and flows are programmed appropriately. At time instance t2, followign path node1<->node2 is chosen and flows are programmed appropriately.
  • To test this, I did a iperf test at time t1, and I get bandwidth close to 100mbps since  node1<->node3<->node2 links have 100mbps speed. When I did iperf test at time t2, I get bandwidth close to 10mbps since  node1<->node2 has link characteristic of 10mbps.

Bandwidth based policy:

  • Here, all paths between nodes are calculated and primary and backup paths are chosen.
  • Traffic flows initially in primary path. The application keeps periodically polling for bandwidth and if the bandwidth exceeds specified threshold, traffic gets switched to the backup path.
  • For the specific example, I chose bandwidth threshold as 1000 bytes/second and to exceed the bandwidth, I did a iperf test and verified that traffic gets switched to backup path.

Complete source code for the application can be found here.

Video demo of this application:

Advertisements

5 thoughts on “Policy based forwarding application

  1. Hi Sreenivas,

    I good blog and helpful too!!

    I tried your policy_fwding.py script with the mentioned mininet topology and controller (hydrogen). After I select the source and destination nodes, this is the error I get:

    Traceback (most recent call last):
    File “policy_fwding.py”, line 170, in
    all_paths = nx.all_simple_paths(graph, src_node_id, dest_node_id)
    AttributeError: ‘module’ object has no attribute ‘all_simple_paths’

    Can you please give me some pointers as to what could be the issue?

  2. Hi Rakhee
    Thanks for the comments.
    Looks like version of Python and Networkx library that you are using is different from mine so its not able to find function all_simple_paths in your case.
    I am using Python 2.7.3. Either u can try to find the same version or u might need to modify the function name based on the library that you have.

    Hope this helps.

    Regards
    Sreenivas

  3. Hi Sreenivas,

    I have seen and read your blog and it is very informative. Thanks for doing all the good work.

    I found your “policy based forwarding application” very interesting and since I am very new to coding and SDN, your application is a very good learning experience for me.

    Can you share your email address as I have a couple of questions and was hoping to discuss it via email?

    Regards,
    Rakhee

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s