Advertisement
Guest User

pox_l2_recurring

a guest
Jun 22nd, 2016
446
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.34 KB | None | 0 0
  1. # Copyright 2011-2012 James McCauley
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at:
  6. #
  7. #     http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14.  
  15. """
  16. An L2 learning switch.
  17.  
  18. It is derived from one written live for an SDN crash course.
  19. It is somwhat similar to NOX's pyswitch in that it installs
  20. exact-match rules for each flow.
  21. """
  22.  
  23. from pox.core import core
  24. import pox.openflow.libopenflow_01 as of
  25. from pox.lib.util import dpid_to_str, str_to_dpid
  26. from pox.lib.util import str_to_bool
  27. from pox.lib.recoco import Timer
  28. import time
  29. import sys
  30. log = core.getLogger()
  31.  
  32. # We don't want to flood immediately when a switch connects.
  33. # Can be overriden on commandline.
  34. _flood_delay = 0
  35. # sys.setrecursionlimit(10000)
  36. class LearningSwitch (object):
  37.   """
  38.  The learning switch "brain" associated with a single OpenFlow switch.
  39.  
  40.  When we see a packet, we'd like to output it on a port which will
  41.  eventually lead to the destination.  To accomplish this, we build a
  42.  table that maps addresses to ports.
  43.  
  44.  We populate the table by observing traffic.  When we see a packet
  45.  from some source coming from some port, we know that source is out
  46.  that port.
  47.  
  48.  When we want to forward traffic, we look up the desintation in our
  49.  table.  If we don't know the port, we simply send the message out
  50.  all ports except the one it came in on.  (In the presence of loops,
  51.  this is bad!).
  52.  
  53.  In short, our algorithm looks like this:
  54.  
  55.  For each packet from the switch:
  56.  1) Use source address and switch port to update address/port table
  57.  2) Is transparent = False and either Ethertype is LLDP or the packet's
  58.     destination address is a Bridge Filtered address?
  59.     Yes:
  60.        2a) Drop packet -- don't forward link-local traffic (LLDP, 802.1x)
  61.            DONE
  62.  3) Is destination multicast?
  63.     Yes:
  64.        3a) Flood the packet
  65.            DONE
  66.  4) Port for destination address in our address/port table?
  67.     No:
  68.        4a) Flood the packet
  69.            DONE
  70.  5) Is output port the same as input port?
  71.     Yes:
  72.        5a) Drop packet and similar ones for a while
  73.  6) Install flow table entry in the switch so that this
  74.     flow goes out the appopriate port
  75.     6a) Send the packet out appropriate port
  76.  """
  77.   flowlist = []
  78.   def __init__ (self, connection, transparent):
  79.     # Switch we'll be adding L2 learning switch capabilities to
  80.     self.connection = connection
  81.     self.transparent = transparent
  82.  
  83.     # Our table
  84.     self.macToPort = {}
  85.  
  86.     # We want to hear PacketIn messages, so we listen
  87.     # to the connection
  88.     connection.addListeners(self)
  89.  
  90.     # We just use this to know when to log a helpful message
  91.     self.hold_down_expired = _flood_delay == 0
  92.  
  93.     #log.debug("Initializing LearningSwitch, transparent=%s",
  94.     #          str(self.transparent))
  95.     LearningSwitch.flowlist = []
  96.     self._timer_func(2)
  97.  
  98.  
  99.   def _timer_func(self, dt):
  100.     """
  101.    recurring function, calls a function every dt seconds
  102.    Returns:
  103.            nada
  104.    """
  105.     Timer(dt, self.print_list, recurring=True)
  106.  
  107.   def print_list(self):
  108.     """
  109.    just print the list
  110.    Returns:
  111.            nada
  112.    """
  113.     if LearningSwitch.flowlist:
  114.       print LearningSwitch.flowlist
  115.  
  116.   def _handle_PacketIn (self, event):
  117.     """
  118.    Handle packet in messages from the switch to implement above algorithm.
  119.    """
  120.  
  121.     packet = event.parsed
  122.     flag = 0
  123.     if packet.type == packet.IP_TYPE:
  124.       ip_packet = packet.payload
  125.       # -------------TCP-----------------
  126.       if (packet.find('tcp')):
  127.         tcp_packet = ip_packet.payload
  128.  
  129.         for thisflow in LearningSwitch.flowlist:
  130.  
  131.           if (thisflow[0] == packet.next.srcip and thisflow[1] == packet.next.dstip and thisflow[
  132.             2] == tcp_packet.srcport and thisflow[3] == tcp_packet.dstport, thisflow[4] == "TCP"):
  133.             thisflow[5] += 1
  134.             flag = 1
  135.         if (flag != 1):
  136.           LearningSwitch.flowlist.append(
  137.             [packet.next.srcip, packet.next.dstip, tcp_packet.srcport, tcp_packet.dstport, "TCP", 1])
  138.  
  139.           # ---------- ICMP --------------------
  140.       elif (packet.find('icmp')):
  141.         icmp_packet = ip_packet.payload
  142.         for thisflow in LearningSwitch.flowlist:
  143.  
  144.           if (thisflow[0] == ip_packet.srcip and thisflow[1] == ip_packet.dstip and thisflow[4] == "ICMP"):
  145.             thisflow[5] += 1
  146.             flag = 1
  147.         if (flag != 1):
  148.           LearningSwitch.flowlist.append([packet.next.srcip, packet.next.dstip, "--", "--", "ICMP", 1])
  149.           # ------------ IPV4 ---------------------
  150.       elif (packet.find('ipv4')):
  151.  
  152.         for thisflow in LearningSwitch.flowlist:
  153.  
  154.           if (thisflow[0] == packet.next.srcip and thisflow[1] == packet.next.dstip and thisflow[4] == "IP"):
  155.             thisflow[5] += 1
  156.             flag = 1
  157.         if (flag != 1):
  158.           LearningSwitch.flowlist.append([packet.next.srcip, packet.next.dstip, "--", "--", "IP", 1])
  159.  
  160.     # print LearningSwitch.flowlist
  161.  
  162.     # ---- The rest of the code is from L2_learning component
  163.     def flood (message = None):
  164.       """ Floods the packet """
  165.       msg = of.ofp_packet_out()
  166.       if time.time() - self.connection.connect_time >= _flood_delay:
  167.         # Only flood if we've been connected for a little while...
  168.  
  169.         if self.hold_down_expired is False:
  170.           # Oh yes it is!
  171.           self.hold_down_expired = True
  172.           log.info("%s: Flood hold-down expired -- flooding",
  173.               dpid_to_str(event.dpid))
  174.  
  175.         if message is not None: log.debug(message)
  176.         #log.debug("%i: flood %s -> %s", event.dpid,packet.src,packet.dst)
  177.         # OFPP_FLOOD is optional; on some switches you may need to change
  178.         # this to OFPP_ALL.
  179.         msg.actions.append(of.ofp_action_output(port = of.OFPP_FLOOD))
  180.       else:
  181.         pass
  182.         #log.info("Holding down flood for %s", dpid_to_str(event.dpid))
  183.       msg.data = event.ofp
  184.       msg.in_port = event.port
  185.       self.connection.send(msg)
  186.  
  187.     def drop (duration = None):
  188.       """
  189.      Drops this packet and optionally installs a flow to continue
  190.      dropping similar ones for a while
  191.      """
  192.       if duration is not None:
  193.         if not isinstance(duration, tuple):
  194.           duration = (duration,duration)
  195.         msg = of.ofp_flow_mod()
  196.         msg.match = of.ofp_match.from_packet(packet)
  197.         msg.idle_timeout = duration[0]
  198.         msg.hard_timeout = duration[1]
  199.         msg.buffer_id = event.ofp.buffer_id
  200.         self.connection.send(msg)
  201.       elif event.ofp.buffer_id is not None:
  202.         msg = of.ofp_packet_out()
  203.         msg.buffer_id = event.ofp.buffer_id
  204.         msg.in_port = event.port
  205.         self.connection.send(msg)
  206.  
  207.     self.macToPort[packet.src] = event.port # 1
  208.  
  209.     if not self.transparent: # 2
  210.       if packet.type == packet.LLDP_TYPE or packet.dst.isBridgeFiltered():
  211.         drop() # 2a
  212.         return
  213.  
  214.     if packet.dst.is_multicast:
  215.       flood() # 3a
  216.     else:
  217.       if packet.dst not in self.macToPort: # 4
  218.         flood("Port for %s unknown -- flooding" % (packet.dst,)) # 4a
  219.       else:
  220.         port = self.macToPort[packet.dst]
  221.         if port == event.port: # 5
  222.           # 5a
  223.           log.warning("Same port for packet from %s -> %s on %s.%s.  Drop."
  224.               % (packet.src, packet.dst, dpid_to_str(event.dpid), port))
  225.           drop(10)
  226.           return
  227.         # 6
  228.         log.debug("installing flow for %s.%i -> %s.%i" %
  229.                   (packet.src, event.port, packet.dst, port))
  230.         msg = of.ofp_flow_mod()
  231.         msg.match = of.ofp_match.from_packet(packet, event.port)
  232.         msg.idle_timeout = 10
  233.         msg.hard_timeout = 30
  234.         msg.actions.append(of.ofp_action_output(port = port))
  235.         msg.data = event.ofp # 6a
  236.         self.connection.send(msg)
  237.  
  238.  
  239. class l2_learning (object):
  240.   """
  241.  Waits for OpenFlow switches to connect and makes them learning switches.
  242.  """
  243.   def __init__ (self, transparent, ignore = None):
  244.     """
  245.    Initialize
  246.  
  247.    See LearningSwitch for meaning of 'transparent'
  248.    'ignore' is an optional list/set of DPIDs to ignore
  249.    """
  250.     core.openflow.addListeners(self)
  251.     self.transparent = transparent
  252.     self.ignore = set(ignore) if ignore else ()
  253.  
  254.   def _handle_ConnectionUp (self, event):
  255.     if event.dpid in self.ignore:
  256.       log.debug("Ignoring connection %s" % (event.connection,))
  257.       return
  258.     log.debug("Connection %s" % (event.connection,))
  259.     LearningSwitch(event.connection, self.transparent)
  260.  
  261.  
  262. def launch (transparent=False, hold_down=_flood_delay, ignore = None):
  263.   """
  264.  Starts an L2 learning switch.
  265.  """
  266.   try:
  267.     global _flood_delay
  268.     _flood_delay = int(str(hold_down), 10)
  269.     assert _flood_delay >= 0
  270.   except:
  271.     raise RuntimeError("Expected hold-down to be a number")
  272.  
  273.   if ignore:
  274.     ignore = ignore.replace(',', ' ').split()
  275.     ignore = set(str_to_dpid(dpid) for dpid in ignore)
  276.  
  277.   core.registerNew(l2_learning, str_to_bool(transparent), ignore)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement