Add support for ipv6 in NamedServerFilter
NamedServerFilter now uses struct sockaddr_in6 to store the source parameter. If ipv4 addresses are used they are mapped to ipv6 struct, this allows the usage of both ipv4 and ipv6. This commit also includes some small refactoring and removing some code that was not needed anymore.
This commit is contained in:
@ -246,11 +246,7 @@ RegexHintFilter::create(const char* name, MXS_CONFIG_PARAMETER* params)
|
|||||||
|
|
||||||
if (*source)
|
if (*source)
|
||||||
{
|
{
|
||||||
if (!set_source_addresses(source, source_addresses, source_hostnames))
|
set_source_addresses(source, source_addresses, source_hostnames);
|
||||||
{
|
|
||||||
MXS_ERROR("Failure setting 'source' from %s", source);
|
|
||||||
error = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int pcre_ops = config_get_enum(params, "options", option_values);
|
int pcre_ops = config_get_enum(params, "options", option_values);
|
||||||
@ -675,53 +671,59 @@ bool RegexHintFilter::check_source_host(const char* remote, const struct sockadd
|
|||||||
|
|
||||||
for (const auto& source : m_sources)
|
for (const auto& source : m_sources)
|
||||||
{
|
{
|
||||||
|
rval = true;
|
||||||
|
|
||||||
if (addr.ss_family == AF_INET6)
|
if (addr.ss_family == AF_INET6)
|
||||||
{
|
{
|
||||||
struct sockaddr_in6* addr6 = (struct sockaddr_in6*)&addr;
|
struct sockaddr_in6* addr6 = (struct sockaddr_in6*)&addr;
|
||||||
|
/* Check only bytes before netmask */
|
||||||
if (IN6_IS_ADDR_V4MAPPED(&addr6->sin6_addr))
|
for (int i = 0; i < source.m_netmask / 8; ++i)
|
||||||
{
|
{
|
||||||
mapped_ipv6_to_ipv4(&addr);
|
if (addr6->sin6_addr.__in6_u.__u6_addr8[i] != source.m_ipv6.sin6_addr.__in6_u.__u6_addr8[i])
|
||||||
}
|
{
|
||||||
else
|
rval = false;
|
||||||
{
|
break;
|
||||||
// TODO handle ipv6 address
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
struct sockaddr_in *check_ipv4 = (struct sockaddr_in*)&addr;
|
else if (addr.ss_family == AF_INET)
|
||||||
|
|
||||||
switch (source.m_netmask)
|
|
||||||
{
|
{
|
||||||
case 32:
|
struct sockaddr_in *check_ipv4 = (struct sockaddr_in*)&addr;
|
||||||
rval = (source.m_address == remote);
|
|
||||||
break;
|
|
||||||
case 24:
|
|
||||||
/* Class C check */
|
|
||||||
check_ipv4->sin_addr.s_addr &= 0x00FFFFFF;
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
/* Class B check */
|
|
||||||
check_ipv4->sin_addr.s_addr &= 0x0000FFFF;
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
/* Class A check */
|
|
||||||
check_ipv4->sin_addr.s_addr &= 0x000000FF;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (source.m_netmask < 32)
|
switch (source.m_netmask)
|
||||||
{
|
{
|
||||||
rval = (check_ipv4->sin_addr.s_addr == source.m_ipv4.sin_addr.s_addr);
|
case 128:
|
||||||
|
break;
|
||||||
|
case 120:
|
||||||
|
/* Class C check */
|
||||||
|
check_ipv4->sin_addr.s_addr &= 0x00FFFFFF;
|
||||||
|
break;
|
||||||
|
case 112:
|
||||||
|
/* Class B check */
|
||||||
|
check_ipv4->sin_addr.s_addr &= 0x0000FFFF;
|
||||||
|
break;
|
||||||
|
case 104:
|
||||||
|
/* Class A check */
|
||||||
|
check_ipv4->sin_addr.s_addr &= 0x000000FF;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If source is mapped ipv4 address the actual ipv4 address is stored
|
||||||
|
* in the last 4 bytes of ipv6 address. So lets compare that to the
|
||||||
|
* client ipv4 address. */
|
||||||
|
if (source.m_ipv6.sin6_addr.__in6_u.__u6_addr32[3] != check_ipv4->sin_addr.s_addr)
|
||||||
|
{
|
||||||
|
rval = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rval)
|
if (rval)
|
||||||
{
|
{
|
||||||
MXS_INFO("Client IP %s matches host source %s%s",
|
MXS_INFO("Client IP %s matches host source %s%s",
|
||||||
remote,
|
remote,
|
||||||
source.m_netmask < 32 ? "with wildcards " : "",
|
source.m_netmask < 128 ? "with wildcards " : "",
|
||||||
source.m_address.c_str());
|
source.m_address.c_str());
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
@ -736,22 +738,9 @@ bool RegexHintFilter::check_source_hostnames(const char* remote, const struct so
|
|||||||
memcpy(&addr, ip, sizeof(addr));
|
memcpy(&addr, ip, sizeof(addr));
|
||||||
char hbuf[NI_MAXHOST];
|
char hbuf[NI_MAXHOST];
|
||||||
|
|
||||||
if (addr.ss_family == AF_INET6)
|
int rc = getnameinfo((struct sockaddr *)&addr, sizeof(addr), hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD);
|
||||||
{
|
|
||||||
struct sockaddr_in6* addr6 = (struct sockaddr_in6*)&addr;
|
|
||||||
|
|
||||||
if (IN6_IS_ADDR_V4MAPPED(&addr6->sin6_addr))
|
if (rc != 0)
|
||||||
{
|
|
||||||
mapped_ipv6_to_ipv4(&addr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// TODO handle ipv6 address
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (int rc = (getnameinfo((struct sockaddr *)&addr, sizeof(sockaddr), hbuf, sizeof(hbuf), 0, 0, NI_NAMEREQD)) != 0)
|
|
||||||
{
|
{
|
||||||
MXS_INFO("Failed to resolve hostname due to %s", gai_strerror(rc));
|
MXS_INFO("Failed to resolve hostname due to %s", gai_strerror(rc));
|
||||||
return false;
|
return false;
|
||||||
@ -770,28 +759,14 @@ bool RegexHintFilter::check_source_hostnames(const char* remote, const struct so
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegexHintFilter::mapped_ipv6_to_ipv4(struct sockaddr_storage* addr)
|
|
||||||
{
|
|
||||||
struct sockaddr_in6* addr6 = (struct sockaddr_in6*)addr;
|
|
||||||
struct sockaddr_in addr4;
|
|
||||||
memset(&addr4, 0, sizeof(addr4));
|
|
||||||
addr4.sin_family = AF_INET;
|
|
||||||
addr4.sin_port = addr6->sin6_port;
|
|
||||||
|
|
||||||
/* When mapped to ipv6, ipv4 addresses are prepended with prefix of length 12.
|
|
||||||
* Lets ignore the prefix and only copy the actual ipv4 address. */
|
|
||||||
memcpy(&addr4.sin_addr.s_addr, addr6->sin6_addr.s6_addr + 12, sizeof(addr4.sin_addr.s_addr));
|
|
||||||
memcpy(addr, &addr4, sizeof(addr4));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate IP address string againt three dots
|
* Validate IP address string against three dots
|
||||||
* and last char not being a dot.
|
* and last char not being a dot.
|
||||||
*
|
*
|
||||||
* Match any, '%' or '%.%.%.%', is not allowed
|
* Match any, '%' or '%.%.%.%', is not allowed
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
bool RegexHintFilter::validate_ip_address(const char* host)
|
bool RegexHintFilter::validate_ipv4_address(const char* host)
|
||||||
{
|
{
|
||||||
int n_dots = 0;
|
int n_dots = 0;
|
||||||
|
|
||||||
@ -845,112 +820,57 @@ bool RegexHintFilter::validate_ip_address(const char* host)
|
|||||||
*/
|
*/
|
||||||
bool RegexHintFilter::add_source_address(const char* input_host, SourceHostVector& source_hosts)
|
bool RegexHintFilter::add_source_address(const char* input_host, SourceHostVector& source_hosts)
|
||||||
{
|
{
|
||||||
mxb_assert(input_host);
|
|
||||||
|
|
||||||
if (!input_host)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!validate_ip_address(input_host))
|
|
||||||
{
|
|
||||||
MXS_WARNING("The given 'source' parameter '%s' is not a valid IPv4 address.",
|
|
||||||
input_host);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string address(input_host);
|
std::string address(input_host);
|
||||||
struct sockaddr_in ipv4 = {};
|
struct sockaddr_in6 ipv6 = {};
|
||||||
int netmask = 32;
|
int netmask = 128;
|
||||||
|
std::string format_host = address;
|
||||||
/* If no wildcards, leave netmask to 32 and return */
|
/* If no wildcards, leave netmask to 128 and return */
|
||||||
if (!strchr(input_host, '%'))
|
if (strchr(input_host, '%') && validate_ipv4_address(input_host))
|
||||||
{
|
{
|
||||||
source_hosts.emplace_back(address, ipv4, netmask);
|
size_t pos = 0;
|
||||||
return true;
|
while((pos = format_host.find('%', pos)) != std::string::npos)
|
||||||
}
|
|
||||||
|
|
||||||
char format_host[strlen(input_host) + 1];
|
|
||||||
char* p = (char*)input_host;
|
|
||||||
char* out = format_host;
|
|
||||||
int bytes = 0;
|
|
||||||
|
|
||||||
while (*p && bytes <= 3)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (*p == '.')
|
|
||||||
{
|
{
|
||||||
bytes++;
|
format_host.replace(pos, 1, "0");
|
||||||
}
|
pos++;
|
||||||
|
|
||||||
if (*p == '%')
|
|
||||||
{
|
|
||||||
*out = bytes == 3 ? '1' : '0';
|
|
||||||
netmask -= 8;
|
netmask -= 8;
|
||||||
|
|
||||||
out++;
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*out++ = *p++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*out = '\0';
|
|
||||||
|
|
||||||
struct addrinfo *ai = NULL, hint = {};
|
struct addrinfo *ai = NULL, hint = {};
|
||||||
hint.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED;
|
hint.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST | AI_V4MAPPED;
|
||||||
int rc = getaddrinfo(format_host, NULL, &hint, &ai);
|
hint.ai_family = AF_INET6;
|
||||||
|
int rc = getaddrinfo(format_host.c_str(), NULL, &hint, &ai);
|
||||||
|
|
||||||
/* fill IPv4 data struct */
|
/* fill IPv6 data struct */
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
{
|
{
|
||||||
mxb_assert(ai->ai_family == AF_INET);
|
memcpy(&ipv6, ai->ai_addr, ai->ai_addrlen);
|
||||||
memcpy(&ipv4, ai->ai_addr, ai->ai_addrlen);
|
|
||||||
|
|
||||||
/* if netmask < 32 there are % wildcards */
|
|
||||||
if (netmask < 32)
|
|
||||||
{
|
|
||||||
/* let's zero the last IP byte: a.b.c.0 we may have set above to 1*/
|
|
||||||
ipv4.sin_addr.s_addr &= 0x00FFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
MXS_INFO("Input %s is valid with netmask %d", address.c_str(), netmask);
|
MXS_INFO("Input %s is valid with netmask %d", address.c_str(), netmask);
|
||||||
freeaddrinfo(ai);
|
freeaddrinfo(ai);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MXS_WARNING("Found invalid IP address for parameter 'source=%s': %s",
|
|
||||||
input_host, gai_strerror(rc));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
source_hosts.emplace_back(address, ipv4, netmask);
|
source_hosts.emplace_back(address, ipv6, netmask);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RegexHintFilter::set_source_addresses(const std::string& input_host_names, SourceHostVector& source_hosts, StringVector& hostnames)
|
void RegexHintFilter::set_source_addresses(const std::string& input_host_names, SourceHostVector& source_hosts, StringVector& hostnames)
|
||||||
{
|
{
|
||||||
|
|
||||||
bool rval = true;
|
|
||||||
std::string host_names(input_host_names);
|
std::string host_names(input_host_names);
|
||||||
|
|
||||||
for (auto host : mxs::strtok(host_names, ","))
|
for (auto host : mxs::strtok(host_names, ","))
|
||||||
{
|
{
|
||||||
char* trimmed_host = trim((char*)host.c_str());
|
char* trimmed_host = trim((char*)host.c_str());
|
||||||
|
|
||||||
if (!validate_ip_address(trimmed_host))
|
if (!add_source_address(trimmed_host, source_hosts))
|
||||||
{
|
{
|
||||||
MXS_INFO("The given 'source' parameter '%s' is not a valid IPv4 address. "
|
MXS_INFO("The given 'source' parameter '%s' is not a valid IP address. "
|
||||||
"adding it as hostname.", trimmed_host);
|
"adding it as hostname.", trimmed_host);
|
||||||
hostnames.emplace_back(trimmed_host);
|
hostnames.emplace_back(trimmed_host);
|
||||||
}
|
}
|
||||||
else if (!add_source_address(trimmed_host, source_hosts))
|
|
||||||
{
|
|
||||||
rval = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return rval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -47,8 +47,6 @@ private:
|
|||||||
|
|
||||||
bool check_source_host(const char *remote, const struct sockaddr_storage *ip);
|
bool check_source_host(const char *remote, const struct sockaddr_storage *ip);
|
||||||
bool check_source_hostnames(const char *remote, const struct sockaddr_storage *ip);
|
bool check_source_hostnames(const char *remote, const struct sockaddr_storage *ip);
|
||||||
/* Change ipv6 mapped ipv4 address to actual ipv4 address*/
|
|
||||||
void mapped_ipv6_to_ipv4(struct sockaddr_storage* ip);
|
|
||||||
public:
|
public:
|
||||||
/* Total statements diverted statistics. Unreliable due to lockless yet
|
/* Total statements diverted statistics. Unreliable due to lockless yet
|
||||||
* shared access. */
|
* shared access. */
|
||||||
@ -69,9 +67,9 @@ public:
|
|||||||
MappingVector* mapping, uint32_t* max_capcount_out);
|
MappingVector* mapping, uint32_t* max_capcount_out);
|
||||||
static bool regex_compile_and_add(int pcre_ops, bool legacy_mode, const std::string& match,
|
static bool regex_compile_and_add(int pcre_ops, bool legacy_mode, const std::string& match,
|
||||||
const std::string& servers, MappingVector* mapping, uint32_t* max_capcount);
|
const std::string& servers, MappingVector* mapping, uint32_t* max_capcount);
|
||||||
static bool validate_ip_address(const char *);
|
static bool validate_ipv4_address(const char *);
|
||||||
static bool add_source_address(const char *, SourceHostVector&);
|
static bool add_source_address(const char *, SourceHostVector&);
|
||||||
static bool set_source_addresses(const std::string& input_host_names, SourceHostVector&, StringVector&);
|
static void set_source_addresses(const std::string& input_host_names, SourceHostVector&, StringVector&);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -120,11 +118,11 @@ struct RegexToServers
|
|||||||
struct SourceHost
|
struct SourceHost
|
||||||
{
|
{
|
||||||
std::string m_address;
|
std::string m_address;
|
||||||
struct sockaddr_in m_ipv4;
|
struct sockaddr_in6 m_ipv6;
|
||||||
int m_netmask;
|
int m_netmask;
|
||||||
SourceHost(std::string address, const struct sockaddr_in& ipv4, int netmask)
|
SourceHost(const std::string& address, const struct sockaddr_in6& ipv6, int netmask)
|
||||||
: m_address(address),
|
: m_address(address),
|
||||||
m_ipv4(ipv4),
|
m_ipv6(ipv6),
|
||||||
m_netmask(netmask)
|
m_netmask(netmask)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user