async def send_notification_message(self, error_code, error_subcode=0, data=b""): """ Send Notification message """ if self.tcp_connection_established: message = bgp_message.Notification(error_code, error_subcode, data) try: self.writer.write(message.write()) await self.writer.drain() except OSError: self.logger.opt(ansi=True, depth=1).info( f"<magenta>[TX-ERR]</> NOTIFICATION - {error_code}, {error_subcode}" ) self.enqueue_event(BgpEvent("Event 18: TcpConnectionFails")) self.tcp_connection_established = False await asyncio.sleep(1) return self.logger.opt(ansi=True, depth=1).info( f"<magenta>[TX]</> NOTIFICATION {error_code}, {error_subcode}") else: self.logger.opt(ansi=True, depth=1).info( f"<magenta>[TX-ERR]</> NOTIFICATION {error_code}, {error_subcode}")
async def open_connection(self): """ Open TCP connection to the BGP peer """ self.logger.opt(depth=0).debug("Opening connection to peer") try: reader, writer = await asyncio.open_connection(self.peer_ip, 179) self.enqueue_event( BgpEvent("Event 16: Tcp_CR_Acked", reader=reader, writer=writer, peer_ip=self.peer_ip, peer_port=179)) except OSError: self.tcp_connection_established = False self.enqueue_event(BgpEvent("Event 18: TcpConnectionFails"))
async def connection_state_tracking(self): """ Restart both connections if both of them are in Idle state """ await asyncio.sleep(1) while True: if self.active_mode and self.active_fsm.state == "Idle" and self.passive_fsm.state != "Established": self.active_fsm.enqueue_event( BgpEvent("Event 3: AutomaticStart")) if self.passive_mode and self.passive_fsm.state == "Idle" and self.active_fsm.state != "Established": self.passive_fsm.enqueue_event( BgpEvent( "Event 5: AutomaticStart_with_PassiveTcpEstablishment") ) self.bgp_listeners[self.peer_ip] = self.passive_fsm await asyncio.sleep(10)
async def connection_collision_detection(self): """ Perform collision detection and shutdown non preferred connection """ self.logger = loguru.logger.bind(peer="S " + self.peer_ip, state="") await asyncio.sleep(1) while True: if self.active_fsm.state != "Idle" and self.passive_fsm.state != "Idle": if self.active_fsm.state == "Established" and self.passive_fsm.state != "Established": self.logger.debug( "Collision detection E/!E - Closing passive connection" ) self.passive_fsm.enqueue_event( BgpEvent("Event 8: AutomaticStop")) if self.passive_fsm.state == "Established" and self.active_fsm.state != "Established": self.logger.debug( "Collision detection !E/E - Closing active connection") self.active_fsm.enqueue_event( BgpEvent("Event 8: AutomaticStop")) if self.active_fsm.state == "OpenConfirm" and self.passive_fsm.state == "OpenConfirm": if struct.unpack("!L", socket.inet_aton( self.local_id))[0] > struct.unpack( "!L", socket.inet_aton( self.active_fsm.peer_id))[0]: self.logger.debug( "Collision detection OC/OC LID > PID - Closing passive connection" ) self.passive_fsm.enqueue_event( BgpEvent("Event 8: AutomaticStop")) else: self.logger.debug( "Collision detection OC/OC LID < PID - Closing active connection" ) self.active_fsm.enqueue_event( BgpEvent("Event 8: AutomaticStop")) await asyncio.sleep(0.1)
async def decrease_hold_timer(self): """ Decrease hold_timer every second if its value is greater than zero """ self.logger.debug("Starting decrease_hold_timer() coroutine") if not hasattr(self, "hold_timer"): self.hold_timer = 0 while True: await asyncio.sleep(1) if self.hold_timer: self.logger.debug(f"hold_timer = {self.hold_timer}") self.hold_timer -= 1 if not self.hold_timer: self.enqueue_event(BgpEvent("Event 10: HoldTimer_Expires"))
async def bgp_broker(reader, writer): """ Evaluate incoming connection and send to BGP session if valid peer """ peer = writer.get_extra_info("peername") passive_fsm = BGP_LISTENERS.pop(peer[0], None) if passive_fsm: passive_fsm.enqueue_event( BgpEvent("Event 17: TcpConnectionConfirmed", reader=reader, writer=writer, peer_ip=peer[0], peer_port=peer[1])) else: writer.close() await writer.wait_closed()
async def send_keepalive_message(self): """ Send Keepalive message """ if self.tcp_connection_established: message = bgp_message.Keepalive() try: self.writer.write(message.write()) await self.writer.drain() except OSError: self.logger.opt(ansi=True, depth=1).error("<magenta>[TX-ERR]</> KEEPALIVE") self.enqueue_event(BgpEvent("Event 18: TcpConnectionFails")) self.tcp_connection_established = False await asyncio.sleep(1) return self.logger.opt(ansi=True, depth=1).info("<magenta>[TX]</> KEEPALIVE") else: self.logger.opt(ansi=True, depth=1).error("<magenta>[TX-ERR]</> KEEPALIVE")
async def send_open_message(self): """ Send Open message """ if self.tcp_connection_established: message = bgp_message.Open(local_id=self.local_id, local_asn=self.local_asn, local_hold_time=self.local_hold_time) try: self.writer.write(message.write()) await self.writer.drain() except OSError: self.logger.opt(ansi=True, depth=1).info("<magenta>[TX-ERR]</> OPEN") self.enqueue_event(BgpEvent("Event 18: TcpConnectionFails")) self.tcp_connection_established = False await asyncio.sleep(1) return self.logger.opt(ansi=True, depth=1).info("<magenta>[TX]</> OPEN") else: self.logger.opt(ansi=True, depth=1).info("<magenta>[TX-ERR]</> OPEN")
async def message_input_loop(self): """ Receive messages from the peer and add them to the input queue """ self.logger.debug("Starting message input loop") while True: if self.tcp_connection_established is False: await asyncio.sleep(1) continue data = await self.reader.read(4096) self.logger.debug(f"Received {len(data)} bytes of data") if len(data) == 0: self.enqueue_event(BgpEvent("Event 18: TcpConnectionFails")) self.tcp_connection_established = False await asyncio.sleep(1) continue while len(data) >= 19: message = bgp_message.DecodeMessage(data, local_id=self.local_id, peer_asn=self.peer_asn) if message.data_len_error: self.logger.warning( f"Received {message.data_len_received} bytes of data, expected at least {message.data_len_expected}" ) self.tcp_connection_established = False self.enqueue_event(BgpEvent("Event 18: TcpConnectionFails")) await asyncio.sleep(1) break data = data[message.len:] if message.message_error_code == bgp_message.MESSAGE_HEADER_ERROR: self.enqueue_event(BgpEvent("Event 21: BGPHeaderErr", message)) break if message.message_error_code == bgp_message.OPEN_MESSAGE_ERROR: self.enqueue_event(BgpEvent("Event 22: BGPOpenMsgErr", message)) break if message.type == bgp_message.OPEN: self.logger.opt(ansi=True).info( f"<green>[RX]</> OPEN - peer_id: {message.id}") self.enqueue_event(BgpEvent("Event 19: BGPOpen", message)) if message.type == bgp_message.UPDATE: self.logger.opt(ansi=True).info( f"<green>[RX]</> UPDATE - add {len(message.prefixes_add)}, del {len(message.prefixes_del)}" ) for attribute in message.attributes: self.logger.opt( ansi=True).info(f"<green>[RX]</> attr: {attribute}") for prefix_add in message.prefixes_add: self.logger.opt(ansi=True).info( f"<green>[RX]</> prefix_add: {prefix_add}") for prefix_del in message.prefixes_del: self.logger.opt(ansi=True).info( f"<green>[RX]</> prefix_del: {prefix_del}") if message.type == bgp_message.NOTIFICATION: self.logger.opt(ansi=True).info( f"<green>[RX]</> NOTIFICATION - {message.error_code}, {message.error_subcode}" ) if message.error_code == bgp_message.MESSAGE_HEADER_ERROR: self.enqueue_event(BgpEvent("Event 25: NotifMsg")) if message.error_code == bgp_message.OPEN_MESSAGE_ERROR and message.error_subcode == bgp_message.UNSUPPORTED_VERSION_NUMBER: self.enqueue_event(BgpEvent("Event 24: NotifMsgVerErr")) if message.error_code == bgp_message.OPEN_MESSAGE_ERROR and message.error_subcode != bgp_message.UNSUPPORTED_VERSION_NUMBER: self.enqueue_event(BgpEvent("Event 25: NotifMsg")) if message.error_code == bgp_message.UPDATE_MESSAGE_ERROR: self.enqueue_event(BgpEvent("Event 25: NotifMsg")) if message.error_code == bgp_message.HOLD_TIMER_EXPIRED: self.enqueue_event(BgpEvent("Event 25: NotifMsg")) if message.error_code == bgp_message.FINITE_STATE_MACHINE_ERROR: self.enqueue_event(BgpEvent("Event 25: NotifMsg")) if message.error_code == bgp_message.CEASE: self.enqueue_event(BgpEvent("Event 25: NotifMsg")) if message.type == bgp_message.KEEPALIVE: self.logger.opt(ansi=True).info("<green>[RX]</> KEEPALIVE") self.enqueue_event(BgpEvent("Event 26: KeepAliveMsg")) await asyncio.sleep(1) else: await asyncio.sleep(1)