Hello all,
I recently have a problem with multiple addresses and address
families. Problem is simple, i have some hosts with IPv4 access only
and some with IPv6 access. This wouldn't be big problem if I had a
stable IP addresses. But sometimes I move to another network with
complete different addresses. So I created patch which on option
BindAddress accept list of addresses. With ip I solved two main
problems:
1. When using "Host *" it is possible to set address from one family
(IPv4 or IPv6), ofcource there can be introduced BindAddress4 and
BindAddress6 options
2. I can move to another net and there no need do modify ~/.ssh/config file
Of course added address called "any" witch means to use any address
(like the are no option BindAddress defined)
So my config file now looks like:
Host *
BindAddress 2001:1010:10::1 192.168.0.4 any # means that tries both
binds, if everything fail will use any address
Host ipv4_only.host
IdentityFile ~/.ssh/ipv4
Host ipv6_only.host
IdentityFile ~/.ssh/ipv6
Host ipv4_ipv6.host
IdentityFile ~/.ssh/secr3t
RemoteForward 127.0.0.1:1234 127.0.0.1:1234
DynamicForward 6666
I think is much easier to have BindAddress configuration in top of config file.
Thanks,
Tautvydas ?elvys
--
This patch is made on openssh-5.9p1 version
--
diff -rupN orig/openssh-5.9p1/readconf.c openssh-5.9p1/readconf.c
--- orig/openssh-5.9p1/readconf.c 2011-05-29 14:42:31.000000000 +0300
+++ openssh-5.9p1/readconf.c 2012-02-12 15:43:43.302048950 +0200
@@ -641,8 +641,10 @@ parse_char_array:
goto parse_string;
case oBindAddress:
- charptr = &options->bind_address;
- goto parse_string;
+ cpptr = (char**)&options->bind_addresses;
+ uintptr = &options->num_bind_address;
+ max_entries =SSH_MAX_BIND_ADDRESSES;
+ goto parse_char_array;
case oPKCS11Provider:
charptr = &options->pkcs11_provider;
@@ -1176,7 +1178,7 @@ initialize_options(Options * options)
options->clear_forwardings = -1;
options->log_level = SYSLOG_LEVEL_NOT_SET;
options->preferred_authentications = NULL;
- options->bind_address = NULL;
+ options->num_bind_address = 0;
options->pkcs11_provider = NULL;
options->enable_ssh_keysign = - 1;
options->no_host_authentication_for_localhost = - 1;
diff -rupN orig/openssh-5.9p1/readconf.h openssh-5.9p1/readconf.h
--- orig/openssh-5.9p1/readconf.h 2011-05-29 14:42:33.000000000 +0300
+++ openssh-5.9p1/readconf.h 2012-02-12 16:05:42.450092058 +0200
@@ -27,8 +27,12 @@ typedef struct {
} Forward;
/* Data structure for representing option data. */
-#define MAX_SEND_ENV 256
-#define SSH_MAX_HOSTS_FILES 256
+#define MAX_SEND_ENV 256
+#define SSH_MAX_HOSTS_FILES 256
+#define SSH_MAX_BIND_ADDRESSES 8 /* 16 addresses, should be enough */
+
+#define SSH_BIND_ADDRESS_ANY "any" /* any address mark, used in
configuration file */
+#define SSH_BIND_ADDRESS_ANYlen strlen(SSH_BIND_ADDRESS_ANY)
typedef struct {
int forward_agent; /* Forward authentication agent. */
@@ -89,7 +93,10 @@ typedef struct {
u_int num_user_hostfiles; /* Path for $HOME/.ssh/known_hosts */
char *user_hostfiles[SSH_MAX_HOSTS_FILES];
char *preferred_authentications;
- char *bind_address; /* local socket address for connection to sshd */
+
+ char *bind_addresses[SSH_MAX_BIND_ADDRESSES]; /* local socket
address list for connection to sshd, main reason for this is ipv4 and
ipv6 only hosts, when using global host match */
+ u_int num_bind_address; /* count of bind_addresses */
+
char *pkcs11_provider; /* PKCS#11 provider */
int verify_host_key_dns; /* Verify host key using DNS */
diff -rupN orig/openssh-5.9p1/ssh.c openssh-5.9p1/ssh.c
--- orig/openssh-5.9p1/ssh.c 2011-08-05 23:18:16.000000000 +0300
+++ openssh-5.9p1/ssh.c 2012-02-12 15:41:39.446044903 +0200
@@ -595,7 +595,8 @@ main(int ac, char **av)
options.control_path = xstrdup(optarg);
break;
case 'b':
- options.bind_address = optarg;
+ options.bind_addresses[0] = optarg;
+ options.num_bind_address = 1;
break;
case 'F':
config = optarg;
diff -rupN orig/openssh-5.9p1/ssh_config openssh-5.9p1/ssh_config
--- orig/openssh-5.9p1/ssh_config 2010-01-12 10:40:27.000000000 +0200
+++ openssh-5.9p1/ssh_config 2012-02-12 16:57:02.150192696 +0200
@@ -45,3 +45,8 @@
# PermitLocalCommand no
# VisualHostKey no
# ProxyCommand ssh -q -W %h:%p gateway.example.com
+
+# --Examble of BindAddress
+# BindAddress 192.168.0.1 3004:aaaa::beef any
+# This means, that ssh tries 192.168.0.1 if fail to bind, next
address willbe 3004:aaaa::beef and if it fails,
+# uses default bind strategy, bind on any address
diff -rupN orig/openssh-5.9p1/ssh_config.5 openssh-5.9p1/ssh_config.5
--- orig/openssh-5.9p1/ssh_config.5 2011-08-05 23:17:32.000000000 +0300
+++ openssh-5.9p1/ssh_config.5 2012-02-12 16:40:31.502160322 +0200
@@ -145,8 +145,10 @@ or
The default is
.Dq no .
.It Cm BindAddress
-Use the specified address on the local machine as the source address of
-the connection.
+Use the specified address (or addresses seperated by space) on the
local machine as the source address of
+the connection. This list can be interrupted with
+.Dq any
+address.
Only useful on systems with more than one address.
Note that this option does not work if
.Cm UsePrivilegedPort
diff -rupN orig/openssh-5.9p1/sshconnect.c openssh-5.9p1/sshconnect.c
--- orig/openssh-5.9p1/sshconnect.c 2011-05-29 14:42:34.000000000 +0300
+++ openssh-5.9p1/sshconnect.c 2012-02-12 16:26:33.986132953 +0200
@@ -189,7 +189,7 @@ ssh_create_socket(int privileged, struct
{
int sock, gaierr;
struct addrinfo hints, *res;
-
+ uint i=0;
/*
* If we are running as root and want to connect to a privileged
* port, bind our own socket to a privileged port.
@@ -214,28 +214,61 @@ ssh_create_socket(int privileged, struct
fcntl(sock, F_SETFD, FD_CLOEXEC);
/* Bind the socket to an alternative local IP address */
- if (options.bind_address == NULL)
+ if (options.num_bind_address == 0){
return sock;
+ }
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = ai->ai_family;
- hints.ai_socktype = ai->ai_socktype;
- hints.ai_protocol = ai->ai_protocol;
- hints.ai_flags = AI_PASSIVE;
- gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res);
- if (gaierr) {
- error("getaddrinfo: %s: %s", options.bind_address,
- ssh_gai_strerror(gaierr));
- close(sock);
- return -1;
+ uint addrfound = 0;
+ uint last_idx = options.num_bind_address - 1; // last address index
+
+ verbose("Trying %d addresses to connect",options.num_bind_address);
+ for(i=0;i<options.num_bind_address;++i){
+
+ if
(strncmp(options.bind_addresses[i],SSH_BIND_ADDRESS_ANY,SSH_BIND_ADDRESS_ANYlen)
== 0){ // any flag, do not bind, can by anywhere
+ logit("warning requested 'any' address in BindAddress");
+ return sock;
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = ai->ai_family;
+ hints.ai_socktype = ai->ai_socktype;
+ hints.ai_protocol = ai->ai_protocol;
+ hints.ai_flags = AI_PASSIVE;
+ gaierr = getaddrinfo(options.bind_addresses[i], NULL, &hints, &res);
+ if (gaierr) {
+ error("getaddrinfo: %s: %s", options.bind_addresses[i],
+ ssh_gai_strerror(gaierr));
+ if ( i == last_idx){
+ close(sock);
+ return -1;
+ } else {
+ error("We have more addresses, trying next address:
%s",options.bind_addresses[i+1]);
+ continue;
+ }
+ }
+
+ if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
+ error("bind: %s: %s", options.bind_addresses[i], strerror(errno));
+ if ( i == last_idx){
+ close(sock);
+ freeaddrinfo(res);
+ return -1;
+ } else {
+ error("We have more addresses, trying next address:
%s",options.bind_addresses[i+1]);
+ continue;
+ }
+ } else {
+ addrfound = 1;
+ break;
+ }
+ freeaddrinfo(res);
}
- if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
- error("bind: %s: %s", options.bind_address, strerror(errno));
+
+ if (!addrfound){
+ error("no bindable addres is found, exiting");
close(sock);
- freeaddrinfo(res);
return -1;
}
- freeaddrinfo(res);
return sock;
}