1import ./make-test-python.nix (
2 { pkgs, lib, ... }:
3 let
4
5 baud = 57600;
6 tty = "/dev/ttyACM0";
7 port = "tnc0";
8 socatPort = 1234;
9
10 createAX25Node = nodeId: {
11
12 boot.kernelPackages = pkgs.linuxPackages_ham;
13 boot.kernelModules = [ "ax25" ];
14
15 networking.firewall.allowedTCPPorts = [ socatPort ];
16
17 environment.systemPackages = with pkgs; [
18 libax25
19 ax25-tools
20 ax25-apps
21 socat
22 ];
23
24 services.ax25.axports."${port}" = {
25 inherit baud tty;
26 enable = true;
27 callsign = "NOCALL-${toString nodeId}";
28 description = "mocked tnc";
29 };
30
31 services.ax25.axlisten = {
32 enable = true;
33 };
34
35 # All mocks radios will connect back to socat-broker on node 1 in order to get
36 # all messages that are "broadcasted over the ether"
37 systemd.services.ax25-mock-hardware = {
38 description = "mock AX.25 TNC and Radio";
39 wantedBy = [ "default.target" ];
40 before = [
41 "ax25-kissattach-${port}.service"
42 "axlisten.service"
43 ];
44 after = [ "network.target" ];
45 serviceConfig = {
46 Type = "exec";
47 ExecStart = "${pkgs.socat}/bin/socat -d -d tcp:192.168.1.1:${toString socatPort} pty,link=${tty},b${toString baud},raw";
48 };
49 };
50 };
51 in
52 {
53 name = "ax25Simple";
54 nodes = {
55 node1 = lib.mkMerge [
56 (createAX25Node 1)
57 # mimicking radios on the same frequency
58 {
59 systemd.services.ax25-mock-ether = {
60 description = "mock radio ether";
61 wantedBy = [ "default.target" ];
62 requires = [ "network.target" ];
63 before = [ "ax25-mock-hardware.service" ];
64 # broken needs access to "ss" or "netstat"
65 path = [ pkgs.iproute2 ];
66 serviceConfig = {
67 Type = "exec";
68 ExecStart = "${pkgs.socat}/bin/socat-broker.sh tcp4-listen:${toString socatPort}";
69 };
70 postStart = "${pkgs.coreutils}/bin/sleep 2";
71 };
72 }
73 ];
74 node2 = createAX25Node 2;
75 node3 = createAX25Node 3;
76 };
77 testScript =
78 { ... }:
79 ''
80 def wait_for_machine(m):
81 m.succeed("lsmod | grep ax25")
82 m.wait_for_unit("ax25-axports.target")
83 m.wait_for_unit("axlisten.service")
84 m.fail("journalctl -o cat -u axlisten.service | grep -i \"no AX.25 port data configured\"")
85
86 # start the first node since the socat-broker needs to be running
87 node1.start()
88 node1.wait_for_unit("ax25-mock-ether.service")
89 wait_for_machine(node1)
90
91 node2.start()
92 node3.start()
93 wait_for_machine(node2)
94 wait_for_machine(node3)
95
96 # Node 1 -> Node 2
97 node1.succeed("echo hello | ax25_call ${port} NOCALL-1 NOCALL-2")
98 node2.sleep(1)
99 node2.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-1 to NOCALL-2 ctl I00\" | grep hello")
100
101 # Node 1 -> Node 3
102 node1.succeed("echo hello | ax25_call ${port} NOCALL-1 NOCALL-3")
103 node3.sleep(1)
104 node3.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-1 to NOCALL-3 ctl I00\" | grep hello")
105
106 # Node 2 -> Node 1
107 # must sleep due to previous ax25_call lingering
108 node2.sleep(5)
109 node2.succeed("echo hello | ax25_call ${port} NOCALL-2 NOCALL-1")
110 node1.sleep(1)
111 node1.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-2 to NOCALL-1 ctl I00\" | grep hello")
112
113 # Node 2 -> Node 3
114 node2.succeed("echo hello | ax25_call ${port} NOCALL-2 NOCALL-3")
115 node3.sleep(1)
116 node3.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-2 to NOCALL-3 ctl I00\" | grep hello")
117
118 # Node 3 -> Node 1
119 # must sleep due to previous ax25_call lingering
120 node3.sleep(5)
121 node3.succeed("echo hello | ax25_call ${port} NOCALL-3 NOCALL-1")
122 node1.sleep(1)
123 node1.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-3 to NOCALL-1 ctl I00\" | grep hello")
124
125 # Node 3 -> Node 2
126 node3.succeed("echo hello | ax25_call ${port} NOCALL-3 NOCALL-2")
127 node2.sleep(1)
128 node2.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-3 to NOCALL-2 ctl I00\" | grep hello")
129 '';
130 }
131)