def __init__(self, *args, **kwargs): super(Topology, self).__init__(*args, **kwargs) self.is_active = True self.explicit_drop = True self.ports = PortDataState() self.links = _LinkState() self.lldp_event = hub.Event() self.link_event = hub.Event() self.threads.append(hub.spawn(self.lldp_loop)) self.threads.append(hub.spawn(self.link_loop))
def __init__(self, *args, **kwargs): super(L2Switch, self).__init__(*args, **kwargs) self.name = 'switches' self.dps = {} # datapath_id => Datapath class self.port_state = {} # datapath_id => ports self.ports = PortDataState() # Port class -> PortData class self.links = LinkState() # Link class -> timestamp self.is_active = True self.lldp_event = hub.Event() self.link_event = hub.Event() self.threads.append(hub.spawn(self.lldp_loop)) self.threads.append(hub.spawn(self.link_loop)) self.link_discovery = True
class Topology(LocalService): _EVENTS = {EventPortAdd, EventPortDelete, EventPortModify, EventLinkAdd, EventLinkDelete} """ Ryu topology module ported to Local Controller """ DEFAULT_TTL = 64 LLDP_PACKET_LEN = len(LLDPPacket.lldp_packet(0, 0, DONTCARE_STR, 0)) LLDP_SEND_GUARD = .05 LLDP_SEND_PERIOD_PER_PORT = .9 TIMEOUT_CHECK_PERIOD = 5. LINK_TIMEOUT = TIMEOUT_CHECK_PERIOD * 2 LINK_LLDP_DROP = 5 LLDP_PRIORITY = 0xFFFF def __init__(self, *args, **kwargs): super(Topology, self).__init__(*args, **kwargs) self.is_active = True self.explicit_drop = True self.ports = PortDataState() self.links = _LinkState() self.lldp_event = hub.Event() self.link_event = hub.Event() self.threads.append(hub.spawn(self.lldp_loop)) self.threads.append(hub.spawn(self.link_loop)) def _port_added(self, port): lldp_data = LLDPPacket.lldp_packet( port.dpid, port.port_no, port.hw_addr, self.DEFAULT_TTL) self.ports.add_port(port, lldp_data) def _switch_enter(self, dp): super(Topology, self)._switch_enter(dp) self._init_flows() ports = [Port(PortData(dp.id, port, dp.ofproto)) for port in dp.ports.values()] for port in ports: if not port.is_reserved(): self._port_added(port) self.ryuo.switch_enter(dp.id, [port.port_data for port in self.ports.keys()]) self.lldp_event.set() def _init_flows(self): self._logger.info('Init flow table.') self.ofctl.set_packet_in_flow(cookie=0, priority=self.LLDP_PRIORITY, eth_type=ETH_TYPE_LLDP, eth_dst=lldp.LLDP_MAC_NEAREST_BRIDGE) def _switch_leave(self): super(Topology, self)._switch_leave() self.links.clear() self.ports.clear() def _get_port(self, port_no): for port in self.ports.keys(): if port.port_no == port_no: return port def _report_port_added(self, port): self.send_event_to_observers(EventPortAdd(port)) self.ryuo.port_added(port.port_data) def _report_port_deleted(self, port): self.send_event_to_observers(EventPortDelete(port)) self.ryuo.port_deleted(port.port_data) def _report_port_modified(self, port): self.send_event_to_observers(EventPortModify(port)) self.ryuo.port_modified(port.port_data) @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER) def _port_status_change(self, ev): if self.dp is None: return msg = ev.msg reason = msg.reason dp = msg.datapath ofpport = msg.desc ofp = dp.ofproto if reason == ofp.OFPPR_ADD: self._logger.info('Port %d added.', ofpport.port_no) port = Port(PortData(self.dp.id, ofpport, ofp)) if not port.is_reserved(): self._port_added(port) self._report_port_added(port) self.lldp_event.set() elif reason == ofp.OFPPR_DELETE: self._logger.info('Port %d deleted.', ofpport.port_no) port = self._get_port(ofpport.port_no) if port and not port.is_reserved(): del self.ports[Port(PortData(self.dp.id, ofpport))] self._report_port_deleted(port) self._link_down(port) self.lldp_event.set() else: self._logger.info('Port %d modified.', ofpport.port_no) port = self._get_port(ofpport.port_no) port.lldp_reply = False if port and not port.is_reserved(): port.modify(ofpport) self._report_port_modified(port) if self.ports.set_down(port): self._link_down(port) self.lldp_event.set() def send_lldp_packet(self, port): try: port_data = self.ports.lldp_sent(port) except KeyError as e: # ports can be modified during our sleep in self.lldp_loop() self._logger.warning('Missing port %d, %s.', port.port_no, e) return if port_data.is_down: return dp = self.dp if dp is None: # datapath was already deleted self._logger.warning('Switch left.') return self.ofctl.send_packet_out(in_port=dp.ofproto.OFPP_CONTROLLER, output=port.port_no, data=port_data.lldp_data) def lldp_loop(self): while self.is_active: self._logger.debug('LLDP loop') self.lldp_event.clear() now = time.time() timeout = None ports_now = [] ports = [] for (key, data) in self.ports.items(): if data.timestamp is None: ports_now.append(key) continue expire = data.timestamp + self.LLDP_SEND_PERIOD_PER_PORT if expire <= now: ports.append(key) continue if timeout is None or timeout > expire - now: timeout = expire - now # break for port in ports_now: self.send_lldp_packet(port) self._logger.debug('Sending LLDP to %d.%d', port.dpid, port.port_no) for port in ports: self.send_lldp_packet(port) self._logger.debug('Sending LLDP to %d.%d', port.dpid, port.port_no) hub.sleep(self.LLDP_SEND_GUARD) # don't burst if timeout is not None and ports: timeout = 0 # We have already slept # LOG.debug('lldp sleep %s', timeout) self.lldp_event.wait(timeout=timeout) def _report_link_deleted(self, link): self.send_event_to_observers(EventLinkDelete(link)) self.ryuo.link_deleted(link.src.port_data, link.dst.port_data) def _report_link_added(self, link): self.send_event_to_observers(EventLinkAdd(link)) self.ryuo.link_added(link.src.port_data, link.dst.port_data) @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def packet_in_handler(self, ev): self._logger.debug('Packet in') msg = ev.msg ofp = msg.datapath.ofproto pkt = packet.Packet(msg.data) headers = dict((p.protocol_name, p) for p in pkt.protocols if type(p) != str) src_mac = headers[ETHERNET].src try: src_dpid, src_port_no = LLDPPacket.lldp_parse(msg.data) except LLDPPacket.LLDPUnknownFormat as e: self._logger.debug('LLDPUnknownFormat %s', e) return dst_port_no = self.ofctl.get_packet_in_inport(msg) dst = self._get_port(dst_port_no) self._logger.debug('LLDP from %d.%d -> %d', src_dpid, src_port_no, dst_port_no) if not dst: self._logger.warning('Dst not found.') return self.ports.lldp_received(dst) src = Port(PortData(src_dpid, port_no=src_port_no, hw_addr=src_mac)) old_peer = self.links.get_peer(dst) need_update = False if old_peer and old_peer != src: self._logger.info('Peer changed.') self._report_link_deleted(Link(old_peer, dst)) need_update = True link = Link(src, dst) if link not in self.links: need_update = True self._report_link_added(Link(src, dst)) self.lldp_event.set() # Always return false, since we don't have the reverse link information if need_update: self.links.update_link(src, dst) self._logger.info('Update link %d.%d -> %d.%d', src.dpid, src.port_no, dst.dpid, dst.port_no) def link_loop(self): while self.is_active: self.link_event.clear() now = time.time() deleted = [] for (link, timestamp) in self.links.items(): # LOG.debug('%s timestamp %d (now %d)', link, timestamp, now) if timestamp + self.LINK_TIMEOUT < now: src = link.src if src in self.ports: port_data = self.ports.get_port(src) # LOG.debug('port_data %s', port_data) if port_data.lldp_dropped() > self.LINK_LLDP_DROP: deleted.append(link) for link in deleted: self.links.link_down(link) # LOG.debug('delete %s', link) self._report_link_deleted(link) self.link_event.wait(timeout=self.TIMEOUT_CHECK_PERIOD) def _link_down(self, port): try: src, rev_link_dst = self.links.dst_port_deleted(port) self._logger.info('Link down: %d.%d -> %d.%d', src.dpid, src.port_no, port.dpid, port.port_no) except KeyError: self._logger.info('Cannot find peer of %d.%d', port.dpid, port.port_no) return self._report_link_deleted(Link(src, port)) def close(self): self.is_active = False self.lldp_event.set() self.link_event.set() super(Topology, self).close()
class L2Switch(RyuApp): _EVENTS = [ event.EventSwitchEnter, event.EventSwitchLeave, event.EventPortAdd, event.EventPortDelete, event.EventPortModify, event.EventLinkAdd, event.EventLinkDelete, event.EventArpReceived ] OFP_VERSIONS = [OFP_VERSION] DEFAULT_TTL = 120 # unused. ignored. LLDP_PACKET_LEN = len(LLDPPacket.lldp_packet(0, 0, DONTCARE_STR, 0)) LLDP_SEND_GUARD = .05 LLDP_SEND_PERIOD_PER_PORT = .9 TIMEOUT_CHECK_PERIOD = 15. LINK_TIMEOUT = TIMEOUT_CHECK_PERIOD * 2 LINK_LLDP_DROP = 5 def __init__(self, *args, **kwargs): super(L2Switch, self).__init__(*args, **kwargs) self.name = 'switches' self.dps = {} # datapath_id => Datapath class self.port_state = {} # datapath_id => ports self.ports = PortDataState() # Port class -> PortData class self.links = LinkState() # Link class -> timestamp self.is_active = True self.lldp_event = hub.Event() self.link_event = hub.Event() self.threads.append(hub.spawn(self.lldp_loop)) self.threads.append(hub.spawn(self.link_loop)) self.link_discovery = True def close(self): self.is_active = False if self.link_discovery: self.lldp_event.set() self.link_event.set() hub.joinall(self.threads) def lldp_loop(self): while self.is_active: self.lldp_event.clear() now = time.time() timeout = None ports_now = [] ports = [] for (key, data) in self.ports.items(): if data.timestamp is None: ports_now.append(key) continue expire = data.timestamp + self.LLDP_SEND_PERIOD_PER_PORT if expire <= now: ports.append(key) continue timeout = expire - now break for port in ports_now: self.send_lldp_packet(port.dpid, port.port_no, port.hw_addr) for port in ports: self.send_lldp_packet(port.dpid, port.port_no, port.hw_addr) hub.sleep(self.LLDP_SEND_GUARD) # don't burst if timeout is not None and ports: timeout = 0 # We have already slept self.lldp_event.wait(timeout=timeout) def link_loop(self): while self.is_active: self.link_event.clear() now = time.time() deleted = [] for (link, timestamp) in self.links.items(): if timestamp + self.LINK_TIMEOUT < now: src = link.src if src in self.ports: port_data = self.ports.get_port(src) if port_data.lldp_dropped() > self.LINK_LLDP_DROP: deleted.append(link) for link in deleted: self.links.link_down(link) self.send_event_to_observers(event.EventLinkDelete(link)) dst = link.dst rev_link = Link(dst, link.src) if rev_link not in deleted: # It is very likely that the reverse link is also # disconnected. Check it early. expire = now - self.LINK_TIMEOUT self.links.rev_link_set_timestamp(rev_link, expire) if dst in self.ports: self.ports.move_front(dst) self.lldp_event.set() self.link_event.wait(timeout=self.TIMEOUT_CHECK_PERIOD) def _get_switch(self, dpid): if dpid in self.dps: switch = Switch(self.dps[dpid]) for ofpport in self.port_state[dpid].itervalues(): switch.add_port(ofpport) return switch def _get_port(self, dpid, port_no): switch = self._get_switch(dpid) if switch: for p in switch.ports: if p.port_no == port_no: return p def _port_added(self, port): lldp_data = LLDPPacket.lldp_packet(port.dpid, port.port_no, port.hw_addr, self.DEFAULT_TTL) self.ports.add_port(port, lldp_data) LOG.debug('_port_added dpid=%s, port_no=%s, live=%s', port.dpid, port.port_no, port.is_live()) def _register(self, dp): assert dp.id is not None assert dp.id not in self.dps self.dps[dp.id] = dp self.port_state[dp.id] = PortState() for port in dp.ports.values(): self.port_state[dp.id].add(port.port_no, port) def _link_down(self, port): try: dst, rev_link_dst = self.links.port_deleted(port) except KeyError: # LOG.debug('key error. src=%s, dst=%s', # port, self.links.get_peer(port)) return link = Link(port, dst) self.send_event_to_observers(event.EventLinkDelete(link)) if rev_link_dst: rev_link = Link(dst, rev_link_dst) self.send_event_to_observers(event.EventLinkDelete(rev_link)) self.ports.move_front(dst) @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER) def port_status_handler(self, ev): msg = ev.msg reason = msg.reason dp = msg.datapath ofpport = msg.desc if reason == dp.ofproto.OFPPR_ADD: LOG.debug( 'A port was added.' + '(datapath id = %s, port number = %s)', dp.id, ofpport.port_no) self.port_state[dp.id].add(ofpport.port_no, ofpport) self.send_event_to_observers( event.EventPortAdd(Port(dp.id, dp.ofproto, ofpport))) if not self.link_discovery: return port = self._get_port(dp.id, ofpport.port_no) if port and not port.is_reserved(): self._port_added(port) self.lldp_event.set() elif reason == dp.ofproto.OFPPR_DELETE: LOG.debug( 'A port was deleted.' + '(datapath id = %s, port number = %s)', dp.id, ofpport.port_no) self.port_state[dp.id].remove(ofpport.port_no) self.send_event_to_observers( event.EventPortDelete(Port(dp.id, dp.ofproto, ofpport))) if not self.link_discovery: return port = self._get_port(dp.id, ofpport.port_no) if port and not port.is_reserved(): self.ports.del_port(port) self._link_down(port) self.lldp_event.set() else: assert reason == dp.ofproto.OFPPR_MODIFY LOG.debug( 'A port was modified.' + '(datapath id = %s, port number = %s)', dp.id, ofpport.port_no) self.port_state[dp.id].modify(ofpport.port_no, ofpport) self.send_event_to_observers( event.EventPortModify(Port(dp.id, dp.ofproto, ofpport))) if not self.link_discovery: return port = self._get_port(dp.id, ofpport.port_no) if port and not port.is_reserved(): if self.ports.set_down(port): self._link_down(port) self.lldp_event.set() @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): """Handle switch features reply to install table miss flow entries.""" datapath = ev.msg.datapath self.install_table_miss(datapath, 0) def create_match(self, parser, fields): """Create OFP match struct from the list of fields.""" match = parser.OFPMatch() for a in fields: match.append_field(*a) return match def send_barrier_request(self, datapath): ofp_parser = datapath.ofproto_parser req = ofp_parser.OFPBarrierRequest(datapath) datapath.send_msg(req) def send_lldp_packet(self, datapath_id, port_no, dl_addr): datapath = self.dps.get(datapath_id, None) if datapath is None: #datapath was already deleted return ofproto = datapath.ofproto parser = datapath.ofproto_parser lldp_data = LLDPPacket.lldp_packet(datapath.id, port_no, dl_addr, self.DEFAULT_TTL) output_port = parser.OFPActionOutput(port_no, ofproto.OFPCML_NO_BUFFER) packet_out = parser.OFPPacketOut(datapath, ofproto.OFPP_ANY, ofproto.OFPP_CONTROLLER, [output_port], lldp_data) datapath.send_msg(packet_out) @set_ev_cls(ofp_event.EventOFPBarrierReply, MAIN_DISPATCHER) def barrier_reply_handler(self, ev): msg = ev.msg datapath = msg.datapath self._register(datapath) switch = self._get_switch(datapath.id) self.send_event_to_observers(event.EventSwitchEnter(switch)) for port in switch.ports: if not port.is_reserved(): self._port_added(port) self.lldp_event.set() def create_flow_mod(self, datapath, priority, table_id, match, instructions): """Create OFP flow mod message.""" ofproto = datapath.ofproto flow_mod = datapath.ofproto_parser.OFPFlowMod( datapath, 0, 0, table_id, ofproto.OFPFC_ADD, 0, 0, priority, ofproto.OFPCML_NO_BUFFER, ofproto.OFPP_ANY, OFPG_ANY, 0, match, instructions) return flow_mod def install_table_miss(self, datapath, table_id): """Create and install table miss flow entries.""" parser = datapath.ofproto_parser ofproto = datapath.ofproto match = parser.OFPMatch() match.set_dl_type(ETH_TYPE_LLDP) output = parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, self.LLDP_PACKET_LEN) write = parser.OFPInstructionActions(ofproto.OFPIT_WRITE_ACTIONS, [output]) instructions = [write] flow_mod = self.create_flow_mod(datapath, 0, table_id, match, instructions) datapath.send_msg(flow_mod) self.send_barrier_request(datapath) @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def _packet_in_handler(self, ev): """Handle packet_in events.""" if not self.link_discovery: return msg = ev.msg in_port = msg.match['in_port'] packet = Packet(msg.data) efm = packet.get_protocol(ethernet.ethernet) if efm.ethertype == ether.ETH_TYPE_ARP: self.send_event_to_observers(event.EventArpReceived(ev)) try: src_dpid, src_port_no = LLDPPacket.lldp_parse(msg.data) except LLDPPacket.LLDPUnknownFormat as e: # This handler can receive all the packtes which can be # not-LLDP packet. Ignore it silently return else: dst_dpid = msg.datapath.id dst_port_no = in_port src = self._get_port(src_dpid, src_port_no) if not src or src.dpid == dst_dpid: return dst = self._get_port(dst_dpid, dst_port_no) if not dst: return old_peer = self.links.get_peer(src) #LOG.debug("Packet-In") #LOG.debug(" src=%s", src) #LOG.debug(" dst=%s", dst) #LOG.debug(" old_peer=%s", old_peer) if old_peer and old_peer != dst: old_link = Link(src, old_peer) self.send_event_to_observers(event.EventLinkDelete(old_link)) link = Link(src, dst) if not link in self.links: self.send_event_to_observers(event.EventLinkAdd(link)) if not self.links.update_link(src, dst): # reverse link is not detected yet. # So schedule the check early because it's very likely it's up try: self.ports.lldp_received(dst) except KeyError as e: # There are races between EventOFPPacketIn and # EventDPPortAdd. So packet-in event can happend before # port add event. In that case key error can happend. # LOG.debug('lldp_received: KeyError %s', e) pass else: self.ports.move_front(dst) self.lldp_event.set()
class L2Switch(RyuApp): _EVENTS = [event.EventSwitchEnter, event.EventSwitchLeave, event.EventPortAdd, event.EventPortDelete, event.EventPortModify, event.EventLinkAdd, event.EventLinkDelete, event.EventArpReceived] OFP_VERSIONS = [OFP_VERSION] DEFAULT_TTL = 120 # unused. ignored. LLDP_PACKET_LEN = len(LLDPPacket.lldp_packet(0, 0, DONTCARE_STR, 0)) LLDP_SEND_GUARD = .05 LLDP_SEND_PERIOD_PER_PORT = .9 TIMEOUT_CHECK_PERIOD = 15. LINK_TIMEOUT = TIMEOUT_CHECK_PERIOD * 2 LINK_LLDP_DROP = 5 def __init__(self, *args, **kwargs): super(L2Switch, self).__init__(*args, **kwargs) self.name = 'switches' self.dps = {} # datapath_id => Datapath class self.port_state = {} # datapath_id => ports self.ports = PortDataState() # Port class -> PortData class self.links = LinkState() # Link class -> timestamp self.is_active = True self.lldp_event = hub.Event() self.link_event = hub.Event() self.threads.append(hub.spawn(self.lldp_loop)) self.threads.append(hub.spawn(self.link_loop)) self.link_discovery = True def close(self): self.is_active = False if self.link_discovery: self.lldp_event.set() self.link_event.set() hub.joinall(self.threads) def send_flow_stats_request(self, datapath): ofp_parser = datapath.ofproto_parser req = ofp_parser.OFPGroupDescStatsRequest(datapath, 0) if datapath.id == 3: datapath.send_msg(req) @set_ev_cls(ofp_event.EventOFPGroupDescStatsReply, MAIN_DISPATCHER) def flow_stats_reply_handler(self, ev): descs = [] for stat in ev.msg.body: descs.append('length=%d type=%d group_id=%d ' 'buckets=%s' % (stat.length, stat.type, stat.group_id, stat.buckets)) LOG.debug('GroupDescStats: %s', descs) def lldp_loop(self): while self.is_active: self.lldp_event.clear() now = time.time() timeout = None ports_now = [] ports = [] for (key, data) in self.ports.items(): if data.timestamp is None: ports_now.append(key) continue expire = data.timestamp + self.LLDP_SEND_PERIOD_PER_PORT if expire <= now: ports.append(key) continue timeout = expire - now break for port in ports_now: self.send_lldp_packet(port.dpid, port.port_no, port.hw_addr) for port in ports: self.send_lldp_packet(port.dpid, port.port_no, port.hw_addr) hub.sleep(self.LLDP_SEND_GUARD) # don't burst if timeout is not None and ports: timeout = 0 # We have already slept self.lldp_event.wait(timeout=timeout) def link_loop(self): while self.is_active: self.link_event.clear() now = time.time() deleted = [] for (link, timestamp) in self.links.items(): if timestamp + self.LINK_TIMEOUT < now: src = link.src if src in self.ports: port_data = self.ports.get_port(src) if port_data.lldp_dropped() > self.LINK_LLDP_DROP: deleted.append(link) #for dp in self.dps.values(): #self.send_flow_stats_request(dp) for link in deleted: self.links.link_down(link) self.send_event_to_observers(event.EventLinkDelete(link)) dst = link.dst rev_link = Link(dst, link.src) if rev_link not in deleted: # It is very likely that the reverse link is also # disconnected. Check it early. expire = now - self.LINK_TIMEOUT self.links.rev_link_set_timestamp(rev_link, expire) if dst in self.ports: self.ports.move_front(dst) self.lldp_event.set() self.link_event.wait(timeout=self.TIMEOUT_CHECK_PERIOD) def _get_switch(self, dpid): if dpid in self.dps: switch = Switch(self.dps[dpid]) for ofpport in self.port_state[dpid].itervalues(): switch.add_port(ofpport) return switch def _get_port(self, dpid, port_no): switch = self._get_switch(dpid) if switch: for p in switch.ports: if p.port_no == port_no: return p def _port_added(self, port): lldp_data = LLDPPacket.lldp_packet( port.dpid, port.port_no, port.hw_addr, self.DEFAULT_TTL) self.ports.add_port(port, lldp_data) LOG.debug('_port_added dpid=%s, port_no=%s, live=%s', port.dpid, port.port_no, port.is_live()) def _register(self, dp): assert dp.id is not None assert dp.id not in self.dps self.dps[dp.id] = dp self.port_state[dp.id] = PortState() for port in dp.ports.values(): self.port_state[dp.id].add(port.port_no, port) def _link_down(self, port): try: dst, rev_link_dst = self.links.port_deleted(port) except KeyError: # LOG.debug('key error. src=%s, dst=%s', # port, self.links.get_peer(port)) return link = Link(port, dst) self.send_event_to_observers(event.EventLinkDelete(link)) if rev_link_dst: rev_link = Link(dst, rev_link_dst) self.send_event_to_observers(event.EventLinkDelete(rev_link)) self.ports.move_front(dst) @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER) def port_status_handler(self, ev): msg = ev.msg reason = msg.reason dp = msg.datapath ofpport = msg.desc if reason == dp.ofproto.OFPPR_ADD: LOG.debug('A port was added.' + '(datapath id = %s, port number = %s)', dp.id, ofpport.port_no) self.port_state[dp.id].add(ofpport.port_no, ofpport) self.send_event_to_observers( event.EventPortAdd(Port(dp.id, dp.ofproto, ofpport))) if not self.link_discovery: return port = self._get_port(dp.id, ofpport.port_no) if port and not port.is_reserved(): self._port_added(port) self.lldp_event.set() elif reason == dp.ofproto.OFPPR_DELETE: LOG.debug('A port was deleted.' + '(datapath id = %s, port number = %s)', dp.id, ofpport.port_no) self.port_state[dp.id].remove(ofpport.port_no) self.send_event_to_observers( event.EventPortDelete(Port(dp.id, dp.ofproto, ofpport))) if not self.link_discovery: return port = self._get_port(dp.id, ofpport.port_no) if port and not port.is_reserved(): self.ports.del_port(port) self._link_down(port) self.lldp_event.set() else: assert reason == dp.ofproto.OFPPR_MODIFY LOG.debug('A port was modified.' + '(datapath id = %s, port number = %s)', dp.id, ofpport.port_no) self.port_state[dp.id].modify(ofpport.port_no, ofpport) self.send_event_to_observers( event.EventPortModify(Port(dp.id, dp.ofproto, ofpport))) if not self.link_discovery: return port = self._get_port(dp.id, ofpport.port_no) if port and not port.is_reserved(): if self.ports.set_down(port): self._link_down(port) self.lldp_event.set() @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): """Handle switch features reply to install table miss flow entries.""" datapath = ev.msg.datapath self.install_table_miss(datapath, 0) def create_match(self, parser, fields): """Create OFP match struct from the list of fields.""" match = parser.OFPMatch() for a in fields: match.append_field(*a) return match def send_barrier_request(self, datapath): ofp_parser = datapath.ofproto_parser req = ofp_parser.OFPBarrierRequest(datapath) datapath.send_msg(req) def send_lldp_packet(self, datapath_id, port_no, dl_addr): datapath = self.dps.get(datapath_id, None) if datapath is None: #datapath was already deleted return ofproto = datapath.ofproto parser = datapath.ofproto_parser lldp_data = LLDPPacket.lldp_packet(datapath.id, port_no, dl_addr, self.DEFAULT_TTL) output_port = parser.OFPActionOutput(port_no, ofproto.OFPCML_NO_BUFFER) packet_out = parser.OFPPacketOut(datapath, ofproto.OFPP_ANY, ofproto.OFPP_CONTROLLER, [output_port], lldp_data) datapath.send_msg(packet_out) @set_ev_cls(ofp_event.EventOFPBarrierReply, MAIN_DISPATCHER) def barrier_reply_handler(self, ev): msg = ev.msg datapath = msg.datapath self._register(datapath) switch = self._get_switch(datapath.id) self.send_event_to_observers(event.EventSwitchEnter(switch)) for port in switch.ports: if not port.is_reserved(): self._port_added(port) self.lldp_event.set() def create_flow_mod(self, datapath, priority, table_id, match, instructions): """Create OFP flow mod message.""" ofproto = datapath.ofproto flow_mod = datapath.ofproto_parser.OFPFlowMod(datapath, 0, 0, table_id, ofproto.OFPFC_ADD, 0, 0, priority, ofproto.OFPCML_NO_BUFFER, ofproto.OFPP_ANY, OFPG_ANY, 0, match, instructions) return flow_mod def install_table_miss(self, datapath, table_id): """Create and install table miss flow entries.""" parser = datapath.ofproto_parser ofproto = datapath.ofproto match = parser.OFPMatch() match.set_dl_type(ETH_TYPE_LLDP) output = parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, self.LLDP_PACKET_LEN) write = parser.OFPInstructionActions(ofproto.OFPIT_WRITE_ACTIONS, [output]) instructions = [write] flow_mod = self.create_flow_mod(datapath, 0, table_id, match, instructions) datapath.send_msg(flow_mod) self.send_barrier_request(datapath) @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def _packet_in_handler(self, ev): """Handle packet_in events.""" if not self.link_discovery: return msg = ev.msg in_port = msg.match['in_port'] packet = Packet(msg.data) efm = packet.next() if efm.ethertype == ether.ETH_TYPE_ARP: self.send_event_to_observers(event.EventArpReceived(ev)) try: src_dpid, src_port_no = LLDPPacket.lldp_parse(msg.data) except LLDPPacket.LLDPUnknownFormat as e: # This handler can receive all the packtes which can be # not-LLDP packet. Ignore it silently return else: dst_dpid = msg.datapath.id dst_port_no = in_port src = self._get_port(src_dpid, src_port_no) if not src or src.dpid == dst_dpid: return dst = self._get_port(dst_dpid, dst_port_no) if not dst: return old_peer = self.links.get_peer(src) #LOG.debug("Packet-In") #LOG.debug(" src=%s", src) #LOG.debug(" dst=%s", dst) #LOG.debug(" old_peer=%s", old_peer) if old_peer and old_peer != dst: old_link = Link(src, old_peer) self.send_event_to_observers(event.EventLinkDelete(old_link)) link = Link(src, dst) if not link in self.links: self.send_event_to_observers(event.EventLinkAdd(link)) if not self.links.update_link(src, dst): # reverse link is not detected yet. # So schedule the check early because it's very likely it's up try: self.ports.lldp_received(dst) except KeyError as e: # There are races between EventOFPPacketIn and # EventDPPortAdd. So packet-in event can happend before # port add event. In that case key error can happend. # LOG.debug('lldp_received: KeyError %s', e) pass else: self.ports.move_front(dst) self.lldp_event.set()