Ejemplo n.º 1
0
  def handle_switch_up(self, ev):
    dp = ev.msg.datapath
    ofp_parser = dp.ofproto_parser
    ofp = dp.ofproto
    self.logger.info("Switch "+str(dp.id)+" says hello.")
    switch = self.nib.save_switch(dp)
    self.logger.info("Connected to Switch: "+self.nib.switch_description(dp))

    if self.mc != None:
      # This will block until the controller actually becomes a primary
      self.mc.handle_datapath(ev)

      # Start background thread to monitor switch-to-controller heartbeat
      self.last_heartbeat = time.time()
      if not self.heartbeat_monitor_started:
        thread.start_new_thread( self.heartbeat_monitor, (self, ) )
      self.heartbeat_monitor_started = True

    OpenflowUtils.delete_all_rules(dp)
    OpenflowUtils.send_table_miss_config(dp)

    self.l2_learning_switch_handler.install_fixed_rules(dp)
    self.cross_campus_handler.install_fixed_rules(dp)
    self.arp_handler.install_fixed_rules(dp)
    self.path_selection_handler.install_fixed_rules(dp)
Ejemplo n.º 2
0
  def write_table_2_rule(self, switch, dp, ip, pkt, actions, direction ):
    parser = dp.ofproto_parser
    if ip.proto == in_proto.IPPROTO_TCP:  
      tcp_pkt = pkt.get_protocols(tcp.tcp)[0]
      match = parser.OFPMatch(
        ipv4_src=ip.src
        ,ipv4_dst=ip.dst
        ,eth_type=ether_types.ETH_TYPE_IP
        ,ip_proto=ip.proto  
        ,vlan_vid=self.nib.vlan_for_switch(switch)
        ,tcp_src = tcp_pkt.src_port
        ,tcp_dst = tcp_pkt.dst_port
      )
      src_port = tcp_pkt.src_port
      dst_port = tcp_pkt.dst_port
    elif ip.proto == in_proto.IPPROTO_UDP:
      udp_pkt = pkt.get_protocols(udp.udp)[0]
      match = parser.OFPMatch(
        ipv4_src=ip.src
        ,ipv4_dst=ip.dst
        ,eth_type=ether_types.ETH_TYPE_IP
        ,ip_proto=ip.proto  
        ,vlan_vid=self.nib.vlan_for_switch(switch)
        ,udp_src = udp_pkt.src_port
        ,udp_dst = udp_pkt.dst_port
      )
      src_port = udp_pkt.src_port
      dst_port = udp_pkt.dst_port

    # The flow will naturally age out after 10 minutes of idleness.  That way we can pick a new path for
    # it if it starts up again.
    OpenflowUtils.add_flow(dp, priority=0, match=match, actions=actions, table_id=2, idle_timeout=self.IDLE_TIMEOUT)
    self.logger.info("Added "+direction+" hash rule for "+str(ip.src)+":" + str(src_port) +  " -> "+
      str(ip.dst) +":" + str(dst_port)
    )
 def arp_for_router(self, dp, switch):
   # Learning the router port is really important, so we send an ARP packet to poke it into submission
   # Note that host .2 may not or may not be a real host, but the reply will always come back to the switch anyway.
   target_ip_net = self.nib.actual_net_for(switch)
   src_ip = NetUtils.ip_for_network(target_ip_net, 2)
   dst_ip = NetUtils.ip_for_network(target_ip_net, 1)  # The IP of the router interface will always be a .1
   OpenflowUtils.send_arp_request(dp, src_ip, dst_ip)  
Ejemplo n.º 4
0
    def install_fixed_rules(self, dp):
        # We grab all ARP requests and replies.  You can't match with any more granularity
        # than that on HP's custom pipleine, unfortunately.
        ofproto = dp.ofproto
        parser = dp.ofproto_parser

        match_arp = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_ARP)
        actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER)]
        OpenflowUtils.add_flow(dp, priority=50, match=match_arp, actions=actions, table_id=3, cookie=self.ARP_RULE)
Ejemplo n.º 5
0
    def install_fixed_rules(self, dp):
        # We grab all ARP requests and replies.  You can't match with any more granularity
        # than that on HP's custom pipleine, unfortunately.
        ofproto = dp.ofproto
        parser = dp.ofproto_parser

        match_arp = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_ARP)
        actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER)]
        OpenflowUtils.add_flow(dp,
                               priority=50,
                               match=match_arp,
                               actions=actions,
                               table_id=3,
                               cookie=self.ARP_RULE)
Ejemplo n.º 6
0
  def handle_switch_up(self, ev):
    dp = ev.msg.datapath
    parser = dp.ofproto_parser
    ofproto = dp.ofproto
    switch = self.nib.save_switch(dp)
    self.logger.info("Connected to Switch: "+self.nib.switch_description(dp))

    # This will block until the controller actually becomes a primary
    self.mc.handle_datapath(ev)

    # Start background thread to monitor switch-to-controller heartbeat
    self.last_heartbeat = time.time()
    if not self.heartbeat_monitor_started:
      thread.start_new_thread( self.heartbeat_monitor, (self, ) )
    self.heartbeat_monitor_started = True

    OpenflowUtils.delete_all_rules(dp)
    OpenflowUtils.send_table_miss_config(dp)

    # Table 0, the Source Mac table, has table miss=Goto Controller so it can learn 
    # newly-connected macs
    match_all = parser.OFPMatch()
    actions = [ parser.OFPActionOutput(ofproto.OFPP_CONTROLLER) ]
    OpenflowUtils.add_flow(dp, priority=0, match=match_all, actions=actions, table_id=0, cookie=self.LEARN_NEW_MACS_RULE)

    # Table 1, the Dest Mac table, has table-miss = Flood.  If the destination exists,
    # it'll send a reply packet that'll get learned in table 0.  We can afford to do a 
    # sloppy non-spanning-tree flood because there's only one switch in our topology
    actions = [ parser.OFPActionOutput(ofproto.OFPP_FLOOD) ]
    OpenflowUtils.add_flow(dp, priority=0, match=match_all, actions=actions, table_id=1)
  def install_fixed_rules(self, dp):
    # Table 0, the Source Mac table, has table miss=Goto Controller so it can learn 
    # newly-connected macs
    switch = self.nib.switch_for_dp(dp)
    ofproto = dp.ofproto
    parser = dp.ofproto_parser
    match_all = parser.OFPMatch()
    actions = [ parser.OFPActionOutput(ofproto.OFPP_CONTROLLER) ]
    OpenflowUtils.add_flow(dp, priority=0, match=match_all, actions=actions, table_id=0, cookie=self.LEARN_NEW_MACS_RULE)

    # Table 1, the Dest Mac table, has table-miss = Flood.  If the destination exists,
    # it'll send a reply packet that'll get learned in table 0.  We can afford to do a 
    # sloppy non-spanning-tree flood because there's only one switch in our topology
    actions = [ parser.OFPActionOutput(ofproto.OFPP_FLOOD) ]
    OpenflowUtils.add_flow(dp, priority=0, match=match_all, actions=actions, table_id=1)

    self.arp_for_router(dp, switch)
Ejemplo n.º 8
0
    def packet_in(self, msg):
        # If we're not doing any IP rewriting, the ARP requests and replies should have been handled by the
        # the L2 switch.
        if not self.nib.ip_rewriting():
            return

        cookie = msg.cookie
        # Ignore all packets that came here by other rules than the ARP rule
        if cookie != self.ARP_RULE:
            return

        dp = msg.datapath

        pkt = packet.Packet(msg.data)
        p_eth = pkt.get_protocols(ethernet.ethernet)[0]
        p_arp = pkt.get_protocols(arp.arp)[0]
        src_ip = p_arp.src_ip
        dst_ip = p_arp.dst_ip
        switch = self.nib.switch_for_dp(dp)
        in_port = msg.match['in_port']

        # We only handle ARP requests for the virtual net here.  All ARP replies and requests were
        # actually forwarded by l2_switch_handler, but no one will answer those for the virtual net.
        # We formulate ARP responses for those requests here.

        if p_arp.opcode == arp.ARP_REQUEST:
            # Translate virtual address to a real one
            real_dest_ip = self.nib.translate_ip(
                dst_ip, self.nib.actual_net_for(switch))

            if real_dest_ip == None:
                pass
            elif self.nib.learned_ip(real_dest_ip):
                real_dest_mac = self.nib.mac_for_ip(real_dest_ip)
                OpenflowUtils.send_arp_reply(dp, in_port, p_eth.src, src_ip,
                                             real_dest_mac, dst_ip)
            else:
                # Send an ARP request to all ports, then just stay out of the way.  If the host is up
                # on an unlearned port, it'll send a response, and that'll trigger learning.  Then
                # when the NEXT ARP request for this address is received (it'll get retried a bunch of
                # times in practice), the reply can be generated from the ARP cache.
                # It doesn't matter so much where the ARP reply goes, because this switch will pick it up.
                switch_net = self.nib.actual_net_for(switch)
                src_ip = NetUtils.ip_for_network(switch_net, 2)
                OpenflowUtils.send_arp_request(dp, src_ip, real_dest_ip)
Ejemplo n.º 9
0
    def packet_in(self, msg):
        # If we're not doing any IP rewriting, the ARP requests and replies should have been handled by the
        # the L2 switch.
        if not self.nib.ip_rewriting():
            return

        cookie = msg.cookie
        # Ignore all packets that came here by other rules than the ARP rule
        if cookie != self.ARP_RULE:
            return

        dp = msg.datapath

        pkt = packet.Packet(msg.data)
        p_eth = pkt.get_protocols(ethernet.ethernet)[0]
        p_arp = pkt.get_protocols(arp.arp)[0]
        src_ip = p_arp.src_ip
        dst_ip = p_arp.dst_ip
        switch = self.nib.switch_for_dp(dp)
        in_port = msg.match["in_port"]

        # We only handle ARP requests for the virtual net here.  All ARP replies and requests were
        # actually forwarded by l2_switch_handler, but no one will answer those for the virtual net.
        # We formulate ARP responses for those requests here.

        if p_arp.opcode == arp.ARP_REQUEST:
            # Translate virtual address to a real one
            real_dest_ip = self.nib.translate_ip(dst_ip, self.nib.actual_net_for(switch))

            if real_dest_ip == None:
                pass
            elif self.nib.learned_ip(real_dest_ip):
                real_dest_mac = self.nib.mac_for_ip(real_dest_ip)
                OpenflowUtils.send_arp_reply(dp, in_port, p_eth.src, src_ip, real_dest_mac, dst_ip)
            else:
                # Send an ARP request to all ports, then just stay out of the way.  If the host is up
                # on an unlearned port, it'll send a response, and that'll trigger learning.  Then
                # when the NEXT ARP request for this address is received (it'll get retried a bunch of
                # times in practice), the reply can be generated from the ARP cache.
                # It doesn't matter so much where the ARP reply goes, because this switch will pick it up.
                switch_net = self.nib.actual_net_for(switch)
                src_ip = NetUtils.ip_for_network(switch_net, 2)
                OpenflowUtils.send_arp_request(dp, src_ip, real_dest_ip)
Ejemplo n.º 10
0
  def handle_packet_in(self, ev):
    # The HP considers a successful Packet In as a probe, so we reset the heartbeat here as well
    self.last_heartbeat = time.time()

    msg = ev.msg
    dp = msg.datapath
    switch = self.nib.switch_for_dp(dp)
    ofproto = dp.ofproto
    parser = dp.ofproto_parser
    in_port = msg.match['in_port']

    # Interesting packet data
    pkt = packet.Packet(msg.data)
    eth = pkt.get_protocols(ethernet.ethernet)[0]
    dst = eth.dst
    src = eth.src    

    if not self.nib.learned(src):
      match_mac_src = parser.OFPMatch( vlan_vid = self.nib.vlan_for_switch(switch), eth_src = src )
      match_mac_dst = parser.OFPMatch( vlan_vid = self.nib.vlan_for_switch(switch), eth_dst = src )

      OpenflowUtils.add_goto_table(dp, priority=65535, match=match_mac_src, goto_table_id=1, table_id=0)

      # And a send-to-port instruction for a destination match in table 1
      actions = [ parser.OFPActionOutput(in_port) ]
      OpenflowUtils.add_flow(dp, priority=0, match=match_mac_dst, actions=actions, table_id=1)

      # Learn it for posterity 
      self.nib.learn(switch, self.nib.ENDHOST_PORT, in_port, src, "0.0.0.0")

    # Now we have to deal with the Mac destination.  If we know it already, send the packet out that port.
    # Otherwise flood it.   
    output_p = self.nib.port_for_mac(dst)
    if output_p == None:
      output_p = ofproto.OFPP_FLOOD

    out = parser.OFPPacketOut(datapath=dp, buffer_id=msg.buffer_id,
      in_port=in_port, actions=[ parser.OFPActionOutput(output_p) ], data=msg.data)
    dp.send_msg(out)
Ejemplo n.º 11
0
    def write_table_2_rule(self, switch, dp, ip, pkt, actions, direction):
        parser = dp.ofproto_parser
        if ip.proto == in_proto.IPPROTO_TCP:
            tcp_pkt = pkt.get_protocols(tcp.tcp)[0]
            match = parser.OFPMatch(ipv4_src=ip.src,
                                    ipv4_dst=ip.dst,
                                    eth_type=ether_types.ETH_TYPE_IP,
                                    ip_proto=ip.proto,
                                    vlan_vid=self.nib.vlan_for_switch(switch),
                                    tcp_src=tcp_pkt.src_port,
                                    tcp_dst=tcp_pkt.dst_port)
            src_port = tcp_pkt.src_port
            dst_port = tcp_pkt.dst_port
        elif ip.proto == in_proto.IPPROTO_UDP:
            udp_pkt = pkt.get_protocols(udp.udp)[0]
            match = parser.OFPMatch(ipv4_src=ip.src,
                                    ipv4_dst=ip.dst,
                                    eth_type=ether_types.ETH_TYPE_IP,
                                    ip_proto=ip.proto,
                                    vlan_vid=self.nib.vlan_for_switch(switch),
                                    udp_src=udp_pkt.src_port,
                                    udp_dst=udp_pkt.dst_port)
            src_port = udp_pkt.src_port
            dst_port = udp_pkt.dst_port

        # The flow will naturally age out after 10 minutes of idleness.  That way we can pick a new path for
        # it if it starts up again.
        OpenflowUtils.add_flow(dp,
                               priority=0,
                               match=match,
                               actions=actions,
                               table_id=2,
                               idle_timeout=self.IDLE_TIMEOUT)
        self.logger.info("Added " + direction + " hash rule for " +
                         str(ip.src) + ":" + str(src_port) + " -> " +
                         str(ip.dst) + ":" + str(dst_port))
  def packet_in(self, msg):
    dp = msg.datapath
    switch = self.nib.switch_for_dp(dp)
    ofproto = dp.ofproto
    parser = dp.ofproto_parser
    in_port = msg.match['in_port']

    # Interesting packet data
    pkt = packet.Packet(msg.data)
    eth = pkt.get_protocols(ethernet.ethernet)[0]
    dst = eth.dst
    src = eth.src    

    # As stated before, we only learn from packets with IP source info in them.  
    if not self.nib.learned(src) and (eth.ethertype == ether_types.ETH_TYPE_IP or eth.ethertype == ether_types.ETH_TYPE_ARP):

      if eth.ethertype == ether_types.ETH_TYPE_IP:
        p_ip = pkt.get_protocols(ipv4.ipv4)[0]
        src_ip = p_ip.src
      elif eth.ethertype == ether_types.ETH_TYPE_ARP:
        p_arp = pkt.get_protocols(arp.arp)[0]
        src_ip = p_arp.src_ip

      match_mac_src = parser.OFPMatch( vlan_vid = self.nib.vlan_for_switch(switch), eth_src = src )
      match_mac_dst = parser.OFPMatch( vlan_vid = self.nib.vlan_for_switch(switch), eth_dst = src )

      target_ip_net = self.nib.actual_net_for(switch)
      if src_ip == NetUtils.ip_for_network(target_ip_net, 1):   # .1 is always the router
        # If this is coming from a router, we add goto table 2 rules in tables 0 and 1 instead.  
        # All packets to/from the router go through table 2 to rewrite IP addresses instead. (See cross_campus_handler)
        OpenflowUtils.add_goto_table(dp, priority=0, match=match_mac_src, goto_table_id=2, table_id=0)
        OpenflowUtils.add_goto_table(dp, priority=0, match=match_mac_dst, goto_table_id=2, table_id=1)

        self.nib.learn(switch, self.nib.ROUTER_PORT, in_port, src, src_ip)

        # We also install path learning rules for table 3.  Since this is the responsibility of
        # cross_campus_handler, it would be better if we installed them there, but I don't know quite
        # how to do it.

        # Incoming Packet Capture (e.g Coscin Ith->NYC on the Ith side), Cookie INCOMING_FLOW_RULE
        match = parser.OFPMatch( eth_dst = src, eth_type=ether_types.ETH_TYPE_IP )
        actions = [ parser.OFPActionOutput(ofproto.OFPP_CONTROLLER) ]
        OpenflowUtils.add_flow(dp, priority=65535, match=match, actions=actions, 
          table_id=3, cookie=CrossCampusHandler.INCOMING_FLOW_RULE)    

        # Outgoing Packet Capture (e.g. Coscin Ith->NYC on the NYC side), Cookie OUTGOING_FLOW_RULE
        match = parser.OFPMatch( eth_src = src, eth_type=ether_types.ETH_TYPE_IP )
        OpenflowUtils.add_flow(dp, priority=65534, match=match, actions=actions, 
          table_id=3, cookie=CrossCampusHandler.OUTGOING_FLOW_RULE)  

      # We don't learn any hosts until we've learned the router.  Otherwise IP packets from the other
      # side of the switch might be learned as the router port.  Send another request just in case it missed
      # the first one.  

      elif self.nib.router_port_for_switch(switch) == None:
        self.arp_for_router(dp, switch)

      else:
        # The packet is from a host, not a router.  A new packet carries information on the mac address, 
        # which we turn into a GOTO-table 1 rule in table 0
        OpenflowUtils.add_goto_table(dp, priority=65535, match=match_mac_src, goto_table_id=1, table_id=0)

        # And a send-to-port instruction for a destination match in table 1
        actions = [ parser.OFPActionOutput(in_port) ]
        OpenflowUtils.add_flow(dp, priority=0, match=match_mac_dst, actions=actions, table_id=1)

        # Learn it for posterity 
        self.nib.learn(switch, self.nib.ENDHOST_PORT, in_port, src, src_ip)

    # Don't send the packet out here if it's coming to/from a router.  
    # Those will be handled by cross_campus_handler, which may rewrite IP's as well.
    cookie = msg.cookie
    if cookie == CrossCampusHandler.INCOMING_FLOW_RULE or cookie == CrossCampusHandler.OUTGOING_FLOW_RULE:
      return

    # Now we have to deal with the Mac destination.  If we know it already, send the packet out that port.
    # Otherwise flood it.   
    output_p = self.nib.port_for_mac(dst)
    if output_p == None:
      output_p = ofproto.OFPP_FLOOD

    out_data = msg.data if msg.buffer_id == 0xffffffff else None 
    out = parser.OFPPacketOut(datapath=dp, buffer_id=msg.buffer_id,
      in_port=in_port, actions=[ parser.OFPActionOutput(output_p) ], data=out_data)
    dp.send_msg(out)