Showing posts with label Ethernet Monitoring. Show all posts
Showing posts with label Ethernet Monitoring. Show all posts

Sunday, May 12, 2013

PyTapDEMon - Part 5 Misc Tools

I developed various scripts and tools for the project with the intention of making them into more baked features. However, they remained to be half-baked at this time. I am putting them in this post for now, if they ever grow into a post of their own I will remove them from here.

### DPKT Ping Test meant to be executed from h1 ###

** Script **

#!/usr/bin/env python
#
# Example from:
# http://jon.oberheide.org/blog/2008/08/25/dpkt-tutorial-1-icmp-echo/
#
# More documentation:
# http://www.commercialventvac.com/dpkt.html#mozTocId305148
#
# This file sends ICMP packet to the destination host.
# This is meant to be run on h1 to generate interesting traffic.
# It has the same affect as Mininet "h1 ping h2"
#

import dpkt
import socket, random

echo = dpkt.icmp.ICMP.Echo()
echo.id = random.randint(0, 0xffff)
echo.seq = random.randint(0, 0xffff)
echo.data = 'hello world'

icmp = dpkt.icmp.ICMP()
icmp.type = dpkt.icmp.ICMP_ECHO
icmp.data = echo

destination = '10.0.0.2'
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, dpkt.ip.IP_PROTO_ICMP)
s.connect((destination, 1))
sent = s.send(str(icmp))

print 'sent %d bytes to %s' % (sent, destination)

** end script **
** output **


root@mininet-vm:~/PyTapDEMON/tests# ./dpktTest_h1_ping.py
sent 19 bytes to 10.0.0.2
root@mininet-vm:~/PyTapDEMON/tests#

### Scapy Ping Test meant to be executed from h1 ###

** Script **

#!/usr/bin/env python
#
# Example from:
# http://www.secdev.org/projects/scapy/build_your_own_tools.html
#
# This is meant to be executed on h1 to ping toward h2
#

from scapy.all import sr1, IP, ICMP

dstIP = '10.0.0.2'

packet = sr1(IP(dst=dstIP)/ICMP())
if packet:
    packet.show()

** end script **
** output **


root@mininet-vm:~/PyTapDEMON/tests# ./scapyTest_h1_ping.py
WARNING: No route found for IPv6 destination :: (no default route?)
Begin emission:
.*Finished to send 1 packets.

Received 2 packets, got 1 answers, remaining 0 packets
###[ IP ]###
  version   = 4L
  ihl       = 5L
  tos       = 0x0
  len       = 28
  id        = 51838
  flags     =
  frag      = 0L
  ttl       = 64
  proto     = icmp
  chksum    = 0x9c60
  src       = 10.0.0.2
  dst       = 10.0.0.1
  \options   \
###[ ICMP ]###
     type      = echo-reply
     code      = 0
     chksum    = 0xffff
     id        = 0x0
     seq       = 0x0
root@mininet-vm:~/PyTapDEMON/tests#

### Scapy Ethernet Sniffing Test meant to be executed on h11 ###

** Script **

#!/usr/bin/env python
#
# This test passively sniffs
# and look for Ethernet packets
# using Scapy.
#
# This is meant to be used on the sniffing
# host, h11 in the PyTapDEMon prototype
# setup.
#

from scapy.all import *

def parsePacket(pkt):
    if pkt.haslayer(Ether):
         print "Found Ethernet packet"
         print str(pkt[Ether].dst)

if __name__ == "__main__":
    sniff(prn=parsePacket, store=0)

** end script **
** output **

1. The tool sits quite until h1 pings h2:

mininet> h1 ping -c3 h2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_req=1 ttl=64 time=0.316 ms
64 bytes from 10.0.0.2: icmp_req=2 ttl=64 time=0.268 ms
64 bytes from 10.0.0.2: icmp_req=3 ttl=64 time=0.266 ms

--- 10.0.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 0.266/0.283/0.316/0.026 ms
mininet> 

root@mininet-vm:~/PyTapDEMON/tests# ./scapyTest_eth_sniff.py
WARNING: No route found for IPv6 destination :: (no default route?)
Found Ethernet packet
2e:40:29:25:03:45
Found Ethernet packet
12:fd:b3:e2:3c:f7
Found Ethernet packet
12:fd:b3:e2:3c:f7
Found Ethernet packet
2e:40:29:25:03:45
Found Ethernet packet
12:fd:b3:e2:3c:f7
Found Ethernet packet
12:fd:b3:e2:3c:f7
Found Ethernet packet
2e:40:29:25:03:45
Found Ethernet packet
2e:40:29:25:03:45
Found Ethernet packet
12:fd:b3:e2:3c:f7
Found Ethernet packet
12:fd:b3:e2:3c:f7
Found Ethernet packet
2e:40:29:25:03:45
Found Ethernet packet
2e:40:29:25:03:45
Found Ethernet packet
12:fd:b3:e2:3c:f7
Found Ethernet packet
12:fd:b3:e2:3c:f7
Found Ethernet packet
2e:40:29:25:03:45
Found Ethernet packet
2e:40:29:25:03:45

### Twisted EchoServer and EchoClient ###

** Echoserver **
#
# Example taken from:
# Twisted Network Programming Essentials, 2nd Edition
# By: Jessica McKellar, Abe Fettig
# OReilly Media, Ebook ISBN:978-1-4493-3330-0
#

from twisted.internet import protocol, reactor
import json

class Echo(protocol.Protocol):
    def dataReceived(self, data):
        print "Received This Data: ", data
        ports = json.loads(data)
        for key in ports: 
            print key, ports[key]
        self.transport.write(data)

class EchoFactory(protocol.Factory):
    def buildProtocol(self, addr):
        print "Received from addr: ", addr
        return Echo()

reactor.listenTCP(8000, EchoFactory())
reactor.run()

*** EchoClient ***
#
# Example taken from:
# Twisted Network Programming Essentials, 2nd Edition
# By: Jessica McKellar, Abe Fettig
# OReilly Media, Ebook ISBN:978-1-4493-3330-0
#

from twisted.internet import reactor, protocol
import json

msg = json.dumps({"s1": {'s1MirrorSrc': [1], 's1MirrorDst': [6,7]}, "s2": {'s2MirrorSrc': [2], 's2MrrorDst': [7]}})

class EchoClient(protocol.Protocol):
    def connectionMade(self):
        print "Sending: ", msg
        self.transport.write(msg)

    def dataReceived(self, data):
        print "Server returned:", data
        self.transport.loseConnection()

class EchoFactory(protocol.ClientFactory):
    def buildProtocol(self, addr):
        print "Build on addr: ", addr
        return EchoClient()

    def clientConnectionFailed(self, connector, reason):
        print "Connection failed."
        reactor.stop()

    def clientConnctionLost(self, connector, reason):
        print "Connection lost."
        reactor.stop()

reactor.connectTCP("localhost", 8000, EchoFactory())
reactor.run()

*** output ***

1. start echoserver:
mininet@mininet-vm:~/PyTapDEMON/twisted$ python echoserver.py 

2. execute echoclient:
mininet@mininet-vm:~/PyTapDEMON/twisted$ python echoclient.py 
Build on addr:  IPv4Address(TCP, '127.0.0.1', 8000)
Sending:  {"s2": {"s2MrrorDst": [7], "s2MirrorSrc": [2]}, "s1": {"s1MirrorDst": [6, 7], "s1MirrorSrc": [1]}}
Server returned: {"s2": {"s2MrrorDst": [7], "s2MirrorSrc": [2]}, "s1": {"s1MirrorDst": [6, 7], "s1MirrorSrc": [1]}}

3. echoserver output:
Received from addr:  IPv4Address(TCP, '127.0.0.1', 35325)
Received This Data:  {"s2": {"s2MrrorDst": [7], "s2MirrorSrc": [2]}, "s1": {"s1MirrorDst": [6, 7], "s1MirrorSrc": [1]}}
s2 {u's2MrrorDst': [7], u's2MirrorSrc': [2]}
s1 {u's1MirrorDst': [6, 7], u's1MirrorSrc': [1]}








PyTapDEMon - Part 4 Unittest for the Network

Here are links to the first three parts of the project:


For part 4, I will attempt to write some testing for the project. Of course, as part of the TDD approach, this really should have been either part 2 of the project, or be integrated into all the steps during the development. But how many of us really write the tests first? So here I am, trying to write some tests. :)

To be honest, in my opinion, this is part of the larger problem that it is hard to write tests against code that either make changes to the network or monitors the network. The network is inherently distributed and traditionally been stateless from one another. Such as the case here, for the test, I can write test to test my code; or I can write test to query the network and see if it is in an expected state. If I have time, I should do both, but comparing the two, I have decided to write test for the network state because ultimately that is what I care about the most. 

The objectives are simple: 

1. Query the switches for the current flow information. 
2. Compare them to the expected port-based flow data. 

I am going to use ovs-ofctl as the OOB channel to query the OpenFlow switch data. This is what the flows should look like on s1, for example, when the flows are installed:

mininet@mininet-vm:~$ sudo ovs-ofctl dump-flows s1
NXST_FLOW reply (xid=0x4):
 cookie=0x0, duration=116.257s, table=0, n_packets=0, n_bytes=0, idle_timeout=120,hard_timeout=120,in_port=3 actions=output:4
 cookie=0x0, duration=116.259s, table=0, n_packets=3, n_bytes=238, idle_timeout=120,hard_timeout=120,in_port=1 actions=output:2,output:6,output:8
 cookie=0x0, duration=116.255s, table=0, n_packets=0, n_bytes=0, idle_timeout=120,hard_timeout=120,in_port=4 actions=output:3
 cookie=0x0, duration=116.258s, table=0, n_packets=3, n_bytes=238, idle_timeout=120,hard_timeout=120,in_port=2 actions=output:1,output:6,output:8
mininet@mininet-vm:~$

And here is what the output would look like when there are no flows: 

mininet@mininet-vm:~$ sudo ovs-ofctl dump-flows s1
NXST_FLOW reply (xid=0x4):
mininet@mininet-vm:~$


Here is the unittest code for the tests: 

***
#!/usr/bin/env python

import unittest
import subprocess

def parseFlows(flows):
    """
    Parse out the string representation of flows passed in.
    Example:
    NXST_FLOW reply (xid=0x4):
     cookie=0x0, duration=4.329s, table=0, n_packets=0, n_bytes=0, idle_timeout=120,hard_timeout=120,in_port=3 actions=output:4
    """
    switchFlows = {}
    for flow in flows.split('\n'):
        line = flow.split()
        if len(line) > 3: #get rid of first line in flow output
            inputPort = line[5].split(',')[2].split('=')[1]
            outputPorts = line[6].split('actions=')[1]
            switchFlows[inputPort] = outputPorts
    return switchFlows
    

globalFlows = {}
for i in range(1, 4):
    """Query switches s1, s2, s3 and dump flows, add to global flow dictionary"""
    switch = 's'+str(i)
    flows = subprocess.check_output(['sudo', 'ovs-ofctl', 'dump-flows', switch])
    switchFlows = parseFlows(flows)
    globalFlows[switch] = switchFlows


class PyTapDEMON_Test(unittest.TestCase):
    def test_s1_port1(self):
        self.assertEqual('output:2,output:6,output:8', globalFlows['s1']['1'])
   
    def test_s2_port1(self):
        self.assertEqual('output:2,output:6,output:8', globalFlows['s2']['1'])

    def test_s3_port10(self):
        self.assertEqual('output:11', globalFlows['s3']['10'])


if __name__ == '__main__':
    unittest.main()

***

Here is the output during passing state: 

1. Generate the packet to install flows in the switches: 

mininet> h1 ping -c3 h2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_req=2 ttl=64 time=0.329 ms
64 bytes from 10.0.0.2: icmp_req=3 ttl=64 time=0.106 ms

--- 10.0.0.2 ping statistics ---
3 packets transmitted, 2 received, 33% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.106/0.217/0.329/0.112 ms
mininet>

2. Run the test: 

mininet@mininet-vm:~/PyTapDEMON/tests$ pytest PyTapDEMON_Test.py
========================  PyTapDEMON_Test.py  ========================
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK
*******************************************************************************

mininet@mininet-vm:~/PyTapDEMON/tests$

*** 

Here is the output during failing state (no flow installed):

1. Verified that there is no flow in s1: 

mininet@mininet-vm:~$ sudo ovs-ofctl dump-flows s1
NXST_FLOW reply (xid=0x4):
mininet@mininet-vm:~$

2. Run the test: 

mininet@mininet-vm:~/PyTapDEMON/tests$ pytest PyTapDEMON_Test.py
========================  PyTapDEMON_Test.py  ========================
EEE
======================================================================
ERROR: test_s1_port1 (PyTapDEMON_Test.PyTapDEMON_Test)
----------------------------------------------------------------------
Traceback (most recent call last)
  File "/usr/lib/python2.7/unittest/case.py", line 332, in run
    testMethod()
  File "/home/mininet/PyTapDEMON/tests/PyTapDEMON_Test.py", line 34, in test_s1_port1
    self.assertEqual('output:2,output:6,output:8', globalFlows['s1']['1'])
KeyError: '1'

                              no stdout                              
                              no stderr                              
======================================================================
ERROR: test_s2_port1 (PyTapDEMON_Test.PyTapDEMON_Test)
----------------------------------------------------------------------
Traceback (most recent call last)
  File "/usr/lib/python2.7/unittest/case.py", line 332, in run
    testMethod()
  File "/home/mininet/PyTapDEMON/tests/PyTapDEMON_Test.py", line 37, in test_s2_port1
    self.assertEqual('output:2,output:6,output:8', globalFlows['s2']['1'])
KeyError: '1'

                              no stdout                              
                              no stderr                              
======================================================================
ERROR: test_s3_port10 (PyTapDEMON_Test.PyTapDEMON_Test)
----------------------------------------------------------------------
Traceback (most recent call last)
  File "/usr/lib/python2.7/unittest/case.py", line 332, in run
    testMethod()
  File "/home/mininet/PyTapDEMON/tests/PyTapDEMON_Test.py", line 40, in test_s3_port10
    self.assertEqual('output:11', globalFlows['s3']['10'])
KeyError: '10'

                              no stdout                              
                              no stderr                              
----------------------------------------------------------------------
Ran 3 tests in 0.041s

FAILED (errors=3)
*******************************************************************************

mininet@mininet-vm:~/PyTapDEMON/tests$

***

It is pretty easy to write 'unittest' for your network in an OpenFlow enabled network, in this example I query the switches individually, but in theory I should interface directly with the controller. 

Cheers. Leave me comments and let me know what you think. 




Sunday, May 5, 2013

PyTapDEMon - Part 3 Pro-Active Monitoring with sFlow-RT

Originally, part 3 of my project was going to be a web front end to allow users to indicate which port to tap and monitor. However, last week I came across a few blog posts on sFlow-RT on the DEMon project that makes things very interesting.

http://blog.sflow.com/2013/04/sdn-packet-broker.html
http://blog.sflow.com/2013/05/software-defined-analytics.html

Here is a screencast of the project. Sorry about the quality, I need to go thru the tutorial of Camtasia to edit the video better. But all the commands in the screencast are all listed below:



It is interesting because, as the blog suggested, sFlow-RT allows for automatic tapping based on criteria set up by the user in a pro-active fashion. I, too, believes this changes the dynamic of distributed monitoring quit a bit. So I have decided to see if I can play around with this approach in my project a bit.

After some thought, this is the simple high level overview: 

  1. All the conditions from my prototype are still the same (PyTapDEMON - Part 2 Prototype). 
  2. Upon observing any web traffic from h1 to h2, port-based flow from h8 and h9 will be pushed down to s2. 
  3. Port 4 will be mirrored to port 7 connected from s2 to s3, and I should then see the traffic on s11 connected to s3. 

I am using my VM setup before (Part 2) plus the following:

    1. sFlow-RT on my host machine, 192.168.56.1 (VM eth1 ip is 192.168.56.101).
    2. In order to differentiate agents from the switches, the following IPs are configured:


sudo ifconfig s1-eth1 192.168.1.1 netmask 255.255.255.0
sudo ifconfig s2-eth1 192.168.2.1 netmask 255.255.255.0
sudo ifconfig s3-eth1 192.168.3.1 netmask 255.255.255.0


    3. Then sFlow export are pushed out manually:


sudo ovs-vsctl -- --id=@s create sFlow agent=s1-eth1 target=\"192.168.56.1:6343\" header=128 polling=10 -- set Bridge s1 sflow=@s

sudo ovs-vsctl -- --id=@s create sFlow agent=s2-eth1 target=\"192.168.56.1:6343\" header=128 polling=10 -- set Bridge s2 sflow=@s

sudo ovs-vsctl -- --id=@s create sFlow agent=s3-eth1 target=\"192.168.56.1:6343\" header=128 polling=10 -- set Bridge s3 sflow=@s

    4. I can see the switches on sFlow-RT:








 

    5. From the sFlow blog above, the condition of monitoring TCP flows is added:


mininet@mininet-vm:~$ curl -H "Content-Type:application/json" -X PUT --data "{keys:'ipsource,ipdestination,tcpsourceport,tcpdestinationport', value:'bytes'}" http://192.168.56.1:8008/flow/tcp/json
mininet@mininet-vm:~$






 

    6. Simple web server is added to h1 and tested on h2 (from Mininet walk thru):

mininet> h1 python -m SimpleHTTPServer 80 &
mininet> h2 wget -O - h1
--2013-05-05 10:17:45--  http://10.0.0.1/
Connecting to 10.0.0.1:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 320 [text/html]
Saving to: `STDOUT'
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>
<title>Directory listing for /</title>
<body>
<h2>Directory listing for /</h2>
<hr>
<ul>
<li><a href="controllers.py">controllers.py</a>
<li><a href="PyPath.py">PyPath.py</a>
</ul>
<hr>
</body>
</html>

     0K                                                       100% 64.7M=0s

2013-05-05 10:17:45 (64.7 MB/s) - written to stdout [320/320]

mininet>

Ok, now I am ready to run the test. Here are steps: 

    1. Here is the Python script to grab the data from sFlow-RT and push out flows when condition is met: 

#!/usr/bin/env python

import json
import urllib2
import subprocess

data = json.load(urllib2.urlopen('http://192.168.56.1:8008/metric/ALL/tcp/json'))

# if TCP traffic is detected
if 'topKeys' in data[0]:
    info =  data[0]['topKeys'][0]['key']
    dst, src, dstPort, srcPort = info.split(',')
    # install flows to s2 for h8 and h9
    # port 4 will also duplicate traffic to port 7 mirror port
    subprocess.call(['sudo','ovs-ofctl','add-flow','s2','hard_timeout=120,in_port=4,actions=output:3,output:7'])
    subprocess.call(['sudo','ovs-ofctl','add-flow','s2','hard_timeout=120,in_port=3,actions=output:4'])

    2. The script is initiated via cron tab:

* * * * * python /home/mininet/PyTapDEMON/tests/sflow-rt_test.py 

    3. Currently h8 cannot reach h9:

mininet> h8 ping -c3 h9
PING 10.0.0.9 (10.0.0.9) 56(84) bytes of data.
From 10.0.0.8 icmp_seq=1 Destination Host Unreachable
From 10.0.0.8 icmp_seq=2 Destination Host Unreachable
From 10.0.0.8 icmp_seq=3 Destination Host Unreachable

--- 10.0.0.9 ping statistics ---
3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2018ms
pipe 3
mininet>

    4. Now generate interesting traffic from h2 to h1: 

mininet> h2 wget -O - h1
--2013-05-05 15:13:09--  http://10.0.0.1/
Connecting to 10.0.0.1:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 320 [text/html]
Saving to: `STDOUT'
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>
<title>Directory listing for /</title>
<body>
<h2>Directory listing for /</h2>
<hr>
<ul>
<li><a href="controllers.py">controllers.py</a>
<li><a href="PyPath.py">PyPath.py</a>
</ul>
<hr>
</body>
</html>

     0K                                                       100% 63.4M=0s

2013-05-05 15:13:09 (63.4 MB/s) - written to stdout [320/320]

mininet>

    5. The traffic an be viewed from sFlow-RT:















    6. The before and after flows from s2:

mininet@mininet-vm:~/PyTapDEMON/tests$ sudo ovs-ofctl dump-flows s2
NXST_FLOW reply (xid=0x4):
 cookie=0x0, duration=8.197s, table=0, n_packets=0, n_bytes=0, idle_timeout=120,hard_timeout=120,in_port=1 actions=output:2,output:6,output:8
 cookie=0x0, duration=8.194s, table=0, n_packets=0, n_bytes=0, idle_timeout=120,hard_timeout=120,in_port=2 actions=output:1,output:6,output:8

mininet@mininet-vm:~/PyTapDEMON/tests$ sudo ovs-ofctl dump-flows s2
NXST_FLOW reply (xid=0x4):
 cookie=0x0, duration=7.323s, table=0, n_packets=0, n_bytes=0, idle_timeout=120,hard_timeout=120,in_port=1 actions=output:2,output:6,output:8
 cookie=0x0, duration=6.431s, table=0, n_packets=5, n_bytes=378, hard_timeout=120,in_port=3 actions=output:4
 cookie=0x0, duration=6.445s, table=0, n_packets=5, n_bytes=378, hard_timeout=120,in_port=4 actions=output:3,output:7
 cookie=0x0, duration=7.323s, table=0, n_packets=0, n_bytes=0, idle_timeout=120,hard_timeout=120,in_port=2 actions=output:1,output:6,output:8
mininet@mininet-vm:~/PyTapDEMON/tests$

    7. Now h8 can ping h9:

mininet> h8 ping -c3 h9
PING 10.0.0.9 (10.0.0.9) 56(84) bytes of data.
64 bytes from 10.0.0.9: icmp_req=1 ttl=64 time=2.09 ms
64 bytes from 10.0.0.9: icmp_req=2 ttl=64 time=0.047 ms
64 bytes from 10.0.0.9: icmp_req=3 ttl=64 time=0.052 ms

--- 10.0.0.9 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.047/0.732/2.099/0.966 ms
mininet>

    8. More importantly, I am now monitoring h9 traffic as intended on h11:











Obviously this had nothing to do with the POX component that I wrote. But that is part of the beauty, they are decoupled, I can use whatever mechanism I choose to tie in sFlow-RT with my setup. The fact that we get sFlow by virtue of default is very valuable. 

I like this approach a lot, this is both simple and powerful. The next step for me is to see if I can make this into a more useful tool in the next few weeks before I need to present this in class. 

Cheers. Let me know what you think. 












Sunday, April 21, 2013

PyTapDEMon - Part 2 Prototype

This is part 2 of my PyTapDEMON project. Here is the link for the overview of the project:
http://blog.pythonicneteng.com/2013/04/introducing-pytapdemon.html

Here is a screencast of the prototype. Sorry about the small font, I will edit this when I have some time to go thru the tutorial for Camtasia, but all the commands in the screencast are listed in this blog:



For my prototype, I am using Mininet included in the OpenFlow VM to simulate 2 filter switch (s1 and s2) with 5 hosts each (h1-h5 on s1, h6-h10 on s2), 5x uplinks to aggregation switch (s3), and a capture host (h11) attached to Eth11 on s3.

Here is the normal state (The custom Mininet config code is toward the end of the post):














The Red link indicates the 'always forward' state. For example, eth6 will always forward to eth1 on s3 and s3 will always forward all traffic from eth1-10 to eth11 to the capture host.

Here is the simulation state:








On s1, (port 1 / port 2) and (port 3 / port 4) will always forward traffic to each other. On s2, (port 1 / port 2) will forward traffic to each other. Switch s1 traffic on eth1 will be mirrored to eth6 and switch s2 eth2 traffic will be mirrored to eth7. To keep things simple, only 1 link on the mirrored is picked so I dont see the same traffic multiple times and eth3/eth4 traffic on s1 is not mirrored to show isolation.

Here is the POX component code:

#!/usr/bin/env python
"""
Custom Topology: PyTapDEMON_topo.py
"""
# These next two imports are common POX convention
from pox.core import core
import pox.openflow.libopenflow_01 as of
# Even a simple usage of the logger is much nicer than print!
log = core.getLogger()
# global flow timeout
timeout = 120
# flow simulation on ports
s1PortPairs = [(1,2), (3,4)]
s2PortPairs = [(1,2)]
# mirror source and destination
f = open('ext/mirrorPorts.txt', 'r')
for num, item in enumerate(f.readlines()):
    if num == 0:
        s1MirrorSrc = item.strip().split(',')[1:]
        s1MirrorSrc = map(lambda x: int(x), s1MirrorSrc) #convert to int
    if num == 1:
        s1MirrorDst = item.strip().split(',')[1:]
        s1MirrorDst = map(lambda x: int(x), s1MirrorDst)
    if num == 2:
        s2MirrorSrc = item.strip().split(',')[1:]
        s2MirrorSrc = map(lambda x: int(x), s2MirrorSrc)
    if num == 3:
        s2MirrorDst = item.strip().split(',')[1:]
        s2MirrorDst = map(lambda x: int(x), s2MirrorDst)

def _PortFlowMod(srcPort, dstPort, timeout, mirrorSrc, mirrorDst):
    print "Push Flow from Source Port %s to Destion Port %s with %s seconds timeout" % \
    (srcPort, dstPort, timeout)
    forwardFlow = of.ofp_flow_mod()
    forwardFlow.idle_timeout = timeout
    forwardFlow.hard_timeout = timeout
    forwardFlow.match.in_port = srcPort
    forwardFlow.actions.append(of.ofp_action_output(port=dstPort))
    # add mirror src and destination
    if srcPort in mirrorSrc:
        for dstMirror in s1MirrorDst:
            forwardFlow.actions.append(of.ofp_action_output(port=dstMirror))
    return forwardFlow

def _simulateTraffic(portPairs, switch):
    print "***Simulating Traffic flow for switch %s" % str(switch)
    for pair in portPairs:
        srcPort, dstPort = pair[0], pair[1]
        print "Pushing flow from %s to %s" % (srcPort, dstPort)
        core.openflow.getConnection(switch).send(_PortFlowMod(srcPort, dstPort, timeout, \
            mirrorSrc=s1MirrorSrc, mirrorDst=s1MirrorDst))
        core.openflow.getConnection(switch).send(_PortFlowMod(dstPort, srcPort, timeout, \
            mirrorSrc=s2MirrorSrc, mirrorDst=s2MirrorDst))

def _handle_pytap (event):
    # packet is an instance of class 'pox.lib.packet.ethernet.ethernet'
    packet = event.parsed
    #print packet.dst, packet.src
    #each switch is a separate conneciton object
    for conn in core.openflow.connections:
        print "Switch %s with DPID %s is connected" % (conn, conn.dpid)
    # push static flows from agg switche ports to packet inspection host
    print "***Pushing static flow on switch " + str(core.openflow.getConnection(3).dpid)
    for port in range(1,11):
        core.openflow.getConnection(3).send(_PortFlowMod(port,11,timeout, [], []))
    # simulation s1 traffic flows
    _simulateTraffic(s1PortPairs, 1)
    # simulation s2 traffic flows
    _simulateTraffic(s2PortPairs, 2)

# function that is invoked upon load to ensure that listeners are
# registered appropriately.
def launch ():
    core.openflow.addListenerByName("PacketIn", _handle_pytap)
    log.info("PyTapDEMON is running.")
As you can see from the code, I have moved the mirror port source and destination into a separate file so they can be changed independent of the code.


mininet@mininet-vm:~/pox/ext$ cat mirrorPorts.txt
s1MirrorSrc,1
s1MirrorDst,6
s2MirrorSrc,2
s2MirrorDst,7
mininet@mininet-vm:~/pox/ext$


Here is the Mininet code:


#!/usr/bin/env python
"""
author: Eric Chou (@ericchou)
1. 2x port Filter Switches with 5 hosts and 5 trunk ports.
2. 1x port Aggregation Switch with 10 trunk ports and Deep inspection host
"""
from mininet.net import Mininet
from mininet.node import OVSSwitch
from mininet.cli import CLI
from mininet.log import setLogLevel
def PyTapDEMON():
    net = Mininet( switch=OVSSwitch, build=False)
    print "** Creating controllers"
    c1 = net.addController('c1', port=6633)
    print "*** Creating switches"
    s1 = net.addSwitch('s1')
    s2 = net.addSwitch('s2')
    s3 = net.addSwitch('s3')
    print "*** Creating hosts"
    host1 = [ net.addHost('h%d' % n) for n in range(1,6)]
    host2 = [ net.addHost('h%d' % n) for n in range(6,11)]
    host3 = net.addHost('h11')
    print "*** Creating links"
    for h in host1:
        net.addLink (s1, h)
    for h in host2:
        net.addLink (s2, h)
    for i in range(1,6):
        net.addLink(s1, s3)
    for i in range(1,6):
        net.addLink(s2, s3)
    net.addLink(host3, s3)
    print "*** Starting network"
    net.build()
    s1.start ( [ c1 ])
    s2.start ( [ c1 ])
    s3.start ( [ c1 ])
    print "*** Running CLI"
    CLI( net )

if __name__ == '__main__':
    setLogLevel('info')
    PyTapDEMON()

Moment of truth:

1. Launch mininet topology on Terminal 1:

mininet@mininet-vm:~/PyTapDEMON/MininetTopology$ sudo mn -c

mininet@mininet-vm:~/PyTapDEMON/MininetTopology$ sudo ./PyTapDEMON_topo.py
** Creating controllers
*** Creating switches
*** Creating hosts
*** Creating links
*** Starting network
*** Configuring hosts
h1 h2 h3 h4 h5 h6 h7 h8 h9 h10 h11
*** Running CLI
*** Starting CLI:
mininet> net
c1
s1 lo:  s1-eth1:h1-eth0 s1-eth2:h2-eth0 s1-eth3:h3-eth0 s1-eth4:h4-eth0 s1-eth5:h5-eth0 s1-eth6:s3-eth1 s1-eth7:s3-eth2 s1-eth8:s3-eth3 s1-eth9:s3-eth4 s1-eth10:s3-eth5
s2 lo:  s2-eth1:h6-eth0 s2-eth2:h7-eth0 s2-eth3:h8-eth0 s2-eth4:h9-eth0 s2-eth5:h10-eth0 s2-eth6:s3-eth6 s2-eth7:s3-eth7 s2-eth8:s3-eth8 s2-eth9:s3-eth9 s2-eth10:s3-eth10
s3 lo:  s3-eth1:s1-eth6 s3-eth2:s1-eth7 s3-eth3:s1-eth8 s3-eth4:s1-eth9 s3-eth5:s1-eth10 s3-eth6:s2-eth6 s3-eth7:s2-eth7 s3-eth8:s2-eth8 s3-eth9:s2-eth9 s3-eth10:s2-eth10 s3-eth11:h11-eth0
h1 h1-eth0:s1-eth1
h2 h2-eth0:s1-eth2
h3 h3-eth0:s1-eth3
h4 h4-eth0:s1-eth4
h5 h5-eth0:s1-eth5
h6 h6-eth0:s2-eth1
h7 h7-eth0:s2-eth2
h8 h8-eth0:s2-eth3
h9 h9-eth0:s2-eth4
h10 h10-eth0:s2-eth5
h11 h11-eth0:s3-eth11
mininet>

2. Launch POX and my component on Terminal 2: 

mininet@mininet-vm:~/pox$ ./pox.py log.level --DEBUG echou_pytapFinal py
POX 0.1.0 (betta) / Copyright 2011-2013 James McCauley, et al.
INFO:echou_pytapFinal:PyTapDEMON is running.
DEBUG:core:POX 0.1.0 (betta) going up...
DEBUG:core:Running on CPython (2.7.3/Sep 26 2012 21:51:14)
DEBUG:core:Platform is Linux-3.5.0-17-generic-x86_64-with-Ubuntu-12.10-quantal
INFO:core:POX 0.1.0 (betta) is up.
This program comes with ABSOLUTELY NO WARRANTY.  This program is free software,
and you are welcome to redistribute it under certain conditions.
Type 'help(pox.license)' for details.
DEBUG:openflow.of_01:Listening on 0.0.0.0:6633
Ready.
POX> 
INFO:openflow.of_01:[00-00-00-00-00-02 1] connected
INFO:openflow.of_01:[00-00-00-00-00-03 3] connected
INFO:openflow.of_01:[00-00-00-00-00-01 2] connected

POX> 

3. Launch Wireshark and starts to capture on s3-eth11. 
4. Launch Xterm for h11 and starts tcpdump: 















5. Now we need an event to trigger all the flow pushes: 

mininet> h1 ping -c3 h2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_req=1 ttl=64 time=997 ms
64 bytes from 10.0.0.2: icmp_req=2 ttl=64 time=0.119 ms
64 bytes from 10.0.0.2: icmp_req=3 ttl=64 time=0.115 ms

--- 10.0.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
rtt min/avg/max/mdev = 0.115/332.493/997.247/470.052 ms
mininet>

6. On the POX Terminal all the print messages will be shown: 

POX> 
***Pushing static flow on switch 3
Push Flow from Source Port 1 to Destion Port 11 with 120 seconds timeout
Push Flow from Source Port 2 to Destion Port 11 with 120 seconds timeout
Push Flow from Source Port 3 to Destion Port 11 with 120 seconds timeout
Push Flow from Source Port 4 to Destion Port 11 with 120 seconds timeout
Push Flow from Source Port 5 to Destion Port 11 with 120 seconds timeout
Push Flow from Source Port 6 to Destion Port 11 with 120 seconds timeout
Push Flow from Source Port 7 to Destion Port 11 with 120 seconds timeout
Push Flow from Source Port 8 to Destion Port 11 with 120 seconds timeout
Push Flow from Source Port 9 to Destion Port 11 with 120 seconds timeout
Push Flow from Source Port 10 to Destion Port 11 with 120 seconds timeout
***Simulating Traffic flow for switch 1
Pushing flow from 1 to 2
Push Flow from Source Port 1 to Destion Port 2 with 120 seconds timeout
Push Flow from Source Port 2 to Destion Port 1 with 120 seconds timeout
Pushing flow from 3 to 4
Push Flow from Source Port 3 to Destion Port 4 with 120 seconds timeout
Push Flow from Source Port 4 to Destion Port 3 with 120 seconds timeout
***Simulating Traffic flow for switch 2
Pushing flow from 1 to 2
Push Flow from Source Port 1 to Destion Port 2 with 120 seconds timeout
Push Flow from Source Port 2 to Destion Port 1 with 120 seconds timeout

POX> 



7. Yay! I can see the h1-h2 traffic on h11:

root@mininet-vm:~/PyTapDEMON/MininetTopology# sudo tcpdump -i h11-eth0 -vvv
tcpdump: listening on h11-eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
18:08:24.151762 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.1 > 10.0.0.2: ICMP echo request, id 2588, seq 1, length 64
18:08:24.151836 IP (tos 0x0, ttl 64, id 16487, offset 0, flags [none], proto ICMP (1), length 84)
    10.0.0.2 > 10.0.0.1: ICMP echo reply, id 2588, seq 1, length 64
18:08:25.153466 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.1 > 10.0.0.2: ICMP echo request, id 2588, seq 2, length 64
18:08:25.153478 IP (tos 0x0, ttl 64, id 16488, offset 0, flags [none], proto ICMP (1), length 84)
    10.0.0.2 > 10.0.0.1: ICMP echo reply, id 2588, seq 2, length 64
18:08:26.154104 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.1 > 10.0.0.2: ICMP echo request, id 2588, seq 3, length 64
18:08:26.154204 IP (tos 0x0, ttl 64, id 16489, offset 0, flags [none], proto ICMP (1), length 84)
    10.0.0.2 > 10.0.0.1: ICMP echo reply, id 2588, seq 3, length 64

8. .. and Wireshark:













9. Repeat and rinse for h6-h7 on s2:


mininet> h6 ping -c3 h7
PING 10.0.0.7 (10.0.0.7) 56(84) bytes of data.
64 bytes from 10.0.0.7: icmp_req=1 ttl=64 time=998 ms
64 bytes from 10.0.0.7: icmp_req=2 ttl=64 time=0.127 ms
64 bytes from 10.0.0.7: icmp_req=3 ttl=64 time=0.069 ms

--- 10.0.0.7 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
rtt min/avg/max/mdev = 0.069/332.780/998.145/470.484 ms
mininet>












10. Repeat and rinse for h3-h4 on s1. Ping succeeds but I dont see the mirrored traffic on h11 nor Wireshark:


mininet> h3 ping -c3 h4
PING 10.0.0.4 (10.0.0.4) 56(84) bytes of data.
64 bytes from 10.0.0.4: icmp_req=1 ttl=64 time=0.242 ms
64 bytes from 10.0.0.4: icmp_req=2 ttl=64 time=0.111 ms
64 bytes from 10.0.0.4: icmp_req=3 ttl=64 time=0.109 ms

--- 10.0.0.4 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.109/0.154/0.242/0.062 ms
mininet>

The next step for me is to add a web frontend for users to indicate the desired mirrored port.

I will do a separate post to dissect the POX component code.

Leave me comments for any feedbacks! :)





Friday, April 12, 2013

PyTapDEMon - Part 1 Introduction


PyTapDEMON is a project that I am working on for a Python class at UW Extension. It is a Distributed Ethernet MOnitoring (DEMon) using OpenFlow-enabled switch with POX as the controller. This is not a new concept, Rich Groves presented this project at Sharkfest. There a number of reports and commercial offerings on the idea. 
I believe this has a lot of potential in offering a cheap, distributed system that is capable of monitoring datacetner-scale networks with extensible capabilities. 

Here is a simple overview: 

Here is the GitHub link for the project: https://github.com/ericchou-python/PyTapDEMON

Here are some links for the tools I will be using to develop this project:
Hardware used for initial testing
Here is a list of the hardware that I will be using to test the setup: 

1. A Linksys WRT54GL flashed with OpenWRT OpenFlow image, http://www.openflow.org/wp/openwrt/.

2. Raspberry Pi for physical hosts, although virtual host will do also.

3. A dump hub to simulate tap ports.



Will update when there are enough progress to warrant an update. Leave me comments if you have any suggestions or questions. :)

Cheers.