async def _broadcast_nodes(self): nodes = self.router.get_nodes() nodes.save("nodes.json") for dl in self.data_links.values(): for nodes_packet in nodes.to_packets( AX25Call.parse(self.config.node_call())): dl.write_packet(nodes_packet)
def load(cls, file: str): if not os.path.exists(file): return None, 0 with open(file) as fp: nodes_json = json.load(fp) sending_alias = nodes_json["nodeAlias"] destinations = [] for dest_json in nodes_json["destinations"]: destinations.append( NodeDestination(AX25Call.parse(dest_json["nodeCall"]), dest_json["nodeAlias"], AX25Call.parse(dest_json["bestNeighbor"]), int(dest_json["quality"]))) return cls(sending_alias, destinations), datetime.datetime.fromisoformat( nodes_json["createdAt"])
def write(self, address, data): payload = AppPayload(0, 1, bytearray()) ax25_address = AX25Call.parse(address) ax25_address.write(payload.buffer) payload.buffer.extend(data) msg = msgpack.packb(dataclasses.asdict(payload)) self.transport.write(msg)
def from_safe_dict(cls, d): instance = cls(node_call=AX25Call.parse(d["call"]), node_alias=d["alias"], freeze=d["freeze"]) instance.neighbor_map = { route_dict["neighbor"]: Route.from_safe_dict(route_dict) for route_dict in d["routes"] } return instance
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 load(cls, filename: str, node_alias: str): if not os.path.exists(filename): return NetRomRoutingTable(node_alias=node_alias, updated_at=datetime.datetime.now()) d = json_load(filename) return NetRomRoutingTable(node_alias=d["node_alias"], updated_at=datetime.datetime.fromisoformat(d["updated_at"]), our_calls={AX25Call.parse(call) for call in d["our_calls"]}, neighbors={n_dict["call"]: Neighbor.from_safe_dict(n_dict) for n_dict in d["neighbors"]}, destinations={d_dict["call"]: Destination.from_safe_dict(d_dict) for d_dict in d["destinations"]})
async def wait_for_network(): tty = TTY() loop.add_reader(sys.stdin, tty.handle_stdin) loop.add_signal_handler(signal.SIGTERM, tty.handle_signal, loop, scheduler) loop.add_signal_handler(signal.SIGINT, tty.handle_signal, loop, scheduler) remote_call = AX25Call.parse(args.remote_call) while True: (found, mtu) = netrom_l3.route_packet(NetRomAddress.from_call(remote_call)) if found: break await asyncio.sleep(0.200) main_logger.info(f"Learned route to {remote_call}") await asyncio.sleep(1.200) netrom_l4.open(protocol_factory=lambda: tty, local_call=AX25Call.parse(args.local_call), remote_call=AX25Call.parse(args.remote_call), origin_user=AX25Call(args.local_alias), origin_node=AX25Call(args.local_call))
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 local_call(self) -> AX25Call: return AX25Call.parse(self.config.node_call())
def run_node(args): # Bootstrap node.ini if not os.path.exists(args.config) and os.path.basename( args.config) == "node.ini": shutil.copyfile("config/node.ini.sample", args.config) # Load settings from ini file s = Settings(".", ["config/defaults.ini", args.config]) node_settings = s.node_config() node_call = AX25Call.parse(node_settings.node_call()) if node_call.callsign == "N0CALL": print("Callsign is missing from config. Please see instructions here " "https://github.com/tarpn/tarpn-node-controller") sys.exit(1) else: print(f"Loaded configuration for {node_call}") # Setup logging logging_config_file = node_settings.get("log.config", "not_set") if logging_config_file != "not_set": log_dir = node_settings.get("log.dir") if not os.path.exists(log_dir): os.makedirs(log_dir) logging.config.fileConfig(logging_config_file, defaults={"log.dir": log_dir}, disable_existing_loggers=False) if args.verbose: logging.getLogger("root").setLevel(logging.DEBUG) # Create thread pool scheduler = Scheduler() # Initialize I/O devices and L2 protocols l3_protocols = L3Protocols() l3_protocols.register(NoLayer3Protocol()) l2_multi = DefaultLinkMultiplexer(L3PriorityQueue, scheduler) # Port UDP mapping # udp.forwarding.enabled = true # udp.address = 192.168.0.160:10093 # udp.destinations = K4DBZ-2,NODES # udp.mapping = KN4ORB-2:1,KA2DEW-2:2 port_queues = {} if node_settings.get_boolean("udp.enabled", False): udp_host, udp_port = node_settings.get("udp.address").split(":") udp_port = int(udp_port) udp_writer = UDPWriter(g8bpq_address=(udp_host, udp_port)) intercept_dests = { AX25Call.parse(c) for c in node_settings.get("udp.destinations", "").split(",") } interceptor = udp_writer.receive_frame udp_mapping = {} for mapping in node_settings.get("udp.mapping", "").split(","): c, i = mapping.split(":") udp_mapping[AX25Call.parse(c)] = int(i) scheduler.submit( UDPThread("0.0.0.0", udp_port, udp_mapping, port_queues, udp_writer)) else: intercept_dests = {} interceptor = lambda frame: None for port_config in s.port_configs(): if port_config.get_boolean("port.enabled") and port_config.get( "port.type") == "serial": l2_queueing = L2FIFOQueue(20, AX25Protocol.maximum_frame_size()) port_queues[port_config.port_id()] = l2_queueing l2 = AX25Protocol(port_config, port_config.port_id(), node_call, scheduler, l2_queueing, l2_multi, l3_protocols, intercept_dests, interceptor) kiss = KISSProtocol( port_config.port_id(), l2_queueing, port_config.get_boolean("kiss.checksum", False)) SerialDevice(kiss, port_config.get("serial.device"), port_config.get_int("serial.speed"), port_config.get_float("serial.timeout"), scheduler) scheduler.submit(L2IOLoop(l2_queueing, l2)) # Register L3 protocols routing_table = tarpn.netrom.router.NetRomRoutingTable.load( f"nodes-{node_settings.node_call()}.json", node_settings.node_alias()) network_configs = s.network_configs() if network_configs.get_boolean("netrom.enabled", False): logger.info("Starting NET/ROM router") netrom_l3 = NetRomL3(node_call, node_settings.node_alias(), scheduler, l2_multi, routing_table) l3_protocols.register(netrom_l3) netrom_l4 = NetRomTransportProtocol(s.network_configs(), netrom_l3, scheduler) l4_handlers = L4Handlers() if network_configs.get_boolean("mesh.enabled", False): mesh_l3 = MeshProtocol(WallTime(), network_configs, l2_multi, l4_handlers, scheduler) l3_protocols.register(mesh_l3) # Create the L4 protocols mesh_l4 = MeshTransportManager(mesh_l3) # Register L4 handlers reliable = ReliableManager(mesh_l3, scheduler) fragment_protocol = FragmentProtocol(mesh_l3, mesh_l4) reliable_protocol = ReliableProtocol(mesh_l3, reliable, l4_handlers) datagram_protocol = DatagramProtocol(mesh_l3, mesh_l4, fragment_protocol, reliable_protocol) broadcast_protocol = BroadcastProtocol(mesh_l3, mesh_l4, reliable) l4_handlers.register_l4(Protocol.FRAGMENT, fragment_protocol) l4_handlers.register_l4(Protocol.RELIABLE, reliable_protocol) l4_handlers.register_l4(Protocol.DATAGRAM, datagram_protocol) l4_handlers.register_l4(Protocol.BROADCAST, broadcast_protocol) # TODO fix circular dependency here mesh_l4.broadcast_protocol = broadcast_protocol mesh_l4.datagram_protocol = datagram_protocol # Bind the command processor ncp_factory = partial(NodeCommandProcessor, config=network_configs, link=l2_multi, network=mesh_l3, transport_manager=mesh_l4, scheduler=scheduler) mesh_l4.bind(ncp_factory, mesh_l3.our_address, 11) # Set up applications for app_config in s.app_configs(): # We have a single unix socket connection multiplexed to many network connections print(app_config) app_multiplexer = TransportMultiplexer() app_address = MeshTransportAddress.parse( app_config.get("app.address")) app_protocol = ApplicationProtocol(app_config.app_name(), app_config.app_alias(), str(app_address.address), mesh_l4, app_multiplexer) scheduler.submit( UnixServerThread(app_config.app_socket(), app_protocol)) multiplexer_protocol = partial(MultiplexingProtocol, app_multiplexer) # TODO bind or connect? mesh_l4.connect(multiplexer_protocol, app_address.address, MeshAddress.parse("00.a2"), app_address.port) sock = node_settings.get("node.sock") print(f"Binding node terminal to {sock}") scheduler.submit( UnixServerThread(sock, TarpnShellProtocol(mesh_l3, mesh_l4))) # Start a metrics reporter #reporter = ConsoleReporter(reporting_interval=300) #scheduler.timer(10_000, reporter.start, True) #scheduler.add_shutdown_hook(reporter.stop) logger.info("Finished Startup") try: # Wait for all threads scheduler.join() except KeyboardInterrupt: scheduler.shutdown()
def data_received(self, data: bytes) -> None: s = data.decode("ASCII").strip().upper() self.info(f"Data: {s}") if self.pending_open is not None: self.println(f"Pending connection to {self.pending_open}") # If connected somewhere else, forward the input if self.client_transport: if s == "B" or s == "BYE": self.client_transport.close() self.println( f"Closing connection to {self.client_transport.remote_call}...", True) else: self.client_transport.write(data) return # If not forwarding, parse the command try: parsed_args = self.parser.parse_args(shlex.split(s.lower())) except BaseException: self.println(self.parser.format_help(), True) return if parsed_args.command is None: parsed_args.command = "help" if parsed_args.command == "help": self.println(self.parser.format_help(), True) elif parsed_args.command == "ports": resp = "Ports:\n" for dlm in self.datalinks: resp += f"{dlm.link_port}: {dlm.link_call}\n" self.println(resp, True) elif parsed_args.command == "links": resp = "Links:\n" for dlm in self.datalinks: for remote_call in dlm.state_machine.get_sessions().keys(): if dlm.state_machine.get_state( str(remote_call)) == AX25StateType.Connected: resp += f"L2 {dlm.link_call} > {str(remote_call)} on port {dlm.link_port}\n" for circuit_id in self.network.get_circuit_ids(): circuit = self.network.get_circuit(circuit_id) if circuit.state == NetRomStateType.Connected: resp += f"L3 {circuit.local_call} > {circuit.local_call} on circuit {circuit_id}\n" self.println(resp, True) elif parsed_args.command == "whoami": if isinstance(self.transport, NetworkTransport): nt = cast(NetworkTransport, self.transport) self.println( f"Current user is {nt.origin_user.callsign} connected from {nt.origin_node}", True) else: self.println( f"Current user is default connected from {self.transport.get_extra_info('peername')}", True) elif parsed_args.command == "hostname": self.println(f"Current host is {self.network.local_call()}", True) elif parsed_args.command == "bye": self.println("Goodbye.") self.transport.close() elif parsed_args.command == "connect": """ Connect to a remote station Create a half-opened client connection to the remote station. Once the connect ack is received, the connection will be completed and we will create the protocol and transport objects. """ remote_call = AX25Call.parse(parsed_args.dest) local_call = AX25Call.parse( self.settings.network_configs().node_call()) self.println(f"Connecting to {remote_call}...", True) if isinstance(self.transport, NetworkTransport): nt = cast(NetworkTransport, self.transport) self.network.open(partial(ConnectProtocol, self), local_call, remote_call, nt.origin_node, nt.origin_user) self.pending_open = remote_call else: logging.warning( f"Connect command is only supported for NetworkTransport, not {self.transport.__class__}" ) self.println( f"Connect command is only supported for NetworkTransport", True) else: logging.warning(f"Unhandled command {parsed_args.command}") self.println(f"Unhandled command {parsed_args.command}", True)
def data_received(self, data: bytes) -> None: s = data.decode("ASCII").strip().upper() self.info(f"Data: {s}") # If we're waiting for a connection, either wait or let user BYE if self.pending_open is not None: if s == "B" or s == "BYE": self.println(f"Cancelling connection to {self.pending_open}", True) self.pending_open = None else: self.println(f"Pending connection to {self.pending_open}", True) return # If connected somewhere else, forward the input if self.client_transport is not None: if s == "B" or s == "BYE": self.client_transport.close() self.println( f"Closing connection to {self.client_transport.get_extra_info('peername')}...", True) self.client_transport = None else: self.client_transport.write(data) return # If not forwarding, parse the command try: parsed_args = self.parser.parse_args(shlex.split(s.lower())) except BaseException: self.println(self.parser.format_help(), True) return if parsed_args.command is None: parsed_args.command = "help" if parsed_args.command == "help": self.println(self.parser.format_help(), True) elif parsed_args.command == "ports": resp = "Ports:\n" for device_id, l2 in self.l2s.l2_devices.items(): resp += f" - Port {device_id}: {l2.get_link_address()}\n" self.println(resp, True) elif parsed_args.command == "links": resp = "Links:\n" for link_id, l2 in self.l2s.logical_links.items(): if l2.peer_connected(link_id): resp += f" - L2 Link {link_id}, Port {l2.get_device_id()}: " resp += f"{l2.get_link_address()}>{l2.get_peer_address(link_id)}\n" for circuit, (transport, protocol) in self.l4.l3_connections.items(): nt = cast(NetRomTransport, transport) if transport == self.transport: resp += f" * L4 Link {circuit}: {nt.local_call}>{nt.remote_call}\n" else: resp += f" - L4 Link {circuit}: {nt.local_call}>{nt.remote_call}\n" self.println(resp, True) elif parsed_args.command == "nodes": routing_table = cast(NetRomRoutingTable, self.l3.router) resp = "Nodes:\n" for i, dest in enumerate(routing_table.destinations.values()): resp += f"{dest.node_alias}:{dest.node_call}\t" if i % 4 == 3: resp += "\n" self.println(resp, True) elif parsed_args.command == "routes": routing_table = cast(NetRomRoutingTable, self.l3.router) resp = "Routes:\n" for dest in routing_table.destinations.values(): for route in dest.neighbor_map.values(): resp += f"{dest.node_alias}:{dest.node_call} via {route.next_hop}, qal={route.quality} obs={route.obsolescence}\n" self.println(resp, True) elif parsed_args.command == "whoami": if isinstance(self.transport, NetRomTransport): nt = cast(NetRomTransport, self.transport) self.println( f"Current user is {nt.origin_user.callsign} connected from {nt.origin_node}", True) else: self.println( f"Current user is default connected from {self.transport.get_extra_info('peername')}", True) elif parsed_args.command == "hostname": self.println(f"Current host is {self.config.node_call()}", True) elif parsed_args.command == "bye": self.println("Goodbye.") self.transport.close() elif parsed_args.command == "connect": """ Connect to a remote station Create a half-opened client connection to the remote station. Once the connect ack is received, the connection will be completed and we will create the protocol and transport objects. """ remote_call = AX25Call.parse(parsed_args.dest) local_call = AX25Call.parse(self.config.node_call()) self.println(f"Connecting to {remote_call}...", True) if isinstance(self.transport, NetRomTransport): nt = cast(NetRomTransport, self.transport) self.l4.open(partial(ConnectProtocol, self), local_call, remote_call, nt.origin_node, nt.origin_user) self.pending_open = remote_call else: logging.warning( f"Connect command is only supported for NetworkTransport, not {self.transport.__class__}" ) self.println( f"Connect command is only supported for NetworkTransport", True) else: logging.warning(f"Unhandled command {parsed_args.command}") self.println(f"Unhandled command {parsed_args.command}", True)
def main(): parser = argparse.ArgumentParser( description='Decode packets from a serial port') parser.add_argument("port", help="Serial port to open") parser.add_argument("baud", type=int, help="Baudrate to use") parser.add_argument("local_call", help="Your callsign (e.g., K4DBZ-10)") parser.add_argument("local_alias", help="Your alias (e.g., ZDBZ10)") parser.add_argument("remote_call", help="Remote callsign") parser.add_argument("-datalink", help="Force L2 mode", action="store_true") parser.add_argument("--check-crc", type=bool, default=False) parser.add_argument("--monitor-port", type=int) parser.add_argument("--debug", action="store_true") args = parser.parse_args() port_config = PortConfig.from_dict( 0, { "port.enabled": True, "port.type": "serial", "serial.device": args.port, "serial.speed": args.baud }) loop = asyncio.get_event_loop() in_queue: asyncio.Queue = asyncio.Queue() out_queue: asyncio.Queue = asyncio.Queue() loop.create_task(kiss_port_factory(in_queue, out_queue, port_config)) # Wire the port with an AX25 layer dlm = DataLinkManager(AX25Call.parse(args.local_call), port_config.port_id(), in_queue, out_queue, loop.create_future) dlm.add_l3_handler(IdHandler()) # Wire up the network network_config = NetworkConfig.from_dict({ "netrom.node.call": args.local_call, "netrom.node.alias": args.local_alias, "netrom.ttl": 7, "netrom.nodes.interval": 60, "netrom.obs.init": 6, "netrom.obs.min": 4, "netrom.nodes.quality.min": 74 }) nl = NetworkManager(network_config, loop) nl.attach_data_link(dlm) tty = TTY(args.local_call, args.local_alias, args.remote_call, nl) loop.create_task(tty.start()) loop.add_reader(sys.stdin, tty.handle_stdin) #server = loop.run_until_complete(loop.create_server(lambda: Monitor(), '127.0.0.1', 8889)) #loop.create_task(server.serve_forever()) # Configure logging main_logger = logging.getLogger("root") main_logger.setLevel(logging.ERROR) #packet_logger.addHandler(logging.StreamHandler(sys.stdout)) if args.debug: main_logger.setLevel(logging.DEBUG) state_logger = logging.getLogger("ax25.state") state_logger.setLevel(logging.DEBUG) state_logger.addHandler(logging.StreamHandler(sys.stdout)) state_logger = logging.getLogger("netrom.state") state_logger.setLevel(logging.DEBUG) state_logger.addHandler(logging.StreamHandler(sys.stdout)) loop.add_signal_handler(signal.SIGTERM, handle_signal, dlm, tty, loop) loop.add_signal_handler(signal.SIGINT, handle_signal, dlm, tty, loop) loop.create_task(dlm.start()) loop.create_task(nl.start()) try: loop.run_forever() finally: loop.close()
def run_node(args): # Load settings from ini file s = Settings(".", args.config) node_settings = s.node_config() # Setup logging logging.config.fileConfig("config/logging.ini", disable_existing_loggers=False) # Create thread pool scheduler = Scheduler() # Initialize I/O devices and L2 protocols l3_protocols = L3Protocols() l2_multi = LinkMultiplexer(L3PriorityQueue, scheduler) for port_config in s.port_configs(): l2_queueing = L2FIFOQueue(20, AX25Protocol.maximum_frame_size()) l2 = AX25Protocol(port_config.port_id(), AX25Call.parse(node_settings.node_call()), scheduler, l2_queueing, l2_multi, l3_protocols) kiss = KISSProtocol(port_config.port_id(), l2_queueing, port_config.get_boolean("kiss.checksum", False)) SerialDevice(kiss, port_config.get("serial.device"), port_config.get_int("serial.speed"), scheduler) scheduler.submit(L2IOLoop(l2_queueing, l2)) # Register L3 protocols routing_table = tarpn.netrom.router.NetRomRoutingTable.load(f"nodes-{s.network_configs().node_call()}.json", s.network_configs().node_alias()) netrom_l3 = NetRomL3(AX25Call.parse(s.network_configs().node_call()), s.network_configs().node_alias(), scheduler, l2_multi, routing_table) l3_protocols.register(netrom_l3) l3_protocols.register(NoLayer3Protocol()) # Create the L4 protocol netrom_l4 = NetRomTransportProtocol(s.network_configs(), netrom_l3, scheduler) # Bind the command processor ncp_factory = partial(NodeCommandProcessor, config=s.network_configs(), l2s=l2_multi, l3=netrom_l3, l4=netrom_l4, scheduler=scheduler) netrom_l4.bind_server(AX25Call.parse(s.network_configs().node_call()), s.network_configs().node_alias(), ncp_factory) # Set up applications for app_config in s.app_configs(): # We have a single unix socket connection multiplexed to many network connections app_multiplexer = TransportMultiplexer() app_protocol = ApplicationProtocol(app_config.app_name(), AX25Call.parse(app_config.app_call()), app_config.app_alias(), netrom_l4, app_multiplexer) scheduler.submit(UnixServerThread(app_config.app_socket(), app_protocol)) multiplexer_protocol = partial(MultiplexingProtocol, app_multiplexer) netrom_l4.bind_server(AX25Call.parse(app_config.app_call()), app_config.app_alias(), multiplexer_protocol) # Start a metrics reporter reporter = ConsoleReporter(reporting_interval=300) reporter.start() scheduler.add_shutdown_hook(reporter.stop) logger.info("Finished Startup") try: # Wait for all threads scheduler.join() except KeyboardInterrupt: scheduler.shutdown()
def from_safe_dict(cls, d): return cls(call=AX25Call.parse(d["call"]), port=d["port"], quality=d["quality"])
def main(): parser = argparse.ArgumentParser(description='Decode packets from a serial port') parser.add_argument("port", help="Serial port to open") parser.add_argument("baud", type=int, help="Baudrate to use") parser.add_argument("local_call", help="Your callsign (e.g., K4DBZ-10)") parser.add_argument("local_alias", help="Your alias (e.g., ZDBZ10)") parser.add_argument("remote_call", help="Remote callsign") parser.add_argument("-datalink", help="Force L2 mode", action="store_true") parser.add_argument("--check-crc", type=bool, default=False) parser.add_argument("--monitor-port", type=int) parser.add_argument("--debug", action="store_true") args = parser.parse_args() # Configure logging main_logger = logging.getLogger("root") main_logger.setLevel(logging.ERROR) if args.debug: main_logger.setLevel(logging.DEBUG) state_logger = logging.getLogger("ax25.state") state_logger.setLevel(logging.DEBUG) state_logger.addHandler(logging.StreamHandler(sys.stdout)) state_logger = logging.getLogger("netrom.state") state_logger.setLevel(logging.DEBUG) state_logger.addHandler(logging.StreamHandler(sys.stdout)) scheduler = Scheduler() # Configure and initialize I/O device and L2 port_config = PortConfig.from_dict(0, { "port.enabled": True, "port.type": "serial", "serial.device": args.port, "serial.speed": args.baud }) # Initialize I/O device and L2 l3_protocols = L3Protocols() l2_multi = DefaultLinkMultiplexer(L3PriorityQueue, scheduler) l2_queueing = L2FIFOQueue(20, AX25Protocol.maximum_frame_size()) l2 = AX25Protocol(port_config, port_config.port_id(), AX25Call.parse(args.local_call), scheduler, l2_queueing, l2_multi, l3_protocols) kiss = KISSProtocol(port_config.port_id(), l2_queueing, port_config.get_boolean("kiss.checksum", False)) SerialDevice(kiss, port_config.get("serial.device"), port_config.get_int("serial.speed"), port_config.get_float("serial.timeout"), scheduler) scheduler.submit(L2IOLoop(l2_queueing, l2)) # Initialize L3 and L4 network_config = NetworkConfig.from_dict({ "netrom.node.call": args.local_call, "netrom.node.alias": args.local_alias, "netrom.ttl": 7, "netrom.nodes.interval": 60, "netrom.obs.init": 6, "netrom.obs.min": 4, "netrom.nodes.quality.min": 74 }) # Register L3 protocols netrom_l3 = NetRomL3(AX25Call.parse(network_config.node_call()), network_config.node_alias(), scheduler, l2_multi, NetRomRoutingTable(network_config.node_alias())) l3_protocols.register(netrom_l3) # Create the L4 protocol netrom_l4 = NetRomTransportProtocol(network_config, netrom_l3, scheduler) async def wait_for_network(): tty = TTY() loop.add_reader(sys.stdin, tty.handle_stdin) loop.add_signal_handler(signal.SIGTERM, tty.handle_signal, loop, scheduler) loop.add_signal_handler(signal.SIGINT, tty.handle_signal, loop, scheduler) remote_call = AX25Call.parse(args.remote_call) while True: (found, mtu) = netrom_l3.route_packet(NetRomAddress.from_call(remote_call)) if found: break await asyncio.sleep(0.200) main_logger.info(f"Learned route to {remote_call}") await asyncio.sleep(1.200) netrom_l4.open(protocol_factory=lambda: tty, local_call=AX25Call.parse(args.local_call), remote_call=AX25Call.parse(args.remote_call), origin_user=AX25Call(args.local_alias), origin_node=AX25Call(args.local_call)) loop = asyncio.get_event_loop() loop.create_task(wait_for_network()) try: loop.run_forever() finally: loop.close()
def from_safe_dict(cls, d): return cls(neighbor=AX25Call.parse(d["neighbor"]), dest=AX25Call.parse(d["destination"]), next_hop=AX25Call.parse(d["next_hop"]), quality=d["quality"], obsolescence=d["obsolescence"])
async def main_async(): parser = argparse.ArgumentParser( description='Decode packets from a serial port') parser.add_argument("config", help="Config file") parser.add_argument("--debug", action="store_true") args = parser.parse_args() s = Settings(".", args.config) node_settings = s.node_config() # Create the main event loop loop = asyncio.get_event_loop() dlms = [] for port_config in s.port_configs(): in_queue: asyncio.Queue = asyncio.Queue() out_queue: asyncio.Queue = asyncio.Queue() asyncio.create_task(kiss_port_factory(in_queue, out_queue, port_config)) # Wire the port with an AX25 layer dlm = DataLinkManager(AX25Call.parse(node_settings.node_call()), port_config.port_id(), in_queue, out_queue, loop.create_future) dlms.append(dlm) # Wire up Layer 3 and default L2 app nl = NetworkManager(s.network_configs(), loop) for dlm in dlms: nl.attach_data_link(dlm) dlm.add_l3_handler(IdHandler()) # Bind apps to netrom and start running the app servers for app_config in s.app_configs(): # This multiplexer bridges the unix socket server and the network connections multiplexer = TransportMultiplexer() # We have a single unix socket connection unix_factory = partial(NetromAppProtocol, app_config.app_name(), AX25Call.parse(app_config.app_call()), app_config.app_alias(), nl, multiplexer) logger.info( f"Creating unix socket server for {app_config.app_call()} at {app_config.app_socket()}" ) await loop.create_unix_server(unix_factory, app_config.app_socket(), start_serving=True) # And many network connections network_factory = partial(MultiplexingProtocol, multiplexer) nl.bind_server(AX25Call.parse(app_config.app_call()), app_config.app_alias(), network_factory) node_app_factory = partial(CommandProcessorProtocol, s, dlms, nl) for dlm in dlms: # TODO add a bind_server thing here too? dlm.add_l3_handler(DataLinkAdapter(dlm, node_app_factory)) node_call = s.network_configs().node_call() node_alias = s.network_configs().node_alias() # Make a default application for L4 nl.bind_server(AX25Call.parse(node_call), node_alias, node_app_factory) if node_settings.admin_enabled(): await loop.create_server(protocol_factory=node_app_factory, host=node_settings.admin_listen(), port=node_settings.admin_port(), start_serving=True) # Configure logging logging.config.fileConfig("config/logging.ini", disable_existing_loggers=False) event_logger = logging.getLogger("events") event_logger.setLevel(logging.INFO) event_logger.addHandler(handler) # Start processing packets tasks = [dlm.start() for dlm in dlms] tasks.append(nl.start()) logger.info("Packet engine started") await asyncio.wait(tasks)
def main(): parser = argparse.ArgumentParser( description='Broadcast to mesh network over serial device') parser.add_argument("device", help="Serial port to open") parser.add_argument("baud", type=int, help="Baudrate to use") parser.add_argument("callsign", help="Your callsign (e.g., K4DBZ-10)") parser.add_argument("address", help="Local address, e.g., 00.1a") parser.add_argument("port", type=int, help="Port", default=10) parser.add_argument("--debug", action="store_true") args = parser.parse_args() # Configure logging main_logger = logging.getLogger("root") main_logger.setLevel(logging.ERROR) main_logger.addHandler(logging.StreamHandler(sys.stdout)) if args.debug: main_logger.setLevel(logging.DEBUG) state_logger = logging.getLogger("ax25.state") state_logger.setLevel(logging.DEBUG) state_logger.addHandler(logging.StreamHandler(sys.stdout)) scheduler = Scheduler() # Configure and initialize I/O device and L2 port_config = PortConfig.from_dict( 0, { "port.enabled": True, "port.type": "serial", "serial.device": args.device, "serial.speed": args.baud }) # Initialize I/O device and L2 l3_protocols = L3Protocols() l2_multi = DefaultLinkMultiplexer(L3PriorityQueue, scheduler) l2_queueing = L2FIFOQueue(20, AX25Protocol.maximum_frame_size()) l2 = AX25Protocol(port_config, port_config.port_id(), AX25Call.parse(args.callsign), scheduler, l2_queueing, l2_multi, l3_protocols) kiss = KISSProtocol(port_config.port_id(), l2_queueing, port_config.get_boolean("kiss.checksum", False)) SerialDevice(kiss, port_config.get("serial.device"), port_config.get_int("serial.speed"), port_config.get_float("serial.timeout"), scheduler) scheduler.submit(L2IOLoop(l2_queueing, l2)) addr = MeshAddress.parse(args.address) mesh_l3 = MeshProtocol(our_address=addr, link_multiplexer=l2_multi, scheduler=scheduler) l3_protocols.register(mesh_l3) mesh_l4 = MeshTransportManager(mesh_l3) tty = TTY() loop = asyncio.get_event_loop() loop.add_reader(sys.stdin, tty.handle_stdin) loop.add_signal_handler(signal.SIGTERM, tty.handle_signal, loop, scheduler) loop.add_signal_handler(signal.SIGINT, tty.handle_signal, loop, scheduler) mesh_l4.connect(protocol_factory=lambda: tty, port=args.port, local_address=addr, remote_address=MeshProtocol.BroadcastAddress) try: loop.run_forever() finally: loop.close()