Exemplo n.º 1
0
  def packet_in(self, dpid, port, payload):
    p_eth = NetUtils.packet(payload, 'ethernet')
    if p_eth.ethertype != 0x0806:
      return

    # Handle ARP requests.
    p_arp = NetUtils.packet(payload, 'arp')
    src_ip = p_arp.src_ip
    dst_ip = p_arp.dst_ip
    switch = self.nib.dpid_to_switch(dpid)
    if p_arp.opcode == arp.ARP_REQUEST:
      preferred_path = self.nib.get_preferred_path()

      # If the request is for a host in the net we're already in, just broadcast it.  The host
      # itself will answer.
      if NetUtils.ip_in_network(src_ip, self.nib.actual_net_for(switch)) and \
           NetUtils.ip_in_network(dst_ip, self.nib.actual_net_for(switch)):
         real_dest_ip = None
      else:    # It's an imaginary host on one of the alternate paths
        real_dest_ip = self.nib.translate_alternate_net(dst_ip) 

      if real_dest_ip == None:
        logging.info("Flooding ARP Request")
        self.main_app.flood(switch, port, payload)
      elif self.nib.learned_ip(real_dest_ip):
        real_dest_mac = self.nib.mac_for_ip(real_dest_ip)
        self.arp_reply(switch, 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)
        # TODO: 250 will work as a host on subnets with a /24, but not any higher.  
        src_ip = NetUtils.ip_for_network(switch_net, 250)
        self.main_app.send_arp_request(switch, src_ip, real_dest_ip)

    # We don't do anything special to ARP replies, just forward them onto their destination
    # unidirectionally
    # TODO: Can't this be handled by L2Switch, since it's bound for a real Mac?
    elif p_arp.opcode == arp.ARP_REPLY:
      # We ignore the text of ARP replies bound for us.  We just used them for learning the port.
      if p_eth.dst == self.main_app.BOGUS_MAC:
        pass
      # The destination port was definitely learned because that's where the request originated
      elif not self.nib.seen_mac(p_eth.dst):
        logging.error("Ooops!  ARP reply bound for a destination we don't know")
        return
      elif self.nib.switch_for_mac(p_eth.dst) != switch:
        logging.error("Ooops!  ARP reply is destined for a different network.  Can't happen.")
        return
      else:
        direct_net_port = self.nib.port_for_mac(p_eth.dst)
        output_actions = [Output(Physical(direct_net_port))]
        self.main_app.pkt_out(dpid, payload, output_actions)
Exemplo n.º 2
0
  def packet_in_router_learning(self, dpid, port, payload):
    p = NetUtils.packet(payload, 'arp')
    p_eth = NetUtils.packet(payload, 'ethernet')
    switch = self.nib.dpid_to_switch(dpid)
    logging.info("Received ("+str(p_eth.ethertype)+"): "+p_eth.src+"/"+p.src_ip+" -> ("+switch+", "+str(port)+") -> "+p.dst_ip)

    # Supposedly, the src mac in the ARP reply and the ethernet frame itself should be the
    # the same, but that's not always the case.  The Ethernet frame is definitive. 
    self.nib.learn(switch, self.nib.ROUTER_PORT, port, p_eth.src, p.src_ip)
    self.waiting_for_router_arps -= 1
    if self.waiting_for_router_arps == 0:
      self.normal_mode()
Exemplo n.º 3
0
    def packet_in(self, dpid, port, payload):
        p_eth = NetUtils.packet(payload, "ethernet")
        if p_eth.ethertype != 0x0800:
            return

        p_ip = NetUtils.packet(payload, "ipv4")
        src_ip = p_ip.src
        dst_ip = p_ip.dst
        switch = self.nib.dpid_to_switch(dpid)

        # If we haven't seen this source, dest pair yet, add it, and the rule with it.
        # Which list we put it in depends on whether we're at the ingress or egress switch
        if self.nib.at_ingress_switch(switch, port):
            # TODO: If this packet is bound for hosts outside the CoSciN network, in production just forward them,
            # For now, just drop them.
            if not self.nib.ip_in_coscin_network(dst_ip):
                logging.info("Internet-bound packet dropped in this test network")
                return
            # It's possible that this is an intra-network packet even though the rule should 've been installed
            # to handle such packets directly.  send_along_direct_path will handle it below.
            elif NetUtils.ip_in_network(dst_ip, self.nib.actual_net_for(switch)):
                pass
            elif not self.nib.seen_src_dest_pair_at_ingress(src_ip, dst_ip):
                self.nib.add_ingress_src_dest_pair(src_ip, dst_ip)
                self.nib.set_dirty()
        elif self.nib.at_egress_switch(switch, port):
            if not self.nib.seen_src_dest_pair_at_egress(src_ip, dst_ip):
                self.nib.add_egress_src_dest_pair(src_ip, dst_ip)
                self.nib.set_dirty()

        # If we have seen it, the rule should've taken care of the next packets, but it might
        # not be in effect yet so we handle it manually
        opposite_switch = self.nib.opposite_switch(switch)
        if NetUtils.ip_in_network(dst_ip, self.nib.actual_net_for(opposite_switch)):
            self.send_along_preferred_path(switch, src_ip, dst_ip, payload)
        elif self.nib.at_ingress_switch(switch, port):
            if NetUtils.ip_in_network(dst_ip, self.nib.actual_net_for(switch)):
                self.send_to_host_without_rewrite(switch, src_ip, dst_ip, payload)
            else:
                self.send_along_direct_path(switch, src_ip, dst_ip, payload)
        elif self.nib.at_egress_switch(switch, port):
            self.send_to_host(switch, src_ip, dst_ip, payload)
Exemplo n.º 4
0
  def packet_in_normal_operation(self, dpid, port, payload):
    switch = self.nib.dpid_to_switch(dpid)
    # TODO: deal with non-IP packets, although that's fairly unlikely
    p_eth = NetUtils.packet(payload, 'ethernet')
    if p_eth.ethertype == 0x0806:
      p_arp = NetUtils.packet(payload, 'arp')
      src_ip = p_arp.src_ip
      dst_ip = p_arp.dst_ip
    elif p_eth.ethertype == 0x0800:
      p_ip = NetUtils.packet(payload, 'ipv4')
      src_ip = p_ip.src
      dst_ip = p_ip.dst
    else:
      src_ip = '0.0.0.0'
      dst_ip = '0.0.0.0'
      logging.info("Received packet of type "+str(p_eth.ethertype))

    # TODO: Handle DHCP requests someday, ... maybe
    if src_ip == '0.0.0.0':
      return

    if self.nib.learn(switch, self.nib.ENDHOST_PORT, port, p_eth.src, src_ip):
      self.nib.set_dirty()

    logging.info("Received ("+str(p_eth.ethertype)+"): "+p_eth.src+"/"+src_ip+" -> ("+switch+", "+str(port)+") -> "+dst_ip)

    self.arp_handler.packet_in(dpid, port, payload)
    #self.broadcast_handler.packet_in(dpid, port, payload)
    self.intranet_handler.packet_in(dpid, port, payload)
    self.cross_campus_handler.packet_in(dpid, port, payload)

    if self.nib.is_dirty():
      logging.info("Installing new policy")
      # This doesn't actually wait two seconds, but it seems to serialize the updates so they occur in the right
      # order, as opposed to just calling update_and_clear_dirty on its own.
      IOLoop.instance().add_timeout(datetime.timedelta(seconds=2), self.update_and_clear_dirty)
Exemplo n.º 5
0
 def packet_in(self, dpid, port, payload):
   p_eth = NetUtils.packet(payload, 'ethernet')
   if p_eth.dst == 0xffffffffffff:
     self.main_app.flood( self.nib.dpid_to_switch(dpid), port, payload )