예제 #1
0
 def __init__(self, *args, **kwargs):
     super(SDNTrace, self).__init__(*args, **kwargs)
     # Configuration File
     self.config = ConfigReader()
     # Topology
     self.switches = Switches()
     self.links = Links()
     # Topology Discovery App
     self.topo_disc = TopologyDiscovery()
     # Graph Coloring App
     self.graph_coloring = GraphColoring()
     # Trace_Manager App
     self.tracer = TraceManager()
     print('SDNTrace Ready!')
예제 #2
0
 def _topology_discovery(self):
     """
         Keeps looping Switches() every PACKET_OUT_INTERVAL seconds
         Send a packet_out w/ LLDP to every port found
     """
     if self.active:
         vlan = ConfigReader().topo.vlan_discovery
         while True:
             # Only send PacketOut + LLDP when more than one switch exists
             if len(Switches()) > 1:
                 for switch in Switches().get_switches():
                     for port in switch.ports:
                         pkt = prepare_lldp_packet(switch, port, vlan)
                         switch.send_packet_out(port, pkt.data, lldp=True)
             hub.sleep(ConfigReader().topo.packet_out_interval)
예제 #3
0
 def switch_info(dpid):
     """
         /sdntrace/switches/00004af7b0f68749/info
         {
             "datapath_id": "00004af7b0f68749",
             "switch_color": "char",
             "tcp_port": integer,
             "openflow_version": "string",
             "switch_vendor": "string",
             "ip_address": "ip_address",
             "switch_name": "string",
             "number_flows": integer,
             "distance": integer
         }
         or
         {} if not found
     """
     info = dict()  # in case user requests before switch appears
     for switch in Switches().get_switches():
         if switch.name == dpid:
             info = {
                     'switch_name': switch.switch_name,
                     'switch_vendor': switch.switch_vendor,
                     'datapath_id': switch.datapath_id,
                     'switch_color': switch.color,
                     'openflow_version': switch.version_name,
                     'ip_address': switch.addr[0],
                     'tcp_port': switch.addr[1],
                     'number_flows': len(switch.flows),
                     'distance': switch.distance
                     }
             break
     return json.dumps(info)
예제 #4
0
    def tracepath(self):
        """
            Do the trace path
            The logic is very simple:
            1 - Generate the probe packet using entries provided
            2 - Results a result and the packet_in (used to generate new probe)
                Possible results: 'timeout' meaning the end of trace
                                  or the trace step {'dpid', 'port'}
                Some networks do vlan rewrite, so it is important to get the
                packetIn msg with the header
            3 - If result is a trace step, send PacketOut to the switch that
                originated the PacketIn. Repeat till reaching timeout
        """
        print("Starting Trace Path for ID %s" % self.id)
        entries = self.init_entries
        color = self.init_switch.color
        switch = self.init_switch
        # Add initial trace step
        self.rest.add_trace_step(self.trace_result, trace_type='starting',
                                 dpid=switch.datapath_id,
                                 port=entries['trace']['switch']['in_port'])

        # A loop waiting for trace_ended. It changes to True when reaches timeout
        while not self.trace_ended:
            in_port, probe_pkt = generate_trace_pkt(entries, color, self.id, self.mydomain)
            result, packet_in = self.send_trace_probe(switch, in_port, probe_pkt)

            if result == 'timeout':
                self.rest.add_trace_step(self.trace_result, trace_type='last')
                print("Intra-Domain Trace Completed!")
                self.trace_ended = True
            else:
                self.rest.add_trace_step(self.trace_result, trace_type='trace',
                                         dpid=result['dpid'], port=result['port'])
                if self.check_loop():
                    self.rest.add_trace_step(self.trace_result, trace_type='last',
                                             reason='loop')
                    self.trace_ended = True
                    break
                # If we got here, that means we need to keep going.
                # Prepare next packet
                prepare = prepare_next_packet
                entries, color, switch = prepare(Switches(), entries, result, packet_in)

        # Check if the last switch has inter-domain neighbors
        # if so, infer is current flows go through the interdomain port
        if switch.is_inter_domain:
            out_port = switch.match_flow(in_port, probe_pkt)
            if out_port in switch.inter_domain_ports.keys():
                neighbor_conf = switch.inter_domain_ports[out_port]
                self.trace_interdomain(switch, neighbor_conf.color_value, entries, in_port)

        # Add final result to trace_results_queue
        # if inter_domain, add a status to the result, f. i, 'running'
        t_result = {"request_id": self.id, "result": self.trace_result,
                    "start_time": str(self.rest.start_time),
                    "total_time": self.rest.get_time()}

        self.trace_mgr.add_result(self.id, t_result)
예제 #5
0
    def get_init_switch(self):
        """
        
            Returns:

        """
        dpid = self.init_entries['trace']['switch']['dpid']
        return Switches().get_switch(dpid, by_name=True)
예제 #6
0
    def _update_topology(self):
        """
            Update topology
        """

        self._topology = {}

        # Collect all inter-domain info from the configuration file
        inter_conf = ConfigReader().interdomain.locals
        inter_names = ConfigReader().interdomain.neighbors

        # Create a temporary dictionary with all inter-domain ports adding
        #  the remote domain's name to it
        inter = dict()
        for node in inter_conf:
            sw_dpid, sw_port = node.split(':')
            inter[sw_dpid] = {}
            for neighbor in inter_names:
                local = ConfigReader().interdomain.get_local_sw(neighbor)
                if local == sw_dpid:
                    inter[sw_dpid][sw_port] = {
                        'type': 'interdomain',
                        'domain_name': neighbor
                    }

        # Create the final dictionary with all switches and ports
        #   Uses the inter dict to add inter-domain info. If no inter-domain
        #   is found, assume it is a host port - for now.
        switches = dict()
        for switch in Switches().get_switches():
            switches[switch.name] = {}
            for port in switch.ports:
                try:
                    switches[switch.name][port] = inter[switch.name][str(port)]
                except (KeyError, ValueError):
                    switches[switch.name][port] = {
                        'type': 'host',
                        'host_name': 'no_name'
                    }

        try:
            # Now, update the switches dictionary with the link info from the
            #   SDNTrace.links, which is the Links class.
            for link in Links().links:
                switches[link.switch_a][link.port_a] = {
                    'type': 'link',
                    'neighbor_dpid': link.switch_z,
                    'neighbor_port': link.port_z
                }
                switches[link.switch_z][link.port_z] = {
                    'type': 'link',
                    'neighbor_dpid': link.switch_a,
                    'neighbor_port': link.port_a
                }
        except KeyError:
            pass

        self._topology = switches
예제 #7
0
 def create_adjacencies(links):
     """
         Everytime Links() is updated, update all
         adjacencies between switches
         Args:
             links: Links()
     """
     for switch in Switches().get_switches():
         switch.create_adjacencies(links)
예제 #8
0
    def install_colored_flows(self):
        """
            First define colors for each node
            Delete old flows
            Then push new flows
        """
        # TODO: Break this method into smaller ones
        colors = self.define_colors(Switches().get_switches())
        # Compare received colors with self.old_colors
        # If the same, ignore
        if colors is not None:
            self.colors = colors
            if len(self.old_colors) is 0:
                self.old_colors = self.colors
            else:
                if self.colors == self.old_colors:
                    return

        # Check all colors in use
        # For each switch:
        # 1 - Delete colored flows
        # 2 - For each switch, check colors of neighbors
        # 3 - Install all neighbors' colors
        for switch in Switches().get_switches():
            # 1 - Delete old colored flows
            switch.delete_colored_flows()
            # 2 - Check colors of all other switches
            neighbor_colors = []
            for color in self.colors:
                # Get Dict Key. Just one Key
                for key in color:
                    if key != switch.name:
                        neighbor = Switches().get_switch(key, by_name=True)
                        if neighbor in switch.adjacencies_list:
                            neighbor_colors.append(color[key])
            # 3 - Install all colors from other switches
            # in some cases, if two neighbors have the same color, the same flow
            # will be installed twice. It is not an issue.
            for color in neighbor_colors:
                switch.install_color(color)
            del neighbor_colors
            switch.old_color = switch.color
        self.old_colors = self.colors
예제 #9
0
 def save_current_colors(self):
     """
         Save all current colors
         If the coloring flow needs to be replaced, it is
             important to know the last color to use as a
             match for deleting old flows
             Just copy current color for old_color variable
     """
     for switch in Switches().get_switches():
         switch.old_color = switch.color
예제 #10
0
 def list_flows(dpid):
     """
         /sdntrace/switches/{dpid}/flows
        {
             "dpid": "0000000000000001",
             "number_flows": 5,
             "flows": [
                 {
                     "idle_timeout": 0,
                     "cookie": 2000002,
                     "priority": 50001,
                     "hard_timeout": 0,
                     "byte_count": 0,
                     "duration_nsec": 71000000,
                     "packet_count": 0,
                     "duration_sec": 4,
                     "table_id": 0,
                     "match": {
                         "wildcards": 3678458,
                         "dl_src": "ee:ee:ee:11:11:11",
                         "in_port": 1
                         ...
                     },
                     "actions | instructions": [
                         {
                             "max_len": 65509,
                             "type": "OFPActionOutput(0)",
                             "port": 65533
                         }
                         ...
                     ],
                 }
                 ...
             ]
         }
         or
         {} if not found
     """
     body = dict()  # in case user requests before switch appears
     flows = list()
     for switch in Switches().get_switches():
         if switch.name == dpid:
             for flow in sorted(sorted(switch.flows, key=lambda f: f.duration_sec, reverse=True),
                                key=lambda f: f.priority, reverse=True):
                 flow_stats = process_flow_stats(switch, flow)
                 flows.append(flow_stats)
             # Finished loop, process output
             final = {
                 "dpid": dpid,
                 "number_flows": len(flows),
                 "flows": flows
             }
             body = json.dumps(final)
             break
     return body
예제 #11
0
    def __init__(self, trace_manager, r_id, initial_entries):
        self.switches = Switches()
        self.trace_mgr = trace_manager
        self.id = r_id
        self.init_entries = initial_entries
        self.trace_result = []
        self.trace_ended = False
        self.init_switch = self.get_init_switch()
        self.rest = FormatRest()
        self.config = ConfigReader()

        # Support for inter-domain
        self.inter_domain = self.trace_mgr.is_interdomain(self.id)
        self.mydomain = self.config.interdomain.my_domain
예제 #12
0
 def _push_colors(self):
     """
         This routine will run every PUSH_COLORS_INTERVAL interval
         and process the Links() to associate colors to OFSwitches.
         Flows will be pushed to switches with the dl_src field set
         to the defined color outputting to controller
         Args:
             self
     """
     while True:
         if len(Switches()) > 1:
             if len(Links()) is not 0:
                 self.install_colored_flows()
         hub.sleep(ConfigReader().trace.push_color_interval)
예제 #13
0
    def entry_validation(self, entries):
        """
            Make sure the switch selected by the user exists.
            In fact, this method has to validate all params inputed.
        Returns:
            True: all set
            False: switch requested doesn't exist

        """
        # TODO: improve with more tests
        dpid = entries['trace']['switch']['dpid']
        init_switch = Switches().get_switch(dpid, by_name=True)
        if not isinstance(init_switch, bool):
            return True
        return False
예제 #14
0
    def add_trace_step(self,
                       trace_result,
                       trace_type,
                       reason='done',
                       dpid=None,
                       port=None,
                       msg="none"):
        """
            Used to define the new REST interface.
                Use docs/trace_results.txt  for examples.
                Only this method should write to self.trace_result
            Args:
                trace_result: variable with results
                trace_type: type of trace
                reason: reason in case trace_type == last
                dpid: switch's dpid
                port: switch's OpenFlow port_no
                msg: message in case of reason == error
        """
        step = dict()
        step["type"] = trace_type
        # Get port name instead of port_no
        if dpid:
            new_switch = Switches().get_switch(dpid, by_name=True)
            try:
                port_name = new_switch.ports[port]["name"]
            except:
                raise Exception('Restart Mininet: port Disappeared ')

        if trace_type == 'starting':
            step["dpid"] = new_switch.name
            step["port"] = port_name
            step["time"] = str(self.start_time)
        elif trace_type == 'trace':
            step["dpid"] = new_switch.name
            step["port"] = port_name
            step["time"] = self.get_time()
        elif trace_type == 'last':
            step["reason"] = reason
            step["msg"] = msg
            step["time"] = self.get_time()
        elif trace_type == 'intertrace':
            pass
        # Add to trace_result array
        trace_result.append(step)
예제 #15
0
 def switch_ports(dpid):
     """
         {
         "1": {
                 "speed": "10GB_FD",
                 "name": "s1-eth1",
                 "port_no": 1,
                 "status": "down|up"
             },
         "2": {
                 "speed": "10GB_FD",
                 "name": "s1-eth2",
                 "port_no": 2,
                 "status": "down|up"
             }
         }
     """
     body = dict()
     for switch in Switches().get_switches():
         if switch.name == dpid:
             ports = switch.ports
             body = json.dumps(ports)
             break
     return body
예제 #16
0
 def list_switches():
     switches = [switch.name for switch in Switches().get_switches()]
     return json.dumps(switches)
예제 #17
0
 def list_colors():
     colors = {}
     for switch in Switches().get_switches():
         colors[switch.name] = {'color': switch.color, 'old_color': switch.old_color}
     return json.dumps(colors)
예제 #18
0
 def switch_neighbors(dpid):
     neighbors = list()
     for switch in Switches().get_switches():
         if switch.name == dpid:
             neighbors = [neighbor.name for neighbor in switch.adjacencies_list]
     return json.dumps(neighbors)
예제 #19
0
class SDNTrace(app_manager.RyuApp):

    OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION, ofproto_v1_3.OFP_VERSION]

    def __init__(self, *args, **kwargs):
        super(SDNTrace, self).__init__(*args, **kwargs)
        # Configuration File
        self.config = ConfigReader()
        # Topology
        self.switches = Switches()
        self.links = Links()
        # Topology Discovery App
        self.topo_disc = TopologyDiscovery()
        # Graph Coloring App
        self.graph_coloring = GraphColoring()
        # Trace_Manager App
        self.tracer = TraceManager()
        print('SDNTrace Ready!')

    @set_ev_cls(event.EventSwitchEnter)
    def get_topology_data(self, ev):
        """
            Get switches' IPs and ports. This method is
            detected after EventOFPSwitchFeatures, so we just
            update the switch address.
        Args:
            ev: EventSwitchEnter received
        """
        self.switches.update_switch_address(ev.switch.dp)

    @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
    def switch_features_handler(self, ev):
        """
            FeatureReply - For each new switch that connects, add to the
            switches dictionary. This dict will be used for sending packetOut
            and generate topology and colors.
            When instantiating a switch, clears old colored flows
            and adds the default LLDP flow
            Args:
                ev: EventOFPSwitchFeatures received
        """
        self.switches.add_switch(ev)

    @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER)
    def port_status(self, ev):
        """
            Process OFP_Port_Status
            Add or Remove ports from OFSwitch.ports
            Args:
                ev: EventOFPPortStatus received
        """
        switch = self.switches.get_switch(ev.msg.datapath)
        switch.port_status(ev)

    @set_ev_cls(ofp_event.EventOFPStateChange, DEAD_DISPATCHER)
    def remove_switch(self, ev):
        """
            If DEAD_DISPATCHER received, remove switch from self.switches
            Args:
                ev: packet captured
        """
        self.switches.del_switch(ev)

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def packet_in_handler(self, ev):
        """
            Process PacketIn
            PacketIN messages are used for topology discovery and traces
            Args:
                ev: EventOFPPacketIn message
        """
        switch = self.switches.get_switch('%016x' % ev.msg.datapath.id,
                                          by_name=True)

        action, result, in_port = switch.process_packetIn(ev)

        if action is 1:  # LLDP
            self.topo_disc.handle_packet_in_lldp(link=result)

        elif action is 2:  # Trace packets
            self.tracer.process_probe_packet(ev, result, in_port, switch)

    @set_ev_cls(ofp_event.EventOFPErrorMsg, MAIN_DISPATCHER)
    def openflow_error(self, ev):
        """
            Print Error Received. Useful for troubleshooting
            Args:
                ev: EventOFPErrorMsg
        """
        print('OFPErrorMsg received: type=0x%02x code=0x%02x message=%s' %
              (ev.msg.type, ev.msg.code, utils.hex_array(ev.msg.data)))

    @set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER)
    def handle_flow_stats(self, ev):
        """
            Process OFPFlowStatsReply saving all the flows
            associated with a switch. These flows will be used for
            the inter-domain trace
            Args:
                ev: EventOFPFlowStatsReply message
        """
        switch = self.switches.get_switch(ev.msg.datapath)
        switch.save_flows(flows=ev.msg.body)

    @set_ev_cls(ofp_event.EventOFPPortDescStatsReply, MAIN_DISPATCHER)
    def port_desc_stats_reply_handler(self, ev):
        """
            Multipart Port Stats Description
            Only used for OF1.3
            Args:
                ev: EventOFPPortDescStatsReply received
        """
        switch = self.switches.get_switch(ev.msg.datapath)
        switch.process_port_desc_stats_reply(ev)

    @set_ev_cls(ofp_event.EventOFPDescStatsReply, MAIN_DISPATCHER)
    def description_stats_reply_handler(self, ev):
        """
            Multipart Description Stats Description
            Only used for OF1.3
            Args:
                ev: EventOFPDescStatsReply received
        """
        switch = self.switches.get_switch(ev.msg.datapath)
        switch.process_description_stats_reply(ev)

    @set_ev_cls(ofp_event.EventOFPEchoReply, MAIN_DISPATCHER)
    def echo_reply_handler(self, ev):
        """
            Echo Res Message
            Args:
                ev: EchoReply message received
        """
        now = float(time.time())
        switch = self.switches.get_switch(ev.msg.datapath)
        switch.process_echo_reply_timestamp(now, ev.msg.data)