diff options
author | Andrew Shadura <bugzilla@tut.by> | 2012-08-15 22:11:15 +0200 |
---|---|---|
committer | Andrew Shadura <bugzilla@tut.by> | 2012-08-15 22:11:15 +0200 |
commit | 02bc804b997f43ea112002310775e3238d218992 (patch) | |
tree | 7bce7705be7e844fed6c83fac256df9c4b64b299 /nat-traverse |
Add initial packaging by GRML project.
Diffstat (limited to 'nat-traverse')
-rwxr-xr-x | nat-traverse | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/nat-traverse b/nat-traverse new file mode 100755 index 0000000..ca7b54c --- /dev/null +++ b/nat-traverse @@ -0,0 +1,404 @@ +#!/usr/bin/perl +# nat-traverse -- Use of UDP to traverse NAT gateways +# Copyright (C) 2005 Ingo Blechschmidt <iblech@web.de> +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. + +use warnings; +use strict; + +use v5.6.0; + +use IO::Socket::INET; +use Getopt::Long; + +# More elegant use constant {...} not available in Perl 5.6.x. +use constant GARBAGE_MAGIC => "nat-traverse-garbage"; +use constant ACK_MAGIC => "nat-traverse-ack"; +use constant PACKET_SIZE => 8 * 1024; + +sub debug($); + +# ARGV parsing. +GetOptions( + "window=i" => \(my $WINDOW = 10), + "timeout=i" => \(my $TIMEOUT = 10), + "quit-after-connect" => \my $QUIT_AFTER_CONNECT, + "cmd=s" => \my $CMD, + "version" => sub { print "nat-traverse 0.4\n" and exit }, + "help" => \&usage, +) or usage(); +usage() unless @ARGV == 1; +my ($LPORT, $PEER, $RPORT) = split /:/, $ARGV[0]; +usage() unless $LPORT =~ /^\d+/ and $RPORT =~ /^\d+/ and $PEER; + +# Helper sub to create our socket... +sub sockgen { + debug "Creating socket localhost:$LPORT <-> $PEER:$RPORT... "; + my $sock = IO::Socket::INET->new( + PeerHost => $PEER, + PeerPort => $RPORT, + LocalPort => $LPORT, + Proto => "udp", + ReuseAddr => 1, + ) or die "Couldn't create socket: $!\n"; + debug "done.\n"; + + return $sock; +} + +# Helper sub to wait for a given char. +sub waitfor { + my ($sock, $match) = @_; + + while(1) { + debug "."; + my $got; + defined(sysread $sock, $got, length $match) or + die "Couldn't read from socket: $!\n"; + last if defined $got and $got eq $match; + } +} + +# Initial phase: Sending of initial packets to make the firewalls think the +# packets are replies. +my $sock = sockgen(); +debug "Sending $WINDOW initial packets... "; +for(1..$WINDOW) { + debug "."; + syswrite $sock, GARBAGE_MAGIC; + sleep 1; +} +syswrite $sock, ACK_MAGIC; +debug " done.\n"; + +# Waiting for ACK packet so we see the connection is established. +debug "Waiting for ACK (timeout: $TIMEOUT\Es)... "; +{ + local $SIG{ALRM} = sub { die " timeout.\n" }; + alarm $TIMEOUT; + waitfor($sock, ACK_MAGIC); + alarm 0; +} +debug " done.\n"; + +# :) +debug "Connection established.\n"; + +debug "Exiting.\n" and exit 0 if $QUIT_AFTER_CONNECT; + +# Either exec() $CMD or relay STDIN and STDOUT appropriately. +if(defined $CMD) { + debug "Redirecting STDIN and STDOUT... "; + open STDOUT, ">&", $sock or die "Couldn't redirect STDOUT: $!\n"; + open STDIN, "<&", $sock or die "Couldn't redirect STDIN: $!\n"; + debug "done.\n"; + debug "exec()ing \"$CMD\"...\n"; + exec $CMD or die "Couldn't exec() \"$CMD\": $!\n"; +} else { + debug "Type ahead.\n"; + $SIG{CHLD} = "IGNORE"; + my $pid = fork; + die "Couldn't fork: $!\n" unless defined $pid; + + if($pid) { + # Parent -- read chars from STDIN and send them to the socket. + my $buf; + while(1) { + my $ret = sysread STDIN, $buf, PACKET_SIZE; + defined $ret or die "Couldn't read from STDIN: $!\n"; + $ret or last; + syswrite $sock, $buf or die "Couldn't write to socket: $!\n"; + } + + # Exit on ^D. + debug "Exiting; sending SIGTERM to child process... "; + kill 15 => $pid or die "Couldn't send SIGTERM to child process (PID $pid): $!\n"; + debug "done.\n"; + + } else { + # Child -- print what's "in the socket". + print $_ while + defined(sysread $sock, $_, PACKET_SIZE) or + die "Couldn't read from socket: $!\n"; + } + + # Clean up after ourselves. + close $sock or die "Couldn't close socket: $!\n"; +} + +# Nice debugging output. +{ + my $fresh; + sub debug($) { + my $msg = shift; + + print STDERR "> " and $fresh++ unless $fresh; + print STDERR $msg; + $fresh = 0 if substr($msg, -1) eq "\n"; + 1; + } +} + +# Display usage info. +sub usage { print STDERR <<'USAGE'; exit } +nat-traverse v0.4 -- Use of UDP to traverse NAT gateways + +Usage: + user@left $ nat-traverse [options] port1:natgw-of-right:port2 + user@right $ nat-traverse [options] port2:natgw-of-left:port1 + where + port1, port2: Two unused UDP ports + left, right: The hosts behind NAT gateways you want to connect + natgw-of-left, The addresses of the NAT gateways of left and right + natgw-of-right: + +Available options: + --window=10 The number of initial garbage packets to send. + --timeout=10 The number of seconds to wait for an acknowledgement + of the connection by the peer. + --cmd="pppd..." The command to run with its STDIN and STDOUT bound to + the socket. + If no command is specified, everything you type is + relayed to the other end of the socket, i.e. + nat-traverse degrades to netcat. + --quit-after-connect Quit nat-traverse after the tunnel was established + successfully. + --version Display version information. + --help This help. + +Options may be abbreviated to uniqueness. +Run "perldoc nat-traverse" for more information. +USAGE + + +=head1 NAME + +nat-traverse - Use of UDP to traverse NAT gateways + +=head1 SYNOPSIS + + user@left $ nat-traverse 40000:natgw-of-right:40001 + user@right $ nat-traverse 40001:natgw-of-left:40000 + +=head1 VERSION + +This document describes nat-traverse v0.4. + +=head1 DESCRIPTION + +nat-traverse establishes connections between nodes which are behind NAT +gateways, i.e. hosts which do I<not> have public IP addresses. Additionally, +you can setup a small VPN by using pppd on top of nat-traverse (see +L</EXAMPLES>). nat-traverse does I<not> need an external server on the +Internet, and it isn't necessary to reconfigure the involved NAT gateways, +either. I<nat-traverse works out-of-the-box.> + +See L</TECHNIQUE> for how this is achieved. + +=head1 OPTIONS + +=over + +=item C<I<local_port>:I<peer>:I<remote_port>> (required) + +Sets the local port to use and the remote address to connect to. + +Note that you have to give the IP address or hostname of the I<NAT gateway> of +the host you want to connect to, as the target host doesn't have a public IP +address. + +=item C<--cmd="I<pppd...>"> + +Runs the specified command after establishing the connection. + +The command will be run with its STDIN and STDOUT bound to the socket, i.e. +everything the command writes to STDOUT will be forwarded to the peer. + +If no command is specified, nat-traverse will relay input from STDIN to the peer +and vice versa, i.e. nat-traverse degrades to netcat. + +=item C<--window=I<10>> + +Sets the number of initial garbage packets to send. The default, 10, should +work with most firewalls. + +=item C<--timeout=I<10>> + +Sets the maximum number of seconds to wait for an acknowledgement by the peer. + +=item C<--quit-after-connect> + +Quits nat-traverse after the tunnel has been established successfully. + +nat-traverse returns a non-C<0> statuscode to indicate that it wasn't able to +establish the tunnel. + +C<--quit-after-connect> is useful if you want another program to use the +tunnel. For example, you could configure OpenVPN to use the the same ports as +nat-traverse -- thus OpenVPN would be able to cross NAT gateways. + +=item C<--version>, C<--help> + +=back + +=head1 TECHNIQUE + +nat-traverse establishes connections between hosts behind NAT gateways, without need +for reconfiguration of the involved NAT gateways. + +=over + +=item 1. + +Firstly, nat-traverse on host C<left> sends garbage UDP packets to the NAT gateway +of C<right>. These packets are, of course, discarded by the firewall. + +=item 2. + +Then C<right>'s nat-traverse sends garbage UDP packets to the NAT gateway of +C<left>. These packets are I<not> discarded, as C<left>'s NAT gateway thinks +these packets are replies to the packets sent in step 1! + +=item 3. + +C<left>'s nat-traverse continues to send garbage packets to C<right>'s NAT gateway. +These packets are now not dropped either, as the NAT gateway thinks the packets +are replies to the packets sent in step 2. + +=item 4. + +Finally, both hosts send an acknowledgement packet to signal readiness. When +these packets are received, the connection is established and nat-traverse can +either relay STDIN to the socket or execute a program. + +=back + +=head1 EXAMPLES + +=head2 Setup of a small VPN with PPP + +It's easy to setup a VPN (Virtual Private Network) by using the Point-to-Point +Protocol Daemon, C<pppd>: + + root@left # nat-traverse \ + --cmd="pppd updetach noauth passive notty \ + ipparam vpn 10.0.0.1:10.0.0.2" + 40000:natgw-of-right:40001 + root@right # nat-traverse \ + --cmd="pppd nodetach notty noauth" + 40001:natgw-of-left:40000 + +C<pppd> creates a new interface, typically C<ppp0>. Using this interface, you +can ping C<10.0.0.1> or C<10.0.0.2>. As you can see, C<pppd> upgrades the +data-only tunnel nat-traverse provides to a full IP tunnel. Thus you can +establish reliable TCP connections over the tunnel, even though the tunnel uses +UDP! Furthermore, you could even add IPv6 addresses to C<ppp0> by running C<ip +-6 addr add...>! + +Note though that although this VPN I<is> a private network, it is I<not> +secured in any way. You may want to use SSH to encrypt the connection. + +=head2 Port Forwarding with netcat + +You can use C<netcat> to forward one of your local UDP or TCP ports to an +arbitrary UDP or TCP port of the remote host, similar to C<ssh -L> or C<ssh +-R>: + + user@left $ nat-traverse 10001:natgw-of-right:10002 \ + --cmd="nc -vlp 20000" + user@right $ nat-traverse 10002:natgw-of-left:10001 \ + --cmd="nc -vlp 22" + +As soon as the tunnel is established (using UDP ports C<10001> and C<10002>), +C<left>'s TCP port C<20000> is forwarded to C<right>'s SSH Daemon (TCP port +C<22>): + + user@some-other-host $ ssh -p 20000 user@left + # Will connect to right's SSH daemon! + +But do note that you lose the reliability of TCP in this example, as the actual +data is transported via UDP. If you want reliable streams, use PPP on top of +nat-traverse, as described above. + +=head1 LIMITATIONS + +Only IPv4 is supported, nat-traverse won't work with IPv6 addresses. Even +though it would be relatively trivial to add IPv6 support, I refrained from +doing that, as there's no need to use NAT with IPv6 (the address space IPv6 +provides is sufficient). + +If you do need IPv6 support, drop me a note and I'll patch nat-traverse. + +=head1 SEE ALSO + +=over + +=item L<RFC 1631 at +http://www.ietf.org/rfc/rfc1631.txt|http://www.ietf.org/rfc/rfc1631.txt> + +The IP Network Address Translator (NAT). K. Egevang, P. Francis. May 1994. +(Obsoleted by RFC3022) (Status: INFORMATIONAL) + +=item L<RFC 3022 at +http://www.ietf.org/rfc/rfc3022.txt|http://www.ietf.org/rfc/rfc3022.txt> + +Traditional IP Network Address Translator (Traditional NAT). P. Srisuresh, +K. Egevang. January 2001. (Obsoletes RFC1631) (Status: INFORMATIONAL) + +=item L<RFC 1661 at +http://www.ietf.org/rfc/rfc1661.txt|http://www.ietf.org/rfc/rfc1661.txt> + +The Point-to-Point Protocol (PPP). W. Simpson, Ed.. July 1994. (Obsoletes +RFC1548) (Updated by RFC2153) (Also STD0051) (Status: STANDARD) + +=item L<http://ppp.samba.org/> + +Website of Paul's PPP Package (open source implementation of the +Point-to-Point Protocol (PPP) on Linux and Solaris) + +=item L<German talk about nat-traverse at +http://linide.sourceforge.net/nat-traverse/nat-traverse-talk.pdf|http://linide.sourceforge.net/nat-traverse/nat-traverse-talk.pdf> + +Dieser Vortrag zeigt, wie man einen Tunnel zwischen zwei Computern, die +beide hinter NAT-Gateways sitzen, hinbekommt. Dazu wird ein neues Programm +vorgestellt, welches sowohl einfache Tastendrücke an die Gegenseite +weiterleiten, als auch beliebige Programme mit Verbindungen zur Gegenseite +starten kann. Damit ist ein einfaches VPN schnell aufgebaut. + +=back + +=head1 AUTHOR + +Copyright (C) 2005 Ingo Blechschmidt, E<lt>iblech@web.deE<gt>. + +You may want to visit nat-traverse's Freshmeat project page, +L<http://freshmeat.net/projects/nat-traverse/>, for new releases. + +=head1 LICENSE + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 51 Franklin +Street, Fifth Floor, Boston, MA 02110-1301, USA. |