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! :)





No comments:

Post a Comment