python - ryu handling packets using a switch after flow was added to switch
Question: I'm using a Ryu open flow controller switch written in python to monitor
packets in my virtual mininet.I have 3 hosts and I'm blocking transportation
from host2 to host3 and from host3 to host2. Other packets are added to the
switch flow table. My problem is that after a flow is added, if their is a
packet between 2 hosts that have a rule in the flow table of the switch, my
event doesn't trigger. For example, if the switch saw a packet from host1 to
host2 it is legal so the flow is added to the table, but if another packet
from host1 to host2 is sent it won't go through the method again. I looked in
Ryu guides but didn't find anyhting regarding the case when a flow was already
added to the switch flow table. How can I catch the packets?

Thanks in advance.

Here's my code:

    
    
    import logging
    import struct
    
    from ryu.base import app_manager
    from ryu.controller import mac_to_port
    from ryu.controller import ofp_event
    from ryu.controller.handler import MAIN_DISPATCHER
    from ryu.controller.handler import set_ev_cls
    from ryu.ofproto import ofproto_v1_0
    from ryu.lib.mac import haddr_to_str
    
    
    
    
    class SimpleSwitch(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]
    counterTraffic=0    
    def __init__(self, *args, **kwargs):
        super(SimpleSwitch, self).__init__(*args, **kwargs)
        self.mac_to_port = {}
    
    def add_flow(self, datapath, in_port, dst, actions):
        ofproto = datapath.ofproto
    
        wildcards = ofproto_v1_0.OFPFW_ALL
        wildcards &= ~ofproto_v1_0.OFPFW_IN_PORT
        wildcards &= ~ofproto_v1_0.OFPFW_DL_DST
    
        match = datapath.ofproto_parser.OFPMatch(
            wildcards, in_port, 0, dst,
            0, 0, 0, 0, 0, 0, 0, 0, 0)
    
        mod = datapath.ofproto_parser.OFPFlowMod(
            datapath=datapath, match=match, cookie=0,
            command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0,
            priority=ofproto.OFP_DEFAULT_PRIORITY,
            flags=ofproto.OFPFF_SEND_FLOW_REM, actions=actions)
        datapath.send_msg(mod)
    
    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)        
    def _packet_in_handler(self, ev):
    
    print("Im in main function")
        msg = ev.msg
        datapath = msg.datapath
        ofproto = datapath.ofproto
    
        dst, src, _eth_type = struct.unpack_from('!6s6sH', buffer(msg.data), 0)
    
        dpid = datapath.id
        self.mac_to_port.setdefault(dpid, {})
    
        self.logger.info("packet in %s %s %s %s",
                         dpid, haddr_to_str(src), haddr_to_str(dst),
                         msg.in_port)
    
    if (haddr_to_str(dst) == "00:00:00:00:00:01"):
        print "dst"
        self.counterTraffic +=1
    
    if not ((haddr_to_str(src) == "00:00:00:00:00:02" and  haddr_to_str(dst) =="00:00:00:00:00:03")or (haddr_to_str(src) == "00:00:00:00:00:03" and  haddr_to_str(dst) =="00:00:00:00:00:02")):
            # learn a mac address to avoid FLOOD next time.
        print("after condition")
            self.mac_to_port[dpid][src] = msg.in_port
    
            if dst in self.mac_to_port[dpid]:
                out_port = self.mac_to_port[dpid][dst]
            else:
                    out_port = ofproto.OFPP_FLOOD
    
                actions = [datapath.ofproto_parser.OFPActionOutput(out_port)]
    
             # install a flow to avoid packet_in next time
            if out_port != ofproto.OFPP_FLOOD:
                    self.add_flow(datapath, msg.in_port, dst, actions)
    
            out = datapath.ofproto_parser.OFPPacketOut(
            datapath=datapath, buffer_id=msg.buffer_id, in_port=msg.in_port,
                    actions=actions)
    
            datapath.send_msg(out)
        if (haddr_to_str(src) == "00:00:00:00:00:01"):
            print "src"
                    self.counterTraffic +=1
        print(self.counterTraffic)
    
    
    @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER)
    def _port_status_handler(self, ev):
        msg = ev.msg
        reason = msg.reason
        port_no = msg.desc.port_no
    
        ofproto = msg.datapath.ofproto
        if reason == ofproto.OFPPR_ADD:
            self.logger.info("port added %s", port_no)
        elif reason == ofproto.OFPPR_DELETE:
            self.logger.info("port deleted %s", port_no)
        elif reason == ofproto.OFPPR_MODIFY:
            self.logger.info("port modified %s", port_no)
        else:
            self.logger.info("Illeagal port state %s %s", port_no, reason)
    


Answer: > I tried to print haddr_to_str(src), haddr_to_str(dst) and got
> 00:00:00:00:00:03 and ff:ff:ff:ff:ff:ff which is not what I expected. I
> wanted to get 2 as source and 3 as dest.

The short story is that you're decoding the destination mac address
correctly... However, IP must ARP to resolve mac addresses, which is why you
see `ff:ff:ff:ff:ff:ff`... those are just the ARP frames in the [ryu
controller](https://github.com/osrg/ryu).

I built a complete controller which, decodes up to the IPv4 layer below...

## Updated ryu switch packet decoder

You've been decoding raw `structs`, but it's much easier to use the [ryu
Packet library](http://ryu.readthedocs.org/en/latest/library_packet.html)
instead of unpacking a raw `struct` of the packet. This is my very quick
replacement of `_packet_in_handler()`, which just prints out the source and
destination mac addresses, as well as the upper layer protocols...

    
    
    from ryu.lib.packet import packet, ethernet, arp, ipv4
    import array
    
    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def _packet_in_handler(self, ev):
    
        ### Mike Pennington's logging modifications
        ## Set up to receive the ethernet src / dst addresses
        pkt = packet.Packet(array.array('B', ev.msg.data))
        eth_pkt = pkt.get_protocol(ethernet.ethernet)
        arp_pkt = pkt.get_protocol(arp.arp)
        ip4_pkt = pkt.get_protocol(ipv4.ipv4)
        if arp_pkt:
            pak = arp_pkt
        elif ip4_pkt:
            pak = ip4_pkt
        else:
            pak = eth_pkt
        self.logger.info('  _packet_in_handler: src_mac -> %s' % eth_pkt.src)
        self.logger.info('  _packet_in_handler: dst_mac -> %s' % eth_pkt.dst)
        self.logger.info('  _packet_in_handler: %s' % pak)
        self.logger.info('  ------')
        src = eth_pkt.src  # Set up the src and dst variables so you can use them
        dst = eth_pkt.dst
        ## Mike Pennington's modifications end here
    
    
        msg = ev.msg
        datapath = msg.datapath
        ofproto = datapath.ofproto
    
        dpid = datapath.id
        self.mac_to_port.setdefault(dpid, {})
    
        # learn a mac address to avoid FLOOD next time.
        self.mac_to_port[dpid][src] = msg.in_port
    
        if dst in self.mac_to_port[dpid]:
            out_port = self.mac_to_port[dpid][dst]
        else:
            out_port = ofproto.OFPP_FLOOD
    
        actions = [datapath.ofproto_parser.OFPActionOutput(out_port)]
    
        # install a flow to avoid packet_in next time
        if out_port != ofproto.OFPP_FLOOD:
            self.add_flow(datapath, msg.in_port, dst, actions)
    
        out = datapath.ofproto_parser.OFPPacketOut(
            datapath=datapath, buffer_id=msg.buffer_id, in_port=msg.in_port,
            actions=actions)
        datapath.send_msg(out)
    

Now, whenever an ethernet packet is sent, you'll see this inside your mininet
session...

    
    
      _packet_in_handler: src_mac -> 00:00:00:00:00:03
      _packet_in_handler: dst_mac -> 33:33:00:00:00:02
      _packet_in_handler: ethernet(dst='33:33:00:00:00:02',ethertype=34525,src='00:00:00:00:00:03')
      ------
    

ARP packets look like this...

    
    
      _packet_in_handler: src_mac -> 00:00:00:00:00:01
      _packet_in_handler: dst_mac -> ff:ff:ff:ff:ff:ff
      _packet_in_handler: arp(dst_ip='10.0.0.2',dst_mac='00:00:00:00:00:00',hlen=6,hwtype=1,opcode=1,plen=4,proto=2048,src_ip='10.0.0.1',src_mac='00:00:00:00:00:01')
      ------
    

## Demo

Assume I saved the modified code above (including other parts of your source)
as `ne_question.py`.

  * First I set up the [ryu controller](https://github.com/osrg/ryu) in [mininet](http://mininet.org/):

    
    
    root@mininet-vm:/home/mininet# ryu-manager ne_question.py &
                     [1] 14073
    loading app ne_question.py
    loading app ryu.controller.ofp_handler
    instantiating app ryu.controller.ofp_handler of OFPHandler
    instantiating app ne_question.py of SimpleSwitch
    
    root@mininet-vm:/home/mininet#
    

  * Next I build the switch topology, as you mentioned in your comment

    
    
    root@mininet-vm:/home/mininet# mn --topo single,3 --mac --switch ovsk --controller remote
    *** Creating network
    *** Adding controller
    *** Adding hosts:
    h1 h2 h3
    *** Adding switches:
    s1
    *** Adding links:
    (h1, s1) (h2, s1) (h3, s1)
    *** Configuring hosts
    h1 h2 h3
    *** Starting controller
    *** Starting 1 switches
    s1
    *** Starting CLI:
    mininet>   _packet_in_handler: src_mac -> 00:00:00:00:00:02
      _packet_in_handler: dst_mac -> 33:33:00:00:00:02
      _packet_in_handler: ethernet(dst='33:33:00:00:00:02',ethertype=34525,src='00:00:00:00:00:02')
      ------
      _packet_in_handler: src_mac -> 00:00:00:00:00:01
      _packet_in_handler: dst_mac -> 33:33:00:00:00:02
      _packet_in_handler: ethernet(dst='33:33:00:00:00:02',ethertype=34525,src='00:00:00:00:00:01')
      ------
      _packet_in_handler: src_mac -> 00:00:00:00:00:03
      _packet_in_handler: dst_mac -> 33:33:00:00:00:02
      _packet_in_handler: ethernet(dst='33:33:00:00:00:02',ethertype=34525,src='00:00:00:00:00:03')
      ------
    

  * Finally, I run the web server, and try to pull a page... notice that ARPs are sent to resolve the destination address of the http GET. The destination address of the ARPs are `ff:ff:ff:ff:ff:ff`. BTW, if you change the `wget` to `h2 wget h1`, everything works correctly... 

    
    
    mininet> h1 python -m SimpleHTTPServer 80 &
    mininet> h2 wget -O - h1
    --2014-03-28 04:22:25--  http://10.0.0.1/
    Connecting to 10.0.0.1:80...   _packet_in_handler: src_mac -> 00:00:00:00:00:02
      _packet_in_handler: dst_mac -> ff:ff:ff:ff:ff:ff
      _packet_in_handler: arp(dst_ip='10.0.0.1',dst_mac='00:00:00:00:00:00',hlen=6,hwtype=1,opcode=1,plen=4,proto=2048,src_ip='10.0.0.2',src_mac='00:00:00:00:00:02')
      ------
    --2014-03-28 04:00:58--  http://10.0.0.1/
    Connecting to 10.0.0.1:80...   _packet_in_handler: src_mac -> 00:00:00:00:00:02
      _packet_in_handler: dst_mac -> 00:00:00:00:00:01
      _packet_in_handler: ipv4(csum=33886,dst='10.0.0.1',flags=2,header_length=5,identification=41563,offset=0,option=None,proto=6,src='10.0.0.2',tos=0,total_length=60,ttl=64,version=4)
      ------
      _packet_in_handler: src_mac -> 00:00:00:00:00:01
      _packet_in_handler: dst_mac -> 00:00:00:00:00:02
      _packet_in_handler: ipv4(csum=9914,dst='10.0.0.2',flags=2,header_length=5,identification=0,offset=0,option=None,proto=6,src='10.0.0.1',tos=0,total_length=60,ttl=64,version=4)
      ------
    connected.
    HTTP request sent, awaiting response...   _packet_in_handler: src_mac -> 00:00:00:00:00:02
      _packet_in_handler: dst_mac -> 00:00:00:00:00:01
      _packet_in_handler: ipv4(csum=33893,dst='10.0.0.1',flags=2,header_length=5,identification=41564,offset=0,option=None,proto=6,src='10.0.0.2',tos=0,total_length=52,ttl=64,version=4)
      ------
      _packet_in_handler: src_mac -> 00:00:00:00:00:02
      _packet_in_handler: dst_mac -> 00:00:00:00:00:01
      _packet_in_handler: ipv4(csum=33784,dst='10.0.0.1',flags=2,header_length=5,identification=41565,offset=0,option=None,proto=6,src='10.0.0.2',tos=0,total_length=160,ttl=64,version=4)
      ------
      _packet_in_handler: src_mac -> 00:00:00:00:00:01
      _packet_in_handler: dst_mac -> 00:00:00:00:00:02
      _packet_in_handler: ipv4(csum=61034,dst='10.0.0.2',flags=2,header_length=5,identification=14423,offset=0,option=None,proto=6,src='10.0.0.1',tos=0,total_length=52,ttl=64,version=4)
      ------
      _packet_in_handler: src_mac -> 00:00:00:00:00:01
      _packet_in_handler: dst_mac -> 00:00:00:00:00:02
      _packet_in_handler: ipv4(csum=61016,dst='10.0.0.2',flags=2,header_length=5,identification=14424,offset=0,option=None,proto=6,src='10.0.0.1',tos=0,total_length=69,ttl=64,version=4)
      ------
      _packet_in_handler: src_mac -> 00:00:00:00:00:01
      _packet_in_handler: dst_mac -> 00:00:00:00:00:02
      _packet_in_handler: ipv4(csum=60037,dst='10.0.0.2',flags=2,header_length=5,identification=14425,offset=0,option=None,proto=6,src='10.0.0.1',tos=0,total_length=1047,ttl=64,version=4)
      ------
    200 OK
    Length: 858 [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=".bash_history">.bash_history</a>
    <li><a href=".bash_logout">.bash_logout</a>
    <li><a href=".bashrc">.bashrc</a>
    <li><a href=".cache/">.cache/</a>
    <li><a href=".gitconfig">.gitconfig</a>
    <li><a href=".profile">.profile</a>
    <li><a href=".rnd">.rnd</a>
    <li><a href=".wireshark/">.wireshark/</a>
    <li><a href="install-mininet-vm.sh">install-mininet-vm.sh</a>
    <li><a href="mininet/">mininet/</a>
    <li><a href="ne_question.py">ne_question.py</a>
    <li><a href="ne_question.pyc">ne_question.pyc</a>
    <li><a href="of-dissector/">of-dissector/</a>
    <li><a href="oflops/">oflops/</a>
    <li><a href="oftest/">oftest/</a>
    <li><a href="openflow/">openflow/</a>
    <li><a href="pox/">pox/</a>
    </ul>
    <hr>
    </body>
    </html>
    
         0K                                                       100%  161M=0s
    
    2014-03-28 04:00:58 (161 MB/s) - written to stdout [858/858]
    
      _packet_in_handler: src_mac -> 00:00:00:00:00:02
    mininet>   _packet_in_handler: dst_mac -> 00:00:00:00:00:01
      _packet_in_handler: ipv4(csum=33891,dst='10.0.0.1',flags=2,header_length=5,identification=41566,offset=0,option=None,proto=6,src='10.0.0.2',tos=0,total_length=52,ttl=64,version=4)
      ------
      _packet_in_handler: src_mac -> 00:00:00:00:00:02
      _packet_in_handler: dst_mac -> 00:00:00:00:00:01
      _packet_in_handler: ipv4(csum=33890,dst='10.0.0.1',flags=2,header_length=5,identification=41567,offset=0,option=None,proto=6,src='10.0.0.2',tos=0,total_length=52,ttl=64,version=4)
      ------
      _packet_in_handler: src_mac -> 00:00:00:00:00:02
      _packet_in_handler: dst_mac -> 00:00:00:00:00:01
      _packet_in_handler: ipv4(csum=33889,dst='10.0.0.1',flags=2,header_length=5,identification=41568,offset=0,option=None,proto=6,src='10.0.0.2',tos=0,total_length=52,ttl=64,version=4)
      ------
      _packet_in_handler: src_mac -> 00:00:00:00:00:01
      _packet_in_handler: dst_mac -> 00:00:00:00:00:02
      _packet_in_handler: ipv4(csum=9922,dst='10.0.0.2',flags=2,header_length=5,identification=0,offset=0,option=None,proto=6,src='10.0.0.1',tos=0,total_length=52,ttl=64,version=4)
      ------
    
    mininet>
    mininet>
    

