Beispiel #1
0
 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)
Beispiel #2
0
 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)
Beispiel #4
0
 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
Beispiel #5
0
 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))
Beispiel #6
0
 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"]})
Beispiel #8
0
    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))
Beispiel #9
0
    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)
Beispiel #10
0
 def local_call(self) -> AX25Call:
     return AX25Call.parse(self.config.node_call())
Beispiel #11
0
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()
Beispiel #12
0
    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)
Beispiel #13
0
    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)
Beispiel #14
0
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()
Beispiel #15
0
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()
Beispiel #16
0
 def from_safe_dict(cls, d):
     return cls(call=AX25Call.parse(d["call"]),
                port=d["port"],
                quality=d["quality"])
Beispiel #17
0
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()
Beispiel #18
0
 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"])
Beispiel #19
0
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)
Beispiel #20
0
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()