1let
2 carolKey = "2d2a338b46f8e4a8c462f0c385b481292a05f678e19a2b82755258cf0f0af7e2";
3 carolPubKey = "n932l3pjvmhtxxcdrqq2qpw5zc58f01vvjx01h4dtd1bb0nnu2h0.k";
4 carolPassword = "678287829ce4c67bc8b227e56d94422ee1b85fa11618157b2f591de6c6322b52";
5 carolIp4 = "192.168.0.9";
6
7 basicConfig =
8 { config, pkgs, ... }:
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 networking.interfaces.eth1.prefixLength = 24;
16 # CJDNS output is incompatible with the XML log.
17 systemd.services.cjdns.serviceConfig.StandardOutput = "null";
18 #networking.firewall.enable = true;
19 networking.firewall.allowPing = true;
20 #networking.firewall.rejectPackets = true;
21 };
22
23in
24
25import ./make-test.nix ({ pkgs, ...} : {
26 name = "cjdns";
27 meta = with pkgs.stdenv.lib.maintainers; {
28 maintainers = [ emery ];
29 };
30
31 nodes = rec
32 { # Alice finds peers over over ETHInterface.
33 alice =
34 { config, ... }:
35 { imports = [ basicConfig ];
36
37 services.cjdns.ETHInterface.bind = "eth1";
38
39 services.httpd.enable = true;
40 services.httpd.adminAddr = "foo@example.org";
41 networking.firewall.allowedTCPPorts = [ 80 ];
42 };
43
44 # Bob explicitly connects to Carol over UDPInterface.
45 bob =
46 { config, lib, nodes, ... }:
47
48 let carolIp4 = lib.mkForce nodes.carol.config.networking.interfaces.eth1; in
49
50 { imports = [ basicConfig ];
51
52 networking.interfaces.eth1.ipAddress = "192.168.0.2";
53
54 services.cjdns =
55 { UDPInterface =
56 { bind = "0.0.0.0:1024";
57 connectTo."192.168.0.1:1024}" =
58 { password = carolPassword;
59 publicKey = carolPubKey;
60 };
61 };
62 };
63 };
64
65 # Carol listens on ETHInterface and UDPInterface,
66 # but knows neither Alice or Bob.
67 carol =
68 { config, lib, nodes, ... }:
69 let
70 carolIp4 = (lib.mkForce nodes.carol.config.networking.interfaces.eth1);
71 in
72 { imports = [ basicConfig ];
73
74 environment.etc."cjdns.keys".text = ''
75 CJDNS_PRIVATE_KEY=${carolKey}
76 CJDNS_ADMIN_PASSWORD=FOOBAR
77 '';
78
79 networking.interfaces.eth1.ipAddress = "192.168.0.1";
80
81 services.cjdns =
82 { authorizedPasswords = [ carolPassword ];
83 ETHInterface.bind = "eth1";
84 UDPInterface.bind = "192.168.0.1:1024";
85 };
86 networking.firewall.allowedUDPPorts = [ 1024 ];
87 };
88
89 };
90
91 testScript =
92 ''
93 startAll;
94
95 $alice->waitForUnit("cjdns.service");
96 $bob->waitForUnit("cjdns.service");
97 $carol->waitForUnit("cjdns.service");
98
99 sub cjdnsIp {
100 my ($machine) = @_;
101 my $ip = (split /[ \/]+/, $machine->succeed("ip -o -6 addr show dev tun0"))[3];
102 $machine->log("has ip $ip");
103 return $ip;
104 }
105
106 my $aliceIp6 = cjdnsIp $alice;
107 my $bobIp6 = cjdnsIp $bob;
108 my $carolIp6 = cjdnsIp $carol;
109
110 # ping a few times each to let the routing table establish itself
111
112 $alice->succeed("ping6 -c 4 $carolIp6");
113 $bob->succeed("ping6 -c 4 $carolIp6");
114
115 $carol->succeed("ping6 -c 4 $aliceIp6");
116 $carol->succeed("ping6 -c 4 $bobIp6");
117
118 $alice->succeed("ping6 -c 4 $bobIp6");
119 $bob->succeed("ping6 -c 4 $aliceIp6");
120
121 $alice->waitForUnit("httpd.service");
122
123 $bob->succeed("curl --fail -g http://[$aliceIp6]");
124 '';
125})