Commit bb529054 authored by Nirbhay Choubey's avatar Nirbhay Choubey

MDEV-8034 : wsrep_node_address can't be IPV6

Updated address parsing logic to include IPv6 format.
parent 31cf362c
...@@ -618,6 +618,7 @@ int wsrep_init() ...@@ -618,6 +618,7 @@ int wsrep_init()
wsrep->provider_vendor, sizeof(provider_vendor) - 1); wsrep->provider_vendor, sizeof(provider_vendor) - 1);
} }
/* Initialize node address */
char node_addr[512]= { 0, }; char node_addr[512]= { 0, };
size_t const node_addr_max= sizeof(node_addr) - 1; size_t const node_addr_max= sizeof(node_addr) - 1;
if (!wsrep_node_address || !strcmp(wsrep_node_address, "")) if (!wsrep_node_address || !strcmp(wsrep_node_address, ""))
...@@ -635,86 +636,81 @@ int wsrep_init() ...@@ -635,86 +636,81 @@ int wsrep_init()
strncpy(node_addr, wsrep_node_address, node_addr_max); strncpy(node_addr, wsrep_node_address, node_addr_max);
} }
/* Initialize node's incoming address */
char inc_addr[512]= { 0, }; char inc_addr[512]= { 0, };
size_t const inc_addr_max= sizeof (inc_addr); size_t const inc_addr_max= sizeof (inc_addr);
/*
In case wsrep_node_incoming_address is either not set or set to AUTO,
we need to use mysqld's my_bind_addr_str:mysqld_port, lastly fallback
to wsrep_node_address' value if mysqld's bind-address is not set either.
*/
if ((!wsrep_node_incoming_address || if ((!wsrep_node_incoming_address ||
!strcmp (wsrep_node_incoming_address, WSREP_NODE_INCOMING_AUTO))) !strcmp (wsrep_node_incoming_address, WSREP_NODE_INCOMING_AUTO)))
{ {
bool is_ipv6= false;
unsigned int my_bind_ip= INADDR_ANY; // default if not set unsigned int my_bind_ip= INADDR_ANY; // default if not set
if (my_bind_addr_str && strlen(my_bind_addr_str)) if (my_bind_addr_str && strlen(my_bind_addr_str))
{ {
my_bind_ip= wsrep_check_ip(my_bind_addr_str); my_bind_ip= wsrep_check_ip(my_bind_addr_str, &is_ipv6);
} }
if (INADDR_ANY != my_bind_ip) if (INADDR_ANY != my_bind_ip)
{ {
/*
If its a not a valid address, leave inc_addr as empty string. mysqld
is not listening for client connections on network interfaces.
*/
if (INADDR_NONE != my_bind_ip && INADDR_LOOPBACK != my_bind_ip) if (INADDR_NONE != my_bind_ip && INADDR_LOOPBACK != my_bind_ip)
{ {
snprintf(inc_addr, inc_addr_max, "%s:%u", const char *fmt= (is_ipv6) ? "[%s]:%u" : "%s:%u";
my_bind_addr_str, (int)mysqld_port); snprintf(inc_addr, inc_addr_max, fmt, my_bind_addr_str, mysqld_port);
} // else leave inc_addr an empty string - mysqld is not listening for }
// client connections on network interfaces.
} }
else // mysqld binds to 0.0.0.0, take IP from wsrep_node_address if possible else /* mysqld binds to 0.0.0.0, try taking IP from wsrep_node_address. */
{ {
size_t const node_addr_len= strlen(node_addr); size_t const node_addr_len= strlen(node_addr);
if (node_addr_len > 0) if (node_addr_len > 0)
{ {
const char* const colon= strrchr(node_addr, ':'); wsp::Address addr(node_addr);
if (strchr(node_addr, ':') == colon) // 1 or 0 ':'
{
size_t const ip_len= colon ? colon - node_addr : node_addr_len;
if (ip_len + 7 /* :55555\0 */ < inc_addr_max)
{
memcpy (inc_addr, node_addr, ip_len);
snprintf(inc_addr + ip_len, inc_addr_max - ip_len, ":%u",
(int)mysqld_port);
}
else
{
WSREP_WARN("Guessing address for incoming client connections: "
"address too long.");
inc_addr[0]= '\0';
}
}
else
{
WSREP_WARN("Guessing address for incoming client connections: "
"too many colons :) .");
inc_addr[0]= '\0';
}
}
if (!strlen(inc_addr)) if (!addr.is_valid())
{ {
WSREP_DEBUG("Could not parse node address : %s", node_addr);
WSREP_WARN("Guessing address for incoming client connections failed. " WSREP_WARN("Guessing address for incoming client connections failed. "
"Try setting wsrep_node_incoming_address explicitly."); "Try setting wsrep_node_incoming_address explicitly.");
goto done;
}
const char *fmt= (addr.is_ipv6()) ? "[%s]:%u" : "%s:%u";
snprintf(inc_addr, inc_addr_max, fmt, addr.get_address(),
(int) mysqld_port);
} }
} }
} }
else if (!strchr(wsrep_node_incoming_address, ':')) // no port included
{
if ((int)inc_addr_max <=
snprintf(inc_addr, inc_addr_max, "%s:%u",
wsrep_node_incoming_address,(int)mysqld_port))
{
WSREP_WARN("Guessing address for incoming client connections: "
"address too long.");
inc_addr[0]= '\0';
}
}
else else
{ {
size_t const need = strlen (wsrep_node_incoming_address); wsp::Address addr(wsrep_node_incoming_address);
if (need >= inc_addr_max) {
WSREP_WARN("wsrep_node_incoming_address too long: %zu", need); if (!addr.is_valid())
inc_addr[0]= '\0'; {
} WSREP_WARN("Could not parse wsrep_node_incoming_address : %s",
else { wsrep_node_incoming_address);
memcpy (inc_addr, wsrep_node_incoming_address, need); goto done;
} }
/*
In case port is not specified in wsrep_node_incoming_address, we use
mysqld_port.
*/
int port= (addr.get_port() > 0) ? addr.get_port() : (int) mysqld_port;
const char *fmt= (addr.is_ipv6()) ? "[%s]:%u" : "%s:%u";
snprintf(inc_addr, inc_addr_max, fmt, addr.get_address(), port);
} }
done:
struct wsrep_init_args wsrep_args; struct wsrep_init_args wsrep_args;
struct wsrep_gtid const state_id = { local_uuid, local_seqno }; struct wsrep_gtid const state_id = { local_uuid, local_seqno };
......
...@@ -629,8 +629,6 @@ static bool SE_initialized = false; ...@@ -629,8 +629,6 @@ static bool SE_initialized = false;
ssize_t wsrep_sst_prepare (void** msg) ssize_t wsrep_sst_prepare (void** msg)
{ {
const ssize_t ip_max= 256;
char ip_buf[ip_max];
const char* addr_in= NULL; const char* addr_in= NULL;
const char* addr_out= NULL; const char* addr_out= NULL;
...@@ -646,27 +644,34 @@ ssize_t wsrep_sst_prepare (void** msg) ...@@ -646,27 +644,34 @@ ssize_t wsrep_sst_prepare (void** msg)
return ret; return ret;
} }
// Figure out SST address. Common for all SST methods /*
Figure out SST receive address. Common for all SST methods.
*/
char ip_buf[256];
const ssize_t ip_max= sizeof(ip_buf);
// Attempt 1: wsrep_sst_receive_address
if (wsrep_sst_receive_address && if (wsrep_sst_receive_address &&
strcmp (wsrep_sst_receive_address, WSREP_SST_ADDRESS_AUTO)) strcmp (wsrep_sst_receive_address, WSREP_SST_ADDRESS_AUTO))
{ {
addr_in= wsrep_sst_receive_address; addr_in= wsrep_sst_receive_address;
} }
//Attempt 2: wsrep_node_address
else if (wsrep_node_address && strlen(wsrep_node_address)) else if (wsrep_node_address && strlen(wsrep_node_address))
{ {
const char* const colon= strchr (wsrep_node_address, ':'); wsp::Address addr(wsrep_node_address);
if (colon)
{ if (!addr.is_valid())
ptrdiff_t const len= colon - wsrep_node_address;
strncpy (ip_buf, wsrep_node_address, len);
ip_buf[len]= '\0';
addr_in= ip_buf;
}
else
{ {
addr_in= wsrep_node_address; WSREP_ERROR("Could not parse wsrep_node_address : %s",
wsrep_node_address);
unireg_abort(1);
} }
memcpy(ip_buf, addr.get_address(), addr.get_address_len());
addr_in= ip_buf;
} }
// Attempt 3: Try to get the IP from the list of available interfaces.
else else
{ {
ssize_t ret= wsrep_guess_ip (ip_buf, ip_max); ssize_t ret= wsrep_guess_ip (ip_buf, ip_max);
...@@ -677,8 +682,7 @@ ssize_t wsrep_sst_prepare (void** msg) ...@@ -677,8 +682,7 @@ ssize_t wsrep_sst_prepare (void** msg)
} }
else else
{ {
WSREP_ERROR("Could not prepare state transfer request: " WSREP_ERROR("Failed to guess address to accept state transfer. "
"failed to guess address to accept state transfer at. "
"wsrep_sst_receive_address must be set manually."); "wsrep_sst_receive_address must be set manually.");
unireg_abort(1); unireg_abort(1);
} }
...@@ -778,8 +782,10 @@ static void sst_reject_queries(my_bool close_conn) ...@@ -778,8 +782,10 @@ static void sst_reject_queries(my_bool close_conn)
if (TRUE == close_conn) wsrep_close_client_connections(FALSE); if (TRUE == close_conn) wsrep_close_client_connections(FALSE);
} }
static int sst_mysqldump_check_addr (const char* user, const char* pswd, static int sst_mysqldump_check_addr (const char* user,
const char* host, const char* port) const char* pswd,
const char* host,
int port)
{ {
return 0; return 0;
} }
...@@ -790,25 +796,17 @@ static int sst_donate_mysqldump (const char* addr, ...@@ -790,25 +796,17 @@ static int sst_donate_mysqldump (const char* addr,
wsrep_seqno_t seqno, wsrep_seqno_t seqno,
bool bypass) bool bypass)
{ {
size_t host_len; char host[256];
const char* port = strchr (addr, ':'); wsp::Address address(addr);
if (port) if (!address.is_valid())
{
port += 1;
host_len = port - addr;
}
else
{ {
port = ""; WSREP_ERROR("Could not parse SST address : %s", addr);
host_len = strlen (addr) + 1; return 0;
} }
char *host= (char *) alloca(host_len); memcpy(host, address.get_address(), address.get_address_len());
int port= address.get_port();
strncpy (host, addr, host_len - 1);
host[host_len - 1] = '\0';
const char* auth = sst_auth_real; const char* auth = sst_auth_real;
const char* pswd = (auth) ? strchr (auth, ':') : NULL; const char* pswd = (auth) ? strchr (auth, ':') : NULL;
size_t user_len; size_t user_len;
...@@ -843,7 +841,7 @@ static int sst_donate_mysqldump (const char* addr, ...@@ -843,7 +841,7 @@ static int sst_donate_mysqldump (const char* addr,
WSREP_SST_OPT_USER" '%s' " WSREP_SST_OPT_USER" '%s' "
WSREP_SST_OPT_PSWD" '%s' " WSREP_SST_OPT_PSWD" '%s' "
WSREP_SST_OPT_HOST" '%s' " WSREP_SST_OPT_HOST" '%s' "
WSREP_SST_OPT_PORT" '%s' " WSREP_SST_OPT_PORT" '%d' "
WSREP_SST_OPT_LPORT" '%u' " WSREP_SST_OPT_LPORT" '%u' "
WSREP_SST_OPT_SOCKET" '%s' " WSREP_SST_OPT_SOCKET" '%s' "
" %s " " %s "
......
...@@ -352,7 +352,7 @@ thd::~thd () ...@@ -352,7 +352,7 @@ thd::~thd ()
} // namespace wsp } // namespace wsp
/* Returns INADDR_NONE, INADDR_ANY, INADDR_LOOPBACK or something else */ /* Returns INADDR_NONE, INADDR_ANY, INADDR_LOOPBACK or something else */
unsigned int wsrep_check_ip (const char* const addr) unsigned int wsrep_check_ip (const char* const addr, bool *is_ipv6)
{ {
unsigned int ret = INADDR_NONE; unsigned int ret = INADDR_NONE;
struct addrinfo *res, hints; struct addrinfo *res, hints;
...@@ -362,6 +362,8 @@ unsigned int wsrep_check_ip (const char* const addr) ...@@ -362,6 +362,8 @@ unsigned int wsrep_check_ip (const char* const addr)
hints.ai_socktype= SOCK_STREAM; hints.ai_socktype= SOCK_STREAM;
hints.ai_family= AF_UNSPEC; hints.ai_family= AF_UNSPEC;
*is_ipv6= false;
int gai_ret = getaddrinfo(addr, NULL, &hints, &res); int gai_ret = getaddrinfo(addr, NULL, &hints, &res);
if (0 == gai_ret) if (0 == gai_ret)
{ {
...@@ -379,6 +381,8 @@ unsigned int wsrep_check_ip (const char* const addr) ...@@ -379,6 +381,8 @@ unsigned int wsrep_check_ip (const char* const addr)
ret= INADDR_LOOPBACK; ret= INADDR_LOOPBACK;
else else
ret= 0xdeadbeef; ret= 0xdeadbeef;
*is_ipv6= true;
} }
freeaddrinfo (res); freeaddrinfo (res);
} }
...@@ -387,10 +391,6 @@ unsigned int wsrep_check_ip (const char* const addr) ...@@ -387,10 +391,6 @@ unsigned int wsrep_check_ip (const char* const addr)
addr, gai_ret, gai_strerror(gai_ret)); addr, gai_ret, gai_strerror(gai_ret));
} }
// uint8_t* b= (uint8_t*)&ret;
// fprintf (stderr, "########## wsrep_check_ip returning: %hhu.%hhu.%hhu.%hhu\n",
// b[0], b[1], b[2], b[3]);
return ret; return ret;
} }
...@@ -398,44 +398,47 @@ extern char* my_bind_addr_str; ...@@ -398,44 +398,47 @@ extern char* my_bind_addr_str;
size_t wsrep_guess_ip (char* buf, size_t buf_len) size_t wsrep_guess_ip (char* buf, size_t buf_len)
{ {
size_t ip_len = 0; size_t ret= 0;
// Attempt 1: Try to get the IP from bind-address.
if (my_bind_addr_str && my_bind_addr_str[0] != '\0') if (my_bind_addr_str && my_bind_addr_str[0] != '\0')
{ {
unsigned int const ip_type= wsrep_check_ip(my_bind_addr_str); bool unused;
unsigned int const ip_type= wsrep_check_ip(my_bind_addr_str, &unused);
if (INADDR_NONE == ip_type) { if (INADDR_NONE == ip_type) {
WSREP_ERROR("Networking not configured, cannot receive state " WSREP_ERROR("Networking not configured, cannot receive state "
"transfer."); "transfer.");
return 0; ret= 0;
} } else if (INADDR_ANY != ip_type) {
if (INADDR_ANY != ip_type) {
strncpy (buf, my_bind_addr_str, buf_len); strncpy (buf, my_bind_addr_str, buf_len);
return strlen(buf); ret= strlen(buf);
} }
goto done;
} }
// mysqld binds to all interfaces - try IP from wsrep_node_address // Attempt 2: mysqld binds to all interfaces, try IP from wsrep_node_address.
if (wsrep_node_address && wsrep_node_address[0] != '\0') { if (wsrep_node_address && wsrep_node_address[0] != '\0') {
const char* const colon_ptr = strchr(wsrep_node_address, ':'); wsp::Address addr(wsrep_node_address);
if (!addr.is_valid())
if (colon_ptr) {
ip_len = colon_ptr - wsrep_node_address; WSREP_WARN("Could not parse wsrep_node_address : %s",
else wsrep_node_address);
ip_len = strlen(wsrep_node_address); ret= 0;
goto done;
if (ip_len >= buf_len) {
WSREP_WARN("default_ip(): buffer too short: %zu <= %zd", buf_len, ip_len);
return 0;
} }
memcpy (buf, wsrep_node_address, ip_len); /* Safety check: Buffer length should be sufficiently large. */
buf[ip_len] = '\0'; DBUG_ASSERT(buf_len >= addr.get_address_len());
return ip_len;
memcpy(buf, addr.get_address(), addr.get_address_len());
ret= addr.get_address_len();
goto done;
} }
/* /*
Attempt 3: Try to get the IP from the list of available interfaces.
getifaddrs() is avaiable at least on Linux since glib 2.3, FreeBSD, getifaddrs() is avaiable at least on Linux since glib 2.3, FreeBSD,
MAC OSX, OpenSolaris, Solaris. MAC OSX, OpenSolaris, Solaris.
...@@ -444,26 +447,42 @@ size_t wsrep_guess_ip (char* buf, size_t buf_len) ...@@ -444,26 +447,42 @@ size_t wsrep_guess_ip (char* buf, size_t buf_len)
*/ */
#if HAVE_GETIFADDRS #if HAVE_GETIFADDRS
struct ifaddrs *ifaddr, *ifa; struct ifaddrs *ifaddr, *ifa;
int family;
if (getifaddrs(&ifaddr) == 0) if (getifaddrs(&ifaddr) == 0)
{ {
for (ifa= ifaddr; ifa != NULL; ifa = ifa->ifa_next) for (ifa= ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{ {
if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET) // TODO AF_INET6 if (!ifa->ifa_addr)
continue;
family= ifa->ifa_addr->sa_family;
if ((family != AF_INET) && (family != AF_INET6))
continue; continue;
// Skip loopback interfaces (like lo:127.0.0.1) // Skip loopback interfaces (like lo:127.0.0.1)
if (ifa->ifa_flags & IFF_LOOPBACK) if (ifa->ifa_flags & IFF_LOOPBACK)
continue; continue;
/*
Get IP address from the socket address. The resulting address may have
zone ID appended for IPv6 addresses (<address>%<zone-id>).
*/
if (vio_getnameinfo(ifa->ifa_addr, buf, buf_len, NULL, 0, NI_NUMERICHOST)) if (vio_getnameinfo(ifa->ifa_addr, buf, buf_len, NULL, 0, NI_NUMERICHOST))
continue; continue;
freeifaddrs(ifaddr); freeifaddrs(ifaddr);
return strlen(buf);
ret= strlen(buf);
goto done;
} }
freeifaddrs(ifaddr); freeifaddrs(ifaddr);
} }
#endif /* HAVE_GETIFADDRS */ #endif /* HAVE_GETIFADDRS */
return 0; done:
WSREP_DEBUG("wsrep_guess_ip() : %s", (ret > 0) ? buf : "????");
return ret;
} }
...@@ -19,11 +19,153 @@ ...@@ -19,11 +19,153 @@
#include "wsrep_priv.h" #include "wsrep_priv.h"
#include "wsrep_mysqld.h" #include "wsrep_mysqld.h"
unsigned int wsrep_check_ip (const char* addr); unsigned int wsrep_check_ip (const char* addr, bool *is_ipv6);
size_t wsrep_guess_ip (char* buf, size_t buf_len); size_t wsrep_guess_ip (char* buf, size_t buf_len);
namespace wsp { namespace wsp {
class Address {
public:
Address()
: m_address_len(0), m_family(UNSPEC), m_port(0), m_valid(false)
{
memset(m_address, 0, sizeof(m_address));
}
Address(const char *addr_in)
: m_address_len(0), m_family(UNSPEC), m_port(0), m_valid(false)
{
memset(m_address, 0, sizeof(m_address));
parse_addr(addr_in);
}
bool is_valid() { return m_valid; }
bool is_ipv6() { return (m_family == INET6); }
const char* get_address() { return m_address; }
size_t get_address_len() { return m_address_len; }
int get_port() { return m_port; }
private:
enum family {
UNSPEC= 0,
INET, /* IPv4 */
INET6, /* IPv6 */
};
char m_address[256];
size_t m_address_len;
family m_family;
int m_port;
bool m_valid;
void parse_addr(const char *addr_in) {
const char *start;
const char *end;
const char *port;
const char* open_bracket= strchr(const_cast<char *>(addr_in), '[');
const char* close_bracket= strchr(const_cast<char *>(addr_in), ']');
const char* colon= strchr(const_cast<char *>(addr_in), ':');
const char* dot= strchr(const_cast<char *>(addr_in), '.');
int cc= colon_count(addr_in);
if (open_bracket != NULL ||
dot == NULL ||
(colon != NULL && (dot == NULL || colon < dot)))
{
// This could be an IPv6 address or a hostname
if (open_bracket != NULL) {
/* Sanity check: Address with '[' must include ']' */
if (close_bracket == NULL &&
open_bracket < close_bracket) /* Error: malformed address */
{
m_valid= false;
return;
}
start= open_bracket + 1;
end= close_bracket;
/* Check for port */
port= strchr(close_bracket, ':');
if ((port != NULL) && parse_port(port + 1))
{
return; /* Error: invalid port */
}
m_family= INET6;
}
else
{
switch (cc) {
case 0:
/* Hostname with no port */
start= addr_in;
end= addr_in + strlen(addr_in);
break;
case 1:
/* Hostname with port (host:port) */
start= addr_in;
end= colon;
parse_port(colon + 1);
break;
default:
/* IPv6 address */
start= addr_in;
end= addr_in + strlen(addr_in);
m_family= INET6;
break;
}
}
} else { /* IPv4 address or hostname */
start= addr_in;
if (colon != NULL) { /* Port */
end= colon;
if (parse_port(colon + 1))
return; /* Error: invalid port */
} else {
end= addr_in + strlen(addr_in);
}
}
size_t len= end - start;
/* Safety */
if (len >= sizeof(m_address))
{
// The supplied address is too large to fit into the internal buffer.
m_valid= false;
return;
}
memcpy(m_address, start, len);
m_address[len]= '\0';
m_address_len= ++ len;
m_valid= true;
return;
}
int colon_count(const char *addr) {
int count= 0, i= 0;
while(addr[i] != '\0')
{
if (addr[i] == ':') ++count;
++ i;
}
return count;
}
bool parse_port(const char *port) {
m_port= strtol(port, NULL, 10);
if (errno == EINVAL || errno == ERANGE)
{
m_port= 0; /* Error: invalid port */
m_valid= false;
return true;
}
return false;
}
};
class Config_state class Config_state
{ {
public: public:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment