- reyk@cvs.openbsd.org 2005/12/06 22:38:28
     [auth-options.c auth-options.h channels.c channels.h clientloop.c]
     [misc.c misc.h readconf.c readconf.h scp.c servconf.c servconf.h]
     [serverloop.c sftp.c ssh.1 ssh.c ssh_config ssh_config.5 sshconnect.c]
     [sshconnect.h sshd.8 sshd_config sshd_config.5]
     Add support for tun(4) forwarding over OpenSSH, based on an idea and
     initial channel code bits by markus@. This is a simple and easy way to
     use OpenSSH for ad hoc virtual private network connections, e.g.
     administrative tunnels or secure wireless access. It's based on a new
     ssh channel and works similar to the existing TCP forwarding support,
     except that it depends on the tun(4) network interface on both ends of
     the connection for layer 2 or layer 3 tunneling. This diff also adds
     support for LocalCommand in the ssh(1) client.

     ok djm@, markus@, jmc@ (manpages), tested and discussed with others
diff --git a/ssh.c b/ssh.c
index 2227755..8a4a0e4 100644
--- a/ssh.c
+++ b/ssh.c
@@ -40,7 +40,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: ssh.c,v 1.254 2005/10/30 08:52:18 djm Exp $");
+RCSID("$OpenBSD: ssh.c,v 1.255 2005/12/06 22:38:27 reyk Exp $");
 
 #include <openssl/evp.h>
 #include <openssl/err.h>
@@ -162,7 +162,7 @@
 "           [-i identity_file] [-L [bind_address:]port:host:hostport]\n"
 "           [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n"
 "           [-R [bind_address:]port:host:hostport] [-S ctl_path]\n"
-"           [user@]hostname [command]\n"
+"           [-w tunnel:tunnel] [user@]hostname [command]\n"
 	);
 	exit(1);
 }
@@ -244,7 +244,7 @@
 
 again:
 	while ((opt = getopt(ac, av,
-	    "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:MNO:PR:S:TVXY")) != -1) {
+	    "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:MNO:PR:S:TVw:XY")) != -1) {
 		switch (opt) {
 		case '1':
 			options.protocol = SSH_PROTO_1;
@@ -340,6 +340,14 @@
 			if (opt == 'V')
 				exit(0);
 			break;
+		case 'w':
+			options.tun_open = 1;
+			options.tun_local = a2tun(optarg, &options.tun_remote);
+			if (options.tun_local < -1) {
+				fprintf(stderr, "Bad tun device '%s'\n", optarg);
+				exit(1);
+			}
+			break;
 		case 'q':
 			options.log_level = SYSLOG_LEVEL_QUIET;
 			break;
@@ -1059,6 +1067,26 @@
 		packet_send();
 	}
 
+	if (options.tun_open) {
+		Channel *c;
+		int fd;
+
+		debug("Requesting tun.");
+		if ((fd = tun_open(options.tun_local)) >= 0) {
+			c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1,
+			    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
+			    0, "tun", 1);
+			c->datagram = 1;
+			packet_start(SSH2_MSG_CHANNEL_OPEN);
+			packet_put_cstring("tun@openssh.com");
+			packet_put_int(c->self);
+			packet_put_int(c->local_window_max);
+			packet_put_int(c->local_maxpacket);
+			packet_put_int(options.tun_remote);
+			packet_send();
+		}
+	}
+
 	client_session2_setup(id, tty_flag, subsystem_flag, getenv("TERM"),
 	    NULL, fileno(stdin), &command, environ, &ssh_subsystem_reply);
 
@@ -1123,6 +1151,11 @@
 	if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN))
 		id = ssh_session2_open();
 
+	/* Execute a local command */
+	if (options.local_command != NULL &&
+	    options.permit_local_command)
+		ssh_local_cmd(options.local_command);
+
 	/* If requested, let ssh continue in the background. */
 	if (fork_after_authentication_flag)
 		if (daemon(1, 1) < 0)