diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 9ffc0db8513b..d8179f85771f 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -213,6 +213,7 @@ in atop = import ./atop.nix { inherit pkgs runTest; }; atticd = runTest ./atticd.nix; atuin = runTest ./atuin.nix; + ax25 = handleTest ./ax25.nix { }; audiobookshelf = runTest ./audiobookshelf.nix; auth-mysql = runTest ./auth-mysql.nix; authelia = runTest ./authelia.nix; diff --git a/nixos/tests/ax25.nix b/nixos/tests/ax25.nix new file mode 100644 index 000000000000..f1092d5de101 --- /dev/null +++ b/nixos/tests/ax25.nix @@ -0,0 +1,131 @@ +import ./make-test-python.nix ( + { pkgs, lib, ... }: + let + + baud = 57600; + tty = "/dev/ttyACM0"; + port = "tnc0"; + socatPort = 1234; + + createAX25Node = nodeId: { + + boot.kernelPackages = pkgs.linuxPackages_ham; + boot.kernelModules = [ "ax25" ]; + + networking.firewall.allowedTCPPorts = [ socatPort ]; + + environment.systemPackages = with pkgs; [ + libax25 + ax25-tools + ax25-apps + socat + ]; + + services.ax25.axports."${port}" = { + inherit baud tty; + enable = true; + callsign = "NOCALL-${toString nodeId}"; + description = "mocked tnc"; + }; + + services.ax25.axlisten = { + enable = true; + }; + + # All mocks radios will connect back to socat-broker on node 1 in order to get + # all messages that are "broadcasted over the ether" + systemd.services.ax25-mock-hardware = { + description = "mock AX.25 TNC and Radio"; + wantedBy = [ "default.target" ]; + before = [ + "ax25-kissattach-${port}.service" + "axlisten.service" + ]; + after = [ "network.target" ]; + serviceConfig = { + Type = "exec"; + ExecStart = "${pkgs.socat}/bin/socat -d -d tcp:192.168.1.1:${toString socatPort} pty,link=${tty},b${toString baud},raw"; + }; + }; + }; + in + { + name = "ax25Simple"; + nodes = { + node1 = lib.mkMerge [ + (createAX25Node 1) + # mimicking radios on the same frequency + { + systemd.services.ax25-mock-ether = { + description = "mock radio ether"; + wantedBy = [ "default.target" ]; + requires = [ "network.target" ]; + before = [ "ax25-mock-hardware.service" ]; + # broken needs access to "ss" or "netstat" + path = [ pkgs.iproute2 ]; + serviceConfig = { + Type = "exec"; + ExecStart = "${pkgs.socat}/bin/socat-broker.sh tcp4-listen:${toString socatPort}"; + }; + postStart = "${pkgs.coreutils}/bin/sleep 2"; + }; + } + ]; + node2 = createAX25Node 2; + node3 = createAX25Node 3; + }; + testScript = + { ... }: + '' + def wait_for_machine(m): + m.succeed("lsmod | grep ax25") + m.wait_for_unit("ax25-axports.target") + m.wait_for_unit("axlisten.service") + m.fail("journalctl -o cat -u axlisten.service | grep -i \"no AX.25 port data configured\"") + + # start the first node since the socat-broker needs to be running + node1.start() + node1.wait_for_unit("ax25-mock-ether.service") + wait_for_machine(node1) + + node2.start() + node3.start() + wait_for_machine(node2) + wait_for_machine(node3) + + # Node 1 -> Node 2 + node1.succeed("echo hello | ax25_call ${port} NOCALL-1 NOCALL-2") + node2.sleep(1) + node2.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-1 to NOCALL-2 ctl I00\" | grep hello") + + # Node 1 -> Node 3 + node1.succeed("echo hello | ax25_call ${port} NOCALL-1 NOCALL-3") + node3.sleep(1) + node3.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-1 to NOCALL-3 ctl I00\" | grep hello") + + # Node 2 -> Node 1 + # must sleep due to previous ax25_call lingering + node2.sleep(5) + node2.succeed("echo hello | ax25_call ${port} NOCALL-2 NOCALL-1") + node1.sleep(1) + node1.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-2 to NOCALL-1 ctl I00\" | grep hello") + + # Node 2 -> Node 3 + node2.succeed("echo hello | ax25_call ${port} NOCALL-2 NOCALL-3") + node3.sleep(1) + node3.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-2 to NOCALL-3 ctl I00\" | grep hello") + + # Node 3 -> Node 1 + # must sleep due to previous ax25_call lingering + node3.sleep(5) + node3.succeed("echo hello | ax25_call ${port} NOCALL-3 NOCALL-1") + node1.sleep(1) + node1.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-3 to NOCALL-1 ctl I00\" | grep hello") + + # Node 3 -> Node 2 + node3.succeed("echo hello | ax25_call ${port} NOCALL-3 NOCALL-2") + node2.sleep(1) + node2.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-3 to NOCALL-2 ctl I00\" | grep hello") + ''; + } +)