Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[backport] Miniupnpd and bittorrent improvements (#46443) #47956

Merged
merged 5 commits into from
Oct 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 2 additions & 22 deletions nixos/modules/services/networking/miniupnpd.nix
Original file line number Diff line number Diff line change
Expand Up @@ -57,32 +57,12 @@ in
};

config = mkIf cfg.enable {
# from miniupnpd/netfilter/iptables_init.sh
networking.firewall.extraCommands = ''
iptables -t nat -N MINIUPNPD
iptables -t nat -A PREROUTING -i ${cfg.externalInterface} -j MINIUPNPD
iptables -t mangle -N MINIUPNPD
iptables -t mangle -A PREROUTING -i ${cfg.externalInterface} -j MINIUPNPD
iptables -t filter -N MINIUPNPD
iptables -t filter -A FORWARD -i ${cfg.externalInterface} ! -o ${cfg.externalInterface} -j MINIUPNPD
iptables -t nat -N MINIUPNPD-PCP-PEER
iptables -t nat -A POSTROUTING -o ${cfg.externalInterface} -j MINIUPNPD-PCP-PEER
${pkgs.bash}/bin/bash -x ${pkgs.miniupnpd}/etc/miniupnpd/iptables_init.sh -i ${cfg.externalInterface}
'';

# from miniupnpd/netfilter/iptables_removeall.sh
networking.firewall.extraStopCommands = ''
iptables -t nat -F MINIUPNPD
iptables -t nat -D PREROUTING -i ${cfg.externalInterface} -j MINIUPNPD
iptables -t nat -X MINIUPNPD
iptables -t mangle -F MINIUPNPD
iptables -t mangle -D PREROUTING -i ${cfg.externalInterface} -j MINIUPNPD
iptables -t mangle -X MINIUPNPD
iptables -t filter -F MINIUPNPD
iptables -t filter -D FORWARD -i ${cfg.externalInterface} ! -o ${cfg.externalInterface} -j MINIUPNPD
iptables -t filter -X MINIUPNPD
iptables -t nat -F MINIUPNPD-PCP-PEER
iptables -t nat -D POSTROUTING -o ${cfg.externalInterface} -j MINIUPNPD-PCP-PEER
iptables -t nat -X MINIUPNPD-PCP-PEER
${pkgs.bash}/bin/bash -x ${pkgs.miniupnpd}/etc/miniupnpd/iptables_removeall.sh -i ${cfg.externalInterface}
'';

systemd.services.miniupnpd = {
Expand Down
1 change: 1 addition & 0 deletions nixos/release.nix
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ in rec {
tests.acme = callTest tests/acme.nix {};
tests.avahi = callTest tests/avahi.nix {};
tests.beegfs = callTest tests/beegfs.nix {};
tests.upnp = callTest tests/upnp.nix {};
tests.bittorrent = callTest tests/bittorrent.nix {};
tests.bind = callTest tests/bind.nix {};
#tests.blivet = callTest tests/blivet.nix {}; # broken since 2017-07024
Expand Down
101 changes: 67 additions & 34 deletions nixos/tests/bittorrent.nix
Original file line number Diff line number Diff line change
Expand Up @@ -13,57 +13,95 @@ let
# 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
'';

internalRouterAddress = "192.168.3.1";
internalClient1Address = "192.168.3.2";
externalRouterAddress = "80.100.100.1";
externalClient2Address = "80.100.100.2";
externalTrackerAddress = "80.100.100.3";
in

{
name = "bittorrent";
meta = with pkgs.stdenv.lib.maintainers; {
maintainers = [ domenkozar eelco chaoflow rob wkennington ];
maintainers = [ domenkozar eelco chaoflow rob wkennington bobvanderlinden ];
};

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;
};
};

Expand All @@ -72,43 +110,38 @@ in
''
startAll;

# Enable NAT on the router and start miniupnpd.
$router->waitForUnit("nat");
$router->succeed(
"iptables -w -t nat -N MINIUPNPD",
"iptables -w -t nat -A PREROUTING -i eth1 -j MINIUPNPD",
"echo 1 > /proc/sys/net/ipv4/ip_forward",
"miniupnpd -f ${miniupnpdConf nodes}"
);
# 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 -p -t http://${(pkgs.lib.head nodes.tracker.config.networking.interfaces.eth1.ipv4.addresses).address}:6969/announce -o /tmp/test.torrent");
$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.target");
$tracker->succeed("opentracker -p 6969 >&2 &");
$tracker->waitForUnit("network-online.target");
$tracker->waitForUnit("opentracker.service");
$tracker->waitForOpenPort(6969);

# Start the initial seeder.
my $pid = $tracker->succeed("transmission-cli /tmp/test.torrent -M -w /tmp/data >&2 & echo \$!");
$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.target");
$client1->succeed("transmission-cli http://tracker/test.torrent -w /tmp >&2 &");
$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->succeed("kill -9 $pid");
# $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.target");
$client2->succeed("transmission-cli http://tracker/test.torrent -M -w /tmp >&2 &");
$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}");
'';
Expand Down
94 changes: 94 additions & 0 deletions nixos/tests/upnp.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# This tests whether UPnP port mappings can be created using Miniupnpd
# and Miniupnpc.
# It runs a Miniupnpd service on one machine, and verifies
# a client can indeed create a port mapping using Miniupnpc. If
# this succeeds an external client will try to connect to the port
# mapping.

import ./make-test.nix ({ pkgs, ... }:

let
internalRouterAddress = "192.168.3.1";
internalClient1Address = "192.168.3.2";
externalRouterAddress = "80.100.100.1";
externalClient2Address = "80.100.100.2";
in
{
name = "upnp";
meta = with pkgs.stdenv.lib.maintainers; {
maintainers = [ bobvanderlinden ];
};

nodes =
{
router =
{ pkgs, nodes, ... }:
{ virtualisation.vlans = [ 1 2 ];
networking.nat.enable = true;
networking.nat.internalInterfaces = [ "eth2" ];
networking.nat.externalInterface = "eth1";
networking.firewall.enable = true;
networking.firewall.trustedInterfaces = [ "eth2" ];
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.miniupnpc pkgs.netcat ];
virtualisation.vlans = [ 2 ];
networking.defaultGateway = internalRouterAddress;
networking.interfaces.eth1.ipv4.addresses = [
{ address = internalClient1Address; prefixLength = 24; }
];
networking.firewall.enable = false;

services.httpd.enable = true;
services.httpd.listen = [{ ip = "*"; port = 9000; }];
services.httpd.adminAddr = "foo@example.org";
services.httpd.documentRoot = "/tmp";
};

client2 =
{ pkgs, ... }:
{ environment.systemPackages = [ pkgs.miniupnpc ];
virtualisation.vlans = [ 1 ];
networking.interfaces.eth1.ipv4.addresses = [
{ address = externalClient2Address; prefixLength = 24; }
];
networking.firewall.enable = false;
};
};

testScript =
{ nodes, ... }:
''
startAll;

# Wait for network and miniupnpd.
$router->waitForUnit("network-online.target");
# $router->waitForUnit("nat");
$router->waitForUnit("firewall.service");
$router->waitForUnit("miniupnpd");

$client1->waitForUnit("network-online.target");

$client1->succeed("upnpc -a ${internalClient1Address} 9000 9000 TCP");

$client1->waitForUnit("httpd");
$client2->waitUntilSucceeds("curl http://${externalRouterAddress}:9000/");
'';

})
16 changes: 14 additions & 2 deletions pkgs/tools/networking/miniupnpd/default.nix
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{ stdenv, fetchurl, iptables, libuuid, pkgconfig }:
{ stdenv, lib, fetchurl, iptables, libuuid, pkgconfig
, which, iproute, gnused, coreutils, gawk, makeWrapper
}:

let
scriptBinEnv = lib.makeBinPath [ which iproute iptables gnused coreutils gawk ];
in
stdenv.mkDerivation rec {
name = "miniupnpd-2.1";

Expand All @@ -10,14 +15,21 @@ stdenv.mkDerivation rec {
};

buildInputs = [ iptables libuuid ];
nativeBuildInputs= [ pkgconfig ];
nativeBuildInputs= [ pkgconfig makeWrapper ];

makefile = "Makefile.linux";

buildFlags = [ "miniupnpd" "genuuid" ];

installFlags = [ "PREFIX=$(out)" "INSTALLPREFIX=$(out)" ];

postFixup = ''
for script in $out/etc/miniupnpd/ip{,6}tables_{init,removeall}.sh
do
wrapProgram $script --set PATH '${scriptBinEnv}:$PATH'
done
'';

meta = with stdenv.lib; {
homepage = http://miniupnp.free.fr/;
description = "A daemon that implements the UPnP Internet Gateway Device (IGD) specification";
Expand Down