2 # Encrypting VPN tunnel for use with userv-ipif.
4 # This comment is reference documentation. See ipif/INSTALL for the
5 # installation instructions and setup tutorial.
8 # To make a tunnel between two machines `alice' and `bob',
9 # on `alice', the active endpoint, run:
12 # [ -l[<alice-command/arg>] ... .
13 # | -e <encryption-mech>[/<encryption-parameter>...]
14 # | -m (`masquerade support': bob gets `Wait' instead of our addr/port)
15 # | -d (`dump keys': when no peer, spew keys rather than reading them;
16 # we always send keys to our peer if there is one)
17 # | -Dcrypto (debug crypto - use with care, prints keys, packets &c on screen!)
18 # | -f<path-to-udptunnel-forwarder>
21 # <alice-phys-addr>,<alice-phys-port>
22 # <bob-phys-addr>,<bob-phys-port>
23 # <alice-virt-addr>,<bob-virt-addr>,<mtu>,<proto>
24 # <keepalive>,<timeout>[,<reannounce>]
25 # <alice-priv-nets> <bob-priv-nets>
26 # [ <bob-command> [<bob-args> ...] ]
28 # This will run udptunnel-forwarder on alice, and use <bob-command>
29 # (usually an ssh invocation) to run udptunnel appropriately on bob.
30 # Key material will be generated by alice and fed to udptunnel on bob
31 # via <bob-command>'s stdin, and the physical address and port on bob
32 # will be (if so configured) returned via <bob-command>'s stdout.
34 # The tunnel will stay up until one of the subprocesses involved dies,
35 # or the keepalive timeout expires. If you want the tunnel to remain
36 # up permanently, you must arrange to invoke it repeatedly (eg, from
37 # inittab). See INSTALL.
39 # <proto> may be slip or cslip
41 # <mtu> will be the MTU of the tunnel interfaces; it is best if this
42 # is enough smaller than the path MTU between the physical interfaces
43 # that the encapsulated packets will fit without fragmentation.
45 # Any <..-addr> supplied to udptunnel may also be hostname; these will
46 # all be looked up on alice and IP addresses passed to bob.
48 # The `local' physical address and ports (ie, alice's own details),
49 # may have these special values:
50 # `Any' choose one ourselves and do not print it (the port chosen
51 # will be supplied to bob)
52 # `Print' choose one ourselves and print both port and addr
53 # (this is not usually useful specified directly; it's
54 # used by udptunnel when it invokes itself on bob via
55 # <bob-command>, to have its other self print the
58 # The `remote' physical address and port (ie, on alice, bob's details),
59 # may also have the special values:
60 # `Command' wait for <bob-command> to tell us the values (this is
61 # usually the right choice on alice for at least the
62 # port). <bob-command> must be specified (ie, this
63 # only makes sense on alice).
64 # `Wait' alice will wait to receive a packet from bob and use
65 # whatever address and port it came from
67 # These special values are case-sensitive. When alice runs udptunnel
68 # on bob they are automatically translated to appropriate other values
69 # in the arguments to bob's udptunnel.
71 # If <bob-command> is specified it should run udptunnel at the
72 # bob end; it will be invoked as
73 # <bob-command> [ <bob-args> ... ]
74 # [ <-e arguments passed along> ]
75 # <bob-phys-addr'>,<bob-phys-port'>
76 # <alice-phys-addr'>,<alice-phys-port'>
77 # <bob-virt-addr>,<alice-virt-addr>,<mtu>,<proto>
78 # <keepalive>,<timeout>[,<reannounce>]
79 # <bob-priv-nets> <alice-priv-nets>
81 # If it was given Print for <bob-phys-foo'>, udptunnel's first stdout
82 # output will be the real <bob-phys-addr>,<bob-phys-port> pair. It
83 # may then produce more stdout which, if any, will be forwarded to the
84 # local end's stdout as debugging info.
86 # After this, if any encryption was specified, the encryption key
87 # material will be fed into its stdin. See the documentation in the
88 # mech-*.c files for details of the parameters. udptunnel on alice
89 # will arrange to feed the keys fd of udptunnel-forwarder into the
90 # stdin of the udptunnel on bob.
92 # <bob-phys-foo'> is as follows:
93 # <bob-phys-foo> <bob-phys-foo'>
94 # actual addr/port that addr/port
98 # <alice-phys-foo'> is as follows:
99 # <alice-phys-foo> <alice-phys-foo'> <alice-phys-foo'>
100 # (-m not specified) (-m specified)
101 # actual addr/port that addr/port `Wait'
102 # `Print' the chosen address `Wait'
103 # `Any' `Wait' for addr, `Wait'
104 # chosen port for port
106 # In each case udptunnel will run userv ipif locally, as
107 # userv root ipif <local-virt-addr>,<remote-virt-addr>,<mtu>,<proto>
109 # or, if -l was given, userv root ipif is replaced with the argument(s)
110 # following -l option(s) until `.'.
112 # udptunnel will also run udptunnel-forwarder with appropriate options.
114 # recommended encryption parameters are:
115 # -e nonce (prepend 32 bit counter)
116 # -e timestamp/<max-skew>/<max-age> (prepend 32 bit time_t, and check on receipt)
117 # -e pkcs5/8 (pad as per PKCS#5 to 8-byte boundary)
118 # -e blowfish-cbcmac/128 (prepend CBC MAC with random IV and 128 bit key)
119 # -e blowfish-cbc/128 (encrypt with CBC, random IV and 128 bit key)
120 # where <max-skew> is perhaps 10 and <max-age> perhaps 30. If your
121 # clocks are not sufficiently well synchronised, you could replace
122 # `-e nonce -e timestamp/...' with just `-e sequence'. Do not just
123 # remove `-e timestamp/...'.
125 # Copyright 1996-2013 Ian Jackson <ijackson@chiark.greenend.org.uk>
126 # Copyright 1998 David Damerell <damerell@chiark.greenend.org.uk>
127 # Copyright 1999,2003
128 # Chancellor Masters and Scholars of the University of Cambridge
129 # Copyright 2010 Tony Finch <fanf@dotat.at>
131 # This is free software; you can redistribute it and/or modify it
132 # under the terms of the GNU General Public License as published by
133 # the Free Software Foundation; either version 3 of the License, or
134 # (at your option) any later version.
136 # This program is distributed in the hope that it will be useful, but
137 # WITHOUT ANY WARRANTY; without even the implied warranty of
138 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
139 # General Public License for more details.
141 # You should have received a copy of the GNU General Public License
142 # along with userv-utils; if not, see http://www.gnu.org/licenses/.
148 $progname= $0; $progname =~ s
,.*/,,;
151 chomp($hostname= `uname -n`);
152 $?
and die "$progname: cannot get hostname (uname failed with code $?)\n";
154 sub quit
($) { die "$progname - $hostname: fatal error: $_[0]\n"; }
155 sub debug
($) { print "$progname - $hostname: debug: $_[0]\n"; }
156 sub fail
($) { quit
("unexpected system call failure: $_[0]: $!"); }
157 sub warning
($) { warn "$progname - $hostname: $_[0]\n"; }
159 sub eat_addr_port
($) {
161 @ARGV or quit
("<addr>,<port> missing");
163 (m/^$x,/i && m/^[a-z]/ || m/,$x$/i && m/,[a-z]/)
164 and warning
("$_: use Mixed Case for special values");
165 m/^([0-9a-z][0-9a-z-+.]+|$x)\,(\d+|$x)$/
166 or quit
("$_: <host/addr>,<port> bad syntax".
167 (m/[A-Z]/ ?
' (use lowercase for hostnames)' : ''));
170 sub conv_host_addr
($) {
172 return INADDR_ANY
() if $s =~ m/^[A-Z][a-z]/;
173 return $r if defined($r= inet_aton
($s));
174 @h= gethostbyname($s) or quit
("$s: cannot get address");
175 $h[2] eq &AF_INET
or quit
("$s: address is not IPv4");
176 @h < 5 or quit
("$s: name maps to no addresses");
178 @h == 5 or warning
("$s: name has several addresses, using ".inet_ntoa
($r));
181 sub conv_port_number
($) {
183 return 0 if $s =~ m/^[A-Z][a-z]/;
185 $r>0 && $r<65536 or quit
("$s: port out of range");
190 @s= unpack_sockaddr_in
($s);
191 return inet_ntoa
($s[1]);
195 @s= unpack_sockaddr_in
($s);
198 sub show_addr_port
($) {
200 return show_addr
($s).','.show_port
($s);
205 return $val if length $val;
206 @ARGV or quit
("$opt needs value");
214 $fcmd= 'udptunnel-forwarder';
217 while ($ARGV[0] =~ m/^-/) {
222 push @lcmd,$_ if length;
223 while (@ARGV && ($_= shift @ARGV) ne '.') { push @lcmd, $_; }
226 $fcmd= arg_value
($_,'-f');
228 $encrarg= arg_value
($_,'-e');
229 push @remoteopts, "-e$encrarg";
230 @thisencryption= split m
#/#, $encrarg;
231 $thisencryption[0] =~ s/^/\|/;
232 push @encryption, @thisencryption;
237 } elsif (s/^-Dcrypto$/-/) {
239 push @remoteopts, '-Dcrypto';
241 quit
("unknown option \`$_'");
246 # Variables \$[lr]a?p?(|s|d|r)
247 # Local/Remote Address&/Port
248 # actualvalue/Specified/Displaypassdown/fromRemote/passtoForwarder
250 ($las,$lps)= eat_addr_port
('Print|Any');
251 $la= conv_host_addr
($las);
252 $lp= conv_port_number
($lps);
253 $ls= pack_sockaddr_in
$lp,$la;
255 ($ras,$rps)= eat_addr_port
('Wait|Command');
256 $ra= conv_host_addr
($ras);
257 $rp= conv_port_number
($rps);
258 $rs= pack_sockaddr_in
$rp,$ra;
261 m/^([.0-9]+),([.0-9]+),(\d+),(slip|cslip)$/
262 or quit
("lvaddr,rvaddr,mtu,proto missing or bad syntax or proto not [c]slip");
263 ($lva,$rva,$mtu,$proto) = ($1,$2,$3,$4);
266 if (m/^(\d+),(\d+)$/) {
267 ($keepalive,$timeout,$reannounce)= ($1+0,$2+0,0);
268 $ka_to_ra= "$keepalive,$timeout";
269 } elsif (m/^(\d+),(\d+),(\d+)$/) {
270 ($keepalive,$timeout,$reannounce)= ($1+0,$2+0,$3);
271 "$keepalive,$timeout",
272 $ka_to_ra= "$keepalive,$timeout,$reannounce";
274 quit
("keepalive,timeout missing or bad syntax");
276 $keepalive && ($timeout > $keepalive*2) or quit
("timeout must be < 2*keepalive")
279 # Variables \$[lr]exn
280 # Local/Remote Extra Nets
284 defined($udp= getprotobyname('udp')) or fail
("getprotobyname udp");
286 socket(L
,PF_INET
,SOCK_DGRAM
,$udp) or fail
("socket");
287 bind(L
,$ls) or quit
("bind failed: $!");
288 defined($ls= getsockname(L
)) or fail
("getsockname");
289 $lad= show_addr
($ls);
290 $lpd= show_port
($ls);
293 print "$lapd\n" or fail
("print addr/port") if ($las eq 'Print' || $lps eq 'Print');
295 $rapcmd= ($ras eq 'Command' || $rps eq 'Command');
296 quit
("need remote-command if Command for remote addr/port") if $rapcmd && !@ARGV;
298 sub xform_remote
($$) {
299 my ($showed,$spec) = @_;
300 return 'Print' if $spec eq 'Command';
301 return 'Any' if $spec eq 'Wait';
306 warning
("-d specified with remote command, ignoring") if $dump;
309 $rad= xform_remote
(show_addr
($rs),$ras);
310 $rpd= xform_remote
(show_port
($rs),$rps);
314 $masq ?
'Wait,Wait' : $las eq 'Any' ?
"Wait,$lpd" : $lapd,
315 "$rva,$lva,$mtu,$proto",
318 debug
("remote command @rcmd");
321 pipe(RAPREAD
,RCMDREADSUB
) or fail
("pipe");
323 pipe(RCMDWRITESUB
,DUMPKEYS
) or fail
("pipe");
324 defined($c_rcmd= fork) or fail
("fork for remote");
326 open STDIN
, "<&RCMDWRITESUB" or fail
("reopen stdin for remote command");
327 open STDOUT
, ">&RCMDREADSUB" or fail
("reopen stdout for remote command")
329 close RAPREAD
if $rapcmd;
334 exec @rcmd; fail
("failed to execute remote command $rcmd[0]");
339 close RCMDREADSUB
if $rapcmd;
343 defined($nread= sysread(RAPREAD
,$_,1,length))
344 or fail
("read from remote command");
348 waitpid $c_rcmd,0 or fail
("wait for remote command");
349 quit
($? ?
"remote command failed (code $?)" :
350 "no details received from remote");
354 m/^([.0-9]+)\,(\d+)$/ or quit
("invalid details from remote end: \`$_'");
355 ($rar,$rpr) = ($1,$2);
356 $ra= conv_host_addr
($rar);
357 $rp= conv_port_number
($rpr);
359 defined($c_catremdebug= fork) or fail
("fork for cat remote debug");
360 if (!$c_catremdebug) {
361 open(STDIN
,"<&RAPREAD") or fail
("redirect remote debug");
364 exec "cat"; fail
("execute cat");
369 open DUMPKEYS
, ">&STDOUT" or fail
("reopen stdout for key material");
372 open DUMPKEYS
, "<&STDIN" or fail
("reopen stdout for key material");
375 $rs= pack_sockaddr_in
$rp,$ra;
377 if ($ras eq 'Wait' || $rps eq 'Wait') {
379 $rapd= ('Wait,Wait');
381 @rapf= (show_addr
($rs), show_port
($rs));
382 $rapd= show_addr_port
($rs);
384 @lcmd= qw(userv root ipif
) unless @lcmd;
386 debug
("using remote $rapd local $lapd");
387 push @lcmd, ("$lva,$rva,$mtu,$proto",$lexn);
388 debug
("local command @lcmd.");
390 pipe(UR
,UW
) or fail
("up pipe");
391 pipe(DR
,DW
) or fail
("down pipe");
393 defined($c_lcmd= fork) or fail
("fork for local command");
396 open(STDIN
,"<&DR") or fail
("reopen stdin for packets");
397 open(STDOUT
,">&UW") or fail
("reopen stdout for packets");
399 quit
("cannot execute $lcmd[0]: $!");
404 $xfwdopts.= 'w' if $dump;
406 @fcmd= ($fcmd, $xfwdopts,
407 fileno(L
), fileno(DW
), fileno(UR
), fileno(DUMPKEYS
),
408 $mtu, $keepalive, $timeout, $reannounce,
411 debug
("forwarding command @fcmd.");
413 defined($c_fwd= fork) or fail
("fork for udptunnel-forwarder");
415 foreach $fd (qw(L DW UR DUMPKEYS
)) {
416 fcntl($fd, F_SETFD
, 0) or fail
("set no-close-on-exec $fd");
418 exec @fcmd; fail
("cannot execute $fcmd[0]");
426 %procs= ($c_fwd, 'forwarder',
427 $c_lcmd, 'local command');
428 $procs{$c_rcmd}= 'remote command' if $c_rcmd;
429 $procs{$c_catremdebug}= 'debug cat' if $c_catremdebug;
433 while (keys %procs) {
435 fail
("wait failed (expecting ". join('; ',keys %procs). ")");
437 warning
("unexpected child reaped: pid $c, code $status"), next
438 unless exists $procs{$c};
441 $status ? warning
("subprocess $str failed with code $status")
442 : debug
("subprocess $str finished");
443 if ($c==$c_lcmd || $c==$c_fwd || $c==$c_rcmd) {
444 kill 15, grep (exists $procs{$_}, $c_fwd, $c_rcmd);
446 $estatus=1 unless $c == $c_catremdebug;
449 debug
("all processes terminated, exiting with status $estatus");