Example #1
0
 def shortest_paths_to_switches (self):
   # tell all switches what their next hop is to all other switches (for shortest paths)
   all_paths = nx.shortest_path(self.graph)
   for src, paths in all_paths.items():
     if 'dpid' not in self.graph.node[src]:
       continue
     src_dpid = self.graph.node[src]['dpid'] 
     if core.openflow.getConnection(src_dpid):
       core.openflow.sendToDPID(src_dpid, nx_flow_mod_table_id())  # Enables multiple tables
       data = []
    
       for dst, path in paths.items():
         if dst == src:
           continue
         
         if 'dpid' not in self.graph.node[dst]:
           continue
         
         dst_dpid = self.graph.node[dst]['dpid']
         #self.log.info(str(src_dpid) + ' --> ' + str(dst_dpid) + ' : ' + str(path))
         next_hop = path[1]
         shortest_path_port = self.graph[src][next_hop]['ports'][src]
        
         fm = ofp_flow_mod_table_id(
                 table_id = 0,
                 command = of01.OFPFC_MODIFY,
                 match = of01.ofp_match(dl_vlan=dst_dpid),
                 actions = [ofp_action_output(port=shortest_path_port)])
         data.append(fm.pack())
     
       core.openflow.sendToDPID(src_dpid, b''.join(data))
Example #2
0
  def broadcast_paths (self):
    # tell all switches what ports to send out broadcast packets
    self.MST = nx.minimum_spanning_tree(self.graph)
    switches = {}
    for edge in self.MST.edges(data=True):
      #self.log.info(edge)
      src = edge[0]
      dst = edge[1]
  
      if src in edge[2]['ports']:
        src_outport = edge[2]['ports'][src]
        if src in switches:
          switches[src].append(src_outport)
        else:
          switches[src] = [src_outport]

      if dst in edge[2]['ports']:
        dst_outport = edge[2]['ports'][dst]
        if dst in switches:
          switches[dst].append(dst_outport)
        else:
          switches[dst] = [dst_outport]
    
    for switch, ports in switches.items():
      if 'dpid' not in self.graph.node[switch]:
        continue
      switch_dpid = self.graph.node[switch]['dpid']
      core.openflow.sendToDPID(switch_dpid, nx_flow_mod_table_id())
      out_actions = [ofp_action_output(port=p) for p in ports]
      fm = ofp_flow_mod_table_id(
              table_id = 0,
              command = of01.OFPFC_MODIFY,
              match = of01.ofp_match(dl_dst=ETHER_BROADCAST),  
              actions = out_actions)
      core.openflow.sendToDPID(switch_dpid, fm.pack())
Example #3
0
    def _add_arp_entry(self, ip_or_arp, eth=None):
        """
    Creates an entry in the switch ARP table

    You can either pass an ARP packet or an IP and Ethernet address
    """
        if not self._conn: return
        if eth is None:
            assert isinstance(ip_or_arp, pkt.arp)
            ip = ip_or_arp.protosrc
            eth = ip_or_arp.hwsrc
        else:
            ip = ip_or_arp
        self.log.debug("Populating ARP table with %s -> %s", ip, eth)

        fm = ovs.ofp_flow_mod_table_id()
        fm.xid = 0
        fm.table_id = ARP_TABLE
        fm.idle_timeout = ARP_IDLE_TIMEOUT
        fm.hard_timeout = ARP_HARD_TIMEOUT
        fm.match.dl_type = pkt.ethernet.IP_TYPE
        fm.match.nw_dst = ip
        fm.actions.append(of.ofp_action_dl_addr.set_dst(eth))
        fm.actions.append(
            ovs.nx_reg_move(src=DST_IP_REGISTER, dst=ovs.NXM_OF_IP_DST))
        fm.actions.append(ovs.nx_output_reg(reg=OUT_PORT_REGISTER))
        self._conn.send(fm)
Example #4
0
 def _clear_table(self, tid):
     if not self._conn: return
     self._invalidate()
     fm = ovs.ofp_flow_mod_table_id()
     fm.command = of.OFPFC_DELETE
     fm.table_id = tid
     self._conn.send(fm)
Example #5
0
 def _init_arp_table(self):
     # ARP_TABLE default entry
     fm = ovs.ofp_flow_mod_table_id()
     fm.table_id = ARP_TABLE
     fm.priority = 0
     fm.cookie = ARP_TABLE_COOKIE
     fm.actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER))
     self._conn.send(fm)
Example #6
0
  def set_hosts (self, host_data):
    """
    Receive list of hosts

    This gets called with a list of dictionaries that each contain information
    about a host.  Each time this is called, you get a complete list of all
    current hosts.  Each entry looks something like this:
      {"ether" : "01:02:03:04:05:06", "ip" : "1.2.3.4",
       "attached_switch" : dpid, "attached_port" : portno},
    In a datacenter, you might get this kind of information from a Cloud
    Management System.  In our case, garnet's sync_hosts() sends us the list
    of Host entities in the "emulated" network garnet is managing.  We
    receive it via the POX Messenger component and the messenger bot above.
    """
    self.last_host_data = host_data
    for host in host_data:
      self.log.info("Got host: %s", " ".join("%s=%s" % kv
                                             for kv in sorted(host.items())))
      
      host_e = str(host['ether'])
      switch_dpid = host['attached_switch']
      switch_port = host['attached_port']
      switch_name = self.graph.names[switch_dpid]

      self.hosts[host_e] = switch_dpid
      self.edge[switch_dpid] = switch_port

      if host_e in self.graph:
        self.graph.remove_node(host_e)
        # alter table info on attached switch 

      # add host to networkX graph 
      attached_switch = self.graph.names[switch_dpid]
      self.graph.add_edge(host_e, attached_switch)
      self.graph.add_edge(attached_switch, host_e)
      port_dict = {'ports': {attached_switch: switch_port}}
      self.graph.edge[host_e][attached_switch] = port_dict   
      self.graph.edge[attached_switch][host_e] = port_dict
       
      core.openflow.sendToDPID(switch_dpid, nx_flow_mod_table_id())  # Enables multiple tables
      data = []

      # construct command to remove VLAN and output to host 
      fm = ofp_flow_mod_table_id(
              table_id = 0,
              match = of01.ofp_match(dl_dst=EthAddr(host_e)),
              command = of01.OFPFC_MODIFY,
              actions = [ofp_action_strip_vlan(), ofp_action_output(port=switch_port)])
      data.append(fm.pack())

      for dst_host, dst_switch_dpid in self.hosts.items(): 
        if dst_host == host_e:
          continue
            
        if not self._connection_is_permitted(host_e, dst_host):
          # If we're not allowed to send to this host (or this host is not allowed to receive), tell our switch
          # to drop all traffic going to this host.
          self.log.info("MatchedDenyACE: src=%s dst=%s" % host_e, dst_host)
          fm = ofp_flow_mod_table_id(
                table_id = 0,
                command = of01.OFPFC_MODIFY,
                match = of01.ofp_match(dl_src=EthAddr(host_e), dl_dst=EthAddr(dst_host)),
                actions = None)
          data.append(fm.pack())
          continue

        dst_switch_name = self.graph.names[dst_switch_dpid]
        #self.log.info(switch_name + ' ' + dst_switch_name)
        if switch_name == dst_switch_name:
          continue
        
        try:
          next_hop = nx.shortest_path(self.graph, source=switch_name, target=dst_switch_name)[1]
        except:
          continue 
        shortest_path_port = self.graph[switch_name][next_hop]['ports'][switch_name]
        #self.log.info(str(host_e) + ' ' + str(dst_host))
        #self.log.info(str(dst_switch_dpid) + ' ' + str(shortest_path_port))
        
        # inform attached switch where other hosts are
        fm = ofp_flow_mod_table_id(
                table_id = 0,
                command = of01.OFPFC_MODIFY,
                match = of01.ofp_match(dl_src=EthAddr(host_e), dl_dst=EthAddr(dst_host)),
                actions = [ofp_action_set_vlan_vid(vlan_vid=dst_switch_dpid), ofp_action_output(port=shortest_path_port)])
        data.append(fm.pack())
 
        core.openflow.sendToDPID(dst_switch_dpid, nx_flow_mod_table_id())  # Enables multiple tables
        try:
          next_hop = nx.shortest_path(self.graph, source=dst_switch_name, target=switch_name)[1]
        except:
          continue
        shortest_path_port = self.graph[dst_switch_name][next_hop]['ports'][dst_switch_name]
        
        # inform other attached switches where this host is
        fm = ofp_flow_mod_table_id(
               table_id = 0,
               command = of01.OFPFC_MODIFY,
               match = of01.ofp_match(dl_src=EthAddr(dst_host), dl_dst=EthAddr(host_e)),
               actions = [ofp_action_set_vlan_vid(vlan_vid=switch_dpid), ofp_action_output(port=shortest_path_port)])
        core.openflow.sendToDPID(dst_switch_dpid, fm.pack()) 
      
      core.openflow.sendToDPID(switch_dpid, b''.join(data))
  
    self.broadcast_paths()
Example #7
0
    def sync_table(self):
        if not self._conn: return

        self._cur = {RIP_NET_TABLE: {}, RIP_PORT_TABLE: {}}
        cur = self._cur

        for e in self.table.values():
            if e.metric >= INFINITY: continue
            fm = ovs.ofp_flow_mod_table_id()
            fm.xid = 0
            fm.table_id = RIP_NET_TABLE
            fm.priority = e.size + 1  # +1 because 0 reserved for fallback
            fm.match.dl_type = pkt.ethernet.IP_TYPE
            fm.match.nw_dst = (e.ip, e.size)
            if e.dev is not None:
                # This is for a directly attached network.  It'll be looked up in
                # the port table.
                fm.actions.append(
                    ovs.nx_action_resubmit.resubmit_table(RIP_PORT_TABLE))
            else:
                # This is for a remote network.
                # Load the gateway into the dst IP; it will be looked up in the port
                # table to find the right port.  The real dst IP will get reloaded
                # from a register before egress.
                fm.actions.append(of.ofp_action_nw_addr.set_dst(e.next_hop))
                fm.actions.append(
                    ovs.nx_action_resubmit.resubmit_table(RIP_PORT_TABLE))
            cur[RIP_NET_TABLE][(e.ip, e.size)] = fm

        for e in self.table.values():
            if e.metric >= INFINITY: continue
            fm = ovs.ofp_flow_mod_table_id()
            fm.xid = 0
            fm.table_id = RIP_PORT_TABLE
            fm.priority = e.size + 1  # +1 because 0 reserved for fallback
            fm.match.dl_type = pkt.ethernet.IP_TYPE
            fm.match.nw_dst = (e.ip, e.size)
            if e.dev is not None:
                # This is for a directly attached network.  Look up the port.
                # Also, fix the dst IP address.
                port = self._conn.ports.get(e.dev)
                if port is None: continue
                fm.actions.append(
                    ovs.nx_reg_load(dst=OUT_PORT_REGISTER, value=e.dev))
                fm.actions.append(of.ofp_action_dl_addr.set_src(port.hw_addr))
                fm.actions.append(
                    ovs.nx_action_resubmit.resubmit_table(ARP_TABLE))
            else:
                # If we get to this table and we don't have a direct entry that
                # matches, we have no working route!
                # Should we install something so that we generate an ICMP unreachable
                # or something?
                pass
            cur[RIP_PORT_TABLE][(e.ip, e.size)] = fm

        if self._conn:
            data1 = b''.join(x.pack()
                             for x in self._cur[RIP_PORT_TABLE].itervalues())
            data2 = b''.join(x.pack()
                             for x in self._cur[RIP_NET_TABLE].itervalues())
            data = data1 + data2
            if data == self._prev: return  # Nothing changed

            self._clear_table(RIP_NET_TABLE)
            self._clear_table(RIP_PORT_TABLE)
            self._init_rip_net_table()
            self._init_rip_port_table()

            self.log.debug("Syncing %s port and %s net table entries",
                           len(cur[RIP_PORT_TABLE]), len(cur[RIP_NET_TABLE]))
            self._conn.send(data)

            self._prev = data
Example #8
0
 def _init_rip_port_table(self):
     # RIP_PORT_TABLE default entry (drop)
     fm = ovs.ofp_flow_mod_table_id()
     fm.table_id = RIP_PORT_TABLE
     fm.priority = 0
     self._conn.send(fm)
Example #9
0
    def _init_ingress_table(self):
        self._clear_table(INGRESS_TABLE)

        # INGRESS_TABLE: Send RIP to controller
        fm = ovs.ofp_flow_mod_table_id()
        fm.table_id = INGRESS_TABLE
        fm.cookie = RIP_PACKET_COOKIE
        fm.match.dl_type = pkt.ethernet.IP_TYPE
        fm.match.dl_dst = RIP.RIP2_ADDRESS.multicast_ethernet_address
        fm.match.nw_dst = RIP.RIP2_ADDRESS
        fm.match.nw_proto = pkt.ipv4.UDP_PROTOCOL
        fm.match.tp_dst = RIP.RIP_PORT
        fm.actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER))
        self._conn.send(fm)

        #TODO: Add RIP entry for unicast advertisements?  Or be liberal here
        #      and validate on the controller side?

        # INGRESS_TABLE: Send ARP requests for router to controller
        fm = ovs.ofp_flow_mod_table_id()
        fm.table_id = INGRESS_TABLE
        fm.cookie = ARP_REQUEST_COOKIE
        fm.match.dl_type = pkt.ethernet.ARP_TYPE
        fm.match.nw_proto = pkt.arp.REQUEST
        fm.actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER))
        for portno, portobj in self._ports.iteritems():
            if portno not in self._conn.ports: continue
            fm.match.in_port = portno
            for ip in portobj.ips:
                fm.match.nw_dst = ip
                self._conn.send(fm)

        # INGRESS_TABLE: Send ARP replies send to router to controller
        fm = ovs.ofp_flow_mod_table_id()
        fm.table_id = INGRESS_TABLE
        fm.cookie = ARP_REPLY_COOKIE
        fm.match.dl_type = pkt.ethernet.ARP_TYPE
        fm.match.nw_proto = pkt.arp.REPLY
        fm.actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER))
        for portno, portobj in self._ports.iteritems():
            if portno not in self._conn.ports: continue
            fm.match.in_port = portno
            fm.match.dl_dst = self._conn.ports[portno].hw_addr
            self._conn.send(fm)

        # INGRESS_TABLE: Send ICMP to controller
        fm = ovs.ofp_flow_mod_table_id()
        fm.table_id = INGRESS_TABLE
        fm.cookie = PING_COOKIE
        fm.match.dl_type = pkt.ethernet.IP_TYPE
        fm.match.nw_proto = pkt.ipv4.ICMP_PROTOCOL
        fm.match.tp_src = pkt.ICMP.TYPE_ECHO_REQUEST  # Type
        fm.match.tp_dst = 0  # Code
        fm.actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER))
        for portno, portobj in self._ports.iteritems():
            if portno not in self._conn.ports: continue
            fm.match.in_port = portno
            fm.match.dl_dst = self._conn.ports[portno].hw_addr
            for ip in self.all_ips:
                fm.match.nw_dst = ip
                self._conn.send(fm)

        if core.hasComponent("DHCPD"):
            # INGRESS_TABLE: Send DHCP to controller
            fm = ovs.ofp_flow_mod_table_id()
            fm.table_id = INGRESS_TABLE
            fm.cookie = DHCP_COOKIE
            fm.match.dl_type = pkt.ethernet.IP_TYPE
            fm.match.nw_proto = pkt.ipv4.UDP_PROTOCOL
            fm.match.tp_src = pkt.dhcp.CLIENT_PORT
            fm.match.tp_dst = pkt.dhcp.SERVER_PORT
            fm.actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER))
            for portno, dhcpd in core.DHCPD.get_ports_for_dpid(self.dpid):
                if portno not in self._conn.ports: continue
                if dhcpd._install_flow:
                    self.log.warn(
                        "Turning off DHCP server table entry installation.")
                    self.log.warn(
                        "You probably want to configure it with no_flow.")
                    dhcpd._install_flow = False
                fm.match.in_port = portno
                fm.match.dl_dst = pkt.ETHERNET.ETHER_BROADCAST
                fm.match.nw_dst = pkt.IPV4.IP_BROADCAST
                self._conn.send(fm)

                fm.match.dl_dst = self._conn.ports[portno].hw_addr
                fm.match.nw_dst = dhcpd.ip_addr
                self._conn.send(fm)

        # INGRESS_TABLE: IP packets (lower priority)
        fm = ovs.ofp_flow_mod_table_id()
        fm.table_id = INGRESS_TABLE
        fm.priority -= 1
        fm.match.dl_type = pkt.ethernet.IP_TYPE
        fm.actions.append(
            ovs.nx_reg_move(dst=DST_IP_REGISTER, src=ovs.NXM_OF_IP_DST))
        fm.actions.append(ovs.nx_action_dec_ttl())
        fm.actions.append(ovs.nx_action_resubmit.resubmit_table(RIP_NET_TABLE))
        self._conn.send(fm)