# The serial half of the Psion sidecar: a PPP link to a Psion 5MX over # /dev/ttyAMA0 (RS232 level shifter on the GPIO header, 115200 8N1 with # RTS/CTS), masqueraded out of wifi, plus a telnet login for the Psion's # terminal client. # # Cleartext telnet and unauthenticated PPP are safe *only* because the link is # a two-node cable: the peer is a machine from 1999 that speaks no TLS. Nothing # here is exposed to wlan0. { pkgs, ... }: let # Point-to-point addresses for the serial link; nothing else routes here. piAddress = "10.0.0.1"; psionAddress = "10.0.0.2"; in { # pppd needs exclusive use of the port. NixOS starts a getty on any serial # console named in boot.kernelParams; ttyAMA0 is not one today, but disable it # explicitly so a later kernel-param change cannot silently steal the line. systemd.services."serial-getty@ttyAMA0".enable = false; services.pppd = { enable = true; peers.psion.config = '' /dev/ttyAMA0 115200 ${piAddress}:${psionAddress} # Hardware flow control, matching the Psion's modem profile. crtscts # A null-modem cable has no carrier detect and no peer to authenticate. local noauth # The systemd unit is Type=notify, so pppd must stay in the foreground. nodetach lock # Wait for the Psion rather than failing when it is unplugged, and keep # waiting for the next time it is plugged back in. passive persist maxfail 0 holdoff 1 # Hand the Psion resolvers over the link, so its Internet profile can set # "get DNS from server = True" instead of hard-coding them. ms-dns 1.1.1.1 ms-dns 8.8.8.8 ''; }; # The Psion's route to the internet. The original write-up used pppd's # proxyarp instead; NAT keeps the Psion out of the LAN broadcast domain and # does not depend on what the wifi router tolerates. networking.nat = { enable = true; externalInterface = "wlan0"; internalIPs = [ "${psionAddress}/32" ]; }; # Everything the Psion connects to (telnet here, POP3/SMTP in # ./email-proxy.nix) is reachable over the PPP link and nowhere else. networking.firewall.trustedInterfaces = [ "ppp0" ]; # The Psion's terminal client speaks telnet over TCP, which it renders far # better than the raw serial console. Socket-activated, one process per # connection; busybox's telnetd in inetd mode hands straight over to login. systemd.sockets.telnetd = { description = "Telnet login socket for the Psion"; wantedBy = [ "sockets.target" ]; listenStreams = [ "${piAddress}:23" ]; socketConfig = { Accept = true; # ppp0 (and with it 10.0.0.1) only exists while the Psion is connected; # FreeBind lets the socket be listening before that. FreeBind = true; }; }; systemd.services."telnetd@" = { description = "Telnet login for the Psion"; serviceConfig = { ExecStart = "-${pkgs.busybox}/bin/busybox telnetd -i -l ${pkgs.shadow}/bin/login"; StandardInput = "socket"; StandardError = "journal"; }; }; }