From 388e952c5f096f4fa3eb7b2e511ce47ee06e1834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Wed, 20 Mar 2019 12:34:59 +0200 Subject: [PATCH] MXS-1951: Fix port conflict detection With the addition of SO_REUSEPORT support, it is no longer possible to rely on the network stack to prevent multiple listeners from listening on the same port. Without explicitly checking for the ports it would be possible for two listeners from two different services to listen on the same port in which case the service would be almost randomly chosen. --- include/maxscale/listener.hh | 13 +++++++++++++ server/core/config.cc | 2 +- server/core/config_runtime.cc | 2 +- server/core/listener.cc | 28 ++++++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/include/maxscale/listener.hh b/include/maxscale/listener.hh index 9cfa62f83..fba50b719 100644 --- a/include/maxscale/listener.hh +++ b/include/maxscale/listener.hh @@ -319,6 +319,19 @@ SListener listener_find(const std::string& name); */ std::vector listener_find_by_service(const SERVICE* service); +/** + * Find listener by configuration + * + * @param socket Optional path to a socket file + * @param address Address where the listener listens + * @param port The port on which the listener listens + * + * @return The matching listener if one was found + */ +SListener listener_find_by_config(const std::string& socket, + const std::string& address, + unsigned short port); + int listener_set_ssl_version(SSL_LISTENER* ssl_listener, const char* version); void listener_set_certificates(SSL_LISTENER* ssl_listener, const std::string& cert, const std::string& key, const std::string& ca_cert); diff --git a/server/core/config.cc b/server/core/config.cc index 0620d0dd2..61a350af9 100644 --- a/server/core/config.cc +++ b/server/core/config.cc @@ -4087,7 +4087,7 @@ int create_new_listener(CONFIG_CONTEXT* obj) address = ""; } - if (auto l = service_find_listener(service, socket, address, port)) + if (auto l = listener_find_by_config(socket, address, port)) { string socket_type = socket_defined ? "socket" : "port"; string socket_definition = socket_defined ? socket : obj->m_parameters.get_string(CN_PORT); diff --git a/server/core/config_runtime.cc b/server/core/config_runtime.cc index db1bda249..58b52ccdf 100644 --- a/server/core/config_runtime.cc +++ b/server/core/config_runtime.cc @@ -1154,7 +1154,7 @@ bool runtime_create_listener(Service* service, { config_runtime_error("Listener '%s' already exists", name); } - else if (SListener l = service_find_listener(service, "", addr, u_port)) + else if (SListener l = listener_find_by_config("", addr, u_port)) { config_runtime_error("Listener '%s' already listens on [%s]:%u", l->name(), addr, u_port); } diff --git a/server/core/listener.cc b/server/core/listener.cc index 049b2677d..0572a313a 100644 --- a/server/core/listener.cc +++ b/server/core/listener.cc @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -275,6 +276,33 @@ std::vector listener_find_by_service(const SERVICE* service) return rval; } +static bool is_all_iface(const std::string& a, const std::string& b) +{ + std::unordered_set addresses {"::", "0.0.0.0"}; + return addresses.count(a) || addresses.count(b); +} + +SListener listener_find_by_config(const std::string& socket, + const std::string& address, + unsigned short port) +{ + SListener rval; + std::lock_guard guard(listener_lock); + + for (const auto& listener : all_listeners) + { + if (port == listener->port() + && (listener->address() == address || listener->address() == socket + || is_all_iface(listener->address(), address))) + { + rval = listener; + break; + } + } + + return rval; +} + /** * Set the maximum SSL/TLS version the listener will support * @param ssl_listener Listener data to configure