MXS-1315 Add support for multiple source addresses

Source parameter in NamedServerFilter can now contain a list of source
addresses seperated by comma. This parameter is parsed into a list of ip
addresses and checked against incoming client connections.
This commit is contained in:
Marko
2018-08-18 21:39:34 +03:00
parent 421ecdd86b
commit f18b6bc28c
2 changed files with 112 additions and 86 deletions

View File

@ -45,14 +45,12 @@
#include <maxscale/server.h> #include <maxscale/server.h>
#include <maxscale/utils.h> #include <maxscale/utils.h>
using std::string;
static void generate_param_names(int pairs); static void generate_param_names(int pairs);
/* These arrays contain the allowed indexed config parameter names. match01, /* These arrays contain the allowed indexed config parameter names. match01,
* target01, match02, target02 ... */ * target01, match02, target02 ... */
static StringArray param_names_match_indexed; static StringVector param_names_match_indexed;
static StringArray param_names_target_indexed; static StringVector param_names_target_indexed;
static const MXS_ENUM_VALUE option_values[] = static const MXS_ENUM_VALUE option_values[] =
{ {
@ -66,10 +64,10 @@ static const char MATCH_STR[] = "match";
static const char SERVER_STR[] = "server"; static const char SERVER_STR[] = "server";
static const char TARGET_STR[] = "target"; static const char TARGET_STR[] = "target";
RegexHintFilter::RegexHintFilter(string user, SourceHost* source, RegexHintFilter::RegexHintFilter(const std::string& user, const SourceHostVector& source,
const MappingArray& mapping, int ovector_size) const MappingVector& mapping, int ovector_size)
: m_user(user), : m_user(user),
m_source(source), m_sources(source),
m_mapping(mapping), m_mapping(mapping),
m_ovector_size(ovector_size), m_ovector_size(ovector_size),
m_total_diverted(0), m_total_diverted(0),
@ -78,7 +76,6 @@ RegexHintFilter::RegexHintFilter(string user, SourceHost* source,
RegexHintFilter::~RegexHintFilter() RegexHintFilter::~RegexHintFilter()
{ {
delete m_source;
for (unsigned int i = 0; i < m_mapping.size(); i++) for (unsigned int i = 0; i < m_mapping.size(); i++)
{ {
pcre2_code_free(m_mapping.at(i).m_regex); pcre2_code_free(m_mapping.at(i).m_regex);
@ -158,8 +155,7 @@ RegexHintFSession* RegexHintFilter::newSession(MXS_SESSION* session)
bool session_active = true; bool session_active = true;
/* Check client IP against 'source' host option */ /* Check client IP against 'source' host option */
if (m_source && m_source->m_address.length() && if (m_sources.size() > 0 && (remote = session_get_remote(session)) != NULL)
(remote = session_get_remote(session)) != NULL)
{ {
session_active = session_active =
check_source_host(remote, &(session->client_dcb->ip)); check_source_host(remote, &(session->client_dcb->ip));
@ -235,13 +231,12 @@ RegexHintFilter*
RegexHintFilter::create(const char* name, MXS_CONFIG_PARAMETER* params) RegexHintFilter::create(const char* name, MXS_CONFIG_PARAMETER* params)
{ {
bool error = false; bool error = false;
SourceHost* source_host = NULL; SourceHostVector source_host;
const char* source = config_get_string(params, "source"); const char* source = config_get_string(params, "source");
if (*source) if (*source)
{ {
source_host = set_source_address(source); if (!set_source_addresses(source, source_host))
if (!source_host)
{ {
MXS_ERROR("Failure setting 'source' from %s", source); MXS_ERROR("Failure setting 'source' from %s", source);
error = true; error = true;
@ -250,8 +245,8 @@ RegexHintFilter::create(const char* name, MXS_CONFIG_PARAMETER* params)
int pcre_ops = config_get_enum(params, "options", option_values); int pcre_ops = config_get_enum(params, "options", option_values);
string match_val_legacy(config_get_string(params, MATCH_STR)); std::string match_val_legacy(config_get_string(params, MATCH_STR));
string server_val_legacy(config_get_string(params, SERVER_STR)); std::string server_val_legacy(config_get_string(params, SERVER_STR));
const bool legacy_mode = (match_val_legacy.length() || server_val_legacy.length()); const bool legacy_mode = (match_val_legacy.length() || server_val_legacy.length());
if (legacy_mode && (!match_val_legacy.length() || !server_val_legacy.length())) if (legacy_mode && (!match_val_legacy.length() || !server_val_legacy.length()))
@ -262,7 +257,7 @@ RegexHintFilter::create(const char* name, MXS_CONFIG_PARAMETER* params)
error = true; error = true;
} }
MappingArray mapping; MappingVector mapping;
uint32_t max_capcount; uint32_t max_capcount;
/* Try to form the mapping with indexed parameter names */ /* Try to form the mapping with indexed parameter names */
form_regex_server_mapping(params, pcre_ops, &mapping, &max_capcount); form_regex_server_mapping(params, pcre_ops, &mapping, &max_capcount);
@ -292,13 +287,12 @@ RegexHintFilter::create(const char* name, MXS_CONFIG_PARAMETER* params)
if (error) if (error)
{ {
delete source_host;
return NULL; return NULL;
} }
else else
{ {
RegexHintFilter* instance = NULL; RegexHintFilter* instance = NULL;
string user(config_get_string(params, "user")); std::string user(config_get_string(params, "user"));
MXS_EXCEPTION_GUARD(instance = MXS_EXCEPTION_GUARD(instance =
new RegexHintFilter(user, source_host, mapping, max_capcount + 1)); new RegexHintFilter(user, source_host, mapping, max_capcount + 1));
return instance; return instance;
@ -369,12 +363,13 @@ void RegexHintFilter::diagnostics(DCB* dcb)
dcb_printf(dcb, "\t\tTotal no. of queries not diverted by filter (approx.): %d\n", dcb_printf(dcb, "\t\tTotal no. of queries not diverted by filter (approx.): %d\n",
m_total_undiverted); m_total_undiverted);
if (m_source) for (unsigned int i = 0; i < m_sources.size(); i++)
{ {
dcb_printf(dcb, dcb_printf(dcb,
"\t\tReplacement limited to connections from %s\n", "\t\tReplacement limited to connections from %s\n",
m_source->m_address.c_str()); m_sources[i].m_address.c_str());
} }
if (m_user.length()) if (m_user.length())
{ {
dcb_printf(dcb, dcb_printf(dcb,
@ -401,26 +396,32 @@ json_t* RegexHintFilter::diagnostics_json() const
{ {
json_t* arr = json_array(); json_t* arr = json_array();
for (MappingArray::const_iterator it = m_mapping.begin(); it != m_mapping.end(); it++) for (const auto& map : m_mapping)
{ {
json_t* obj = json_object(); json_t* obj = json_object();
json_t* targets = json_array(); json_t* targets = json_array();
for (StringArray::const_iterator it2 = it->m_targets.begin(); it2 != it->m_targets.end(); it2++) for (const auto& target : map.m_targets)
{ {
json_array_append_new(targets, json_string(it2->c_str())); json_array_append_new(targets, json_string(target.c_str()));
} }
json_object_set_new(obj, "match", json_string(it->m_match.c_str())); json_object_set_new(obj, "match", json_string(map.m_match.c_str()));
json_object_set_new(obj, "targets", targets); json_object_set_new(obj, "targets", targets);
} }
json_object_set_new(rval, "mappings", arr); json_object_set_new(rval, "mappings", arr);
} }
if (m_source) if (!m_sources.empty())
{ {
json_object_set_new(rval, "source", json_string(m_source->m_address.c_str())); json_t* arr = json_array();
for (const auto& source : m_sources)
{
json_array_append_new(arr, json_string(source.m_address.c_str()));
}
json_object_set_new(rval, "sources", arr);
} }
if (m_user.length()) if (m_user.length())
@ -438,7 +439,7 @@ json_t* RegexHintFilter::diagnostics_json() const
* @param server_names The list of servers as read from the config file * @param server_names The list of servers as read from the config file
* @return How many were found * @return How many were found
*/ */
int RegexToServers::add_servers(string server_names, bool legacy_mode) int RegexToServers::add_servers(const std::string& server_names, bool legacy_mode)
{ {
if (legacy_mode) if (legacy_mode)
{ {
@ -523,8 +524,8 @@ int RegexToServers::add_servers(string server_names, bool legacy_mode)
} }
bool RegexHintFilter::regex_compile_and_add(int pcre_ops, bool legacy_mode, bool RegexHintFilter::regex_compile_and_add(int pcre_ops, bool legacy_mode,
const string& match, const string& servers, const std::string& match, const std::string& servers,
MappingArray* mapping, uint32_t* max_capcount) MappingVector* mapping, uint32_t* max_capcount)
{ {
bool success = true; bool success = true;
int errorcode = -1; int errorcode = -1;
@ -588,7 +589,7 @@ bool RegexHintFilter::regex_compile_and_add(int pcre_ops, bool legacy_mode,
* @param max_capcount_out The maximum detected pcre2 capture count is written here. * @param max_capcount_out The maximum detected pcre2 capture count is written here.
*/ */
void RegexHintFilter::form_regex_server_mapping(MXS_CONFIG_PARAMETER* params, int pcre_ops, void RegexHintFilter::form_regex_server_mapping(MXS_CONFIG_PARAMETER* params, int pcre_ops,
MappingArray* mapping, uint32_t* max_capcount_out) MappingVector* mapping, uint32_t* max_capcount_out)
{ {
mxb_assert(param_names_match_indexed.size() == param_names_target_indexed.size()); mxb_assert(param_names_match_indexed.size() == param_names_target_indexed.size());
bool error = false; bool error = false;
@ -601,8 +602,8 @@ void RegexHintFilter::form_regex_server_mapping(MXS_CONFIG_PARAMETER* params, in
{ {
const char* param_name_match = param_names_match_indexed[i].c_str(); const char* param_name_match = param_names_match_indexed[i].c_str();
const char* param_name_target = param_names_target_indexed[i].c_str(); const char* param_name_target = param_names_target_indexed[i].c_str();
string match(config_get_string(params, param_name_match)); std::string match(config_get_string(params, param_name_match));
string target(config_get_string(params, param_name_target)); std::string target(config_get_string(params, param_name_target));
/* Check that both the regex and server config parameters are found */ /* Check that both the regex and server config parameters are found */
if (match.length() < 1 || target.length() < 1) if (match.length() < 1 || target.length() < 1)
@ -651,41 +652,46 @@ void RegexHintFilter::form_regex_server_mapping(MXS_CONFIG_PARAMETER* params, in
int RegexHintFilter::check_source_host(const char* remote, const struct sockaddr_storage *ip) int RegexHintFilter::check_source_host(const char* remote, const struct sockaddr_storage *ip)
{ {
int ret = 0; int ret = 0;
struct sockaddr_in check_ipv4; for (const auto& source : m_sources)
memcpy(&check_ipv4, ip, sizeof(check_ipv4));
switch (m_source->m_netmask)
{ {
case 32: struct sockaddr_in check_ipv4;
ret = (m_source->m_address == remote) ? 1 : 0;
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;
}
ret = (m_source->m_netmask < 32) ? memcpy(&check_ipv4, ip, sizeof(check_ipv4));
(check_ipv4.sin_addr.s_addr == m_source->m_ipv4.sin_addr.s_addr) :
ret;
if (ret) switch (source.m_netmask)
{ {
MXS_INFO("Client IP %s matches host source %s%s", case 32:
remote, ret = (source.m_address == remote) ? 1 : 0;
m_source->m_netmask < 32 ? "with wildcards " : "", break;
m_source->m_address.c_str()); 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)
{
ret = (check_ipv4.sin_addr.s_addr == source.m_ipv4.sin_addr.s_addr);
}
if (ret)
{
MXS_INFO("Client IP %s matches host source %s%s",
remote,
source.m_netmask < 32 ? "with wildcards " : "",
source.m_address.c_str());
return ret;
}
} }
return ret; return ret;
@ -749,7 +755,7 @@ bool RegexHintFilter::validate_ip_address(const char* host)
* @param input_host The config source parameter * @param input_host The config source parameter
* @return The filled struct with netmask, or null on error * @return The filled struct with netmask, or null on error
*/ */
SourceHost* RegexHintFilter::set_source_address(const char* input_host) bool RegexHintFilter::add_source_address(const char* input_host, SourceHostVector& source_hosts)
{ {
mxb_assert(input_host); mxb_assert(input_host);
@ -765,14 +771,15 @@ SourceHost* RegexHintFilter::set_source_address(const char* input_host)
return NULL; return NULL;
} }
string address(input_host); std::string address(input_host);
struct sockaddr_in ipv4 = {}; struct sockaddr_in ipv4 = {};
int netmask = 32; int netmask = 32;
/* If no wildcards, leave netmask to 32 and return */ /* If no wildcards, leave netmask to 32 and return */
if (!strchr(input_host, '%')) if (!strchr(input_host, '%'))
{ {
return new SourceHost(address, ipv4, netmask); source_hosts.emplace_back(address, ipv4, netmask);
return true;
} }
char format_host[strlen(input_host) + 1]; char format_host[strlen(input_host) + 1];
@ -827,10 +834,28 @@ SourceHost* RegexHintFilter::set_source_address(const char* input_host)
{ {
MXS_WARNING("Found invalid IP address for parameter 'source=%s': %s", MXS_WARNING("Found invalid IP address for parameter 'source=%s': %s",
input_host, gai_strerror(rc)); input_host, gai_strerror(rc));
return NULL; return false;
} }
source_hosts.emplace_back(address, ipv4, netmask);
return true;
}
return new SourceHost(address, ipv4, netmask); bool RegexHintFilter::set_source_addresses(const std::string& input_host_names, SourceHostVector& source_hosts)
{
bool rval = true;
std::string host_names(input_host_names);
for (auto host : mxs::strtok(host_names, ","))
{
char* trimmed_host = trim((char*)host.c_str());
if (!add_source_address(trimmed_host, source_hosts))
{
rval = false;
}
}
return rval;
} }
/** /**

View File

@ -29,9 +29,9 @@ class RegexHintFSession;
struct RegexToServers; struct RegexToServers;
struct SourceHost; struct SourceHost;
using std::string; using StringVector = std::vector<std::string>;
typedef std::vector<string> StringArray; using MappingVector = std::vector<RegexToServers>;
typedef std::vector<RegexToServers> MappingArray; using SourceHostVector = std::vector<SourceHost>;
/** /**
* Filter instance definition * Filter instance definition
@ -39,9 +39,9 @@ typedef std::vector<RegexToServers> MappingArray;
class RegexHintFilter : public maxscale::Filter<RegexHintFilter, RegexHintFSession> class RegexHintFilter : public maxscale::Filter<RegexHintFilter, RegexHintFSession>
{ {
private: private:
const string m_user; /* User name to restrict matches with */ const std::string m_user; /* User name to restrict matches with */
SourceHost* m_source; /* Source address to restrict matches */ SourceHostVector m_sources; /* Source addresses to restrict matches */
MappingArray m_mapping; /* Regular expression to serverlist mapping */ MappingVector m_mapping; /* Regular expression to serverlist mapping */
const int m_ovector_size; /* Given to pcre2_match_data_create() */ const int m_ovector_size; /* Given to pcre2_match_data_create() */
int check_source_host(const char *remote, const struct sockaddr_storage *ip); int check_source_host(const char *remote, const struct sockaddr_storage *ip);
@ -51,7 +51,7 @@ public:
volatile unsigned int m_total_diverted; volatile unsigned int m_total_diverted;
volatile unsigned int m_total_undiverted; volatile unsigned int m_total_undiverted;
RegexHintFilter(string user, SourceHost* source, const MappingArray& map, RegexHintFilter(const std::string& user, const SourceHostVector& source, const MappingVector& map,
int ovector_size); int ovector_size);
~RegexHintFilter(); ~RegexHintFilter();
static RegexHintFilter* create(const char* zName, MXS_CONFIG_PARAMETER* ppParams); static RegexHintFilter* create(const char* zName, MXS_CONFIG_PARAMETER* ppParams);
@ -62,11 +62,12 @@ public:
const RegexToServers* find_servers(char* sql, int sql_len, pcre2_match_data* mdata); const RegexToServers* find_servers(char* sql, int sql_len, pcre2_match_data* mdata);
static void form_regex_server_mapping(MXS_CONFIG_PARAMETER* params, int pcre_ops, static void form_regex_server_mapping(MXS_CONFIG_PARAMETER* params, int pcre_ops,
MappingArray* 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 string& match, static bool regex_compile_and_add(int pcre_ops, bool legacy_mode, const std::string& match,
const string& servers, MappingArray* 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_ip_address(const char *);
static SourceHost* set_source_address(const char *); static bool add_source_address(const char *, SourceHostVector&);
static bool set_source_addresses(const std::string& input_host_names, SourceHostVector&);
}; };
/** /**
@ -95,29 +96,29 @@ public:
* does not manage the regex memory. That is done by the filter instance. */ * does not manage the regex memory. That is done by the filter instance. */
struct RegexToServers struct RegexToServers
{ {
string m_match; /* Regex in text form */ std::string m_match; /* Regex in text form */
pcre2_code* m_regex; /* Compiled regex */ pcre2_code* m_regex; /* Compiled regex */
StringArray m_targets; /* List of target servers. */ StringVector m_targets; /* List of target servers. */
HINT_TYPE m_htype; /* For special hint types */ HINT_TYPE m_htype; /* For special hint types */
volatile bool m_error_printed; /* Has an error message about volatile bool m_error_printed; /* Has an error message about
* matching this regex been printed yet? */ * matching this regex been printed yet? */
RegexToServers(string match, pcre2_code* regex) RegexToServers(const std::string& match, pcre2_code* regex)
: m_match(match), : m_match(match),
m_regex(regex), m_regex(regex),
m_htype(HINT_ROUTE_TO_NAMED_SERVER), m_htype(HINT_ROUTE_TO_NAMED_SERVER),
m_error_printed(false) m_error_printed(false)
{} {}
int add_servers(string server_names, bool legacy_mode); int add_servers(const std::string& server_names, bool legacy_mode);
}; };
/* Container for address-specific filtering */ /* Container for address-specific filtering */
struct SourceHost struct SourceHost
{ {
string m_address; std::string m_address;
struct sockaddr_in m_ipv4; struct sockaddr_in m_ipv4;
int m_netmask; int m_netmask;
SourceHost(string address, const struct sockaddr_in& ipv4, int netmask) SourceHost(std::string address, const struct sockaddr_in& ipv4, int netmask)
: m_address(address), : m_address(address),
m_ipv4(ipv4), m_ipv4(ipv4),
m_netmask(netmask) m_netmask(netmask)