at master 5.5 kB view raw
1let 2 aliceIp6 = "202:b70:9b0b:cf34:f93c:8f18:bbfd:7034"; 3 aliceKeys = { 4 PublicKey = "3e91ec9e861960d86e1ce88051f97c435bdf2859640ab681dfa906eb45ad5182"; 5 PrivateKey = "a867f9e078e4ce58d310cf5acd4622d759e2a21df07e1d6fc380a2a26489480d3e91ec9e861960d86e1ce88051f97c435bdf2859640ab681dfa906eb45ad5182"; 6 }; 7 bobIp6 = "202:a483:73a4:9f2d:a559:4a19:bc9:8458"; 8 bobPrefix = "302:a483:73a4:9f2d"; 9 bobConfig = { 10 InterfacePeers = { 11 eth1 = [ "tcp://192.168.1.200:12345" ]; 12 }; 13 MulticastInterfaces = [ 14 { 15 Regex = ".*"; 16 Beacon = true; 17 Listen = true; 18 Port = 54321; 19 Priority = 0; 20 } 21 ]; 22 PublicKey = "2b6f918b6c1a4b54d6bcde86cf74e074fb32ead4ee439b7930df2aa60c825186"; 23 PrivateKey = "0c4a24acd3402722ce9277ed179f4a04b895b49586493c25fbaed60653d857d62b6f918b6c1a4b54d6bcde86cf74e074fb32ead4ee439b7930df2aa60c825186"; 24 }; 25 danIp6 = bobPrefix + "::2"; 26 27in 28{ pkgs, ... }: 29{ 30 name = "yggdrasil"; 31 meta = with pkgs.lib.maintainers; { 32 maintainers = [ gazally ]; 33 }; 34 35 nodes = { 36 # Alice is listening for peerings on a specified port, 37 # but has multicast peering disabled. Alice has part of her 38 # yggdrasil config in Nix and part of it in a file. 39 alice = 40 { ... }: 41 { 42 networking = { 43 interfaces.eth1.ipv4.addresses = [ 44 { 45 address = "192.168.1.200"; 46 prefixLength = 24; 47 } 48 ]; 49 firewall.allowedTCPPorts = [ 50 80 51 12345 52 ]; 53 }; 54 services.httpd.enable = true; 55 services.httpd.adminAddr = "foo@example.org"; 56 57 services.yggdrasil = { 58 enable = true; 59 settings = { 60 Listen = [ "tcp://0.0.0.0:12345" ]; 61 MulticastInterfaces = [ ]; 62 }; 63 configFile = toString ( 64 pkgs.writeTextFile { 65 name = "yggdrasil-alice-conf"; 66 text = builtins.toJSON aliceKeys; 67 } 68 ); 69 }; 70 }; 71 72 # Bob is set up to peer with Alice, and also to do local multicast 73 # peering. Bob's yggdrasil config is in a file. 74 bob = 75 { ... }: 76 { 77 networking.firewall.allowedTCPPorts = [ 54321 ]; 78 services.yggdrasil = { 79 enable = true; 80 openMulticastPort = true; 81 configFile = toString ( 82 pkgs.writeTextFile { 83 name = "yggdrasil-bob-conf"; 84 text = builtins.toJSON bobConfig; 85 } 86 ); 87 }; 88 89 boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = 1; 90 91 networking = { 92 bridges.br0.interfaces = [ ]; 93 interfaces.br0 = { 94 ipv6.addresses = [ 95 { 96 address = bobPrefix + "::1"; 97 prefixLength = 64; 98 } 99 ]; 100 }; 101 }; 102 103 # dan is a node inside a container running on bob's host. 104 containers.dan = { 105 autoStart = true; 106 privateNetwork = true; 107 hostBridge = "br0"; 108 config = { 109 networking.interfaces.eth0.ipv6 = { 110 addresses = [ 111 { 112 address = bobPrefix + "::2"; 113 prefixLength = 64; 114 } 115 ]; 116 routes = [ 117 { 118 address = "200::"; 119 prefixLength = 7; 120 via = bobPrefix + "::1"; 121 } 122 ]; 123 }; 124 services.httpd.enable = true; 125 services.httpd.adminAddr = "foo@example.org"; 126 networking.firewall.allowedTCPPorts = [ 80 ]; 127 }; 128 }; 129 }; 130 131 # Carol only does local peering. Carol's yggdrasil config is all Nix. 132 carol = 133 { ... }: 134 { 135 networking.firewall.allowedTCPPorts = [ 43210 ]; 136 services.yggdrasil = { 137 enable = true; 138 extraArgs = [ 139 "-loglevel" 140 "error" 141 ]; 142 denyDhcpcdInterfaces = [ "ygg0" ]; 143 settings = { 144 IfTAPMode = true; 145 IfName = "ygg0"; 146 MulticastInterfaces = [ 147 { 148 Port = 43210; 149 } 150 ]; 151 openMulticastPort = true; 152 }; 153 persistentKeys = true; 154 }; 155 }; 156 }; 157 158 testScript = '' 159 import re 160 161 # Give Alice a head start so she is ready when Bob calls. 162 alice.start() 163 alice.wait_for_unit("yggdrasil.service") 164 165 bob.start() 166 carol.start() 167 bob.wait_for_unit("default.target") 168 carol.wait_for_unit("yggdrasil.service") 169 170 ip_addr_show = "ip -o -6 addr show dev ygg0 scope global" 171 carol.wait_until_succeeds(f"[ `{ip_addr_show} | grep -v tentative | wc -l` -ge 1 ]") 172 carol_ip6 = re.split(" +|/", carol.succeed(ip_addr_show))[3] 173 174 # If Alice can talk to Carol, then Bob's outbound peering and Carol's 175 # local peering have succeeded and everybody is connected. 176 alice.wait_until_succeeds(f"ping -c 1 {carol_ip6}") 177 alice.succeed("ping -c 1 ${bobIp6}") 178 179 bob.succeed("ping -c 1 ${aliceIp6}") 180 bob.succeed(f"ping -c 1 {carol_ip6}") 181 182 carol.succeed("ping -c 1 ${aliceIp6}") 183 carol.succeed("ping -c 1 ${bobIp6}") 184 carol.succeed("ping -c 1 ${bobPrefix}::1") 185 carol.succeed("ping -c 8 ${danIp6}") 186 187 carol.fail("journalctl -u dhcpcd | grep ygg0") 188 189 alice.wait_for_unit("httpd.service") 190 carol.succeed("curl --fail -g http://[${aliceIp6}]") 191 carol.succeed("curl --fail -g http://[${danIp6}]") 192 ''; 193}