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())