1let
2 carolKey = "2d2a338b46f8e4a8c462f0c385b481292a05f678e19a2b82755258cf0f0af7e2";
3 carolPubKey = "n932l3pjvmhtxxcdrqq2qpw5zc58f01vvjx01h4dtd1bb0nnu2h0.k";
4 carolPassword = "678287829ce4c67bc8b227e56d94422ee1b85fa11618157b2f591de6c6322b52";
5
6 basicConfig =
7 { ... }:
8 {
9 services.cjdns.enable = true;
10
11 # Turning off DHCP isn't very realistic but makes
12 # the sequence of address assignment less stochastic.
13 networking.useDHCP = false;
14
15 # CJDNS output is incompatible with the XML log.
16 systemd.services.cjdns.serviceConfig.StandardOutput = "null";
17 };
18
19in
20
21import ./make-test-python.nix (
22 { pkgs, ... }:
23 {
24 name = "cjdns";
25 meta = with pkgs.lib.maintainers; {
26 maintainers = [ ehmry ];
27 };
28
29 nodes = {
30 # Alice finds peers over over ETHInterface.
31 alice =
32 { ... }:
33 {
34 imports = [ basicConfig ];
35
36 services.cjdns.ETHInterface.bind = "eth1";
37
38 services.httpd.enable = true;
39 services.httpd.adminAddr = "foo@example.org";
40 networking.firewall.allowedTCPPorts = [ 80 ];
41 };
42
43 # Bob explicitly connects to Carol over UDPInterface.
44 bob =
45 { ... }:
46
47 {
48 imports = [ basicConfig ];
49
50 networking.interfaces.eth1.ipv4.addresses = [
51 {
52 address = "192.168.0.2";
53 prefixLength = 24;
54 }
55 ];
56
57 services.cjdns = {
58 UDPInterface = {
59 bind = "0.0.0.0:1024";
60 connectTo."192.168.0.1:1024" = {
61 password = carolPassword;
62 publicKey = carolPubKey;
63 };
64 };
65 };
66 };
67
68 # Carol listens on ETHInterface and UDPInterface,
69 # but knows neither Alice or Bob.
70 carol =
71 { ... }:
72 {
73 imports = [ basicConfig ];
74
75 environment.etc."cjdns.keys".text = ''
76 CJDNS_PRIVATE_KEY=${carolKey}
77 CJDNS_ADMIN_PASSWORD=FOOBAR
78 '';
79
80 networking.interfaces.eth1.ipv4.addresses = [
81 {
82 address = "192.168.0.1";
83 prefixLength = 24;
84 }
85 ];
86
87 services.cjdns = {
88 authorizedPasswords = [ carolPassword ];
89 ETHInterface.bind = "eth1";
90 UDPInterface.bind = "192.168.0.1:1024";
91 };
92 networking.firewall.allowedUDPPorts = [ 1024 ];
93 };
94
95 };
96
97 testScript = ''
98 import re
99
100 start_all()
101
102 alice.wait_for_unit("cjdns.service")
103 bob.wait_for_unit("cjdns.service")
104 carol.wait_for_unit("cjdns.service")
105
106
107 def cjdns_ip(machine):
108 res = machine.succeed("ip -o -6 addr show dev tun0")
109 ip = re.split("\s+|/", res)[3]
110 machine.log("has ip {}".format(ip))
111 return ip
112
113
114 alice_ip6 = cjdns_ip(alice)
115 bob_ip6 = cjdns_ip(bob)
116 carol_ip6 = cjdns_ip(carol)
117
118 # ping a few times each to let the routing table establish itself
119
120 alice.succeed("ping -c 4 {}".format(carol_ip6))
121 bob.succeed("ping -c 4 {}".format(carol_ip6))
122
123 carol.succeed("ping -c 4 {}".format(alice_ip6))
124 carol.succeed("ping -c 4 {}".format(bob_ip6))
125
126 alice.succeed("ping -c 4 {}".format(bob_ip6))
127 bob.succeed("ping -c 4 {}".format(alice_ip6))
128
129 alice.wait_for_unit("httpd.service")
130
131 bob.succeed("curl --fail -g http://[{}]".format(alice_ip6))
132 '';
133 }
134)