On Linux, ssh would need to call setsockopt(IPV6_ADDR_PREFERENCES) to request a specific address type. (This is not mentioned in Linux setsockopt docs, but the code is present in the kernel and seems to match the RFC 5014 specification.)
Since OpenSSH doesn't have any config options that would let you specify address preferences, it'd have to be a hack like this one (only with a different sockopt). You can try asking the OpenSSH developers to add such an option, but they generally do not want to add any OS features that OpenBSD doesn't also support.
It's probably easier to script something to get the correct address for -b; for example, if you're on Linux you could use ip | jq:
addr=$(ip -json -6 addr ls dev eno1 scope global \
| jq -r ".[].addr_info[]|select(.local)|select(.temporary|not)|.local")
ssh -b $addr ...
Alternatively – turn off temporary addresses (Privacy Extensions) completely.
But if you want to try your luck with LD_PRELOAD, you can test this (I haven't tested it at all; it might very well be that it will be triggered too late to have any effect):
/* gcc -shared -o ipv6pref.so ipv6pref.c */
/* LD_PRELOAD="$HOME/ipv6pref.so" ssh whatever */
#define _GNU_SOURCE
#include <dlfcn.h>
#include <err.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
int setsockopt(int fd, int level, int name, const void value, socklen_t len)
{
static int (real_setsockopt)(int, int, int, const void *, socklen_t);
uint32_t flags = IPV6_PREFER_SRC_PUBLIC;
int r;
if (!real_setsockopt)
real_setsockopt = dlsym(RTLD_NEXT, "setsockopt");
if ((level == SOL_IP && name == IP_TOS) ||
(level == SOL_IPV6 && name == IPV6_TCLASS))
{
/* This is probably the TCP socket that will be used for SSH. */
r = real_setsockopt(fd, IPPROTO_IPV6, IPV6_ADDR_PREFERENCES, &flags, sizeof flags);
if (r != 0)
warn("Could not set address preference");
}
return real_setsockopt(fd, level, name, value, len);
}