tunnel device name acquisition?

Damien Miller djm at mindrot.org
Thu Oct 12 10:05:02 AEDT 2017



On Tue, 10 Oct 2017, Gabriel L. Somlo wrote:

> Numerous how-tos all over the Internet show how one would set up
> a tunnel using ssh, e.g.:
> 
> 	ssh -f -o Tunnel=ethernet <server_ip> true
> 
> I was wondering if there's a way to subsequently acquire the names
> of the local and remote tun/tap interfaces (e.g., using the default
> "-w any:any") for subsequent automatic tunnel configuration, e.g.:
> 
> 	ip link set $TapDev up
> 	ip link set $TapDev master <client-or-server-side-bridge>
> 
> Most examples out there pick something silly like "-w 5:5" then
> proceed to configure the hard-coded "tap5" on both client and server.
> However, that's unreliable -- what if "tap5" is already in use on the
> server, and we have to pick something else? What if I want to set up a
> server to accept multiple connections from random clients in random
> order?
> 
> Ideally, I'd start ssh-based "tunnel client" and "tunnel server"
> services at boot, and having to pick names manually, then manually
> configure everything on both ends is quite limiting.
> 
> I tried starting the client with "-vvv" hoping the verbose debugging
> output would include some grep-able hint as to what interface names
> were picked, but couldn't see anything useful.

The following might do what you want; on the client side it expands
%T tokens in LocalCommand to the local tunnel device name and exposes
the remote device name via $SSH_TUNNEL on the server.

Lightly tested.

diff --git a/clientloop.c b/clientloop.c
index 791d336e..18564296 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -1601,12 +1601,13 @@ client_request_agent(struct ssh *ssh, const char *request_type, int rchan)
 	return c;
 }
 
-int
+char *
 client_request_tun_fwd(struct ssh *ssh, int tun_mode,
     int local_tun, int remote_tun)
 {
 	Channel *c;
 	int fd;
+	char *ifname = NULL;
 
 	if (tun_mode == SSH_TUNMODE_NO)
 		return 0;
@@ -1614,10 +1615,11 @@ client_request_tun_fwd(struct ssh *ssh, int tun_mode,
 	debug("Requesting tun unit %d in mode %d", local_tun, tun_mode);
 
 	/* Open local tunnel device */
-	if ((fd = tun_open(local_tun, tun_mode)) == -1) {
+	if ((fd = tun_open(local_tun, tun_mode, &ifname)) == -1) {
 		error("Tunnel device open failed.");
-		return -1;
+		return NULL;
 	}
+	debug("Tunnel forwarding using interface %s", ifname);
 
 	c = channel_new(ssh, "tun", SSH_CHANNEL_OPENING, fd, fd, -1,
 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
@@ -1638,7 +1640,7 @@ client_request_tun_fwd(struct ssh *ssh, int tun_mode,
 	packet_put_int(remote_tun);
 	packet_send();
 
-	return 0;
+	return ifname;
 }
 
 /* XXXX move to generic input handler */
diff --git a/clientloop.h b/clientloop.h
index a1975ccc..5f9e3a9e 100644
--- a/clientloop.h
+++ b/clientloop.h
@@ -46,7 +46,7 @@ int	 client_x11_get_proto(struct ssh *, const char *, const char *,
 void	 client_global_request_reply_fwd(int, u_int32_t, void *);
 void	 client_session2_setup(struct ssh *, int, int, int,
 	    const char *, struct termios *, int, Buffer *, char **);
-int	 client_request_tun_fwd(struct ssh *, int, int, int);
+char	 *client_request_tun_fwd(struct ssh *, int, int, int);
 void	 client_stop_mux(void);
 
 /* Escape filter for protocol 2 sessions */
diff --git a/misc.c b/misc.c
index 05950a47..1b976448 100644
--- a/misc.c
+++ b/misc.c
@@ -724,16 +724,19 @@ read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz,
 }
 
 int
-tun_open(int tun, int mode)
+tun_open(int tun, int mode, char **ifname)
 {
 #if defined(CUSTOM_SYS_TUN_OPEN)
-	return (sys_tun_open(tun, mode));
+	return (sys_tun_open(tun, mode, ifname));
 #elif defined(SSH_TUN_OPENBSD)
 	struct ifreq ifr;
 	char name[100];
 	int fd = -1, sock;
 	const char *tunbase = "tun";
 
+	if (devname != NULL)
+		*devname = NULL;
+
 	if (mode == SSH_TUNMODE_ETHERNET)
 		tunbase = "tap";
 
@@ -780,6 +783,9 @@ tun_open(int tun, int mode)
 		}
 	}
 
+	if (ifname != NULL)
+		*ifname = xstrdup(ifr.ifr_name);
+
 	close(sock);
 	return fd;
 
diff --git a/misc.h b/misc.h
index 153d1137..fe088d70 100644
--- a/misc.h
+++ b/misc.h
@@ -84,7 +84,7 @@ void	 replacearg(arglist *, u_int, char *, ...)
 	     __attribute__((format(printf, 3, 4)));
 void	 freeargs(arglist *);
 
-int	 tun_open(int, int);
+int	 tun_open(int, int, char **);
 
 /* Common definitions for ssh tunnel device forwarding */
 #define SSH_TUNMODE_NO		0x00
diff --git a/openbsd-compat/port-tun.c b/openbsd-compat/port-tun.c
index 7579c608..9d3f7a0d 100644
--- a/openbsd-compat/port-tun.c
+++ b/openbsd-compat/port-tun.c
@@ -56,12 +56,15 @@
 #include <linux/if_tun.h>
 
 int
-sys_tun_open(int tun, int mode)
+sys_tun_open(int tun, int mode, char **ifname)
 {
 	struct ifreq ifr;
 	int fd = -1;
 	const char *name = NULL;
 
+	if (ifname != NULL)
+		*ifname = NULL;
+
 	if ((fd = open("/dev/net/tun", O_RDWR)) == -1) {
 		debug("%s: failed to open tunnel control interface: %s",
 		    __func__, strerror(errno));
@@ -99,6 +102,9 @@ sys_tun_open(int tun, int mode)
 	else
 		debug("%s: %s mode %d fd %d", __func__, ifr.ifr_name, mode, fd);
 
+	if (ifname != NULL && (*ifname = strdup(ifr.ifr_name)) == NULL)
+		goto failed;
+
 	return (fd);
 
  failed:
@@ -116,13 +122,16 @@ sys_tun_open(int tun, int mode)
 #endif
 
 int
-sys_tun_open(int tun, int mode)
+sys_tun_open(int tun, int mode, char **ifname)
 {
 	struct ifreq ifr;
 	char name[100];
 	int fd = -1, sock, flag;
 	const char *tunbase = "tun";
 
+	if (ifname != NULL)
+		*ifname = NULL;
+
 	if (mode == SSH_TUNMODE_ETHERNET) {
 #ifdef SSH_TUN_NO_L2
 		debug("%s: no layer 2 tunnelling support", __func__);
@@ -180,6 +189,9 @@ sys_tun_open(int tun, int mode)
 			goto failed;
 	}
 
+	if (ifname != NULL && (*ifname = strdup(ifr.ifr_name)) == NULL)
+		goto failed;
+
 	close(sock);
 	return (fd);
 
diff --git a/openbsd-compat/port-tun.h b/openbsd-compat/port-tun.h
index 10351437..926bc93e 100644
--- a/openbsd-compat/port-tun.h
+++ b/openbsd-compat/port-tun.h
@@ -22,7 +22,7 @@ struct ssh;
 
 #if defined(SSH_TUN_LINUX) || defined(SSH_TUN_FREEBSD)
 # define CUSTOM_SYS_TUN_OPEN
-int	  sys_tun_open(int, int);
+int	  sys_tun_open(int, int, char **);
 #endif
 
 #if defined(SSH_TUN_COMPAT_AF) || defined(SSH_TUN_PREPEND_AF)
diff --git a/serverloop.c b/serverloop.c
index 24bbae32..cd02e4b2 100644
--- a/serverloop.c
+++ b/serverloop.c
@@ -99,6 +99,9 @@ static volatile sig_atomic_t received_sigterm = 0;
 /* prototypes */
 static void server_init_dispatch(void);
 
+/* requested tunnel forwarding interface(s), shared with session.c */
+char *tun_fwd_ifnames = NULL;
+
 /*
  * we write to this pipe if a SIGCHLD is caught in order to avoid
  * the race between select() and child_terminated
@@ -519,6 +522,7 @@ server_request_tun(struct ssh *ssh)
 	Channel *c = NULL;
 	int mode, tun;
 	int sock;
+	char *tmp, *ifname = NULL;
 
 	mode = packet_get_int();
 	switch (mode) {
@@ -541,9 +545,12 @@ server_request_tun(struct ssh *ssh)
 			goto done;
 		tun = forced_tun_device;
 	}
-	sock = tun_open(tun, mode);
+	/* XXX remember tun name and stash it in the environment */
+	sock = tun_open(tun, mode, &ifname);
 	if (sock < 0)
 		goto done;
+	debug("Tunnel forwarding using interface %s", ifname);
+
 	c = channel_new(ssh, "tun", SSH_CHANNEL_OPEN, sock, sock, -1,
 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
 	c->datagram = 1;
@@ -553,6 +560,15 @@ server_request_tun(struct ssh *ssh)
 		    sys_tun_outfilter, NULL, NULL);
 #endif
 
+	/* Update the list of names exposed to the session */
+	tmp = tun_fwd_ifnames;
+	xasprintf(&tun_fwd_ifnames, "%s%s%s",
+	    tun_fwd_ifnames == NULL ? "" : tun_fwd_ifnames,
+	    tun_fwd_ifnames == NULL ? "" : ",",
+	    ifname);
+	free(tmp);
+	free(ifname);
+
  done:
 	if (c == NULL)
 		packet_send_debug("Failed to open the tunnel device.");
diff --git a/session.c b/session.c
index 4bccb62d..eac7cca1 100644
--- a/session.c
+++ b/session.c
@@ -140,6 +140,7 @@ extern u_int utmp_len;
 extern int startup_pipe;
 extern void destroy_sensitive_data(void);
 extern Buffer loginmsg;
+char *tun_fwd_ifnames; /* serverloop.c */
 
 /* original command from peer. */
 const char *original_command = NULL;
@@ -168,6 +169,7 @@ static char *auth_info_file = NULL;
 static char *auth_sock_name = NULL;
 static char *auth_sock_dir = NULL;
 
+
 /* removes the agent forwarding socket */
 
 static void
@@ -1066,6 +1068,8 @@ do_setup_env(struct ssh *ssh, Session *s, const char *shell)
 	free(laddr);
 	child_set_env(&env, &envsize, "SSH_CONNECTION", buf);
 
+	if (tun_fwd_ifnames != NULL)
+		child_set_env(&env, &envsize, "SSH_TUNNEL", tun_fwd_ifnames);
 	if (auth_info_file != NULL)
 		child_set_env(&env, &envsize, "SSH_USER_AUTH", auth_info_file);
 	if (s->ttyfd != -1)
diff --git a/ssh.c b/ssh.c
index ae37432b..cee0c9bd 100644
--- a/ssh.c
+++ b/ssh.c
@@ -168,6 +168,10 @@ char *config = NULL;
  */
 char *host;
 
+/* Various strings used to to percent_expand() arguments */
+char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
+char uidstr[32], *host_arg, *conn_hash_hex;
+
 /* socket address the host resolves to */
 struct sockaddr_storage hostaddr;
 
@@ -208,7 +212,7 @@ usage(void)
 	exit(255);
 }
 
-static int ssh_session2(struct ssh *);
+static int ssh_session2(struct ssh *, struct passwd *);
 static void load_public_identity_files(void);
 static void main_sigchld_handler(int);
 
@@ -511,9 +515,8 @@ main(int ac, char **av)
 	struct ssh *ssh = NULL;
 	int i, r, opt, exit_status, use_syslog, direct, timeout_ms;
 	int config_test = 0, opt_terminated = 0;
-	char *p, *cp, *line, *argv0, buf[PATH_MAX], *host_arg, *logfile;
-	char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
-	char cname[NI_MAXHOST], uidstr[32], *conn_hash_hex;
+	char *p, *cp, *line, *argv0, buf[PATH_MAX], *logfile;
+	char cname[NI_MAXHOST];
 	struct stat st;
 	struct passwd *pw;
 	extern int optind, optreset;
@@ -1177,6 +1180,7 @@ main(int ac, char **av)
 	if (options.user == NULL)
 		options.user = xstrdup(pw->pw_name);
 
+	/* Set up strings used to percent_expand() arguments */
 	if (gethostname(thishost, sizeof(thishost)) == -1)
 		fatal("gethostname: %s", strerror(errno));
 	strlcpy(shorthost, thishost, sizeof(shorthost));
@@ -1194,24 +1198,11 @@ main(int ac, char **av)
 	ssh_digest_free(md);
 	conn_hash_hex = tohex(conn_hash, ssh_digest_bytes(SSH_DIGEST_SHA1));
 
-	if (options.local_command != NULL) {
-		debug3("expanding LocalCommand: %s", options.local_command);
-		cp = options.local_command;
-		options.local_command = percent_expand(cp,
-		    "C", conn_hash_hex,
-		    "L", shorthost,
-		    "d", pw->pw_dir,
-		    "h", host,
-		    "l", thishost,
-		    "n", host_arg,
-		    "p", portstr,
-		    "r", options.user,
-		    "u", pw->pw_name,
-		    (char *)NULL);
-		debug3("expanded LocalCommand: %s", options.local_command);
-		free(cp);
-	}
-
+	/*
+	 * Expand tokens in arguments. NB. LocalCommand is expanded later,
+	 * after port-forwarding is set up, so it may pick up any local
+	 * tunnel interface name allocated.
+	 */
 	if (options.remote_command != NULL) {
 		debug3("expanding RemoteCommand: %s", options.remote_command);
 		cp = options.remote_command;
@@ -1230,7 +1221,6 @@ main(int ac, char **av)
 		free(cp);
 		buffer_append(&command, options.remote_command,
 		    strlen(options.remote_command));
-
 	}
 
 	if (options.control_path != NULL) {
@@ -1465,7 +1455,7 @@ main(int ac, char **av)
 	}
 
  skip_connect:
-	exit_status = ssh_session2(ssh);
+	exit_status = ssh_session2(ssh, pw);
 	packet_close();
 
 	if (options.control_path != NULL && muxserver_sock != -1)
@@ -1624,7 +1614,7 @@ ssh_init_stdio_forwarding(struct ssh *ssh)
 }
 
 static void
-ssh_init_forwarding(struct ssh *ssh)
+ssh_init_forwarding(struct ssh *ssh, char **ifname)
 {
 	int success = 0;
 	int i;
@@ -1682,8 +1672,9 @@ ssh_init_forwarding(struct ssh *ssh)
 
 	/* Initiate tunnel forwarding. */
 	if (options.tun_open != SSH_TUNMODE_NO) {
-		if (client_request_tun_fwd(ssh, options.tun_open,
-		    options.tun_local, options.tun_remote) == -1) {
+		if ((*ifname = client_request_tun_fwd(ssh,
+		    options.tun_open, options.tun_local,
+		    options.tun_remote)) == NULL) {
 			if (options.exit_on_forward_failure)
 				fatal("Could not request tunnel forwarding.");
 			else
@@ -1798,14 +1789,35 @@ ssh_session2_open(struct ssh *ssh)
 }
 
 static int
-ssh_session2(struct ssh *ssh)
+ssh_session2(struct ssh *ssh, struct passwd *pw)
 {
 	int id = -1;
+	char *cp, *tun_fwd_ifname = NULL;
 
 	/* XXX should be pre-session */
 	if (!options.control_persist)
 		ssh_init_stdio_forwarding(ssh);
-	ssh_init_forwarding(ssh);
+
+	ssh_init_forwarding(ssh, &tun_fwd_ifname);
+
+	if (options.local_command != NULL) {
+		debug3("expanding LocalCommand: %s", options.local_command);
+		cp = options.local_command;
+		options.local_command = percent_expand(cp,
+		    "C", conn_hash_hex,
+		    "L", shorthost,
+		    "d", pw->pw_dir,
+		    "h", host,
+		    "l", thishost,
+		    "n", host_arg,
+		    "p", portstr,
+		    "r", options.user,
+		    "u", pw->pw_name,
+		    "T", tun_fwd_ifname == NULL ? "NONE" : tun_fwd_ifname,
+		    (char *)NULL);
+		debug3("expanded LocalCommand: %s", options.local_command);
+		free(cp);
+	}
 
 	/* Start listening for multiplex clients */
 	if (!packet_get_mux())


More information about the openssh-unix-dev mailing list