def write_packet(self, packet: NetRomPacket, forward: bool = False) -> bool: possible_routes = self.router.route(packet) routed = False for route in possible_routes: neighbor = self.router.neighbors.get(str(route)) if neighbor is None: self.logger.warning(f"No neighbor for route {route}") continue #print(f"Trying route {route} to neighbor {neighbor}") data_link = self.data_links.get(neighbor.port) if data_link.link_state(neighbor.call) == AX25StateType.Connected: data_link.dl_data_request(neighbor.call, L3Protocol.NetRom, packet.buffer) routed = True EventBus.emit("netrom.outbound", [packet]) if forward: # Log this transmission differently if it's being forwarded self.logger.info( f"[L3 Route={route} Neighbor={neighbor.call}] TX: {packet}" ) else: self.debug(f"TX: {packet}") packet_logger.info(f"TX: {packet}") break if not routed: self.warning( f"Could not route packet to {packet.dest}. Possible routes were {possible_routes}" ) pass return routed
def _loop_sync(self, frame: FrameData): try: packet = decode_ax25_packet(frame.data) if packet.dest == AX25Call("NODES"): self.debug(f"RX: {packet}") else: self.debug(f"RX: {packet}") packet_logger.info(f"RX: {packet}") EventBus.emit("packet", [packet]) except Exception: self.error(f"Had an error parsing packet: {frame}") return try: # Check if this is a special L3 message should_continue = True for l3 in self.l3_handlers: should_continue = l3.maybe_handle_special(frame.port, packet) if not should_continue: self.debug(f"Handled by L3 {l3}") break # If it has not been handled by L3 if should_continue: if not packet.dest == self.link_call: self.warning( f"Discarding packet not for us {packet}. We are {self.link_call}" ) else: self.state_machine.handle_packet(packet) else: self.debug( "Not continuing because this packet was handled by L3") except Exception: self.error(f"Had handling packet {packet}")
def bind(self, l4_call: AX25Call, l4_alias: str): self.router.listen_for_address(l4_call, l4_alias) self.l3_apps[l4_call] = l4_alias # Need to bind this here so the application can start sending packets right away EventBus.bind( EventListener( f"netrom.{l4_call}.outbound", f"netrom_{l4_call}_outbound", lambda remote_call, data: self.nl_data_request( remote_call, l4_call, data)), True)
def nl_data_indication(self, my_circuit_idx: int, my_circuit_id: int, remote_call: AX25Call, local_call: AX25Call, data: bytes): # Called from the state machine to indicate data to higher layers EventBus.emit(f"netrom.{local_call}.inbound", my_circuit_idx, remote_call, data) if my_circuit_idx in self.l3_connections: self.l3_connections[my_circuit_idx][1].data_received(data) else: self.warning( f"Data indication for unknown circuit {my_circuit_idx}")
def nl_disconnect_indication(self, my_circuit_idx: int, my_circuit_id: int, remote_call: AX25Call, local_call: AX25Call): EventBus.emit(f"netrom.{local_call}.disconnect", my_circuit_idx, remote_call) if local_call in self.l3_servers: if my_circuit_idx in self.l3_connections: self.l3_connections[my_circuit_idx][1].connection_lost(None) del self.l3_connections[my_circuit_idx] else: self.warning( f"Disconnect indication received for unknown circuit {my_circuit_idx}" )
def maybe_handle_special(self, port: int, packet: AX25Packet) -> bool: """L3Handler.maybe_handle_special""" if type(packet) == UIFrame: ui = cast(UIFrame, packet) if ui.protocol == L3Protocol.NetRom and ui.dest == AX25Call( "NODES"): # Parse this NODES packet and mark it as handled nodes = parse_netrom_nodes(ui.info) EventBus.emit("netrom.nodes", [nodes]) asyncio.get_event_loop().create_task( self._update_nodes(packet.source, port, nodes)) # Stop further processing return False return True
def dl_data_indication(self, remote_call: AX25Call, local_call: AX25Call, protocol: L3Protocol, data: bytes): EventBus.emit(f"link.{local_call}.inbound", remote_call, protocol, data) handled = False for l3 in self.l3_handlers: if l3.can_handle(protocol): handled = l3.handle(self.link_port, remote_call, data) if handled: break if not handled: self.debug( f"No handler defined for protocol {repr(protocol)}. Discarding" )
async def _handle_packet_async(self, netrom_packet: NetRomPacket): """If packet is for us, handle it, otherwise forward it using our L3 routing table""" EventBus.emit("netrom.incoming", [netrom_packet]) self.debug(f"RX: {netrom_packet}") packet_logger.info(f"RX: {netrom_packet}") if netrom_packet.dest == AX25Call("KEEPLI-0"): # What are these?? Just ignore them pass elif netrom_packet.dest == AX25Call.parse(self.config.node_call()): # Destination is this node self.sm.handle_packet(netrom_packet) elif netrom_packet.dest in self.l3_apps: # Destination is an app served by this node self.sm.handle_packet(netrom_packet) elif netrom_packet.dest in self.l3_servers: # Destination is an app served by this node self.sm.handle_packet(netrom_packet) else: # Destination is somewhere else self.write_packet(netrom_packet, forward=True)
def nl_connect_indication(self, my_circuit_idx: int, my_circuit_id: int, remote_call: AX25Call, local_call: AX25Call, origin_node: AX25Call, origin_user: AX25Call): # Send a connect event EventBus.emit(f"netrom.{local_call}.connect", my_circuit_idx, remote_call) if my_circuit_idx in self.l3_half_open: # Complete a half-opened connection protocol_factory = self.l3_half_open[my_circuit_idx] protocol = protocol_factory() transport = NetworkTransport(self, local_call, remote_call, my_circuit_idx, origin_node, origin_user) protocol.connection_made(transport) self.l3_connections[my_circuit_idx] = (transport, protocol) del self.l3_half_open[my_circuit_idx] elif local_call in self.l3_servers: if my_circuit_idx in self.l3_connections: # An existing connection, re-connect it self.l3_connections[my_circuit_idx][1].connection_lost( RuntimeError("Remote end reconnected")) self.l3_connections[my_circuit_idx][1].connection_made( self.l3_connections[my_circuit_idx][0]) else: # This a new connection, create the transport and protocol transport = NetworkTransport(self, local_call, remote_call, my_circuit_idx, origin_node, origin_user) protocol = self.l3_servers[local_call]() protocol.connection_made(transport) self.l3_connections[my_circuit_idx] = (transport, protocol) else: self.warning( f"Got unexpected connection from {remote_call} at {local_call}:{my_circuit_idx}" )
def __init__(self, my_call: str, my_alias: str, remote_call, nl: NetRom): self.local_call = AX25Call.parse(my_call) self.local_alias = AX25Call(callsign=my_alias) self.remote_call = AX25Call.parse(remote_call) self.nl = nl self.stdin_queue = Queue() self.connected = False self.circuit_id = None EventBus.bind( EventListener(f"netrom.{my_call}.connect", f"netrom_{my_call}_connect", self.handle_connect)) EventBus.bind( EventListener(f"netrom.{my_call}.disconnect", f"netrom_{my_call}_disconnect", self.handle_disconnect)) EventBus.bind( EventListener(f"netrom.{my_call}.inbound", f"netrom_{my_call}_inbound", self.handle_data))
def __init__(self, local_call: str, remote_call: str, datalink: DataLinkManager): self.local_call = AX25Call.parse(local_call) self.remote_call = AX25Call.parse(remote_call) self.dl = datalink self.stdin_queue = Queue() self.connected = False self.circuit_id = None EventBus.bind( EventListener(f"link.{local_call}.connect", f"link_{local_call}_connect", self.handle_connect)) EventBus.bind( EventListener(f"link.{local_call}.disconnect", f"link_{local_call}_disconnect", self.handle_disconnect)) EventBus.bind( EventListener(f"link.{local_call}.inbound", f"link_{local_call}_inbound", self.handle_data))
def dl_disconnect_indication(self, remote_call: AX25Call, local_call: AX25Call): EventBus.emit(f"link.{local_call}.disconnect", remote_call)
def dl_error(self, remote_call: AX25Call, local_call: AX25Call, error_code: str): EventBus.emit(f"link.{local_call}.error", error_code)