def __init__(self, *args, **kwargs): super(MultipathController, self).__init__(*args, **kwargs) # Random ethertype to evaluate latency self.PROBE_ETHERTYPE = 0x07C7 # Set to true once there is enoough informatio # to start multipath computation self.network_is_measured = False # Maximum delay imbalance to use a reordering buffer # Example 25ms,50ms = |(25/75)-0.5| = 0.16 self.MDI_REORDERING_THRESHOLD = 0.2 # Maximum delay imbalance threshold for not using multipath self.MDI_DROP_THRESHOLD = 0.25 # Minimum available capacity a path needs to be used in B/s self.MIN_MULTIPATH_CAPACITY = 1000 # Maximum paths allowed for a multipath flow self.MAX_PATHS_PER_MULTIPATH_FLOW = 2 # Recalculates bucket only on addition or failures in the topology self.UPDATE_FORWARDING_ON_TOPOLOGY_CHANGE_ONLY = False # Used for REST APIs wsgi = kwargs['wsgi'] wsgi.register(MultipathRestController, {API_INSTANCE_NAME: self}) # Holds the topology data and structure self.topo_shape = NetworkTopology(self)
def __init__(self, *args, **kwargs): super(MultipathController, self).__init__(*args, **kwargs) # Random ethertype to evaluate latency self.PROBE_ETHERTYPE = 0x07C7 # Set to true once there is enoough informatio # to start multipath computation self.network_is_measured = False # Maximum delay imbalance to use a reordering buffer # Example 25ms,50ms = |(25/75)-0.5| = 0.16 self.MDI_REORDERING_THRESHOLD = 0.2 # Maximum delay imbalance threshold for not using multipath self.MDI_DROP_THRESHOLD = 0.25 # If this value is changed in the configuration, the path setup # algorithm is halted if a path has more hops to the destination # than this limit. # If left -1, MDI_DROP will be used instead! # It is suited for L2 Networks where link delays will be # very similar self.MAX_HOP_DIFFERENCE = -1 # Minimum available capacity a path needs to be used in B/s self.MIN_MULTIPATH_CAPACITY = 100 # Maximum paths allowed for a multipath flow self.MAX_PATHS_PER_MULTIPATH_FLOW = 2 # Recalculates bucket only on addition or failures in the topology self.UPDATE_FORWARDING_ON_TOPOLOGY_CHANGE_ONLY = False # Recomputes forwarding continuously reardless of congestion/failures self.UPDATE_FORWARDING_CONTINOUSLY = False # High priority self.PRIORITY_PROBE_PACKETS = 65000 # Monitoring frequency for port stats. self.MONITORING_PORT_STATS_FREQUENCY = 5 self.MONITORING_PORT_STATS = False # Used for REST APIs wsgi = kwargs['wsgi'] wsgi.register(MultipathRestController, {API_INSTANCE_NAME: self}) # Holds the topology data and structure self.topo_shape = NetworkTopology(self)
def topology(self): network = str(self.txtNetwork_tab3.text()) dem = str(self.txtDEM_tab3.text()) NetworkTopology(network, dem) self.close()
class MultipathController(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] _CONTEXTS = {'wsgi': WSGIApplication} def __init__(self, *args, **kwargs): super(MultipathController, self).__init__(*args, **kwargs) # Random ethertype to evaluate latency self.PROBE_ETHERTYPE = 0x07C7 # Set to true once there is enoough informatio # to start multipath computation self.network_is_measured = False # Maximum delay imbalance to use a reordering buffer # Example 25ms,50ms = |(25/75)-0.5| = 0.16 self.MDI_REORDERING_THRESHOLD = 0.2 # Maximum delay imbalance threshold for not using multipath self.MDI_DROP_THRESHOLD = 0.25 # Minimum available capacity a path needs to be used in B/s self.MIN_MULTIPATH_CAPACITY = 1000 # Maximum paths allowed for a multipath flow self.MAX_PATHS_PER_MULTIPATH_FLOW = 2 # Recalculates bucket only on addition or failures in the topology self.UPDATE_FORWARDING_ON_TOPOLOGY_CHANGE_ONLY = False # Used for REST APIs wsgi = kwargs['wsgi'] wsgi.register(MultipathRestController, {API_INSTANCE_NAME: self}) # Holds the topology data and structure self.topo_shape = NetworkTopology(self) ########################################## # UTILITY FUNCTIONS # ########################################## def run(self): ''' Called to start the monitoring/computation in the controller It can be called with a GET to the API: /multipath/start_path_computation ''' # Network monitor module hub.spawn_after(3, self.network_monitor) # Multipath computation module hub.spawn_after(5, self.multipath_computation) def add_flow(self, datapath, priority, match, actions, buffer_id=None): ''' Adds a flow to a datapath ''' ofproto = datapath.ofproto parser = datapath.ofproto_parser inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)] if buffer_id: mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, priority=priority, match=match, instructions=inst) else: mod = parser.OFPFlowMod(datapath=datapath, priority=priority, match=match, instructions=inst) datapath.send_msg(mod) def _send_packet(self, datapath, port, pkt): ''' Instructs a datapath to output a packet to one of his ports ''' ofproto = datapath.ofproto parser = datapath.ofproto_parser pkt.serialize() self.logger.debug('packet-out %s' % (pkt,)) data = pkt.data actions = [parser.OFPActionOutput(port=port)] out = parser.OFPPacketOut(datapath=datapath, buffer_id=ofproto.OFP_NO_BUFFER, in_port=ofproto.OFPP_CONTROLLER, actions=actions, data=data) datapath.send_msg(out) def set_edge_ports(self): # Setting the edge port [port connected to host networks] # of a switch to be the highest in number for dp_id, switch in self.topo_shape.dpid_to_switch.iteritems(): switch.edge_port = len(switch.ports.keys()) def multipath_computation(self): self.set_edge_ports() while True: if not self.topo_shape.is_empty() and self.network_is_measured: computation_start = time.time() self.logger.info('Starting multipath computation sub-routine') self.topo_shape.multipath_computation() self.logger.info( 'Multipath computation finished in %f seconds', time.time() - computation_start ) hub.sleep(10) ########################################## # NETWORK DISCOVERY HANDLERS # ########################################## @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): ''' Switch features handler callback ''' self.logger.debug('EventOFPSwitchFeatures') datapath = ev.msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser match = parser.OFPMatch() actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER)] self.add_flow(datapath, 0, match, actions) # Installing the flow rules to send latency probe packets match = parser.OFPMatch(eth_type=self.PROBE_ETHERTYPE) actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER)] self.add_flow(datapath, 65000, match, actions) @set_ev_cls(event.EventSwitchEnter, MAIN_DISPATCHER) def switch_enter_handler(self, event): self.logger.info('EventSwitchEnter') self.topo_shape.add_switch(event.switch) @set_ev_cls(event.EventSwitchLeave, MAIN_DISPATCHER) def switch_leave_handler(self, event): self.logger.info('EventSwitchLeave') self.topo_shape.remove_switch(event.switch) @set_ev_cls(event.EventLinkAdd, MAIN_DISPATCHER) def link_add_handler(self, event): self.logger.info('EventLinkAdd') self.topo_shape.add_link(event) @set_ev_cls(event.EventLinkDelete, MAIN_DISPATCHER) def link_delete_handler(self, event): self.logger.info('EventLinkDelete %s' % event) # It seems to be called at the wrong moment for a bug # in ryu topology discovery module # self.topo_shape.remove_link(event) @set_ev_cls(event.EventPortAdd, MAIN_DISPATCHER) def port_add_handler(self, event): self.logger.info('EventPortAdd') self.topo_shape.add_port(event.port) @set_ev_cls(event.EventPortDelete, MAIN_DISPATCHER) def port_delete_handler(self, event): self.logger.info('EventPortDelete') self.topo_shape.remove_port(event.port) ########################################## # NETWORK MONITORING # ########################################## def network_monitor(self): ''' Monitors network RTT and Congestion ''' self.logger.info('Starting monitoring sub-routine') # First, we get an estimation of the link benchmark_network_capacity # in a state where the network will be idle. self.benchmark_network_capacity() # Then we start the periodic measurement of RTT times and port # utilization counter = 0 while True: if not self.topo_shape.is_empty(): self.logger.info('Requesting port stats to ' 'measure utilization') for dpid, s in self.topo_shape.dpid_to_switch.iteritems(): s.port_stats_request_time = time.time() # Requesting portstats to calculate controller # to switch delay and congeston self._request_port_stats(s) # Calculating peering switches RTT (once every 10 portstats # so ~10 secs) if counter % 10 == 0: self.send_latency_probe_packet(s) counter += 1 hub.sleep(1) def benchmark_network_capacity(self): ''' This mechanism is left for future work. It can send a high rate UDP from hosts to destination to and measure on the receiving switch the drop rate of the packets, to infer the link capacity Currently the max capacity is set through REST APIs ''' pass def send_latency_probe_packet(self, switch): ''' Injects latency probe packets in the network ''' self.logger.info('Injecting latency probe packets') for peer_switch, peer_port in switch.peer_to_local_port.iteritems(): self.logger.debug('Sending probe packet from %s to %s through ' ' port %s', switch, peer_switch, peer_port ) actions = [switch.dp.ofproto_parser.OFPActionOutput(peer_port)] pkt = packet.Packet() pkt.add_protocol(ethernet.ethernet(ethertype=self.PROBE_ETHERTYPE, dst=0x000000000001, src=0x000000000000) ) pkt.serialize() payload = '%d;%d;%f' % ( switch.dp.id, peer_switch.dp.id, time.time()) data = pkt.data + payload out = switch.dp.ofproto_parser.OFPPacketOut( datapath=switch.dp, buffer_id=switch.dp.ofproto.OFP_NO_BUFFER, data=data, in_port=switch.dp.ofproto.OFPP_CONTROLLER, actions=actions ) switch.dp.send_msg(out) def probe_packet_handler(self, pkt): ''' Handles a latency probe packets and computes the delay between two switches ''' try: receive_time = time.time() # Ignoring 14 bytes of ethernet header data = pkt.data[14:].split(';') send_dpid = int(data[0]) recv_dpid = int(data[1]) inc_time = float(data[2]) sample_delay = receive_time - inc_time self.topo_shape.dpid_to_switch[send_dpid].calculate_delay_to_peer( self.topo_shape.dpid_to_switch[recv_dpid], sample_delay) self.network_is_measured = True except: traceback.print_exc() self.logger.error('Unable to parse incoming probe packet') @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER) def _port_stats_reply_handler(self, ev): ''' Handles a PORT STATS response from a switch ''' ports = ev.msg.body port_stats_reply_time = time.time() # Calculate switch-controller RTT switch = self.topo_shape.dpid_to_switch[ev.msg.datapath.id] switch.calculate_delay_to_controller(port_stats_reply_time) sorted_port_table = sorted(ports, key=lambda l: l.port_no) for stat in sorted_port_table: if stat.port_no not in switch.ports: continue port = switch.ports[stat.port_no] utilization = stat.tx_bytes + stat.rx_bytes if port.last_request_time: timedelta = port_stats_reply_time - port.last_request_time datadelta = utilization - port.last_utilization_value utilization_bps = datadelta / timedelta port.capacity = port.max_capacity - utilization_bps self.logger.debug('p %d s %s utilization %f real_capacity %f', stat.port_no, switch, datadelta, port.capacity) port.last_request_time = port_stats_reply_time port.last_utilization_value = utilization @set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER) def _flow_stats_reply_handler(self, ev): ''' Handles a FLOW STATS reply ''' self.logger.debug( 'Receive flow stats response from: %016x at t: %f', ev.msg.datapath.id, time.time() ) body = ev.msg.body self.logger.debug('datapath ' 'in-port eth-dst ' 'out-port packets bytes') self.logger.debug('---------------- ' '-------- ----------------- ' '-------- -------- --------') self.logger.debug(ev.msg.body) for stat in sorted( [flow for flow in body if flow.priority == 1], key=lambda flow: (flow.match['in_port'], flow.match['eth_dst']) ): self.logger.info( '%016x %8x %17s %8x %8d %8d', ev.msg.datapath.id, stat.match['in_port'], stat.match['eth_dst'], stat.instructions[0].actions[0].port, stat.packet_count, stat.byte_count ) def _request_flow_stats(self, switch): ''' Requests flow stats for a switch ''' self.logger.debug( 'Request flow stats for: %016x at t: %f', switch.dp.id, time.time() ) parser = switch.dp.ofproto_parser req = parser.OFPFlowStatsRequest(switch.dp) switch.dp.send_msg(req) def _request_port_stats(self, switch): ''' Request port statistic to a switch ''' self.logger.debug( 'Request port stats for dp %s at t: %f', switch.dp.id, time.time() ) ofproto = switch.dp.ofproto parser = switch.dp.ofproto_parser req = parser.OFPPortStatsRequest(switch.dp, 0, ofproto.OFPP_ANY) switch.dp.send_msg(req) ########################################## # PACKET IN HANDLER # ########################################## @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def _packet_in_handler(self, ev): msg = ev.msg pkt = packet.Packet(msg.data) pkt_ethernet = pkt.get_protocols(ethernet.ethernet)[0] datapath = msg.datapath port = msg.match['in_port'] # Checking if it's the probe packet for RTT estimation if pkt_ethernet.ethertype == self.PROBE_ETHERTYPE: self.probe_packet_handler(pkt) return # Ignoring LLDP Packets try: LLDPPacket.lldp_parse(msg.data) self.logger.debug('Received LLDP Packet') return except: pass self.logger.debug('EventOFPPacketIn %s' % pkt) # Handling ARP pkt_arp = pkt.get_protocol(arp.arp) if pkt_arp: self._handle_arp(msg, datapath, port, pkt, pkt_arp) return pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) pkt_icmp = pkt.get_protocol(icmp.icmp) # Handling ICMPv4 if pkt_icmp: self._handle_icmpv4( msg, datapath, port, pkt_ethernet, pkt_ipv4, pkt_icmp) return # Handing IPv4 if pkt_ipv4: self._handle_ipv4(datapath, port, pkt_ethernet, pkt_ipv4) return pkt_ipv6 = pkt.get_protocol(ipv6.ipv6) pkt_icmp = pkt.get_protocol(icmpv6.icmpv6) # Handling ICMPv6 if pkt_icmp: self._handle_icmpv6( datapath, port, pkt_ethernet, pkt_ipv6, pkt_icmp) return # Handing IPv6 if pkt_ipv4: self._handle_ipv6(datapath, port, pkt_ethernet, pkt_ipv4) return # Unhandled self.logger.debug('Unknown packet %s' % str(pkt)) def _handle_arp(self, msg, datapath, in_port_no, pkt, pkt_arp): self.logger.debug('ARP Packet %s' % pkt_arp) def _handle_icmpv4(self, msg, datapath, in_port_no, pkt_ethernet, pkt_ipv4, pkt_icmp): self.logger.debug('Handling ICMP packet %s', pkt_icmp) def _handle_ipv4(self, datapath, port, pkt_ethernet, pkt_ipv4): self.logger.debug( 'Handling ip packet from port %d - %s' % (port, pkt_ipv4) ) def _handle_ipv6(self, datapath, port, pkt_ethernet, pkt_ipv6): self.logger.debug('Handling ipv6 packet') def _handle_icmpv6(self, datapath, port, pkt_ethernet, pkt_ipv6, pkt_icmp): self.logger.debug('Handling ICMPv6 Packet')
class MultipathController(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] _CONTEXTS = {'wsgi': WSGIApplication} def __init__(self, *args, **kwargs): super(MultipathController, self).__init__(*args, **kwargs) # Random ethertype to evaluate latency self.PROBE_ETHERTYPE = 0x07C7 # Set to true once there is enoough informatio # to start multipath computation self.network_is_measured = False # Maximum delay imbalance to use a reordering buffer # Example 25ms,50ms = |(25/75)-0.5| = 0.16 self.MDI_REORDERING_THRESHOLD = 0.2 # Maximum delay imbalance threshold for not using multipath self.MDI_DROP_THRESHOLD = 0.25 # If this value is changed in the configuration, the path setup # algorithm is halted if a path has more hops to the destination # than this limit. # If left -1, MDI_DROP will be used instead! # It is suited for L2 Networks where link delays will be # very similar self.MAX_HOP_DIFFERENCE = -1 # Minimum available capacity a path needs to be used in B/s self.MIN_MULTIPATH_CAPACITY = 100 # Maximum paths allowed for a multipath flow self.MAX_PATHS_PER_MULTIPATH_FLOW = 2 # Recalculates bucket only on addition or failures in the topology self.UPDATE_FORWARDING_ON_TOPOLOGY_CHANGE_ONLY = False # Recomputes forwarding continuously reardless of congestion/failures self.UPDATE_FORWARDING_CONTINOUSLY = False # High priority self.PRIORITY_PROBE_PACKETS = 65000 # Monitoring frequency for port stats. self.MONITORING_PORT_STATS_FREQUENCY = 5 self.MONITORING_PORT_STATS = False # Used for REST APIs wsgi = kwargs['wsgi'] wsgi.register(MultipathRestController, {API_INSTANCE_NAME: self}) # Holds the topology data and structure self.topo_shape = NetworkTopology(self) ########################################## # UTILITY FUNCTIONS # ########################################## def run(self): ''' Called to start the monitoring/computation in the controller It can be called with a GET to the API: /multipath/start_path_computation ''' # Network monitor module self.monitoringhub = hub.spawn(self.network_monitor) # Multipath computation module hub.spawn_after(5, self.multipath_computation) def add_flow(self, datapath, priority, match, actions, buffer_id=None): ''' Adds a flow to a datapath ''' ofproto = datapath.ofproto parser = datapath.ofproto_parser inst = [ parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions) ] if buffer_id: mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, priority=priority, match=match, instructions=inst) else: mod = parser.OFPFlowMod(datapath=datapath, priority=priority, match=match, instructions=inst) datapath.send_msg(mod) def _send_packet(self, datapath, port, pkt): ''' Instructs a datapath to output a packet to one of his ports ''' ofproto = datapath.ofproto parser = datapath.ofproto_parser pkt.serialize() self.logger.debug('packet-out %s' % (pkt, )) data = pkt.data actions = [parser.OFPActionOutput(port=port)] out = parser.OFPPacketOut(datapath=datapath, buffer_id=ofproto.OFP_NO_BUFFER, in_port=ofproto.OFPP_CONTROLLER, actions=actions, data=data) datapath.send_msg(out) def set_edge_ports(self): # Setting the edge port [port connected to host networks] # of a switch to be the highest in number # (In mininet it corresponds to the host port when they are # added at the end of the topology creation) for dp_id, switch in self.topo_shape.dpid_to_switch.iteritems(): switch.edge_port = len(switch.ports.keys()) def multipath_computation(self): while True: if not self.topo_shape.is_empty() and self.network_is_measured: computation_start = time.time() self.logger.info('Starting multipath computation sub-routine') self.topo_shape.multipath_computation() self.logger.info( 'Multipath computation finished in %f seconds', time.time() - computation_start) self.MONITORING_PORT_STATS = True if self.UPDATE_FORWARDING_CONTINOUSLY: hub.sleep(10) else: break def add_default_flows(self, datapath): ''' Adds default unknown flows to the controller ''' ofproto = datapath.ofproto parser = datapath.ofproto_parser match = parser.OFPMatch() actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER)] self.add_flow(datapath, 1, match, actions) # Installing the flow rules to send latency probe packets match = parser.OFPMatch(eth_type=self.PROBE_ETHERTYPE) actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER)] self.add_flow(datapath, self.PRIORITY_PROBE_PACKETS, match, actions) def add_default_for_all(self): for dpid, s in self.topo_shape.dpid_to_switch: self.add_default_flows(s.dp) def stop_monitoring(self): self.keep_monitoring = False ########################################## # NETWORK DISCOVERY HANDLERS # ########################################## @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): ''' Switch features handler callback ''' self.logger.debug('EventOFPSwitchFeatures') self.add_default_flows(ev.msg.datapath) @set_ev_cls(event.EventSwitchEnter, MAIN_DISPATCHER) def switch_enter_handler(self, event): self.logger.info('EventSwitchEnter') self.topo_shape.add_switch(event.switch) @set_ev_cls(event.EventSwitchLeave, MAIN_DISPATCHER) def switch_leave_handler(self, event): self.logger.debug('EventSwitchLeave') # Redundant for Mininet emulated network since # you cannot remove switches/link once the topology # has started # self.topo_shape.remove_switch(event.switch) @set_ev_cls(event.EventLinkAdd, MAIN_DISPATCHER) def link_add_handler(self, event): self.logger.info('EventLinkAdd') self.topo_shape.add_link(event) @set_ev_cls(event.EventLinkDelete, MAIN_DISPATCHER) def link_delete_handler(self, event): self.logger.debug('EventLinkDelete %s' % event) # Redundant for Mininet emulated network since # you cannot remove switches/link once the topology # has started # self.topo_shape.remove_link(event) @set_ev_cls(event.EventPortAdd, MAIN_DISPATCHER) def port_add_handler(self, event): self.logger.info('EventPortAdd') self.topo_shape.add_port(event.port) @set_ev_cls(event.EventPortDelete, MAIN_DISPATCHER) def port_delete_handler(self, event): self.logger.debug('EventPortDelete') # Redundant for Mininet emulated network since # you cannot remove switches/link once the topology # has started # self.topo_shape.remove_port(event.port) ########################################## # NETWORK MONITORING # ########################################## def network_monitor(self): ''' Monitors network RTT and Congestion ''' self.logger.info('Starting monitoring sub-routine') # First, we get an estimation of the link benchmark_network_capacity # in a state where the network will be idle. self.benchmark_network_capacity() self.keep_monitoring = True # Then we start the periodic measurement of RTT times and port # utilization counter = 0 while self.keep_monitoring: if not self.topo_shape.is_empty(): self.logger.debug('Requesting port stats to ' 'measure utilization') self.logger.info('\n------------------') for dpid, s in self.topo_shape.dpid_to_switch.iteritems(): s.port_stats_request_time = time.time() # Requesting portstats to calculate controller # to switch delay and congeston # if self.MONITORING_PORT_STATS: self._request_port_stats(s) # Calculating peering switches RTT (once every 10 portstats # so ~10 secs) if counter % 30 == 0: self.send_latency_probe_packet(s) counter += 1 hub.sleep(self.MONITORING_PORT_STATS_FREQUENCY) self.logger.info('Stopping monitor') def benchmark_network_capacity(self): ''' This mechanism is left for future work. It can send a high rate UDP from hosts to destination to and measure on the receiving switch the drop rate of the packets, to infer the link capacity Currently the max capacity is set through REST APIs ''' pass def send_latency_probe_packet(self, switch): ''' Injects latency probe packets in the network ''' self.logger.info('Injecting latency probe packets') for peer_switch, peer_port in switch.peer_to_local_port.iteritems(): self.logger.debug( 'Sending probe packet from %s to %s through ' ' port %s', switch, peer_switch, peer_port) actions = [switch.dp.ofproto_parser.OFPActionOutput(peer_port)] pkt = packet.Packet() pkt.add_protocol( ethernet.ethernet(ethertype=self.PROBE_ETHERTYPE, dst=0x000000000001, src=0x000000000000)) pkt.serialize() payload = '%d;%d;%f' % (switch.dp.id, peer_switch.dp.id, time.time()) data = pkt.data + payload out = switch.dp.ofproto_parser.OFPPacketOut( datapath=switch.dp, buffer_id=switch.dp.ofproto.OFP_NO_BUFFER, data=data, in_port=switch.dp.ofproto.OFPP_CONTROLLER, actions=actions) switch.dp.send_msg(out) def probe_packet_handler(self, pkt): ''' Handles a latency probe packets and computes the delay between two switches ''' try: receive_time = time.time() # Ignoring 14 bytes of ethernet header data = pkt.data[14:].split(';') send_dpid = int(data[0]) recv_dpid = int(data[1]) inc_time = float(data[2]) sample_delay = receive_time - inc_time self.topo_shape.dpid_to_switch[send_dpid].calculate_delay_to_peer( self.topo_shape.dpid_to_switch[recv_dpid], sample_delay) self.network_is_measured = True except: traceback.print_exc() self.logger.error('Unable to parse incoming probe packet') @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER) def _port_stats_reply_handler(self, ev): ''' Handles a PORT STATS response from a switch ''' ports = ev.msg.body port_stats_reply_time = time.time() # Calculate switch-controller RTT switch = self.topo_shape.dpid_to_switch[ev.msg.datapath.id] switch.calculate_delay_to_controller(port_stats_reply_time) sorted_port_table = sorted(ports, key=lambda l: l.port_no) for stat in sorted_port_table: if stat.port_no not in switch.ports: continue port = switch.ports[stat.port_no] utilization = stat.tx_bytes + stat.rx_bytes if port.last_request_time: timedelta = port_stats_reply_time - port.last_request_time datadelta = utilization - port.last_utilization_value utilization_bps = datadelta / timedelta port.capacity = port.max_capacity - utilization_bps self.logger.info( 's[%s] p[%d] utilization %2.f max_capacity %s', switch.dp.id, stat.port_no, utilization_bps, port.max_capacity) port.last_request_time = port_stats_reply_time port.last_utilization_value = utilization @set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER) def _flow_stats_reply_handler(self, ev): ''' Handles a FLOW STATS reply ''' self.logger.debug('Receive flow stats response from: %016x at t: %f', ev.msg.datapath.id, time.time()) body = ev.msg.body self.logger.debug('datapath ' 'in-port eth-dst ' 'out-port packets bytes') self.logger.debug('---------------- ' '-------- ----------------- ' '-------- -------- --------') self.logger.debug(ev.msg.body) for stat in sorted([flow for flow in body if flow.priority == 1], key=lambda flow: (flow.match['in_port'], flow.match['eth_dst'])): self.logger.info('%016x %8x %17s %8x %8d %8d', ev.msg.datapath.id, stat.match['in_port'], stat.match['eth_dst'], stat.instructions[0].actions[0].port, stat.packet_count, stat.byte_count) def _request_flow_stats(self, switch): ''' Requests flow stats for a switch ''' self.logger.debug('Request flow stats for: %016x at t: %f', switch.dp.id, time.time()) parser = switch.dp.ofproto_parser req = parser.OFPFlowStatsRequest(switch.dp) switch.dp.send_msg(req) def _request_port_stats(self, switch): ''' Request port statistic to a switch ''' self.logger.debug('Request port stats for dp %s at t: %f', switch.dp.id, time.time()) ofproto = switch.dp.ofproto parser = switch.dp.ofproto_parser req = parser.OFPPortStatsRequest(switch.dp, 0, ofproto.OFPP_ANY) switch.dp.send_msg(req) ########################################## # PACKET IN HANDLER # ########################################## @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def _packet_in_handler(self, ev): msg = ev.msg pkt = packet.Packet(msg.data) pkt_ethernet = pkt.get_protocols(ethernet.ethernet)[0] datapath = msg.datapath port = msg.match['in_port'] # Checking if it's the probe packet for RTT estimation if pkt_ethernet.ethertype == self.PROBE_ETHERTYPE: self.probe_packet_handler(pkt) return # Ignoring LLDP Packets try: LLDPPacket.lldp_parse(msg.data) self.logger.debug('Received LLDP Packet') return except: pass self.logger.debug('EventOFPPacketIn %s' % pkt) # Handling ARP pkt_arp = pkt.get_protocol(arp.arp) if pkt_arp: self._handle_arp(msg, datapath, port, pkt, pkt_arp) return pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) pkt_icmp = pkt.get_protocol(icmp.icmp) # Handling ICMPv4 if pkt_icmp: self._handle_icmpv4(msg, datapath, port, pkt_ethernet, pkt_ipv4, pkt_icmp) return # Handing IPv4 if pkt_ipv4: self._handle_ipv4(datapath, port, pkt_ethernet, pkt_ipv4) return pkt_ipv6 = pkt.get_protocol(ipv6.ipv6) pkt_icmp = pkt.get_protocol(icmpv6.icmpv6) # Handling ICMPv6 if pkt_icmp: self._handle_icmpv6(datapath, port, pkt_ethernet, pkt_ipv6, pkt_icmp) return # Handing IPv6 if pkt_ipv4: self._handle_ipv6(datapath, port, pkt_ethernet, pkt_ipv4) return # Unhandled self.logger.debug('Unknown packet %s' % str(pkt)) def _handle_arp(self, msg, datapath, in_port_no, pkt, pkt_arp): self.logger.debug('ARP Packet %s' % pkt_arp) def _handle_icmpv4(self, msg, datapath, in_port_no, pkt_ethernet, pkt_ipv4, pkt_icmp): self.logger.debug('Handling ICMP packet %s', pkt_icmp) def _handle_ipv4(self, datapath, port, pkt_ethernet, pkt_ipv4): self.logger.debug('Handling ip packet from port %d - %s' % (port, pkt_ipv4)) def _handle_ipv6(self, datapath, port, pkt_ethernet, pkt_ipv6): self.logger.debug('Handling ipv6 packet') def _handle_icmpv6(self, datapath, port, pkt_ethernet, pkt_ipv6, pkt_icmp): self.logger.debug('Handling ICMPv6 Packet')