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()) _______________________________________________ openssh-unix-dev mailing list openssh-unix-dev@xxxxxxxxxxx https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev