- reyk@cvs.openbsd.org 2005/12/08 18:34:11
     [auth-options.c includes.h misc.c misc.h readconf.c servconf.c]
     [serverloop.c ssh.c ssh_config.5 sshd_config.5 configure.ac]
     two changes to the new ssh tunnel support. this breaks compatibility
     with the initial commit but is required for a portable approach.
     - make the tunnel id u_int and platform friendly, use predefined types.
     - support configuration of layer 2 (ethernet) or layer 3
     (point-to-point, default) modes. configuration is done using the
     Tunnel (yes|point-to-point|ethernet|no) option is ssh_config(5) and
     restricted by the PermitTunnel (yes|point-to-point|ethernet|no) option
     in sshd_config(5).
     ok djm@, man page bits by jmc@
diff --git a/ChangeLog b/ChangeLog
index c71d853..508745b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -36,6 +36,18 @@
    - jmc@cvs.openbsd.org 2005/12/08 15:06:29
      [ssh_config.5]
      keep options in order;
+   - reyk@cvs.openbsd.org 2005/12/08 18:34:11
+     [auth-options.c includes.h misc.c misc.h readconf.c servconf.c]
+     [serverloop.c ssh.c ssh_config.5 sshd_config.5 configure.ac]
+     two changes to the new ssh tunnel support. this breaks compatibility
+     with the initial commit but is required for a portable approach.
+     - make the tunnel id u_int and platform friendly, use predefined types.
+     - support configuration of layer 2 (ethernet) or layer 3
+     (point-to-point, default) modes. configuration is done using the
+     Tunnel (yes|point-to-point|ethernet|no) option is ssh_config(5) and
+     restricted by the PermitTunnel (yes|point-to-point|ethernet|no) option
+     in sshd_config(5).
+     ok djm@, man page bits by jmc@
 
 20051201
  - (djm) [envpass.sh] Remove regress script that was accidentally committed 
@@ -3428,4 +3440,4 @@
    - (djm) Trim deprecated options from INSTALL. Mention UsePAM
    - (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu
 
-$Id: ChangeLog,v 1.4023 2005/12/13 08:30:45 djm Exp $
+$Id: ChangeLog,v 1.4024 2005/12/13 08:33:19 djm Exp $
diff --git a/auth-options.c b/auth-options.c
index 54798d9..ad97e61 100644
--- a/auth-options.c
+++ b/auth-options.c
@@ -10,7 +10,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: auth-options.c,v 1.32 2005/12/06 22:38:27 reyk Exp $");
+RCSID("$OpenBSD: auth-options.c,v 1.33 2005/12/08 18:34:11 reyk Exp $");
 
 #include "xmalloc.h"
 #include "match.h"
@@ -296,7 +296,7 @@
 			tun[i] = 0;
 			forced_tun_device = a2tun(tun, NULL);
 			xfree(tun);
-			if (forced_tun_device < -1) {
+			if (forced_tun_device == SSH_TUNID_ERR) {
 				debug("%.100s, line %lu: invalid tun device",
 				    file, linenum);
 				auth_debug_add("%.100s, line %lu: invalid tun device",
diff --git a/configure.ac b/configure.ac
index 0afb487..b24d371 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-# $Id: configure.ac,v 1.312 2005/11/29 02:40:34 tim Exp $
+# $Id: configure.ac,v 1.313 2005/12/13 08:33:20 djm Exp $
 #
 # Copyright (c) 1999-2004 Damien Miller
 #
@@ -654,6 +654,7 @@
 	login_cap.h \
 	maillock.h \
 	ndir.h \
+	net/if.h \
 	netdb.h \
 	netgroup.h \
 	netinet/in_systm.h \
diff --git a/includes.h b/includes.h
index 12d948b..cf2d6c6 100644
--- a/includes.h
+++ b/includes.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: includes.h,v 1.20 2005/11/15 11:59:54 millert Exp $	*/
+/*	$OpenBSD: includes.h,v 1.21 2005/12/08 18:34:11 reyk Exp $	*/
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -148,6 +148,9 @@
 #include <netinet/in.h> /* For IPv6 macros */
 #include <netinet/ip.h> /* For IPTOS macros */
 #include <netinet/tcp.h>
+#ifdef HAVE_NET_IF_H
+# include <net/if.h>
+#endif
 #include <arpa/inet.h>
 #if defined(HAVE_NETDB_H)
 # include <netdb.h>
diff --git a/misc.c b/misc.c
index 9b23e2c..4f41332 100644
--- a/misc.c
+++ b/misc.c
@@ -24,7 +24,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: misc.c,v 1.36 2005/12/06 22:38:27 reyk Exp $");
+RCSID("$OpenBSD: misc.c,v 1.37 2005/12/08 18:34:11 reyk Exp $");
 
 #include "misc.h"
 #include "log.h"
@@ -202,7 +202,7 @@
 	int tun;
 
 	if (remote != NULL) {
-		*remote = -1;
+		*remote = SSH_TUNID_ANY;
 		sp = xstrdup(s);
 		if ((ep = strchr(sp, ':')) == NULL) {
 			xfree(sp);
@@ -212,15 +212,15 @@
 		*remote = a2tun(ep, NULL);
 		tun = a2tun(sp, NULL);
 		xfree(sp);
-		return (tun);
+		return (*remote == SSH_TUNID_ERR ? *remote : tun);
 	}
 
 	if (strcasecmp(s, "any") == 0)
-		return (-1);
+		return (SSH_TUNID_ANY);
 
-	tun = strtonum(s, 0, INT_MAX, &errstr);
-	if (errstr != NULL || tun < -1)
-		return (-2);
+	tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr);
+	if (errstr != NULL)
+		return (SSH_TUNID_ERR);
 
 	return (tun);
 }
@@ -539,27 +539,60 @@
 }
 
 int
-tun_open(int tun)
+tun_open(int tun, int mode)
 {
+	struct ifreq ifr;
 	char name[100];
-	int i, fd;
+	int fd = -1, sock;
 
-	if (tun > -1) {
+	/* Open the tunnel device */
+	if (tun <= SSH_TUNID_MAX) {
 		snprintf(name, sizeof(name), "/dev/tun%d", tun);
-		if ((fd = open(name, O_RDWR)) >= 0)  {
-			debug("%s: %s: %d", __func__, name, fd);
-			return (fd);
+		fd = open(name, O_RDWR);
+	} else if (tun == SSH_TUNID_ANY) {
+		for (tun = 100; tun >= 0; tun--) {
+			snprintf(name, sizeof(name), "/dev/tun%d", tun);
+			if ((fd = open(name, O_RDWR)) >= 0)
+				break;
 		}
 	} else {
-		for (i = 100; i >= 0; i--) {
-			snprintf(name, sizeof(name), "/dev/tun%d", i);
-			if ((fd = open(name, O_RDWR)) >= 0)  {
-				debug("%s: %s: %d", __func__, name, fd);
-				return (fd);
-			}
-		}
+		debug("%s: invalid tunnel %u\n", __func__, tun);
+		return (-1);
 	}
-	debug("%s: %s failed: %s", __func__, name, strerror(errno));
+
+	if (fd < 0) {
+		debug("%s: %s open failed: %s", __func__, name, strerror(errno));
+		return (-1);
+	}
+
+	debug("%s: %s mode %d fd %d", __func__, name, mode, fd);
+
+	/* Set the tunnel device operation mode */
+	snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tun%d", tun);
+	if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
+		goto failed;
+
+	if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)
+		goto failed;
+	if (mode == SSH_TUNMODE_ETHERNET) {
+		ifr.ifr_flags |= IFF_LINK0;
+		if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
+			goto failed;
+	}
+	ifr.ifr_flags |= IFF_UP;
+	if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
+		goto failed;
+
+	close(sock);
+	return (fd);
+
+ failed:
+	if (fd >= 0)
+		close(fd);
+	if (sock >= 0)
+		close(sock);
+	debug("%s: failed to set %s mode %d: %s", __func__, name,
+	    mode, strerror(errno));
 	return (-1);
 }
 
diff --git a/misc.h b/misc.h
index ff2ba1b..4159106 100644
--- a/misc.h
+++ b/misc.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: misc.h,v 1.27 2005/12/06 22:38:27 reyk Exp $	*/
+/*	$OpenBSD: misc.h,v 1.28 2005/12/08 18:34:11 reyk Exp $	*/
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -50,4 +50,16 @@
 char	*read_passphrase(const char *, int);
 int	 ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
 int	 read_keyfile_line(FILE *, const char *, char *, size_t, u_long *);
-int	 tun_open(int);
+
+int	 tun_open(int, int);
+
+/* Common definitions for ssh tunnel device forwarding */
+#define SSH_TUNMODE_NO		0x00
+#define SSH_TUNMODE_POINTOPOINT	0x01
+#define SSH_TUNMODE_ETHERNET	0x02
+#define SSH_TUNMODE_DEFAULT	SSH_TUNMODE_POINTOPOINT
+#define SSH_TUNMODE_YES		(SSH_TUNMODE_POINTOPOINT|SSH_TUNMODE_ETHERNET)
+
+#define SSH_TUNID_ANY		0x7fffffff
+#define SSH_TUNID_ERR		(SSH_TUNID_ANY - 1)
+#define SSH_TUNID_MAX		(SSH_TUNID_ANY - 2)
diff --git a/readconf.c b/readconf.c
index b6aad9d..1fbf597 100644
--- a/readconf.c
+++ b/readconf.c
@@ -12,7 +12,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: readconf.c,v 1.144 2005/12/06 22:38:27 reyk Exp $");
+RCSID("$OpenBSD: readconf.c,v 1.145 2005/12/08 18:34:11 reyk Exp $");
 
 #include "ssh.h"
 #include "xmalloc.h"
@@ -273,7 +273,7 @@
 		xfree(options->remote_forwards[i].connect_host);
 	}
 	options->num_remote_forwards = 0;
-	options->tun_open = 0;
+	options->tun_open = SSH_TUNMODE_NO;
 }
 
 /*
@@ -835,14 +835,32 @@
 
 	case oTunnel:
 		intptr = &options->tun_open;
-		goto parse_flag;
+		arg = strdelim(&s);
+		if (!arg || *arg == '\0')
+			fatal("%s line %d: Missing yes/point-to-point/"
+			    "ethernet/no argument.", filename, linenum);
+		value = 0;	/* silence compiler */
+		if (strcasecmp(arg, "ethernet") == 0)
+			value = SSH_TUNMODE_ETHERNET;
+		else if (strcasecmp(arg, "point-to-point") == 0)
+			value = SSH_TUNMODE_POINTOPOINT;
+		else if (strcasecmp(arg, "yes") == 0)
+			value = SSH_TUNMODE_DEFAULT;
+		else if (strcasecmp(arg, "no") == 0)
+			value = SSH_TUNMODE_NO;
+		else
+			fatal("%s line %d: Bad yes/point-to-point/ethernet/"
+			    "no argument: %s", filename, linenum, arg);
+		if (*activep)
+			*intptr = value;
+		break;
 
 	case oTunnelDevice:
 		arg = strdelim(&s);
 		if (!arg || *arg == '\0')
 			fatal("%.200s line %d: Missing argument.", filename, linenum);
 		value = a2tun(arg, &value2);
-		if (value < -1)
+		if (value == SSH_TUNID_ERR)
 			fatal("%.200s line %d: Bad tun device.", filename, linenum);
 		if (*activep) {
 			options->tun_local = value;
@@ -1132,7 +1150,11 @@
 	if (options->hash_known_hosts == -1)
 		options->hash_known_hosts = 0;
 	if (options->tun_open == -1)
-		options->tun_open = 0;
+		options->tun_open = SSH_TUNMODE_NO;
+	if (options->tun_local == -1)
+		options->tun_local = SSH_TUNID_ANY;
+	if (options->tun_remote == -1)
+		options->tun_remote = SSH_TUNID_ANY;
 	if (options->permit_local_command == -1)
 		options->permit_local_command = 0;
 	/* options->local_command should not be set by default */
diff --git a/servconf.c b/servconf.c
index 91a0ced..81953bb 100644
--- a/servconf.c
+++ b/servconf.c
@@ -10,7 +10,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: servconf.c,v 1.145 2005/12/06 22:38:27 reyk Exp $");
+RCSID("$OpenBSD: servconf.c,v 1.146 2005/12/08 18:34:11 reyk Exp $");
 
 #include "ssh.h"
 #include "log.h"
@@ -231,7 +231,7 @@
 	if (options->authorized_keys_file == NULL)
 		options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS;
 	if (options->permit_tun == -1)
-		options->permit_tun = 0;
+		options->permit_tun = SSH_TUNMODE_NO;
 
 	/* Turn privilege separation on by default */
 	if (use_privsep == -1)
@@ -968,7 +968,25 @@
 
 	case sPermitTunnel:
 		intptr = &options->permit_tun;
-		goto parse_flag;
+		arg = strdelim(&cp);
+		if (!arg || *arg == '\0')
+			fatal("%s line %d: Missing yes/point-to-point/"
+			    "ethernet/no argument.", filename, linenum);
+		value = 0;	/* silence compiler */
+		if (strcasecmp(arg, "ethernet") == 0)
+			value = SSH_TUNMODE_ETHERNET;
+		else if (strcasecmp(arg, "point-to-point") == 0)
+			value = SSH_TUNMODE_POINTOPOINT;
+		else if (strcasecmp(arg, "yes") == 0)
+			value = SSH_TUNMODE_YES;
+		else if (strcasecmp(arg, "no") == 0)
+			value = SSH_TUNMODE_NO;
+		else
+			fatal("%s line %d: Bad yes/point-to-point/ethernet/"
+			    "no argument: %s", filename, linenum, arg);
+		if (*intptr == -1)
+			*intptr = value;
+		break;
 
 	case sDeprecated:
 		logit("%s line %d: Deprecated option %s",
diff --git a/serverloop.c b/serverloop.c
index 199f769..eff27d9 100644
--- a/serverloop.c
+++ b/serverloop.c
@@ -35,7 +35,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: serverloop.c,v 1.122 2005/12/06 22:38:27 reyk Exp $");
+RCSID("$OpenBSD: serverloop.c,v 1.123 2005/12/08 18:34:11 reyk Exp $");
 
 #include "xmalloc.h"
 #include "packet.h"
@@ -917,20 +917,31 @@
 server_request_tun(void)
 {
 	Channel *c = NULL;
-	int sock, tun;
+	int mode, tun;
+	int sock;
 
-	if (!options.permit_tun) {
-		packet_send_debug("Server has disabled tunnel device forwarding.");
+	mode = packet_get_int();
+	switch (mode) {
+	case SSH_TUNMODE_POINTOPOINT:
+	case SSH_TUNMODE_ETHERNET:
+		break;
+	default:
+		packet_send_debug("Unsupported tunnel device mode.");
+		return NULL;
+	}
+	if ((options.permit_tun & mode) == 0) {
+		packet_send_debug("Server has rejected tunnel device "
+		    "forwarding");
 		return NULL;
 	}
 
 	tun = packet_get_int();
-	if (forced_tun_device != -1) {
-	 	if (tun != -1 && forced_tun_device != tun)
+	if (forced_tun_device != SSH_TUNID_ANY) {
+	 	if (tun != SSH_TUNID_ANY && forced_tun_device != tun)
 			goto done;
 		tun = forced_tun_device;
 	}
-	sock = tun_open(tun);
+	sock = tun_open(tun, mode);
 	if (sock < 0)
 		goto done;
 	c = channel_new("tun", SSH_CHANNEL_OPEN, sock, sock, -1,
diff --git a/ssh.c b/ssh.c
index 8a4a0e4..dd627ce 100644
--- a/ssh.c
+++ b/ssh.c
@@ -40,7 +40,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: ssh.c,v 1.255 2005/12/06 22:38:27 reyk Exp $");
+RCSID("$OpenBSD: ssh.c,v 1.256 2005/12/08 18:34:11 reyk Exp $");
 
 #include <openssl/evp.h>
 #include <openssl/err.h>
@@ -341,9 +341,10 @@
 				exit(0);
 			break;
 		case 'w':
-			options.tun_open = 1;
+			if (options.tun_open == -1)
+				options.tun_open = SSH_TUNMODE_DEFAULT;
 			options.tun_local = a2tun(optarg, &options.tun_remote);
-			if (options.tun_local < -1) {
+			if (options.tun_local == SSH_TUNID_ERR) {
 				fprintf(stderr, "Bad tun device '%s'\n", optarg);
 				exit(1);
 			}
@@ -1067,12 +1068,13 @@
 		packet_send();
 	}
 
-	if (options.tun_open) {
+	if (options.tun_open != SSH_TUNMODE_NO) {
 		Channel *c;
 		int fd;
 
 		debug("Requesting tun.");
-		if ((fd = tun_open(options.tun_local)) >= 0) {
+		if ((fd = tun_open(options.tun_local,
+		    options.tun_open)) >= 0) {
 			c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1,
 			    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
 			    0, "tun", 1);
@@ -1082,6 +1084,7 @@
 			packet_put_int(c->self);
 			packet_put_int(c->local_window_max);
 			packet_put_int(c->local_maxpacket);
+			packet_put_int(options.tun_open);
 			packet_put_int(options.tun_remote);
 			packet_send();
 		}
diff --git a/ssh_config.5 b/ssh_config.5
index 281b404..6806118 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -34,7 +34,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $OpenBSD: ssh_config.5,v 1.67 2005/12/08 15:06:29 jmc Exp $
+.\" $OpenBSD: ssh_config.5,v 1.68 2005/12/08 18:34:11 reyk Exp $
 .Dd September 25, 1999
 .Dt SSH_CONFIG 5
 .Os
@@ -911,9 +911,13 @@
 .It Cm Tunnel
 Request starting
 .Xr tun 4
-device forwarding between the client and the server.
+device forwarding between the client and the server.  This option also
+allows requesting layer 2 (ethernet) instead of layer 3
+(point-to-point) tunneling from the server.
 The argument must be
-.Dq yes
+.Dq yes ,
+.Dq point-to-point ,
+.Dq ethernet
 or
 .Dq no .
 The default is
diff --git a/sshd_config.5 b/sshd_config.5
index 3835fcd..a10b365 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -34,7 +34,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $OpenBSD: sshd_config.5,v 1.46 2005/12/06 22:38:28 reyk Exp $
+.\" $OpenBSD: sshd_config.5,v 1.47 2005/12/08 18:34:11 reyk Exp $
 .Dd September 25, 1999
 .Dt SSHD_CONFIG 5
 .Os
@@ -506,6 +506,12 @@
 Specifies whether
 .Xr tun 4
 device forwarding is allowed.
+The argument must be
+.Dq yes ,
+.Dq point-to-point ,
+.Dq ethernet
+or
+.Dq no .
 The default is
 .Dq no .
 .It Cm PermitUserEnvironment