anchor.diff:    nodes =
       { tracker =
           { pkgs, ... }:
  -        { environment.systemPackages = [ pkgs.transmission pkgs.opentracker ];
  +        { environment.systemPackages = [ pkgs.transmission ];
  +
  +          virtualisation.vlans = [ 1 ];
  +          networking.interfaces.eth1.ipv4.addresses = [
  +            { address = externalTrackerAddress; prefixLength = 24; }
  +          ];
   
             # We need Apache on the tracker to serve the torrents.
             services.httpd.enable = true;
             services.httpd.adminAddr = "foo@example.org";
             services.httpd.documentRoot = "/tmp";
   
  -          networking.firewall.enable = false; # FIXME: figure out what ports we actually need
  +          networking.firewall.enable = false;
  +
  +          services.opentracker.enable = true;
  +
  +          services.transmission.enable = true;
  +          services.transmission.settings.dht-enabled = false;
  +          services.transmission.settings.port-forwaring-enabled = false;
           };
   
         router =
  -        { pkgs, ... }:
  -        { environment.systemPackages = [ pkgs.miniupnpd ];
  -          virtualisation.vlans = [ 1 2 ];
  +        { pkgs, nodes, ... }:
  +        { virtualisation.vlans = [ 1 2 ];
             networking.nat.enable = true;
             networking.nat.internalInterfaces = [ "eth2" ];
             networking.nat.externalInterface = "eth1";
  -          networking.firewall.enable = false;
  +          networking.firewall.enable = true;
  +          networking.firewall.trustedInterfaces = [ "eth2" ];
  +          networking.interfaces.eth0.ipv4.addresses = [];
  +          networking.interfaces.eth1.ipv4.addresses = [
  +            { address = externalRouterAddress; prefixLength = 24; }
  +          ];
  +          networking.interfaces.eth2.ipv4.addresses = [
  +            { address = internalRouterAddress; prefixLength = 24; }
  +          ];
  +          services.miniupnpd = {
  +            enable = true;
  +            externalInterface = "eth1";
  +            internalIPs = [ "eth2" ];
  +            appendConfig = ''
  +              ext_ip=${externalRouterAddress}
  +            '';
  +          };
           };
   
         client1 =
           { pkgs, nodes, ... }:
  -        { environment.systemPackages = [ pkgs.transmission ];
  +        { environment.systemPackages = [ pkgs.transmission pkgs.miniupnpc ];
             virtualisation.vlans = [ 2 ];
  -          networking.defaultGateway =
  -            (pkgs.lib.head nodes.router.config.networking.interfaces.eth2.ipv4.addresses).address;
  +          networking.interfaces.eth0.ipv4.addresses = [];
  +          networking.interfaces.eth1.ipv4.addresses = [
  +            { address = internalClient1Address; prefixLength = 24; }
  +          ];
  +          networking.defaultGateway = internalRouterAddress;
             networking.firewall.enable = false;
  +          services.transmission.enable = true;
  +          services.transmission.settings.dht-enabled = false;
  +          services.transmission.settings.message-level = 3;
           };
   
         client2 =
           { pkgs, ... }:
           { environment.systemPackages = [ pkgs.transmission ];
  +          virtualisation.vlans = [ 1 ];
  +          networking.interfaces.eth0.ipv4.addresses = [];
  +          networking.interfaces.eth1.ipv4.addresses = [
  +            { address = externalClient2Address; prefixLength = 24; }
  +          ];
             networking.firewall.enable = false;
  +          services.transmission.enable = true;
  +          services.transmission.settings.dht-enabled = false;
  +          services.transmission.settings.port-forwaring-enabled = false;
           };
       };
   
  
positive.code:   # Some random file to serve.
    file = pkgs.hello.src;
  
    miniupnpdConf = nodes: pkgs.writeText "miniupnpd.conf"
      ''
        ext_ifname=eth1
        listening_ip=${(pkgs.lib.head nodes.router.config.networking.interfaces.eth2.ipv4.addresses).address}/24
        allow 1024-65535 192.168.2.0/24 1024-65535
      '';
  
  in
  
  {
  
negative.code:     ''
        startAll;
  
        # Wait for network and miniupnpd.
        $router->waitForUnit("network-online.target");
        $router->waitForUnit("miniupnpd");
  
        # Create the torrent.
        $tracker->succeed("mkdir /tmp/data");
        $tracker->succeed("cp ${file} /tmp/data/test.tar.bz2");
        $tracker->succeed("transmission-create /tmp/data/test.tar.bz2 --private --tracker http://${externalTrackerAddress}:6969/announce --outfile /tmp/test.torrent");
        $tracker->succeed("chmod 644 /tmp/test.torrent");
  
        # Start the tracker.  !!! use a less crappy tracker
        $tracker->waitForUnit("network-online.target");
        $tracker->waitForUnit("opentracker.service");
        $tracker->waitForOpenPort(6969);
  
        # Start the initial seeder.
        $tracker->succeed("transmission-remote --add /tmp/test.torrent --no-portmap --no-dht --download-dir /tmp/data");
  
        # Now we should be able to download from the client behind the NAT.
        $tracker->waitForUnit("httpd");
        $client1->waitForUnit("network-online.target");
        $client1->succeed("transmission-remote --add http://${externalTrackerAddress}/test.torrent --download-dir /tmp >&2 &");
        $client1->waitForFile("/tmp/test.tar.bz2");
        $client1->succeed("cmp /tmp/test.tar.bz2 ${file}");
  
        # Bring down the initial seeder.
        # $tracker->stopJob("transmission");
  
        # Now download from the second client.  This can only succeed if
        # the first client created a NAT hole in the router.
        $client2->waitForUnit("network-online.target");
        $client2->succeed("transmission-remote --add http://${externalTrackerAddress}/test.torrent --no-portmap --no-dht --download-dir /tmp >&2 &");
        $client2->waitForFile("/tmp/test.tar.bz2");
        $client2->succeed("cmp /tmp/test.tar.bz2 ${file}");
      '';
  