As my previous patch (msgid 20210410140417.19505-1-anton@xxxxxxxxxxx) was rejected because it added an option that only took effect on Linux, I am submitting a stripped-down version that does not add any new options. Hopefully this is more acceptable for inclusion. >8-------------------------------------------------------------------- When both public and temporary IPv6 addresses can be used to reach a given server, it makes sense for the SSH client to prefer the public ones, since - temporary addresses are typically short-lived, which breaks long-running connections - certain OpenSSH features (like from=... in authorized_keys or Match Address/Host in sshd_config) depend on the client connecting from a predictable address - since SSH is an authenticated protocol, it rarely makes sense for the client to hide its identity from the server Uses the socket options defined in RFC5014, which are currently only implemented on Linux. Inspired by a patch posted by Maciej Żenczykowski at https://bugzilla.redhat.com/show_bug.cgi?id=512032 --- configure.ac | 4 ++++ defines.h | 13 +++++++++++++ openbsd-compat/port-net.c | 31 +++++++++++++++++++++++++++++++ openbsd-compat/port-net.h | 2 ++ sshconnect.c | 5 +++++ 5 files changed, 55 insertions(+) diff --git a/configure.ac b/configure.ac index 1c2757ca..7de46ba8 100644 --- a/configure.ac +++ b/configure.ac @@ -860,6 +860,10 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16)) ]) AC_CHECK_HEADERS([linux/seccomp.h linux/filter.h linux/audit.h], [], [], [#include <linux/types.h>]) + AC_CHECK_DECLS([IPV6_ADDR_PREFERENCES], + AC_DEFINE([SYS_IPV6_ADDR_PREF_LINUX], [1], + [IPv6 address preferences on Linux]), [], + [#include <linux/ipv6.h>]) # Obtain MIPS ABI case "$host" in mips*) diff --git a/defines.h b/defines.h index d6a1d014..1bda872d 100644 --- a/defines.h +++ b/defines.h @@ -901,4 +901,17 @@ struct winsize { #ifdef VARIABLE_LENGTH_ARRAYS # define USE_SNTRUP761X25519 1 #endif + +/* RFC5014 IPv6 source address selection flags. + * They do not have standard values or a header + * so we define our own values and convert to the + * platform-specific ones on use. + */ +#define SSH_IPV6_PREFER_SRC_HOME (1 << 0) +#define SSH_IPV6_PREFER_SRC_COA (1 << 1) +#define SSH_IPV6_PREFER_SRC_TMP (1 << 2) +#define SSH_IPV6_PREFER_SRC_PUBLIC (1 << 3) +#define SSH_IPV6_PREFER_SRC_CGA (1 << 4) +#define SSH_IPV6_PREFER_SRC_NONCGA (1 << 5) + #endif /* _DEFINES_H */ diff --git a/openbsd-compat/port-net.c b/openbsd-compat/port-net.c index 198e73f0..f541cc11 100644 --- a/openbsd-compat/port-net.c +++ b/openbsd-compat/port-net.c @@ -46,6 +46,10 @@ #include <linux/if.h> #endif +#ifdef SYS_IPV6_ADDR_PREF_LINUX +#include <linux/ipv6.h> +#endif + #if defined(SYS_RDOMAIN_LINUX) char * sys_get_rdomain(int fd) @@ -376,3 +380,30 @@ sys_tun_outfilter(struct ssh *ssh, struct Channel *c, return (buf); } #endif /* SSH_TUN_FILTER */ + +void sys_set_ipv6_addrpref(int sock, int addr_pref) +{ +#ifdef SYS_IPV6_ADDR_PREF_LINUX + int val = 0; + + if (addr_pref & SSH_IPV6_PREFER_SRC_HOME) + val |= IPV6_PREFER_SRC_HOME; + if (addr_pref & SSH_IPV6_PREFER_SRC_COA) + val |= IPV6_PREFER_SRC_COA; + if (addr_pref & SSH_IPV6_PREFER_SRC_TMP) + val |= IPV6_PREFER_SRC_TMP; + if (addr_pref & SSH_IPV6_PREFER_SRC_PUBLIC) + val |= IPV6_PREFER_SRC_PUBLIC; + if (addr_pref & SSH_IPV6_PREFER_SRC_CGA) + val |= IPV6_PREFER_SRC_CGA; + if (addr_pref & SSH_IPV6_PREFER_SRC_NONCGA) + val |= IPV6_PREFER_SRC_NONCGA; + + if (val != 0 && + setsockopt(sock, IPPROTO_IPV6, IPV6_ADDR_PREFERENCES, + &val, sizeof(val)) == -1) { + error("setsockopt %d, IPV6_ADDR_PREFERENCES %d: %.100s", + sock, val, strerror(errno)); + } +#endif +} diff --git a/openbsd-compat/port-net.h b/openbsd-compat/port-net.h index 3a0d1104..040e2120 100644 --- a/openbsd-compat/port-net.h +++ b/openbsd-compat/port-net.h @@ -45,4 +45,6 @@ int sys_valid_rdomain(const char *name); void sys_set_process_rdomain(const char *name); #endif +void sys_set_ipv6_addrpref(int sock, int addr_pref); + #endif diff --git a/sshconnect.c b/sshconnect.c index 47f0b1c9..e604212b 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -370,6 +370,11 @@ ssh_create_socket(struct addrinfo *ai) if (options.ip_qos_interactive != INT_MAX) set_sock_tos(sock, options.ip_qos_interactive); + /* Prefer public IPv6 source addresses when both public and + * temporary are available */ + if (ai->ai_family == AF_INET6 && options.bind_address == NULL) + sys_set_ipv6_addrpref(sock, SSH_IPV6_PREFER_SRC_PUBLIC); + /* Bind the socket to an alternative local IP address */ if (options.bind_address == NULL && options.bind_interface == NULL) return sock; -- 2.20.1 _______________________________________________ openssh-unix-dev mailing list openssh-unix-dev@xxxxxxxxxxx https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev