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)  
 def egress_src_dest_pairs_policy(self):
     policies = []
     for src_dest_pair in self.nib.get_egress_src_dest_pairs():
         (src_ip, dst_ip) = src_dest_pair
         # Convert dst_ip to its real form.  First find out what the egress switch actually is:
         for ap in self.nib.alternate_paths():
             if NetUtils.ip_in_network(dst_ip, ap["ithaca"]):
                 switch = "ithaca"
                 imaginary_net = ap["ithaca"]
             elif NetUtils.ip_in_network(dst_ip, ap["nyc"]):
                 switch = "nyc"
                 imaginary_net = ap["nyc"]
         real_net = self.nib.actual_net_for(switch)
         dst_host = NetUtils.host_of_ip(dst_ip, imaginary_net)
         new_dest_ip = NetUtils.ip_for_network(real_net, dst_host)
         # If it's not in the ARP cache, it already has an ARP request on the way so ignore it for now.
         if self.nib.learned_ip(new_dest_ip):
             direct_net_port = self.nib.port_for_ip(new_dest_ip)
             new_src_ip = self.nib.translate_alternate_net(src_ip)
             output_actions = SetIP4Src(new_src_ip) >> SetIP4Dst(new_dest_ip) >> Send(direct_net_port)
             policies.append(
                 Filter(SwitchEq(self.nib.switch_to_dpid(switch)) & Policies.is_ip_from_to(src_ip, dst_ip))
                 >> output_actions
             )
     return Union(policies)
    def __init__(self, logger, topo_file, routing_table_file):
        self.logger = logger

        # Read the Fixed routing table first
        f = open(routing_table_file, "r")
        routing_config = json.load(f)
        f.close()
        for rt in routing_config:
            sn = Subnet(rt["subnet"], rt["router_port"], rt["router_mac"],
                        rt["gateway"])
            self.subnets.append(sn)

        # Read the fixed configuration from the topology file
        topo_agraph = pgv.AGraph(topo_file)
        switchnames = {}

        # Read router name and dpid from topo
        for node in topo_agraph.nodes():
            if node.startswith("s"):
                if node.attr["router"]:
                    router_name = str(node)
                    self.router_dpid = NetUtils.int_from_mac_colon_hex(
                        node.attr["dpid"])
                else:
                    switchnames[str(node)] = NetUtils.int_from_mac_colon_hex(
                        node.attr["dpid"])

        # Mark all the internal ports in the switches, since these need special attention
        for link in topo_agraph.edges():
            (src_node, dst_node) = link
            if str(src_node) == router_name:
                dpid = switchnames[str(dst_node)]
                if dpid not in self.internal_ports:
                    self.internal_ports[dpid] = []
                self.internal_ports[dpid].append(int(link.attr['dport']))
  def __init__(self, logger, topo_file, routing_table_file):
    self.logger = logger

    # Read the Fixed routing table first
    f = open(routing_table_file, "r")
    routing_config = json.load(f)
    f.close()
    for rt in routing_config:
      sn = Subnet(rt["subnet"],rt["router_port"],rt["router_mac"],rt["gateway"])
      self.subnets.append(sn)

    # Read the fixed configuration from the topology file
    topo_agraph = pgv.AGraph(topo_file)
    switchnames = {}

    # Read router name and dpid from topo
    for node in topo_agraph.nodes():
      if node.startswith("s"): 
        if node.attr["router"]:
          router_name = str(node)
          self.router_dpid = NetUtils.int_from_mac_colon_hex(node.attr["dpid"])
        else: 
          switchnames[str(node)] = NetUtils.int_from_mac_colon_hex(node.attr["dpid"])

    # Mark all the internal ports in the switches, since these need special attention
    for link in topo_agraph.edges():
      (src_node, dst_node) = link
      if str(src_node) == router_name:
        dpid = switchnames[ str(dst_node) ]
        if dpid not in self.internal_ports:
          self.internal_ports[dpid] = []
        self.internal_ports[dpid].append(int(link.attr['dport']))
 def coscin_net_for(self, src_ip):
   for side in [ "ithaca", "nyc" ]:
     if NetUtils.ip_in_network(src_ip, self.actual_net_for(side)):
       return self.actual_net_for(side)
     for ap in self.alternate_paths():
       if NetUtils.ip_in_network(src_ip, ap[side]):
         return ap[side]
   return None
 def coscin_net_for(self, src_ip):
     for side in ["ithaca", "nyc"]:
         if NetUtils.ip_in_network(src_ip, self.actual_net_for(side)):
             return self.actual_net_for(side)
         for ap in self.alternate_paths():
             if NetUtils.ip_in_network(src_ip, ap[side]):
                 return ap[side]
     return None
 def ip_in_coscin_network(self, dst_ip):
   if NetUtils.ip_in_network(dst_ip, self.actual_net_for("ithaca")):
     return True
   if NetUtils.ip_in_network(dst_ip, self.actual_net_for("nyc")):
     return True    
   for ap in self.alternate_paths():
     if NetUtils.ip_in_network(dst_ip, ap["ithaca"]) or NetUtils.ip_in_network(dst_ip, ap["nyc"]):
       return True
   return False      
Beispiel #8
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)
 def ip_in_coscin_network(self, dst_ip):
     if NetUtils.ip_in_network(dst_ip, self.actual_net_for("ithaca")):
         return True
     if NetUtils.ip_in_network(dst_ip, self.actual_net_for("nyc")):
         return True
     for ap in self.alternate_paths():
         if NetUtils.ip_in_network(dst_ip,
                                   ap["ithaca"]) or NetUtils.ip_in_network(
                                       dst_ip, ap["nyc"]):
             return True
     return False
Beispiel #10
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()
    def add_outgoing_dynamic_flow(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']

        pkt = packet.Packet(msg.data)
        eth = pkt.get_protocols(ethernet.ethernet)[0]
        ip = pkt.get_protocols(ipv4.ipv4)[0]
        src_ip = ip.src
        dst_ip = ip.dst

        actions = []

        # We only add IP rewriting for Coscin packets.  All non-coscin packets will get a flow rule, but no
        # IP rewriting.
        if self.nib.ip_rewriting() and self.nib.ip_in_coscin_network(dst_ip):
            opposite_switch = self.nib.opposite_switch(switch)
            # If this is bound for the "virtual" network on the other side, pick the path and rewrite
            # the destination IP's
            if NetUtils.ip_in_network(
                    dst_ip, self.nib.actual_net_for(opposite_switch)):
                new_src_ip = self.nib.translate_ip(
                    src_ip, self.nib.preferred_net(switch))
                actions.append(parser.OFPActionSetField(ipv4_src=new_src_ip))

                new_dst_ip = self.nib.translate_ip(
                    dst_ip, self.nib.preferred_net(opposite_switch))
                actions.append(parser.OFPActionSetField(ipv4_dst=new_dst_ip))
            # If it's trying to communicate with the router on the same side (like ping), just let it
            # through
            elif NetUtils.ip_in_network(dst_ip,
                                        self.nib.actual_net_for(switch)):
                pass
            else:
                # If it's a direct route (e.g 56.100 -> 157.200), we only need to renumber the source.
                # But we have to select the right imaginary net so the path is "straight"
                new_src_ip = self.nib.translate_ip(
                    src_ip, self.nib.opposite_net_for(dst_ip))
                actions.append(parser.OFPActionSetField(ipv4_src=new_src_ip))

        # No matter what, our rule will always send the packet to the router
        actions.append(
            parser.OFPActionOutput(self.nib.router_port_for_switch(switch)))

        # Only TCP and UDP packets are handled by installing rules in custom pipleine hash table.
        # (But we had to compute the actios regardless because they'll be used in a Packet Out)
        if ip.proto == in_proto.IPPROTO_TCP or ip.proto == in_proto.IPPROTO_UDP:
            self.write_table_2_rule(switch, dp, ip, pkt, actions, "outgoing")

        return actions
    def send_along_direct_path(self, switch, src_ip, dst_ip, payload):
        opposite_switch = self.nib.opposite_switch(switch)
        for ap in self.nib.alternate_paths():
            if NetUtils.ip_in_network(dst_ip, ap[opposite_switch]):
                src_net = ap[switch]

        src_host = NetUtils.host_of_ip(src_ip, self.nib.actual_net_for(switch))
        # Translate this to the direct path IP
        new_src = NetUtils.ip_for_network(src_net, src_host)

        output_actions = [SetIP4Src(new_src), Output(Physical(self.nib.router_port_for_switch(switch)))]
        dpid = self.nib.switch_to_dpid(switch)
        self.main_app.pkt_out(dpid, payload, output_actions)
  def translate_alternate_net(self, dst_ip):
    # First find out which side (ithaca or nyc) it's on
    found_side = None
    for ap in self.alternate_paths():
      for side in ["ithaca", "nyc"]:
        if NetUtils.ip_in_network(dst_ip, ap[side]):
          found_side = side
          imaginary_net = ap[side]

    if side == None:
      logging.error("Ooops.  Got an ARP request for a net we don't know about.  Oh well.")
      return False
    else:
      host = NetUtils.host_of_ip(dst_ip, imaginary_net)
      return NetUtils.ip_for_network(self.actual_net_for(found_side), host)
 def opposite_net_for(self, src_ip):
   for ap in self.alternate_paths():
     for side in [ "ithaca", "nyc" ]:
       opposite_side = self.opposite_switch(side)
       if NetUtils.ip_in_network(src_ip, ap[opposite_side]):
         return ap[side] 
   return None
 def opposite_net_for(self, src_ip):
     for ap in self.alternate_paths():
         for side in ["ithaca", "nyc"]:
             opposite_side = self.opposite_switch(side)
             if NetUtils.ip_in_network(src_ip, ap[opposite_side]):
                 return ap[side]
     return None
  def add_outgoing_dynamic_flow(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']

    pkt = packet.Packet(msg.data)
    eth = pkt.get_protocols(ethernet.ethernet)[0]
    ip = pkt.get_protocols(ipv4.ipv4)[0]
    src_ip = ip.src
    dst_ip = ip.dst

    actions = [ ]

    # We only add IP rewriting for Coscin packets.  All non-coscin packets will get a flow rule, but no
    # IP rewriting.  
    if self.nib.ip_rewriting() and self.nib.ip_in_coscin_network(dst_ip):
      opposite_switch = self.nib.opposite_switch(switch)
      # If this is bound for the "virtual" network on the other side, pick the path and rewrite
      # the destination IP's
      if NetUtils.ip_in_network(dst_ip, self.nib.actual_net_for(opposite_switch)):
        new_src_ip = self.nib.translate_ip(src_ip, self.nib.preferred_net(switch))
        actions.append( parser.OFPActionSetField( ipv4_src = new_src_ip ) )

        new_dst_ip = self.nib.translate_ip(dst_ip, self.nib.preferred_net(opposite_switch))
        actions.append( parser.OFPActionSetField( ipv4_dst = new_dst_ip ) )
      # If it's trying to communicate with the router on the same side (like ping), just let it
      # through
      elif NetUtils.ip_in_network(dst_ip, self.nib.actual_net_for(switch)):
        pass
      else:
        # If it's a direct route (e.g 56.100 -> 157.200), we only need to renumber the source. 
        # But we have to select the right imaginary net so the path is "straight" 
        new_src_ip = self.nib.translate_ip(src_ip, self.nib.opposite_net_for(dst_ip))
        actions.append( parser.OFPActionSetField( ipv4_src = new_src_ip ) )

    # No matter what, our rule will always send the packet to the router
    actions.append(parser.OFPActionOutput(self.nib.router_port_for_switch(switch)))

    # Only TCP and UDP packets are handled by installing rules in custom pipleine hash table. 
    # (But we had to compute the actios regardless because they'll be used in a Packet Out)
    if ip.proto == in_proto.IPPROTO_TCP or ip.proto == in_proto.IPPROTO_UDP:
      self.write_table_2_rule(switch, dp, ip, pkt, actions, "outgoing" )

    return actions
    def capture_new_source_dest_pairs_policy(self):
        policies = []
        for switch in self.nib.switches_present():
            dpid = self.nib.switch_to_dpid(switch)
            for endhost in self.nib.get_endhosts(switch):
                (host, host_port, host_mac, host_ip) = endhost
                opposite_switch = self.nib.opposite_switch(switch)
                dest_cdr = self.nib.actual_net_for(opposite_switch)
                (dest_net, dest_mask) = NetUtils.net_mask(dest_cdr)

                # Note we really don't have to test for source IP, but it's extra security
                policies.append(
                    Filter(
                        Policies.at_switch_port(dpid, host_port)
                        & Policies.is_ip()
                        & IP4SrcEq(host_ip)
                        & IP4DstEq(dest_net, dest_mask)
                        & self.destination_not_known_host_on_net(host_ip, dest_cdr)
                    )
                    >> Policies.send_to_controller()
                )
                for ap in self.nib.alternate_paths():
                    dest_cdr = ap[opposite_switch]
                    (dest_net, dest_mask) = NetUtils.net_mask(dest_cdr)
                    policies.append(
                        Filter(
                            Policies.at_switch_port(dpid, host_port)
                            & Policies.is_ip()
                            & IP4SrcEq(host_ip)
                            & IP4DstEq(dest_net, dest_mask)
                            & self.destination_not_known_host_on_net(host_ip, dest_cdr)
                        )
                        >> Policies.send_to_controller()
                    )

            # Now handle incoming packets for all our home networks.  We need to learn
            # those (src, dest) pairs as well
            for ap in self.nib.alternate_paths():
                dest_cdr = ap[switch]
                (dest_net, dest_mask) = NetUtils.net_mask(dest_cdr)
                policies.append(
                    Filter(Policies.is_ip() & IP4DstEq(dest_net, dest_mask) & self.src_dest_pair_not_learned(dest_cdr))
                    >> Policies.send_to_controller()
                )

        return Union(policies)
    def send_along_preferred_path(self, switch, src_ip, dst_ip, payload):
        # Get host from src_ip
        src_pref_net = self.nib.preferred_net(switch)
        src_host = NetUtils.host_of_ip(src_ip, self.nib.actual_net_for(switch))
        # Translate this to the preferred path IP
        new_src = NetUtils.ip_for_network(src_pref_net, src_host)
        # And do the same for the destination
        opposite_switch = self.nib.opposite_switch(switch)
        dest_host = NetUtils.host_of_ip(dst_ip, self.nib.actual_net_for(opposite_switch))
        new_dest = NetUtils.ip_for_network(self.nib.preferred_net(opposite_switch), dest_host)

        output_actions = [
            SetIP4Src(new_src),
            SetIP4Dst(new_dest),
            Output(Physical(self.nib.router_port_for_switch(switch))),
        ]
        dpid = self.nib.switch_to_dpid(switch)
        self.main_app.pkt_out(dpid, payload, output_actions)
Beispiel #19
0
    def get_net_info():
        '''
        Get host information on current machine
        return dictionary of host information
        '''
        import socket
        results = dict()
        try:
            host_infos = socket.gethostbyaddr(socket.gethostname())
            results['hostname'] = host_infos[0]
            results['host_alias'] = ', '.join(host_infos[1])
            results['ip_address_alternate'] = ', '.join(host_infos[2])

            net_util = NetUtils(10)
            results['ip_address'] = net_util.get_host_ip()
        except:
            print('Failed to get net info')
            #print traceback.print_exc()
        return results
    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)
  def learn(self, switch, port_type, port, mac, src_ip):
    if self.learned(switch, port):
      return False

    logging.info("Learning: "+mac+"/"+src_ip+" attached to ( "+switch+", "+str(port)+" )")
    self.hosts[mac] = (switch, port)
    self.arp_cache[src_ip] = (switch, port, mac)
    self.unlearned_ports[switch].remove(port)
    if port_type == self.ENDHOST_PORT:
      host_portion = NetUtils.host_of_ip(src_ip, self.coscin_config[switch]["network"])
      self.endhosts[switch].append( (host_portion, port, mac, src_ip) )
      # We also add entries for this host on all its imaginary paths
      for ap in self.coscin_config["alternate_paths"]:
        virtual_ip = NetUtils.ip_for_network(ap[switch], host_portion)
        self.arp_cache[virtual_ip] = (switch, port, mac)
    elif port_type == self.ROUTER_PORT:
      self.router_port[switch] = port
    else:
      logging.error("Unknown port type: "+str(port_type))
      return False
    return True
 def destination_not_known_host_on_net(self, host_ip, dest_net):
     # Given an IP, find all src_dest pairs we've seen for this src, filter the dests down to
     # those on the dest_net, and return a clause that doesn't match any of them
     preds = []
     for src_dest_pair in self.nib.get_ingress_src_dest_pairs():
         (src_ip, dst_ip) = src_dest_pair
         if src_ip == host_ip and NetUtils.ip_in_network(dst_ip, dest_net):
             preds.append(IP4DstEq(dst_ip))
     if not preds:
         return true
     else:
         return Not(Or(preds))
 def src_dest_pair_not_learned(self, dest_net):
     # Given a destination net, find all learned src, dest pairs that match it and return
     # a clause that doesn't match any of them.
     preds = []
     for src_dest_pair in self.nib.get_egress_src_dest_pairs():
         (src_ip, dst_ip) = src_dest_pair
         if NetUtils.ip_in_network(dst_ip, dest_net):
             preds.append(IP4SrcEq(src_ip) & IP4DstEq(dst_ip))
     if not preds:
         return true
     else:
         return Not(Or(preds))
 def send_to_host(self, switch, src_ip, dst_ip, payload):
     # Convert dst_ip to its real form.  First find out what the egress switch actually is:
     for ap in self.nib.alternate_paths():
         if NetUtils.ip_in_network(dst_ip, ap[switch]):
             imaginary_net = ap[switch]
     real_net = self.nib.actual_net_for(switch)
     dst_host = NetUtils.host_of_ip(dst_ip, imaginary_net)
     new_dest_ip = NetUtils.ip_for_network(real_net, dst_host)
     # If we don't know the port for this address (which might happen if the
     # IP is on this network, but the host isn't up or doesn't exist) there's not
     # much we can do with this packet.  Send an ARP request and hope the
     # original packet gets retransmitted (which is normally the case)
     if not self.nib.learned_ip(new_dest_ip):
         src_ip = NetUtils.ip_for_network(real_net, 250)
         self.main_app.send_arp_request(switch, src_ip, new_dest_ip)
     else:
         direct_net_port = self.nib.port_for_ip(new_dest_ip)
         # We also need to translate the alternately-numbered net to a real one.  Otherwise the
         # host (which only knows real networks) may not know what to do with it.
         new_src_ip = self.nib.translate_alternate_net(src_ip)
         output_actions = [SetIP4Src(new_src_ip), SetIP4Dst(new_dest_ip), Output(Physical(direct_net_port))]
         dpid = self.nib.switch_to_dpid(switch)
         self.main_app.pkt_out(dpid, payload, output_actions)
    def add_incoming_dynamic_flow(self, msg):
        dp = msg.datapath
        switch = self.nib.switch_for_dp(dp)
        ofproto = dp.ofproto
        parser = dp.ofproto_parser

        pkt = packet.Packet(msg.data)
        eth = pkt.get_protocols(ethernet.ethernet)[0]
        ip = pkt.get_protocols(ipv4.ipv4)[0]
        src_ip = ip.src
        dst_ip = ip.dst

        # If the destination IP is in the real network (ip_in_network returns true),
        # then the other side of the network is not properly rewriting the destination.
        # or the packet is coming from the non-Coscin Internet.  Just leave those IP's alone (we assume
        # there's nothing crazy like the source using a virtual address here.)
        actions = []
        if self.nib.ip_rewriting() and not NetUtils.ip_in_network(
                dst_ip, self.nib.actual_net_for(switch)):
            opposite_switch = self.nib.opposite_switch(switch)

            # Check the source IP.  It should be coming from the same path subnets as the detination.  If it's not,
            # it's some arbitrary Internet host trying to contact the imaginary net.  Drop the packet like a hot potato.
            new_src_ip = self.nib.translate_ip(
                src_ip, self.nib.actual_net_for(opposite_switch))
            if new_src_ip == None:
                return []
            actions.append(parser.OFPActionSetField(ipv4_src=new_src_ip))

            # The destination IP is always on one of the imaginary nets, or else it wouldn't have been delivered
            # to this switch at all.
            new_dst_ip = self.nib.translate_ip(dst_ip,
                                               self.nib.actual_net_for(switch))
            actions.append(parser.OFPActionSetField(ipv4_dst=new_dst_ip))

        # The destination mac in the packet is guaranteed OK because the router placed it there as a result
        # of its ARP cache.  However, that doesn't necessarily mean we have learned that port yet, so act like
        # an L2 switch.  But in that case, don't install the rule because we don't want to just flood the switch
        # everytime it happens.
        output_p = self.nib.port_for_mac(eth.dst)
        if output_p == None:
            output_p = ofproto.OFPP_FLOOD
        actions.append(parser.OFPActionOutput(output_p))

        if (ip.proto == in_proto.IPPROTO_TCP or ip.proto
                == in_proto.IPPROTO_UDP) and output_p != ofproto.OFPP_FLOOD:
            self.write_table_2_rule(switch, dp, ip, pkt, actions, "incoming")

        return actions
Beispiel #26
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)
 def send_to_host_without_rewrite(self, switch, src_ip, dst_ip, payload):
     # Convert dst_ip to its real form.  First find out what the egress switch actually is:
     real_net = self.nib.actual_net_for(switch)
     # If we don't know the port for this address (which might happen if the
     # IP is on this network, but the host isn't up or doesn't exist) just send
     # an ARP request.  Alternatively, we could flood it out, but ARP has the advantage
     # of making the port get learned.
     if not self.nib.learned_ip(dst_ip):
         arp_src_ip = NetUtils.ip_for_network(real_net, 250)
         self.main_app.send_arp_request(switch, arp_src_ip, dst_ip)
     else:
         direct_net_port = self.nib.port_for_ip(dst_ip)
         output_actions = [Output(Physical(direct_net_port))]
         dpid = self.nib.switch_to_dpid(switch)
         self.main_app.pkt_out(dpid, payload, output_actions)
Beispiel #28
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)
  def add_incoming_dynamic_flow(self, msg):
    dp = msg.datapath
    switch = self.nib.switch_for_dp(dp)
    ofproto = dp.ofproto
    parser = dp.ofproto_parser

    pkt = packet.Packet(msg.data)
    eth = pkt.get_protocols(ethernet.ethernet)[0]
    ip = pkt.get_protocols(ipv4.ipv4)[0]
    src_ip = ip.src
    dst_ip = ip.dst

    # If the destination IP is in the real network (ip_in_network returns true), 
    # then the other side of the network is not properly rewriting the destination.
    # or the packet is coming from the non-Coscin Internet.  Just leave those IP's alone (we assume 
    # there's nothing crazy like the source using a virtual address here.)
    actions = []
    if self.nib.ip_rewriting() and not NetUtils.ip_in_network(dst_ip, self.nib.actual_net_for(switch)):
      opposite_switch = self.nib.opposite_switch(switch)

      # Check the source IP.  It should be coming from the same path subnets as the detination.  If it's not,
      # it's some arbitrary Internet host trying to contact the imaginary net.  Drop the packet like a hot potato.
      new_src_ip = self.nib.translate_ip(src_ip, self.nib.actual_net_for(opposite_switch))
      if new_src_ip == None:
        return []
      actions.append( parser.OFPActionSetField(ipv4_src=new_src_ip) )

      # The destination IP is always on one of the imaginary nets, or else it wouldn't have been delivered
      # to this switch at all.
      new_dst_ip = self.nib.translate_ip(dst_ip, self.nib.actual_net_for(switch))
      actions.append( parser.OFPActionSetField(ipv4_dst=new_dst_ip) )

    # The destination mac in the packet is guaranteed OK because the router placed it there as a result
    # of its ARP cache.  However, that doesn't necessarily mean we have learned that port yet, so act like
    # an L2 switch.  But in that case, don't install the rule because we don't want to just flood the switch
    # everytime it happens.  
    output_p = self.nib.port_for_mac(eth.dst)
    if output_p == None:
      output_p = ofproto.OFPP_FLOOD
    actions.append(parser.OFPActionOutput(output_p))

    if (ip.proto == in_proto.IPPROTO_TCP or ip.proto == in_proto.IPPROTO_UDP) and output_p != ofproto.OFPP_FLOOD:
      self.write_table_2_rule(switch, dp, ip, pkt, actions, "incoming" )

    return actions
Beispiel #30
0
  def policy(self):
    policies = []
    for switch in self.nib.switches_present():
      dpid = self.nib.switch_to_dpid(switch)
      # In normal mode, we capture ARP requests for IP's that don't really exist.  You can
      # think of them as symbolic links to the real IP.  We capture .1 address of
      # each of the endpoint networks, plus any real hosts on the net

      # And we capture ARP requests for the alternate paths.  These will always be for 
      # hosts that have no real estate on the imaginary link, as in 192.168.156.100 along 
      # the 192.168.156.* imaginary network.  This will be translated to the real net 192.168.56.100
      # Note: only the routers actually send these requests, not end hosts, who always send them
      # to a default gateway.  
      for ap in self.nib.alternate_paths():
        (net, mask) = NetUtils.net_mask(ap[switch])
        policies.append(Filter(Policies.at_switch(dpid) & Policies.is_arp() & IP4DstEq(net,mask)) >> Policies.send_to_controller())

    return Union(policies)
Beispiel #31
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)
    def ingress_src_dest_pairs_policy(self):
        policies = []
        for src_dest_pair in self.nib.get_ingress_src_dest_pairs():
            (src_ip, dst_ip) = src_dest_pair
            switch = self.nib.switch_for_ip(src_ip)
            dpid = self.nib.switch_to_dpid(switch)
            port = self.nib.port_for_ip(src_ip)
            src_host = NetUtils.host_of_ip(src_ip, self.nib.actual_net_for(switch))
            # If this is going to the preferred network, write a rule choosing the
            # correct route here.
            opposite_switch = self.nib.opposite_switch(switch)
            if NetUtils.ip_in_network(dst_ip, self.nib.actual_net_for(opposite_switch)):
                # Get host from src_ip
                src_pref_net = self.nib.preferred_net(switch)
                new_src = NetUtils.ip_for_network(src_pref_net, src_host)
                dest_host = NetUtils.host_of_ip(dst_ip, self.nib.actual_net_for(opposite_switch))
                new_dest = NetUtils.ip_for_network(self.nib.preferred_net(opposite_switch), dest_host)

                router_port = self.nib.router_port_for_switch(switch)
                output_actions = SetIP4Src(new_src) >> SetIP4Dst(new_dest) >> Send(router_port)
                policies.append(
                    Filter(Policies.at_switch_port(dpid, port) & Policies.is_ip_from_to(src_ip, dst_ip))
                    >> output_actions
                )

            else:
                # It's a direct path.  Find the path first.
                for ap in self.nib.alternate_paths():
                    if NetUtils.ip_in_network(dst_ip, ap[opposite_switch]):
                        alternate_path = ap
                new_src = NetUtils.ip_for_network(alternate_path[switch], src_host)
                router_port = self.nib.router_port_for_switch(switch)
                output_actions = SetIP4Src(new_src) >> Send(router_port)
                policies.append(
                    Filter(Policies.at_switch_port(dpid, port) & Policies.is_ip_from_to(src_ip, dst_ip))
                    >> output_actions
                )
        return Union(policies)
  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)
Beispiel #34
0
 def dest_real_net(self, switch):
   net_and_mask = self.nib.actual_net_for(switch)
   (net, mask) = NetUtils.net_mask(net_and_mask)
   return IP4DstEq(net,mask)
 def translate_ip(self, src_ip, new_net):
   current_net = self.coscin_net_for(src_ip)
   if current_net == None:
     return None
   src_host = NetUtils.host_of_ip(src_ip, current_net)
   return NetUtils.ip_for_network(new_net, src_host)
 def router_ip_for_switch(self, switch):
   return NetUtils.ip_for_network(self.actual_net_for(switch), "1")
 def translate_ip(self, src_ip, new_net):
     current_net = self.coscin_net_for(src_ip)
     if current_net == None:
         return None
     src_host = NetUtils.host_of_ip(src_ip, current_net)
     return NetUtils.ip_for_network(new_net, src_host)
 def subnet_for(self, ip):
     for sn in self.subnets:
         if NetUtils.ip_in_network(ip, sn.subnet_cidr):
             return sn
     return None
Beispiel #39
0
 def gateway_ip(self, switch):
   return NetUtils.ip_for_network(self.nib.actual_net_for(switch), 1)