Building the learning switch

Now, we change and enhance the behavior of our OpenFlow switch to an intelligent (learning) Ethernet switch. Let's review the operation of a learning switch. When a packet arrives on any port of the learning switch, it can learn that the sending host is located on the port on which the packet has arrived. So, it can simply maintain a lookup table that associates the MAC address of the host with the port on which they are connected to the switch. So the switch stores the source MAC address of the packet along with the incoming port in its lookup table. Upon receiving a packet, the switch looks up the destination MAC address of the packet; in case of a match, the switch figures out the output port and instead of flooding the packet, it simply sends the packet to its correct destination host (through the looked-up port). In the OpenFlow paradigm, each incoming packet basically generates a new rule in the flow table of the OpenFlow switch. In order to observe this behavior, we restart our experimental network with the l2_learning switch (l2_learning.py) functionality. The learning switch algorithm, which is implemented in the l2_learning.py script consists of the following steps:

  1. The first step is to use the source MAC address of the packet and the switch port to update the switching lookup table (the address/port table), maintained inside the controller as a hash table.
  2. The second step is to drop certain types of the packets (packets with an Ethertype of LLDP or packets with a bridge-filtered destination address).
  3. In the third step, the controller checks if the destination address is a multicast address. In that case, the packet is simply flooded.
  4. If the destination MAC address of the packet is not already inside the address/port table (the hash table, which is maintained inside the controller), then the controller instructs the OpenFlow switch to flood the packet on all ports (except the incoming one).
  5. If the output port is the same as the input port, the controller instructs the switch to drop the packet to avoid loops.
  6. Otherwise, the controller sends a flow table entry modification command (flow mod) to the switch, using the source MAC address and corresponding port, which instructs the switch that future packets addressed to that specific MAC address will be sent to the associated output port (rather than flooding).

In order to see the learning switch behavior of our setup, we first clean up the existing setup and start our experimental network again:

mininet@mininet-vm:~$ sudo mn -c
... (screen messages are removed)
mininet@mininet-vm:~$ sudo mn --topo single,3 --mac --switch ovsk 
--controller remote

Now using another SSH terminal, we connect to our Mininet VM and start the POX controller, which executes the Ethernet L2 (layer 2) learning switch algorithm:

mininet@mininet-vm:~/pox$ ./pox.py forwarding.l2_learning  

Upon startup of the POX controller, as in the Ethernet hub case, we can observe that the OpenFlow switch will get connected to the controller. Now if we go back to the Mininet console and issue the pingall command, we will see that all hosts are reachable:

mininet> pingall  

The following will be the output:

*** Ping: testing ping reachability
h1 -> h2 h3
h2 -> h1 h3
h3 -> h1 h2
*** Results: 0% dropped (0/6 lost)  

So far, the behavior is like in the Ethernet hub case. However, if we dump the flow table of the switch (using the dpctl program), we can observe a bunch of different flow table entries. In fact, the flow table entries show different destination MAC addresses along with the associated output ports that incoming packets addressed to that MAC should be forwarded to. For instance, packets addressed to 00:00:00:00:00:03 will be forwarded to the output port number 3.

   mininet@mininet-vm:~$ dpctl dump-flows tcp:127.0.0.1:6634  

The following will be the output:

stats_reply (xid=0xababe6ce): flags=none type=1(flow)
cookie=0, duration_sec=7s, duration_nsec=912000000s, table_id=0, 
priority=32768, n_packets=1, n_bytes=98, 
    idle_timeout=10,hard_timeout=30,icmp,dl_vlan=0xffff,dl_vlan_pcp=0x00,
    dl_src=00:00:00:00:00:02,dl_dst=00:00:00:00:00:03,nw_src=10.0.0.2,nw_
dst=10.0.0.3,nw_tos=0x00,icmp_type=0,icmp_code=0,actions=output:3
...
...
(more entries are not shown)  

Let's take a look at the Python code (l2_learning.py) that implements the Ethernet learning switch functionality. The launch method as usual registers the l2_learning object with the core POX controller. Upon being instantiated, the l2_learning object adds a listener to ensure that it can handle connection-up events from OpenFlow switches that connect to this controller. This object then instantiates the learning switch object and passes the connection event to that object (see the highlighted code in the following snippet):

... 
... 
class l2_learning (EventMixin): 
  """ 
  Waits for OpenFlow switches to connect and makes them learning  
switches. 
  """ 
  def __init__ (self, transparent): 
    self.listenTo(core.openflow) 
    self.transparent = transparent 
 
  def _handle_ConnectionUp (self, event): 
    log.debug("Connection %s" % (event.connection,)) 
    LearningSwitch(event.connection, self.transparent) 
  
def launch (transparent=False): 
  """ 
  Starts an L2 learning switch. 
  """ 
  core.registerNew(l2_learning, str_to_bool(transparent)) 

Going through the learning switch object, we can observe that upon instantiation of the address/port, a hash table is created (self.macToPort= {}) a listener is registered for the packet-in messages (connection.addListeners(self)) and then we can see the packet-in handler method (_handle_PacketIn (self, event)). The learning switch algorithm portion of the code is as follows:

    self.macToPort[packet.src] = event.port  
    if not self.transparent: 
      if packet.type == packet.LLDP_TYPE or  
      packet.dst.isBridgeFiltered():  
        drop() 
        return 
      if packet.dst.isMulticast(): 
        flood()  
      else: 
        if packet.dst not in self.macToPort:  
          log.debug("Port for %s unknown -- flooding" %  
          (packet.dst,)) 
            flood()  
        else: 
        port = self.macToPort[packet.dst] 
      if port == event.port:  
        log.warning("Same port for packet from %s -> %s on %s.   
        Drop." % 
      (packet.src, packet.dst, port), dpidToStr(event.dpid)) 
      drop(10) 
      return 
      log.debug("installing flow for %s.%i -> %s.%i" % 
      (packet.src, event.port, packet.dst, port)) 
      msg = of.ofp_flow_mod() 
      msg.match = of.ofp_match.from_packet(packet) 
      msg.idle_timeout = 10 
      msg.hard_timeout = 30 
      msg.actions.append(of.ofp_action_output(port = port)) 
      msg.buffer_id = event.ofp.buffer_id  
    self.connection.send(msg) 

The first step is to update the address/port hash table (self.macToPort[packet.src] = event.port). This will associate the MAC address of the sender to the switch port on which the packet has been received by the switch. Certain types of packet are dropped. Multicast traffic is properly flooded. If the destination of the packet is not available in the address/port hash table, the packet is also flooded. If the input and output ports are the same, then the packet will be dropped to avoid a loop (if port == event.port:). Finally, a proper flow table entry gets installed inside the flow table of the OpenFlow switch. In summary, the l2_learning.py program implements the required logic and algorithm to change the behavior of our OpenFlow switch to an Ethernet learning switch one. In the next section, we will take one more step toward changing the learning switch to a simple firewall.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset