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