Skip to content

KISS over TCP Interface

Warning

TNCs that are able to be controlled by TCP connections should not be directly connected to the Internet, or else should have firewall rules restricting which IPs can connect to them.

The TCPKISSInterface allows you to connect to a TNC in KISS mode over TCP. Some TNC manufacturers enable the ability to control your TNC over 'telnet', which in practice means a standard TCP/IP network connection. Your TNC must be in KISS mode before connecting this way. This can also be used to connect to a software TNC like Direwolf1.

To set up your TCP-connected TNC, you will need its IP address or hostname, and its port number.

You can see a working example of the TCP Interface configuration in the tutorial.

pax25.interfaces.tcp_kiss.TCPKISSInterface

Interface for connecting to TCP-hosting TNCs.

Source code in pax25/interfaces/tcp_kiss.py
class TCPKISSInterface(Interface[TCPKISSSettings]):
    """
    Interface for connecting to TCP-hosting TNCs.
    """

    type = "KISSOverTNC"
    connection: TCPKISSConnection | None

    def __init__(
        self,
        name: str,
        settings: TCPKISSSettings,
        station: Station,
    ) -> None:
        """
        Initialize the Interface.
        """
        self.name = name
        self._settings = settings
        self.station = station
        self.shutting_down = False
        self.connection = None

    def build_connection(self, delay: int = 0) -> None:
        """
        Set up the connection object that will be sending and receiving frames.
        """
        self.connection = TCPKISSConnection(
            remote_tnc=ResolvedConnectionSpec(
                host=self._settings.get("host", DEFAULT_ADDRESS),
                port=self._settings.get("port", DEFAULT_PORT),
            ),
            interface=self,
            close_callback=self.connection_closed,
            delay=delay,
        )

    def connection_closed(self, connection: TCPKISSConnection) -> None:
        """
        Callback for closing the TCP connection.
        """
        if self.shutting_down:
            # Do nothing, this will be cleaned up elsewhere.
            return
        # Need to retry.
        self.start(delay=self.settings.get("retry_delay", 5))

    def start(self, delay: int = 0) -> None:
        """
        Set up the loops.
        """
        self.shutting_down = False
        self.build_connection(delay=delay)

    def send_frame(self, frame: Frame) -> None:
        """
        Send a frame out to all the connections which are listening.
        """
        if self.connection:
            self.connection.send_frame(frame)

    @property
    def listening(self) -> bool:
        """
        Returns true if we are connected to the TNC.
        """
        return bool(
            self.connection
            and self.connection.writer
            and not self.connection.writer.is_closing()
        )

    @property
    def sudo(self) -> bool:
        """
        Randos over the air shouldn't be superusers.
        """
        return False

    @property
    def gateway(self) -> bool:
        return self.settings.get("gateway", True)

    async def reload_settings(self, settings: TCPKISSSettings) -> None:
        """
        Reload interface settings.
        """
        await self.shutdown()
        self._settings = settings
        self.shutting_down = False
        self.start()

    async def shutdown(self) -> None:
        """
        Close out all connections.
        """
        self.shutting_down = True
        if self.connection:
            await self.connection.close()
            self.connection = None

listening: bool property

Returns true if we are connected to the TNC.

sudo: bool property

Randos over the air shouldn't be superusers.

__init__(name: str, settings: TCPKISSSettings, station: Station) -> None

Initialize the Interface.

Source code in pax25/interfaces/tcp_kiss.py
def __init__(
    self,
    name: str,
    settings: TCPKISSSettings,
    station: Station,
) -> None:
    """
    Initialize the Interface.
    """
    self.name = name
    self._settings = settings
    self.station = station
    self.shutting_down = False
    self.connection = None

build_connection(delay: int = 0) -> None

Set up the connection object that will be sending and receiving frames.

Source code in pax25/interfaces/tcp_kiss.py
def build_connection(self, delay: int = 0) -> None:
    """
    Set up the connection object that will be sending and receiving frames.
    """
    self.connection = TCPKISSConnection(
        remote_tnc=ResolvedConnectionSpec(
            host=self._settings.get("host", DEFAULT_ADDRESS),
            port=self._settings.get("port", DEFAULT_PORT),
        ),
        interface=self,
        close_callback=self.connection_closed,
        delay=delay,
    )

connection_closed(connection: TCPKISSConnection) -> None

Callback for closing the TCP connection.

Source code in pax25/interfaces/tcp_kiss.py
def connection_closed(self, connection: TCPKISSConnection) -> None:
    """
    Callback for closing the TCP connection.
    """
    if self.shutting_down:
        # Do nothing, this will be cleaned up elsewhere.
        return
    # Need to retry.
    self.start(delay=self.settings.get("retry_delay", 5))

reload_settings(settings: TCPKISSSettings) -> None async

Reload interface settings.

Source code in pax25/interfaces/tcp_kiss.py
async def reload_settings(self, settings: TCPKISSSettings) -> None:
    """
    Reload interface settings.
    """
    await self.shutdown()
    self._settings = settings
    self.shutting_down = False
    self.start()

send_frame(frame: Frame) -> None

Send a frame out to all the connections which are listening.

Source code in pax25/interfaces/tcp_kiss.py
def send_frame(self, frame: Frame) -> None:
    """
    Send a frame out to all the connections which are listening.
    """
    if self.connection:
        self.connection.send_frame(frame)

shutdown() -> None async

Close out all connections.

Source code in pax25/interfaces/tcp_kiss.py
async def shutdown(self) -> None:
    """
    Close out all connections.
    """
    self.shutting_down = True
    if self.connection:
        await self.connection.close()
        self.connection = None

start(delay: int = 0) -> None

Set up the loops.

Source code in pax25/interfaces/tcp_kiss.py
def start(self, delay: int = 0) -> None:
    """
    Set up the loops.
    """
    self.shutting_down = False
    self.build_connection(delay=delay)

  1. This statement not tested by the document author.