Skip to content
Permalink
Browse files
add config option to select the type of IPv6 address
When using IPv6 autoconfiguration, the IP address is deducted using the unique
hardware address of the network card and the announced network prefix. Since
this might lead to privacy issues, most operating systems generate
pseudo-random addresses that are rotated in regular intervals.

This can be a problem for long-running connections if a address is invalidated
while still in use - the connection "hangs". Even though expired addresses are
usually retained for a long timeframe to prevent this, accidental dis- and
reconnection (e.g. when using a wireless network) flushes the list of
previously used addresses.

By setting appropiate socket options, the kernel can be instructed to use the
public (and static) source address for the outgoing connection. This change
implements this functionality for SSH, adding a configuration option
"Ipv6BindPref" that can be set to "pub(lic)", "t(e)mp" or just "none" (which is
the default), indicating the preference for the address to be used.
  • Loading branch information
wertarbyte committed Aug 18, 2016
1 parent 33ba55d commit 028bb9a
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 0 deletions.
@@ -784,6 +784,12 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
[Define if cmsg_type is not passed correctly])
;;
esac
# linux specific IPv6 sock options
AC_CHECK_HEADERS([linux/in6.h])
if test "x$ac_cv_header_linux_in6_h" = "xyes" ; then
AC_DEFINE([LINUX_IPV6_PREF_FLAGS], [1],
[Use Linux specific IPv6 options])
fi
# tun(4) forwarding compat code
AC_CHECK_HEADERS([linux/if_tun.h])
if test "x$ac_cv_header_linux_if_tun_h" = "xyes" ; then
@@ -171,6 +171,7 @@ typedef enum {
oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes,
oPubkeyAcceptedKeyTypes, oProxyJump,
oIpv6BindPref,
oIgnoredUnknownOption, oDeprecated, oUnsupported
} OpCodes;

@@ -296,6 +297,11 @@ static struct {
{ "pubkeyacceptedkeytypes", oPubkeyAcceptedKeyTypes },
{ "ignoreunknown", oIgnoreUnknown },
{ "proxyjump", oProxyJump },
#ifdef LINUX_IPV6_PREF_FLAGS
{ "ipv6bindpref", oIpv6BindPref },
#else
{ "ipv6bindpref", oUnsupported },
#endif

{ NULL, oBadOption }
};
@@ -757,6 +763,14 @@ static const struct multistate multistate_addressfamily[] = {
{ "any", AF_UNSPEC },
{ NULL, -1 }
};
static const struct multistate multistate_ipv6bindpref[] = {
{ "none", SSH_IPV6BINDPREF_NONE },
{ "temp", SSH_IPV6BINDPREF_TMP },
{ "tmp", SSH_IPV6BINDPREF_TMP },
{ "pub", SSH_IPV6BINDPREF_PUB },
{ "public", SSH_IPV6BINDPREF_PUB },
{ NULL, -1 }
};
static const struct multistate multistate_controlmaster[] = {
{ "true", SSHCTL_MASTER_YES },
{ "yes", SSHCTL_MASTER_YES },
@@ -1145,6 +1159,11 @@ process_config_line_depth(Options *options, struct passwd *pw, const char *host,
}
return 0;

case oIpv6BindPref:
intptr = &options->ipv6_bind_pref;
multistate_ptr = multistate_ipv6bindpref;
goto parse_multistate;

case oPort:
intptr = &options->port;
parse_int:
@@ -1858,6 +1877,7 @@ initialize_options(Options * options)
options->update_hostkeys = -1;
options->hostbased_key_types = NULL;
options->pubkey_key_types = NULL;
options->ipv6_bind_pref = -1;
}

/*
@@ -2045,6 +2065,8 @@ fill_default_options(Options * options)
options->fingerprint_hash = SSH_FP_HASH_DEFAULT;
if (options->update_hostkeys == -1)
options->update_hostkeys = 0;
if (options->ipv6_bind_pref == -1)
options->ipv6_bind_pref = SSH_IPV6BINDPREF_NONE;
if (kex_assemble_names(KEX_CLIENT_ENCRYPT, &options->ciphers) != 0 ||
kex_assemble_names(KEX_CLIENT_MAC, &options->macs) != 0 ||
kex_assemble_names(KEX_CLIENT_KEX, &options->kex_algorithms) != 0 ||
@@ -87,6 +87,7 @@ typedef struct {
char *user_hostfiles[SSH_MAX_HOSTS_FILES];
char *preferred_authentications;
char *bind_address; /* local socket address for connection to sshd */
int ipv6_bind_pref; /* which types of IPv6 addresses to prefer (pub/tmp) */
char *pkcs11_provider; /* PKCS#11 provider */
int verify_host_key_dns; /* Verify host key using DNS */

@@ -181,6 +182,10 @@ typedef struct {
#define SSHCTL_MASTER_ASK 3
#define SSHCTL_MASTER_AUTO_ASK 4

#define SSH_IPV6BINDPREF_NONE 0
#define SSH_IPV6BINDPREF_TMP 1
#define SSH_IPV6BINDPREF_PUB 2

#define REQUEST_TTY_AUTO 0
#define REQUEST_TTY_NO 1
#define REQUEST_TTY_YES 2
@@ -1105,6 +1105,15 @@ The default is
for interactive sessions and
.Dq throughput
for non-interactive sessions.
.It Cm Ipv6BindPref
Specified which kind of local IPv6 address to prefer.
The argument to this keyword must be
.Dq temp ,
.Dq pub
or
.Dq none .
The default is
.Dq none .
.It Cm KbdInteractiveAuthentication
Specifies whether to use keyboard-interactive authentication.
The argument to this keyword must be
@@ -27,6 +27,11 @@
#include <netinet/in.h>
#include <arpa/inet.h>

#ifdef LINUX_IPV6_PREF_FLAGS
/* this only works on linux */
# include <linux/in6.h>
#endif

#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
@@ -267,6 +272,32 @@ ssh_kill_proxy_command(void)
kill(proxy_command_pid, SIGHUP);
}


#ifdef LINUX_IPV6_PREF_FLAGS
static int
ssh_mod_ipv6bindpref(int fd, int add, int del)
{
int val;
int err;
socklen_t len = sizeof(val);
err = getsockopt(fd, IPPROTO_IPV6, IPV6_ADDR_PREFERENCES, &val, &len);
if (err < 0) {
error("getsockopt IPV6_ADDR_PREFERENCES: %.100s",
strerror(errno));
return err;
}

val &= ~del;
val |= add;
err = setsockopt(fd, IPPROTO_IPV6, IPV6_ADDR_PREFERENCES, &val, len);
if (err < 0) {
error("setsockopt IPV6_ADDR_PREFERENCES: %.100s",
strerror(errno));
}
return err;
}
#endif

/*
* Creates a (possibly privileged) socket for use as the ssh connection.
*/
@@ -283,6 +314,32 @@ ssh_create_socket(int privileged, struct addrinfo *ai)
}
fcntl(sock, F_SETFD, FD_CLOEXEC);

#ifdef LINUX_IPV6_PREF_FLAGS
/*
* Advise the OS whether to use public or temporary IPv6 addresses for
* our socket
*/
if (ai->ai_family == AF_INET6) {
switch (options.ipv6_bind_pref) {
case SSH_IPV6BINDPREF_PUB:
ssh_mod_ipv6bindpref(sock,
IPV6_PREFER_SRC_PUBLIC,
(IPV6_PREFER_SRC_TMP |
IPV6_PREFER_SRC_PUBTMP_DEFAULT));
break;
case SSH_IPV6BINDPREF_TMP:
ssh_mod_ipv6bindpref(sock,
IPV6_PREFER_SRC_TMP,
(IPV6_PREFER_SRC_PUBLIC |
IPV6_PREFER_SRC_PUBTMP_DEFAULT));
break;
default:
/* do nothing */
break;
}
}
#endif

/* Bind the socket to an alternative local IP address */
if (options.bind_address == NULL && !privileged)
return sock;

0 comments on commit 028bb9a

Please sign in to comment.