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
21{ pkgs, ... }:
22{
23 name = "cjdns";
24
25 nodes = {
26 # Alice finds peers over over ETHInterface.
27 alice =
28 { ... }:
29 {
30 imports = [ basicConfig ];
31
32 services.cjdns.ETHInterface.bind = "eth1";
33
34 services.httpd.enable = true;
35 services.httpd.adminAddr = "foo@example.org";
36 networking.firewall.allowedTCPPorts = [ 80 ];
37 };
38
39 # Bob explicitly connects to Carol over UDPInterface.
40 bob =
41 { ... }:
42
43 {
44 imports = [ basicConfig ];
45
46 networking.interfaces.eth1.ipv4.addresses = [
47 {
48 address = "192.168.0.2";
49 prefixLength = 24;
50 }
51 ];
52
53 services.cjdns = {
54 UDPInterface = {
55 bind = "0.0.0.0:1024";
56 connectTo."192.168.0.1:1024" = {
57 password = carolPassword;
58 publicKey = carolPubKey;
59 };
60 };
61 };
62 };
63
64 # Carol listens on ETHInterface and UDPInterface,
65 # but knows neither Alice or Bob.
66 carol =
67 { ... }:
68 {
69 imports = [ basicConfig ];
70
71 environment.etc."cjdns.keys".text = ''
72 CJDNS_PRIVATE_KEY=${carolKey}
73 CJDNS_ADMIN_PASSWORD=FOOBAR
74 '';
75
76 networking.interfaces.eth1.ipv4.addresses = [
77 {
78 address = "192.168.0.1";
79 prefixLength = 24;
80 }
81 ];
82
83 services.cjdns = {
84 authorizedPasswords = [ carolPassword ];
85 ETHInterface.bind = "eth1";
86 UDPInterface.bind = "192.168.0.1:1024";
87 };
88 networking.firewall.allowedUDPPorts = [ 1024 ];
89 };
90
91 };
92
93 testScript = ''
94 import re
95
96 start_all()
97
98 alice.wait_for_unit("cjdns.service")
99 bob.wait_for_unit("cjdns.service")
100 carol.wait_for_unit("cjdns.service")
101
102
103 def cjdns_ip(machine):
104 res = machine.succeed("ip -o -6 addr show dev tun0")
105 ip = re.split("\\s+|/", res)[3]
106 machine.log("has ip {}".format(ip))
107 return ip
108
109
110 alice_ip6 = cjdns_ip(alice)
111 bob_ip6 = cjdns_ip(bob)
112 carol_ip6 = cjdns_ip(carol)
113
114 # ping a few times each to let the routing table establish itself
115
116 alice.wait_until_succeeds("ping -c 4 {}".format(carol_ip6))
117 bob.wait_until_succeeds("ping -c 4 {}".format(carol_ip6))
118
119 carol.wait_until_succeeds("ping -c 4 {}".format(alice_ip6))
120 carol.wait_until_succeeds("ping -c 4 {}".format(bob_ip6))
121
122 alice.wait_until_succeeds("ping -c 4 {}".format(bob_ip6))
123 bob.wait_until_succeeds("ping -c 4 {}".format(alice_ip6))
124
125 alice.wait_for_unit("httpd.service")
126
127 bob.succeed("curl --fail -g http://[{}]".format(alice_ip6))
128 '';
129}