diff --git a/configs/envoy_service_to_service.template.json b/configs/envoy_service_to_service.template.json index 20133d8a7b69b..0e18d9703edef 100644 --- a/configs/envoy_service_to_service.template.json +++ b/configs/envoy_service_to_service.template.json @@ -245,8 +245,14 @@ "type": "read", "name": "tcp_proxy", "config": { - "cluster": "mongo_{{ key }}", - "stat_prefix": "mongo_{{ key }}" + "stat_prefix": "mongo_{{ key }}", + "route_config": { + "routes": [ + { + "cluster": "mongo_{{ key }}" + } + ] + } } }] }{% if not loop.last %},{% endif -%} diff --git a/docs/configuration/network_filters/tcp_proxy_filter.rst b/docs/configuration/network_filters/tcp_proxy_filter.rst index 39eb2cdc8e719..2966ed9a4c2b0 100644 --- a/docs/configuration/network_filters/tcp_proxy_filter.rst +++ b/docs/configuration/network_filters/tcp_proxy_filter.rst @@ -11,19 +11,114 @@ TCP proxy :ref:`architecture overview `. "type": "read", "name": "tcp_proxy", "config": { - "cluster": "...", - "stat_prefix": "..." + "stat_prefix": "...", + "route_config": "{...}" } } -cluster - *(required, string)* The :ref:`cluster manager ` cluster to connect - to when a new downstream network connection is received. +:ref:`route_config ` + *(required, object)* The route table for the filter. + All filter instances must have a route table, even if it is empty. stat_prefix *(required, string)* The prefix to use when emitting :ref:`statistics `. +.. _config_network_filters_tcp_proxy_route_config: + +Route Configuration +------------------- + +.. code-block:: json + + { + "routes": [] + } + +:ref:`routes ` + *(required, array)* An array of route entries that make up the route table. + +.. _config_network_filters_tcp_proxy_route: + +Route +----- + +A TCP proxy route consists of a set of optional L4 criteria and the name of a +:ref:`cluster `. If a downstream connection matches +all the specified criteria, the cluster in the route is used for the corresponding upstream +connection. Routes are tried in the order specified until a match is found. If no match is +found, the connection is closed. A route with no criteria is valid and always produces a match. + +.. code-block:: json + + { + "cluster": "...", + "destination_ip_list": [], + "destination_ports": "...", + "source_ip_list": [], + "source_ports": "..." + } + +cluster + *(required, string)* The :ref:`cluster ` to connect + to when a the downstream network connection matches the specified criteria. + +destination_ip_list + *(optional, array)* An optional list of IPv4 subnets in the form "a.b.c.d/xx". + The criteria is satisfied if the destination IP address of the downstream connection is + contained in at least one of the specified subnets. + If the parameter is not specified or the list is empty, the destination IP address is ignored. + The destination IP address of the downstream connection might be different from the addresses + on which the proxy is listening if the connection has been redirected. Example: + + .. code-block:: json + + [ + "192.168.3.0/24", + "50.1.2.3/32", + "10.15.0.0/16" + ] + +destination_ports + *(optional, string)* An optional string containing a comma-separated list of port numbers or + ranges. The criteria is satisfied if the destination port of the downstream connection + is contained in at least one of the specified ranges. + If the parameter is not specified or the list is empty, the destination port is ignored. + The destination port address of the downstream connection might be different from the port + on which the proxy is listening if the connection has been redirected. Example: + + .. code-block:: json + + { + "destination_ports": "1-1024,2048-4096,12345" + } + +source_ip_list + *(optional, array)* An optional list of IPv4 subnets in the form "a.b.c.d/xx". + The criteria is satisfied if the source IP address of the downstream connection is contained + in at least one of the specified subnets. If the parameter is not specified or the list is empty, + the source IP address is ignored. Example: + + .. code-block:: json + + [ + "192.168.3.0/24", + "50.1.2.3/32", + "10.15.0.0/16" + ] + +source_ports + *(optional, string)* An optional string containing a comma-separated list of port numbers or + ranges. The criteria is satisfied if the source port of the downstream connection is contained + in at least one of the specified ranges. If the parameter is not specified or the list is empty, + the source port is ignored. Example: + + .. code-block:: json + + { + "source_ports": "1-1024,2048-4096,12345" + } + .. _config_network_filters_tcp_proxy_stats: Statistics @@ -37,5 +132,8 @@ statistics are rooted at *tcp..* with the following statistics: :header: Name, Type, Description :widths: 1, 1, 2 + downstream_cx_total, Counter, Total number of connections handled by the filter. + downstream_cx_no_route, Counter, Number of connections for which no matching route was found. downstream_cx_tx_bytes_total, Counter, Total bytes written to the downstream connection. downstream_cx_tx_bytes_buffered, Gauge, Total bytes currently buffered to the downstream connection. + diff --git a/include/envoy/network/connection.h b/include/envoy/network/connection.h index 50bdc0b97f6a9..b6c49e0e8ec5c 100644 --- a/include/envoy/network/connection.h +++ b/include/envoy/network/connection.h @@ -111,10 +111,18 @@ class Connection : public Event::DeferredDeletable, public FilterManager { virtual bool readEnabled() PURE; /** - * @return The address of the remote client + * @return The address of the remote client. */ virtual const std::string& remoteAddress() PURE; + /** + * @return the local address of the connection. For client connections, this is the origin + * address. For server connections, this is the local destination address. For server connections + * it can be different from the proxy address if the downstream connection has been redirected or + * the proxy is operating in transparent mode. + */ + virtual const std::string& localAddress() PURE; + /** * Set the buffer stats to update when the connection's read/write buffers change. Note that * for performance reasons these stats are eventually consistent and may not always accurately diff --git a/source/common/filter/auth/client_ssl.cc b/source/common/filter/auth/client_ssl.cc index fab197b72bfda..91c5fc47907ae 100644 --- a/source/common/filter/auth/client_ssl.cc +++ b/source/common/filter/auth/client_ssl.cc @@ -8,6 +8,7 @@ #include "common/http/message_impl.h" #include "common/http/utility.h" #include "common/json/config_schemas.h" +#include "common/network/utility.h" namespace Filter { namespace Auth { @@ -18,7 +19,7 @@ Config::Config(const Json::Object& config, ThreadLocal::Instance& tls, Upstream: Runtime::RandomGenerator& random) : RestApiFetcher(cm, config.getString("auth_api_cluster"), dispatcher, random, std::chrono::milliseconds(config.getInteger("refresh_interval_ms", 60000))), - tls_(tls), tls_slot_(tls.allocateSlot()), ip_white_list_(config), + tls_(tls), tls_slot_(tls.allocateSlot()), ip_white_list_(config, "ip_white_list"), stats_(generateStats(stats_store, config.getString("stat_prefix"))) { config.validateSchema(Json::Schema::CLIENT_SSL_NETWORK_FILTER_SCHEMA); @@ -97,7 +98,8 @@ void Instance::onEvent(uint32_t events) { } ASSERT(read_callbacks_->connection().ssl()); - if (config_->ipWhiteList().contains(read_callbacks_->connection().remoteAddress())) { + if (config_->ipWhiteList().contains( + Network::Utility::hostFromUrl(read_callbacks_->connection().remoteAddress()))) { config_->stats().auth_ip_white_list_.inc(); read_callbacks_->continueReading(); return; diff --git a/source/common/filter/auth/client_ssl.h b/source/common/filter/auth/client_ssl.h index 39f16c7b024a4..d85e6f04e6cc3 100644 --- a/source/common/filter/auth/client_ssl.h +++ b/source/common/filter/auth/client_ssl.h @@ -74,7 +74,7 @@ class Config : public Http::RestApiFetcher { Stats::Store& stats_store, Runtime::RandomGenerator& random); const AllowedPrincipals& allowedPrincipals(); - const Network::IpWhiteList& ipWhiteList() { return ip_white_list_; } + const Network::IpList& ipWhiteList() { return ip_white_list_; } GlobalStats& stats() { return stats_; } private: @@ -92,7 +92,7 @@ class Config : public Http::RestApiFetcher { ThreadLocal::Instance& tls_; uint32_t tls_slot_; - Network::IpWhiteList ip_white_list_; + Network::IpList ip_white_list_; GlobalStats stats_; }; diff --git a/source/common/filter/tcp_proxy.cc b/source/common/filter/tcp_proxy.cc index a0fd94de2a832..90603dd0fa05b 100644 --- a/source/common/filter/tcp_proxy.cc +++ b/source/common/filter/tcp_proxy.cc @@ -8,23 +8,97 @@ #include "envoy/upstream/upstream.h" #include "common/common/assert.h" +#include "common/common/empty_string.h" #include "common/json/config_schemas.h" #include "common/json/json_loader.h" namespace Filter { +TcpProxyConfig::Route::Route(const Json::Object& config) { + cluster_name_ = config.getString("cluster"); + + if (config.hasObject("source_ip_list")) { + source_ips_ = Network::IpList(config.getStringArray("source_ip_list")); + } + + if (config.hasObject("source_ports")) { + const std::string source_ports = config.getString("source_ports"); + if (source_ports.empty()) { + throw EnvoyException("source_ports cannot be empty"); + } + Network::Utility::parsePortRangeList(source_ports, source_port_ranges_); + } + + if (config.hasObject("destination_ip_list")) { + destination_ips_ = Network::IpList(config.getStringArray("destination_ip_list")); + } + + if (config.hasObject("destination_ports")) { + const std::string destination_ports = config.getString("destination_ports"); + if (destination_ports.empty()) { + throw EnvoyException("destination_ports cannot be empty"); + } + Network::Utility::parsePortRangeList(destination_ports, destination_port_ranges_); + } +} + TcpProxyConfig::TcpProxyConfig(const Json::Object& config, Upstream::ClusterManager& cluster_manager, Stats::Store& stats_store) - : cluster_name_(config.getString("cluster")), - stats_(generateStats(config.getString("stat_prefix"), stats_store)) { - + : stats_(generateStats(config.getString("stat_prefix"), stats_store)) { config.validateSchema(Json::Schema::TCP_PROXY_NETWORK_FILTER_SCHEMA); - if (!cluster_manager.get(cluster_name_)) { - throw EnvoyException(fmt::format("tcp proxy: unknown cluster '{}'", cluster_name_)); + for (const Json::ObjectPtr& route_desc : + config.getObject("route_config")->getObjectArray("routes")) { + routes_.emplace_back(Route(*route_desc)); + + if (!cluster_manager.get(route_desc->getString("cluster"))) { + throw EnvoyException(fmt::format("tcp proxy: unknown cluster '{}' in TCP route", + route_desc->getString("cluster"))); + } } } +const std::string& TcpProxyConfig::getRouteFromEntries(Network::Connection& connection) { + for (const TcpProxyConfig::Route& route : routes_) { + if (!route.source_port_ranges_.empty() && + !Network::Utility::portInRangeList( + Network::Utility::portFromUrl(connection.remoteAddress()), route.source_port_ranges_)) { + continue; // no match, try next route + } + + if (!route.source_ips_.empty() && + !route.source_ips_.contains(Network::Utility::hostFromUrl(connection.remoteAddress()))) { + continue; // no match, try next route + } + + // If the route needs to match on destination address and port but they are not available + // (localAddress is empty), we skip it. The connection has a chance to match a different + // route that does not depend on destination address and port. + if ((!route.destination_port_ranges_.empty() || !route.destination_ips_.empty()) && + connection.localAddress().empty()) { + continue; + } + + if (!route.destination_port_ranges_.empty() && + !Network::Utility::portInRangeList(Network::Utility::portFromUrl(connection.localAddress()), + route.destination_port_ranges_)) { + continue; // no match, try next route + } + + if (!route.destination_ips_.empty() && + !route.destination_ips_.contains( + Network::Utility::hostFromUrl(connection.localAddress()))) { + continue; // no match, try next route + } + + // if we made it past all checks, the route matches + return route.cluster_name_; + } + + // no match, no more routes to try + return EMPTY_STRING; +} + TcpProxy::TcpProxy(TcpProxyConfigPtr config, Upstream::ClusterManager& cluster_manager) : config_(config), cluster_manager_(cluster_manager), downstream_callbacks_(*this), upstream_callbacks_(new UpstreamCallbacks(*this)) {} @@ -52,6 +126,7 @@ TcpProxyStats TcpProxyConfig::generateStats(const std::string& name, Stats::Stor void TcpProxy::initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callbacks) { read_callbacks_ = &callbacks; conn_log_info("new tcp proxy session", read_callbacks_->connection()); + config_->stats().downstream_cx_total_.inc(); read_callbacks_->connection().addConnectionCallbacks(downstream_callbacks_); read_callbacks_->connection().setBufferStats({config_->stats().downstream_cx_rx_bytes_total_, config_->stats().downstream_cx_rx_bytes_buffered_, @@ -60,14 +135,25 @@ void TcpProxy::initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callb } Network::FilterStatus TcpProxy::initializeUpstreamConnection() { - Upstream::ClusterInfoPtr cluster = cluster_manager_.get(config_->clusterName()); + const std::string& cluster_name = config_->getRouteFromEntries(read_callbacks_->connection()); + + Upstream::ClusterInfoPtr cluster = cluster_manager_.get(cluster_name); + + if (cluster) { + conn_log_debug("Creating connection to cluster {}", read_callbacks_->connection(), + cluster_name); + } else { + config_->stats().downstream_cx_no_route_.inc(); + read_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); + return Network::FilterStatus::StopIteration; + } + if (!cluster->resourceManager(Upstream::ResourcePriority::Default).connections().canCreate()) { cluster->stats().upstream_cx_overflow_.inc(); read_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); return Network::FilterStatus::StopIteration; } - Upstream::Host::CreateConnectionData conn_info = - cluster_manager_.tcpConnForCluster(config_->clusterName()); + Upstream::Host::CreateConnectionData conn_info = cluster_manager_.tcpConnForCluster(cluster_name); upstream_connection_ = std::move(conn_info.connection_); read_callbacks_->upstreamHost(conn_info.host_description_); diff --git a/source/common/filter/tcp_proxy.h b/source/common/filter/tcp_proxy.h index 8986860769832..6b87be460cb13 100644 --- a/source/common/filter/tcp_proxy.h +++ b/source/common/filter/tcp_proxy.h @@ -9,6 +9,7 @@ #include "common/common/logger.h" #include "common/json/json_loader.h" #include "common/network/filter_impl.h" +#include "common/network/utility.h" namespace Filter { @@ -20,7 +21,9 @@ namespace Filter { COUNTER(downstream_cx_rx_bytes_total) \ GAUGE (downstream_cx_rx_bytes_buffered) \ COUNTER(downstream_cx_tx_bytes_total) \ - GAUGE (downstream_cx_tx_bytes_buffered) + GAUGE (downstream_cx_tx_bytes_buffered) \ + COUNTER(downstream_cx_total) \ + COUNTER(downstream_cx_no_route) // clang-format on /** @@ -38,13 +41,32 @@ class TcpProxyConfig { TcpProxyConfig(const Json::Object& config, Upstream::ClusterManager& cluster_manager, Stats::Store& stats_store); - const std::string& clusterName() { return cluster_name_; } + /** + * Find out which cluster an upstream connection should be opened to based on the + * parameters of a downstream connection. + * @param connection supplies the parameters of the downstream connection for + * which the proxy needs to open the corresponding upstream. + * @return the cluster name to be used for the upstream connection. + * If no route applies, returns the empty string. + */ + const std::string& getRouteFromEntries(Network::Connection& connection); + const TcpProxyStats& stats() { return stats_; } private: + struct Route { + Route(const Json::Object& config); + + Network::IpList source_ips_; + Network::PortRangeList source_port_ranges_; + Network::IpList destination_ips_; + Network::PortRangeList destination_port_ranges_; + std::string cluster_name_; + }; + static TcpProxyStats generateStats(const std::string& name, Stats::Store& store); - std::string cluster_name_; + std::vector routes_; const TcpProxyStats stats_; }; diff --git a/source/common/http/conn_manager_utility.cc b/source/common/http/conn_manager_utility.cc index e95f483bc159c..b9e65769a6365 100644 --- a/source/common/http/conn_manager_utility.cc +++ b/source/common/http/conn_manager_utility.cc @@ -1,5 +1,6 @@ #include "conn_manager_utility.h" +#include "common/common/empty_string.h" #include "common/http/access_log/access_log_formatter.h" #include "common/http/headers.h" #include "common/http/utility.h" @@ -38,10 +39,11 @@ void ConnectionManagerUtility::mutateRequestHeaders(Http::HeaderMap& request_hea // peer. Cases where we don't "use remote address" include trusted double proxy where we expect // our peer to have already properly set XFF, etc. if (config.useRemoteAddress()) { - if (Network::Utility::isLoopbackAddress(connection.remoteAddress().c_str())) { + const std::string remote_address = Network::Utility::hostFromUrl(connection.remoteAddress()); + if (Network::Utility::isLoopbackAddress(remote_address.c_str())) { Utility::appendXff(request_headers, config.localAddress()); } else { - Utility::appendXff(request_headers, connection.remoteAddress()); + Utility::appendXff(request_headers, remote_address); } request_headers.insertForwardedProto().value( connection.ssl() ? Headers::get().SchemeValues.Https : Headers::get().SchemeValues.Http); @@ -94,7 +96,8 @@ void ConnectionManagerUtility::mutateRequestHeaders(Http::HeaderMap& request_hea // If we are an external request, AND we are "using remote address" (see above), we set // x-envoy-external-address since this is our first ingress point into the trusted network. if (edge_request) { - request_headers.insertEnvoyExternalAddress().value(connection.remoteAddress()); + request_headers.insertEnvoyExternalAddress().value( + Network::Utility::hostFromUrl(connection.remoteAddress())); } // Generate x-request-id for all edge requests, or if there is none. diff --git a/source/common/json/config_schemas.cc b/source/common/json/config_schemas.cc index 2a326c496654f..b7425464c3db6 100644 --- a/source/common/json/config_schemas.cc +++ b/source/common/json/config_schemas.cc @@ -198,13 +198,50 @@ const std::string Json::Schema::REDIS_PROXY_NETWORK_FILTER_SCHEMA(R"EOF( const std::string Json::Schema::TCP_PROXY_NETWORK_FILTER_SCHEMA(R"EOF( { - "$schema": "http://json-schema.org/schema#", - "properties":{ - "stat_prefix" : {"type" : "string"}, - "cluster" : {"type" : "string"} - }, - "required": ["stat_prefix", "cluster"], - "additionalProperties": false + "$schema": "http://json-schema.org/schema#", + "properties": { + "stat_prefix": {"type" : "string"}, + "route_config": { + "type": "object", + "properties": { + "routes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "cluster": { + "type": "string" + }, + "source_ip_list" : { + "type": "array", + "items": { + "type": "string" + } + }, + "source_ports": { + "type": "string" + }, + "destination_ip_list" : { + "type": "array", + "items": { + "type": "string" + } + }, + "destination_ports": { + "type": "string" + } + }, + "required": ["cluster"], + "additionalProperties": false + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "required": ["stat_prefix", "route_config"], + "additionalProperties": false } )EOF"); diff --git a/source/common/network/connection_impl.cc b/source/common/network/connection_impl.cc index ee32ff78374e2..b76b0b5ba82ab 100644 --- a/source/common/network/connection_impl.cc +++ b/source/common/network/connection_impl.cc @@ -1,4 +1,5 @@ #include "connection_impl.h" +#include "utility.h" #include "envoy/event/timer.h" #include "envoy/common/exception.h" @@ -33,9 +34,9 @@ void ConnectionImplUtility::updateBufferStats(uint64_t delta, uint64_t new_total std::atomic ConnectionImpl::next_global_id_; ConnectionImpl::ConnectionImpl(Event::DispatcherImpl& dispatcher, int fd, - const std::string& remote_address) - : filter_manager_(*this, *this), remote_address_(remote_address), dispatcher_(dispatcher), - fd_(fd), id_(++next_global_id_) { + const std::string& remote_address, const std::string& local_address) + : filter_manager_(*this, *this), remote_address_(remote_address), local_address_(local_address), + dispatcher_(dispatcher), fd_(fd), id_(++next_global_id_) { // Treat the lack of a valid fd (which in practice only happens if we run out of FDs) as an OOM // condition and just crash. @@ -395,9 +396,10 @@ void ConnectionImpl::updateWriteBufferStats(uint64_t num_written, uint64_t new_s buffer_stats_->write_current_); } +// TODO: see if we can pass something more meaningful than EMPTY_STRING as localAddress ClientConnectionImpl::ClientConnectionImpl(Event::DispatcherImpl& dispatcher, int fd, const std::string& url) - : ConnectionImpl(dispatcher, fd, url) {} + : ConnectionImpl(dispatcher, fd, url, EMPTY_STRING) {} Network::ClientConnectionPtr ClientConnectionImpl::create(Event::DispatcherImpl& dispatcher, const std::string& url) { diff --git a/source/common/network/connection_impl.h b/source/common/network/connection_impl.h index a3f8bc863bddc..d96304a993293 100644 --- a/source/common/network/connection_impl.h +++ b/source/common/network/connection_impl.h @@ -37,7 +37,9 @@ class ConnectionImpl : public virtual Connection, public BufferSource, protected Logger::Loggable { public: - ConnectionImpl(Event::DispatcherImpl& dispatcher, int fd, const std::string& remote_address); + ConnectionImpl(Event::DispatcherImpl& dispatcher, int fd, const std::string& remote_address, + const std::string& local_address); + ~ConnectionImpl(); // Network::FilterManager @@ -56,6 +58,7 @@ class ConnectionImpl : public virtual Connection, void readDisable(bool disable) override; bool readEnabled() override; const std::string& remoteAddress() override { return remote_address_; } + const std::string& localAddress() override { return local_address_; } void setBufferStats(const BufferStats& stats) override; Ssl::Connection* ssl() override { return nullptr; } State state() override; @@ -79,6 +82,7 @@ class ConnectionImpl : public virtual Connection, FilterManagerImpl filter_manager_; const std::string remote_address_; + const std::string local_address_; Buffer::OwnedImpl read_buffer_; Buffer::OwnedImpl write_buffer_; diff --git a/source/common/network/listener_impl.cc b/source/common/network/listener_impl.cc index cc95ca1baabf2..87539f1df07fa 100644 --- a/source/common/network/listener_impl.cc +++ b/source/common/network/listener_impl.cc @@ -14,36 +14,34 @@ namespace Network { -void ListenerImpl::listenCallback(evconnlistener*, evutil_socket_t fd, sockaddr* addr, int, +void ListenerImpl::listenCallback(evconnlistener*, evutil_socket_t fd, sockaddr* remote_addr, int, void* arg) { ListenerImpl* listener = static_cast(arg); - if (listener->use_original_dst_) { - sockaddr_storage orig_dst_addr; - memset(&orig_dst_addr, 0, sizeof(orig_dst_addr)); + sockaddr_storage local_addr; + memset(&local_addr, 0, sizeof(local_addr)); - bool success = Utility::getOriginalDst(fd, &orig_dst_addr); + bool success = Utility::getOriginalDst(fd, &local_addr); - if (success) { - std::string orig_sock_name = std::to_string( - listener->getAddressPort(reinterpret_cast(&orig_dst_addr))); + if (success && listener->use_original_dst_) { + std::string orig_sock_name = + std::to_string(listener->getAddressPort(reinterpret_cast(&local_addr))); - // A listener that has the use_original_dst flag set to true can still receive connections - // that are NOT redirected using iptables. If a connection was not redirected, - // the address and port returned by getOriginalDst() match the listener port. - // In this case the listener handles the connection directly and does not hand it off. - if (listener->socket_.name() != orig_sock_name) { - ListenerImpl* new_listener = - dynamic_cast(listener->connection_handler_.findListener(orig_sock_name)); + // A listener that has the use_original_dst flag set to true can still receive connections + // that are NOT redirected using iptables. If a connection was not redirected, + // the address and port returned by getOriginalDst() match the listener port. + // In this case the listener handles the connection directly and does not hand it off. + if (listener->socket_.name() != orig_sock_name) { + ListenerImpl* new_listener = + dynamic_cast(listener->connection_handler_.findListener(orig_sock_name)); - if (new_listener != nullptr) { - listener = new_listener; - } + if (new_listener != nullptr) { + listener = new_listener; } } } - listener->newConnection(fd, addr); + listener->newConnection(fd, remote_addr, reinterpret_cast(&local_addr)); } ListenerImpl::ListenerImpl(Network::ConnectionHandler& conn_handler, @@ -72,30 +70,37 @@ void ListenerImpl::errorCallback(evconnlistener*, void*) { PANIC(fmt::format("listener accept failure: {}", strerror(errno))); } -void ListenerImpl::newConnection(int fd, sockaddr* addr) { +void ListenerImpl::newConnection(int fd, sockaddr* remote_addr, sockaddr* local_addr) { evutil_make_socket_nonblocking(fd); if (use_proxy_proto_) { proxy_protocol_.newConnection(dispatcher_, fd, *this); } else { - newConnection(fd, getAddressName(addr)); + newConnection( + fd, Network::Utility::urlForTcp(getAddressName(remote_addr), getAddressPort(remote_addr)), + Network::Utility::urlForTcp(getAddressName(local_addr), getAddressPort(local_addr))); } } -void ListenerImpl::newConnection(int fd, const std::string& remote_address) { - ConnectionPtr new_connection(new ConnectionImpl(dispatcher_, fd, remote_address)); +void ListenerImpl::newConnection(int fd, const std::string& remote_address, + const std::string& local_address) { + ConnectionPtr new_connection(new ConnectionImpl(dispatcher_, fd, remote_address, local_address)); cb_.onNewConnection(std::move(new_connection)); } -void SslListenerImpl::newConnection(int fd, sockaddr* addr) { +void SslListenerImpl::newConnection(int fd, sockaddr* remote_addr, sockaddr* local_addr) { if (use_proxy_proto_) { proxy_protocol_.newConnection(dispatcher_, fd, *this); } else { - newConnection(fd, getAddressName(addr)); + newConnection( + fd, Network::Utility::urlForTcp(getAddressName(remote_addr), getAddressPort(remote_addr)), + Network::Utility::urlForTcp(getAddressName(local_addr), getAddressPort(local_addr))); } } -void SslListenerImpl::newConnection(int fd, const std::string& remote_address) { - ConnectionPtr new_connection(new Ssl::ConnectionImpl(dispatcher_, fd, remote_address, ssl_ctx_, +void SslListenerImpl::newConnection(int fd, const std::string& remote_address, + const std::string& local_address) { + ConnectionPtr new_connection(new Ssl::ConnectionImpl(dispatcher_, fd, remote_address, + local_address, ssl_ctx_, Ssl::ConnectionImpl::InitialState::Server)); cb_.onNewConnection(std::move(new_connection)); } diff --git a/source/common/network/listener_impl.h b/source/common/network/listener_impl.h index d82103970b24b..5f809c9193f76 100644 --- a/source/common/network/listener_impl.h +++ b/source/common/network/listener_impl.h @@ -26,15 +26,18 @@ class ListenerImpl : public Listener { * Accept/process a new connection. * @param fd supplies the new connection's fd. * @param remote_address supplies the remote address for the new connection. + * @param local_address supplies the local address for the new connection. */ - virtual void newConnection(int fd, sockaddr* addr); + virtual void newConnection(int fd, sockaddr* remote_address, sockaddr* local_address); /** * Accept/process a new connection with the given remote address. * @param fd supplies the new connection's fd. * @param remote_address supplies the remote address for the new connection. + * @param local_address supplies the local address for the new connection. */ - virtual void newConnection(int fd, const std::string& remote_address); + virtual void newConnection(int fd, const std::string& remote_address, + const std::string& local_address); /** * @return the socket supplied to the listener at construction time @@ -72,8 +75,9 @@ class SslListenerImpl : public ListenerImpl { ssl_ctx_(ssl_ctx) {} // ListenerImpl - void newConnection(int fd, sockaddr* addr) override; - void newConnection(int fd, const std::string& remote_address) override; + void newConnection(int fd, sockaddr* remote_addr, sockaddr* local_addr) override; + void newConnection(int fd, const std::string& remote_address, + const std::string& local_address) override; private: Ssl::Context& ssl_ctx_; diff --git a/source/common/network/proxy_protocol.cc b/source/common/network/proxy_protocol.cc index 094317146c855..58c087b997f00 100644 --- a/source/common/network/proxy_protocol.cc +++ b/source/common/network/proxy_protocol.cc @@ -6,6 +6,9 @@ #include "envoy/event/file_event.h" #include "envoy/stats/stats.h" +#include "common/common/empty_string.h" +#include "common/network/utility.h" + namespace Network { const std::string ProxyProtocol::ActiveConnection::PROXY_TCP4 = "PROXY TCP4 "; @@ -68,7 +71,8 @@ void ProxyProtocol::ActiveConnection::onReadWorker() { removeFromList(parent_.connections_); - listener.newConnection(fd, remote_address); + // TODO: pass in something more meaningful than 0 as remote port and EMPTY_STRING as local_address + listener.newConnection(fd, Network::Utility::urlForTcp(remote_address, 0), EMPTY_STRING); } void ProxyProtocol::ActiveConnection::close() { diff --git a/source/common/network/utility.cc b/source/common/network/utility.cc index 377f4159c9df4..7dc920ee21001 100644 --- a/source/common/network/utility.cc +++ b/source/common/network/utility.cc @@ -11,12 +11,8 @@ namespace Network { -IpWhiteList::IpWhiteList(const Json::Object& config) { - if (!config.hasObject("ip_white_list")) { - return; - } - - for (const std::string& entry : config.getStringArray("ip_white_list")) { +IpList::IpList(const std::vector& subnets) { + for (const std::string& entry : subnets) { std::vector parts = StringUtil::split(entry, '/'); if (parts.size() != 2) { throw EnvoyException( @@ -37,33 +33,32 @@ IpWhiteList::IpWhiteList(const Json::Object& config) { fmt::format("invalid ipv4/mask combo '{}' (mask bits must be <= 32)", entry)); } - Ipv4Entry white_list_entry; - white_list_entry.ipv4_address_ = ntohl(addr.s_addr); + Ipv4Entry list_entry; + list_entry.ipv4_address_ = ntohl(addr.s_addr); // The 1ULL below makes sure that the RHS is computed as a 64-bit value, so that we do not // over-shift to the left when mask = 0. The assignment to ipv4_mask_ then truncates // the value back to 32 bits. - white_list_entry.ipv4_mask_ = ~((1ULL << (32 - mask)) - 1); + list_entry.ipv4_mask_ = ~((1ULL << (32 - mask)) - 1); // Check to make sure applying the mask to the address equals the address. This can prevent // user error. - if ((white_list_entry.ipv4_address_ & white_list_entry.ipv4_mask_) != - white_list_entry.ipv4_address_) { + if ((list_entry.ipv4_address_ & list_entry.ipv4_mask_) != list_entry.ipv4_address_) { throw EnvoyException( fmt::format("invalid ipv4/mask combo '{}' ((address & mask) != address)", entry)); } - ipv4_white_list_.push_back(white_list_entry); + ipv4_list_.push_back(list_entry); } } -bool IpWhiteList::contains(const std::string& remote_address) const { +bool IpList::contains(const std::string& remote_address) const { in_addr addr; int rc = inet_pton(AF_INET, remote_address.c_str(), &addr); if (1 != rc) { return false; } - for (const Ipv4Entry& entry : ipv4_white_list_) { + for (const Ipv4Entry& entry : ipv4_list_) { if ((ntohl(addr.s_addr) & entry.ipv4_mask_) == entry.ipv4_address_) { return true; } @@ -72,6 +67,10 @@ bool IpWhiteList::contains(const std::string& remote_address) const { return false; } +IpList::IpList(const Json::Object& config, const std::string& member_name) + : IpList(config.hasObject(member_name) ? config.getStringArray(member_name) + : std::vector()) {} + const std::string Utility::TCP_SCHEME = "tcp://"; const std::string Utility::UNIX_SCHEME = "unix://"; @@ -235,4 +234,38 @@ bool Utility::getOriginalDst(int fd, sockaddr_storage* orig_addr) { return (status == 0); } +void Utility::parsePortRangeList(const std::string& string, std::list& list) { + std::vector ranges = StringUtil::split(string.c_str(), ','); + for (const std::string& s : ranges) { + std::stringstream ss(s); + uint32_t min = 0; + uint32_t max = 0; + + if (s.find('-') != std::string::npos) { + char dash = 0; + ss >> min; + ss >> dash; + ss >> max; + } else { + ss >> min; + max = min; + } + + if (s.empty() || (min > 65535) || (max > 65535) || ss.fail() || !ss.eof()) { + throw EnvoyException(fmt::format("invalid port number or range '{}'", s)); + } + + list.emplace_back(PortRange(min, max)); + } +} + +bool Utility::portInRangeList(uint32_t port, const std::list& list) { + for (const Network::PortRange& p : list) { + if (p.contains(port)) { + return true; + } + } + return false; +} + } // Network diff --git a/source/common/network/utility.h b/source/common/network/utility.h index 85f5044c729a0..49e770c0762cb 100644 --- a/source/common/network/utility.h +++ b/source/common/network/utility.h @@ -16,11 +16,14 @@ namespace Network { * Utility class for keeping a list of IPV4 addresses and masks, and then determining whether an * IP address is in the address/mask list. */ -class IpWhiteList { +class IpList { public: - IpWhiteList(const Json::Object& config); + IpList(const std::vector& subnets); + IpList(const Json::Object& config, const std::string& member_name); + IpList(){}; - bool contains(const std::string& remote_address) const; + bool contains(const std::string& address) const; + bool empty() const { return ipv4_list_.empty(); } private: struct Ipv4Entry { @@ -28,9 +31,25 @@ class IpWhiteList { uint32_t ipv4_mask_; }; - std::vector ipv4_white_list_; + std::vector ipv4_list_; }; +/** + * Utility class to represent TCP/UDP port range + */ +class PortRange { +public: + PortRange(uint32_t min, uint32_t max) : min_(min), max_(max) {} + + bool contains(uint32_t port) const { return (port >= min_ && port <= max_); } + +private: + const uint32_t min_; + const uint32_t max_; +}; + +typedef std::list PortRangeList; + /** * Common network utility routines. */ @@ -130,6 +149,24 @@ class Utility { * @return true if the operation succeeded, false otherwise */ static bool getOriginalDst(int fd, sockaddr_storage* orig_addr); + + /** + * Parses a string containing a comma-separated list of port numbers and/or + * port ranges and appends the values to a caller-provided list of PortRange structures. + * For example, the string "1-1024,2048-4096,12345" causes 3 PortRange structures + * to be appended to the supplied list. + * @param str is the string containing the port numbers and ranges + * @param list is the list to append the new data structures to + */ + static void parsePortRangeList(const std::string& string, std::list& list); + + /** + * Checks whether a given port number appears in at least one of the port ranges in a list + * @param port is the port number to search + * @param list the list of port ranges in which the port may appear + * @return whether the port appears in at least one of the ranges in the list + */ + static bool portInRangeList(uint32_t port, const std::list& list); }; } // Network diff --git a/source/common/ssl/connection_impl.cc b/source/common/ssl/connection_impl.cc index 901e49e4268a9..d36ce9f7a1768 100644 --- a/source/common/ssl/connection_impl.cc +++ b/source/common/ssl/connection_impl.cc @@ -10,8 +10,9 @@ namespace Ssl { ConnectionImpl::ConnectionImpl(Event::DispatcherImpl& dispatcher, int fd, - const std::string& remote_address, Context& ctx, InitialState state) - : Network::ConnectionImpl(dispatcher, fd, remote_address), + const std::string& remote_address, const std::string& local_address, + Context& ctx, InitialState state) + : Network::ConnectionImpl(dispatcher, fd, remote_address, local_address), ctx_(dynamic_cast(ctx)), ssl_(ctx_.newSsl()) { BIO* bio = BIO_new_socket(fd, 0); SSL_set_bio(ssl_.get(), bio, bio); @@ -185,10 +186,11 @@ std::string ConnectionImpl::sha256PeerCertificateDigest() { return Hex::encode(computed_hash); } +// TODO: see if we can pass something more meaningful than EMPTY_STRING as localAddress ClientConnectionImpl::ClientConnectionImpl(Event::DispatcherImpl& dispatcher, Context& ctx, const std::string& url) - : ConnectionImpl(dispatcher, socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0), url, ctx, - InitialState::Client) {} + : ConnectionImpl(dispatcher, socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0), url, EMPTY_STRING, + ctx, InitialState::Client) {} void ClientConnectionImpl::connect() { Network::AddrInfoPtr addr_info = diff --git a/source/common/ssl/connection_impl.h b/source/common/ssl/connection_impl.h index 0f38d29275b9a..7e87fd16b7441 100644 --- a/source/common/ssl/connection_impl.h +++ b/source/common/ssl/connection_impl.h @@ -11,7 +11,7 @@ class ConnectionImpl : public Network::ConnectionImpl, public Connection { enum class InitialState { Client, Server }; ConnectionImpl(Event::DispatcherImpl& dispatcher, int fd, const std::string& remote_address, - Context& ctx, InitialState state); + const std::string& local_address, Context& ctx, InitialState state); ~ConnectionImpl(); // Network::Connection diff --git a/test/common/filter/auth/client_ssl_test.cc b/test/common/filter/auth/client_ssl_test.cc index be63b4b472dc5..76852913f2186 100644 --- a/test/common/filter/auth/client_ssl_test.cc +++ b/test/common/filter/auth/client_ssl_test.cc @@ -136,7 +136,7 @@ TEST_F(ClientSslAuthFilterTest, Ssl) { createAuthFilter(); ON_CALL(filter_callbacks_.connection_, ssl()).WillByDefault(Return(&ssl_)); EXPECT_CALL(filter_callbacks_.connection_, remoteAddress()) - .WillOnce(ReturnRefOfCopy(std::string("192.168.1.1"))); + .WillOnce(ReturnRefOfCopy(std::string("tcp://192.168.1.1:0"))); EXPECT_CALL(ssl_, sha256PeerCertificateDigest()).WillOnce(Return("digest")); EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush)); EXPECT_EQ(Network::FilterStatus::StopIteration, instance_->onNewConnection()); @@ -155,7 +155,7 @@ TEST_F(ClientSslAuthFilterTest, Ssl) { // Create a new filter for an SSL connection with an authorized cert. createAuthFilter(); EXPECT_CALL(filter_callbacks_.connection_, remoteAddress()) - .WillOnce(ReturnRefOfCopy(std::string("192.168.1.1"))); + .WillOnce(ReturnRefOfCopy(std::string("tcp://192.168.1.1:0"))); EXPECT_CALL(ssl_, sha256PeerCertificateDigest()) .WillOnce(Return("1b7d42ef0025ad89c1c911d6c10d7e86a4cb7c5863b2980abcbad1895f8b5314")); EXPECT_EQ(Network::FilterStatus::StopIteration, instance_->onNewConnection()); @@ -168,7 +168,7 @@ TEST_F(ClientSslAuthFilterTest, Ssl) { // White list case. createAuthFilter(); EXPECT_CALL(filter_callbacks_.connection_, remoteAddress()) - .WillOnce(ReturnRefOfCopy(std::string("1.2.3.4"))); + .WillOnce(ReturnRefOfCopy(std::string("tcp://1.2.3.4:0"))); EXPECT_EQ(Network::FilterStatus::StopIteration, instance_->onNewConnection()); EXPECT_CALL(filter_callbacks_, continueReading()); filter_callbacks_.connection_.raiseEvents(Network::ConnectionEvent::Connected); diff --git a/test/common/filter/tcp_proxy_test.cc b/test/common/filter/tcp_proxy_test.cc index 673c1277b4827..a842d6c6cc316 100644 --- a/test/common/filter/tcp_proxy_test.cc +++ b/test/common/filter/tcp_proxy_test.cc @@ -12,18 +12,39 @@ using testing::_; using testing::NiceMock; using testing::Return; +using testing::ReturnRefOfCopy; using testing::SaveArg; namespace Filter { -TEST(TcpProxyConfigTest, NoCluster) { +TEST(TcpProxyConfigTest, NoRouteConfig) { std::string json = R"EOF( { - "cluster": "fake_cluster", "stat_prefix": "name" } )EOF"; + Json::ObjectPtr config = Json::Factory::LoadFromString(json); + NiceMock cluster_manager; + EXPECT_THROW( + TcpProxyConfig(*config, cluster_manager, cluster_manager.cluster_.info_->stats_store_), + EnvoyException); +} + +TEST(TcpProxyConfigTest, NoCluster) { + std::string json = R"EOF( + { + "stat_prefix": "name", + "route_config": { + "routes": [ + { + "cluster": "fake_cluster" + } + ] + } + } + )EOF"; + Json::ObjectPtr config = Json::Factory::LoadFromString(json); NiceMock cluster_manager; EXPECT_CALL(cluster_manager, get("fake_cluster")).WillOnce(Return(nullptr)); @@ -36,7 +57,13 @@ TEST(TcpProxyConfigTest, BadTcpProxyConfig) { std::string json_string = R"EOF( { "stat_prefix": 1, - "cluster_name" : "fake_cluster" + "route_config": { + "routes": [ + { + "cluster": "fake_cluster" + } + ] + } } )EOF"; @@ -47,13 +74,226 @@ TEST(TcpProxyConfigTest, BadTcpProxyConfig) { Json::Exception); } +TEST(TcpProxyConfigTest, Routes) { + std::string json = R"EOF( + { + "stat_prefix": "name", + "route_config": { + "routes": [ + { + "destination_ip_list": [ + "10.10.10.10/32", + "10.10.11.0/24", + "10.11.0.0/16", + "11.0.0.0/8", + "128.0.0.0/1" + ], + "cluster": "with_destination_ip_list" + }, + { + "destination_ports": "1-1024,2048-4096,12345", + "cluster": "with_destination_ports" + }, + { + "source_ports": "23457,23459", + "cluster": "with_source_ports" + }, + { + "destination_ip_list": [ + "10.0.0.0/24" + ], + "source_ip_list": [ + "20.0.0.0/24" + ], + "destination_ports" : "10000", + "source_ports": "20000", + "cluster": "with_everything" + }, + { + "cluster": "catch_all" + } + ] + } + } + )EOF"; + + Json::ObjectPtr json_config = Json::Factory::LoadFromString(json); + NiceMock cm_; + + TcpProxyConfig config_obj(*json_config, cm_, cm_.cluster_.info_->stats_store_); + + { + // hit route with destination_ip (10.10.10.10/32) + NiceMock connection; + EXPECT_CALL(connection, localAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://10.10.10.10:0"))); + EXPECT_EQ(std::string("with_destination_ip_list"), config_obj.getRouteFromEntries(connection)); + } + + { + // fall-through + NiceMock connection; + EXPECT_CALL(connection, localAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://10.10.10.11:0"))); + EXPECT_CALL(connection, remoteAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://0.0.0.0:0"))); + EXPECT_EQ(std::string("catch_all"), config_obj.getRouteFromEntries(connection)); + } + + { + // hit route with destination_ip (10.10.11.0/24) + NiceMock connection; + EXPECT_CALL(connection, localAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://10.10.11.11:0"))); + EXPECT_EQ(std::string("with_destination_ip_list"), config_obj.getRouteFromEntries(connection)); + } + + { + // fall-through + NiceMock connection; + EXPECT_CALL(connection, localAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://10.10.12.12:0"))); + EXPECT_CALL(connection, remoteAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://0.0.0.0:0"))); + EXPECT_EQ(std::string("catch_all"), config_obj.getRouteFromEntries(connection)); + } + + { + // hit route with destination_ip (10.11.0.0/16) + NiceMock connection; + EXPECT_CALL(connection, localAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://10.11.11.11:0"))); + EXPECT_EQ(std::string("with_destination_ip_list"), config_obj.getRouteFromEntries(connection)); + } + + { + // fall-through + NiceMock connection; + EXPECT_CALL(connection, localAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://10.12.12.12:0"))); + EXPECT_CALL(connection, remoteAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://0.0.0.0:0"))); + EXPECT_EQ(std::string("catch_all"), config_obj.getRouteFromEntries(connection)); + } + + { + // hit route with destination_ip (11.0.0.0/8) + NiceMock connection; + EXPECT_CALL(connection, localAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://11.11.11.11:0"))); + EXPECT_EQ(std::string("with_destination_ip_list"), config_obj.getRouteFromEntries(connection)); + } + + { + // fall-through + NiceMock connection; + EXPECT_CALL(connection, localAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://12.12.12.12:0"))); + EXPECT_CALL(connection, remoteAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://0.0.0.0:0"))); + EXPECT_EQ(std::string("catch_all"), config_obj.getRouteFromEntries(connection)); + } + + { + // hit route with destination_ip (128.0.0.0/8) + NiceMock connection; + EXPECT_CALL(connection, localAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://128.255.255.255:0"))); + EXPECT_EQ(std::string("with_destination_ip_list"), config_obj.getRouteFromEntries(connection)); + } + + { + // hit route with destination port range + NiceMock connection; + EXPECT_CALL(connection, localAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://1.2.3.4:12345"))); + EXPECT_EQ(std::string("with_destination_ports"), config_obj.getRouteFromEntries(connection)); + } + + { + // fall through + NiceMock connection; + EXPECT_CALL(connection, localAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://1.2.3.4:23456"))); + EXPECT_CALL(connection, remoteAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://0.0.0.0:0"))); + EXPECT_EQ(std::string("catch_all"), config_obj.getRouteFromEntries(connection)); + } + + { + // hit route with source port range + NiceMock connection; + EXPECT_CALL(connection, localAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://1.2.3.4:23456"))); + EXPECT_CALL(connection, remoteAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://0.0.0.0:23459"))); + EXPECT_EQ(std::string("with_source_ports"), config_obj.getRouteFromEntries(connection)); + } + + { + // fall through + NiceMock connection; + EXPECT_CALL(connection, localAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://1.2.3.4:23456"))); + EXPECT_CALL(connection, remoteAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://0.0.0.0:23458"))); + EXPECT_EQ(std::string("catch_all"), config_obj.getRouteFromEntries(connection)); + } + + { + // hit the route with all criterias present + NiceMock connection; + EXPECT_CALL(connection, localAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://10.0.0.0:10000"))); + EXPECT_CALL(connection, remoteAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://20.0.0.0:20000"))); + EXPECT_EQ(std::string("with_everything"), config_obj.getRouteFromEntries(connection)); + } + + { + // fall through + NiceMock connection; + EXPECT_CALL(connection, localAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://10.0.0.0:10000"))); + EXPECT_CALL(connection, remoteAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://30.0.0.0:20000"))); + EXPECT_EQ(std::string("catch_all"), config_obj.getRouteFromEntries(connection)); + } +} + +TEST(TcpProxyConfigTest, EmptyRouteConfig) { + std::string json = R"EOF( + { + "stat_prefix": "name", + "route_config": { + "routes": [ + ] + } + } + )EOF"; + + Json::ObjectPtr json_config = Json::Factory::LoadFromString(json); + NiceMock cm_; + + TcpProxyConfig config_obj(*json_config, cm_, cm_.cluster_.info_->stats_store_); + + NiceMock connection; + EXPECT_EQ(std::string(""), config_obj.getRouteFromEntries(connection)); +} + class TcpProxyTest : public testing::Test { public: TcpProxyTest() { std::string json = R"EOF( { - "cluster": "fake_cluster", - "stat_prefix": "name" + "stat_prefix": "name", + "route_config": { + "routes": [ + { + "cluster": "fake_cluster" + } + ] + } } )EOF"; @@ -206,4 +446,80 @@ TEST_F(TcpProxyTest, UpstreamConnectionLimit) { cluster_manager_.cluster_.info_->stats_store_.counter("upstream_cx_overflow").value()); } +class TcpProxyRoutingTest : public testing::Test { +public: + TcpProxyRoutingTest() { + std::string json = R"EOF( + { + "stat_prefix": "name", + "route_config": { + "routes": [ + { + "destination_ports": "1-9999", + "cluster": "fake_cluster" + } + ] + } + } + )EOF"; + + Json::ObjectPtr config = Json::Factory::LoadFromString(json); + config_.reset(new TcpProxyConfig(*config, cluster_manager_, + cluster_manager_.cluster_.info_->stats_store_)); + } + + void setup() { + EXPECT_CALL(filter_callbacks_, connection()).WillRepeatedly(ReturnRef(connection_)); + + filter_.reset(new TcpProxy(config_, cluster_manager_)); + filter_->initializeReadFilterCallbacks(filter_callbacks_); + } + + TcpProxyConfigPtr config_; + NiceMock connection_; + NiceMock filter_callbacks_; + NiceMock cluster_manager_; + std::unique_ptr filter_; +}; + +TEST_F(TcpProxyRoutingTest, NonRoutableConnection) { + uint32_t total_cx = config_->stats().downstream_cx_total_.value(); + uint32_t non_routable_cx = config_->stats().downstream_cx_no_route_.value(); + + setup(); + + // port 10000 is outside the specified destination port range + EXPECT_CALL(connection_, localAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://1.2.3.4:10000"))); + + // getRouteFromEntries() returns an empty string if no route matches + EXPECT_CALL(cluster_manager_, get("")).WillRepeatedly(Return(nullptr)); + + // Expect filter to stop iteration and close connection + EXPECT_CALL(connection_, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onNewConnection()); + + EXPECT_EQ(total_cx + 1, config_->stats().downstream_cx_total_.value()); + EXPECT_EQ(non_routable_cx + 1, config_->stats().downstream_cx_no_route_.value()); +} + +TEST_F(TcpProxyRoutingTest, RoutableConnection) { + uint32_t total_cx = config_->stats().downstream_cx_total_.value(); + uint32_t non_routable_cx = config_->stats().downstream_cx_no_route_.value(); + + setup(); + + // port 9999 is within the specified destination port range + EXPECT_CALL(connection_, localAddress()) + .WillRepeatedly(ReturnRefOfCopy(std::string("tcp://1.2.3.4:9999"))); + + // Expect filter to try to open a connection to specified cluster + EXPECT_CALL(cluster_manager_, tcpConnForCluster_("fake_cluster")); + + filter_->onNewConnection(); + + EXPECT_EQ(total_cx + 1, config_->stats().downstream_cx_total_.value()); + EXPECT_EQ(non_routable_cx, config_->stats().downstream_cx_no_route_.value()); +} + } // Filter diff --git a/test/common/http/conn_manager_impl_test.cc b/test/common/http/conn_manager_impl_test.cc index 81fa836557da6..ee40a4d04c9a3 100644 --- a/test/common/http/conn_manager_impl_test.cc +++ b/test/common/http/conn_manager_impl_test.cc @@ -27,6 +27,7 @@ using testing::Invoke; using testing::NiceMock; using testing::Return; using testing::ReturnRef; +using testing::ReturnRefOfCopy; using testing::Sequence; using testing::Test; @@ -60,6 +61,8 @@ class HttpConnectionManagerImplTest : public Test, public ConnectionManagerConfi server_name_ = server_name; ON_CALL(filter_callbacks_.connection_, ssl()).WillByDefault(Return(ssl_connection_.get())); + ON_CALL(filter_callbacks_.connection_, remoteAddress()) + .WillByDefault(ReturnRefOfCopy(std::string("tcp://0.0.0.0:0"))); conn_manager_.reset(new ConnectionManagerImpl(*this, drain_close_, random_, tracer_, runtime_)); conn_manager_->initializeReadFilterCallbacks(filter_callbacks_); } diff --git a/test/common/http/conn_manager_utility_test.cc b/test/common/http/conn_manager_utility_test.cc index aa04e4acd3b80..29d40a96305bc 100644 --- a/test/common/http/conn_manager_utility_test.cc +++ b/test/common/http/conn_manager_utility_test.cc @@ -1,5 +1,6 @@ #include "common/http/conn_manager_utility.h" #include "common/http/headers.h" +#include "common/network/utility.h" #include "common/runtime/runtime_impl.h" #include "common/runtime/uuid_util.h" @@ -46,7 +47,7 @@ TEST_F(ConnectionManagerUtilityTest, generateStreamId) { } TEST_F(ConnectionManagerUtilityTest, UseRemoteAddressWhenNotLocalHostRemoteAddress) { - const std::string not_local_host_remote_address = "12.12.12.12"; + const std::string not_local_host_remote_address = "tcp://12.12.12.12:0"; EXPECT_CALL(config_, useRemoteAddress()).WillRepeatedly(Return(true)); EXPECT_CALL(connection_, remoteAddress()) .WillRepeatedly(ReturnRef(not_local_host_remote_address)); @@ -55,11 +56,12 @@ TEST_F(ConnectionManagerUtilityTest, UseRemoteAddressWhenNotLocalHostRemoteAddre ConnectionManagerUtility::mutateRequestHeaders(headers, connection_, config_, random_, runtime_); EXPECT_TRUE(headers.has(Headers::get().ForwardedFor)); - EXPECT_EQ(not_local_host_remote_address, headers.get_(Headers::get().ForwardedFor)); + EXPECT_EQ(Network::Utility::hostFromUrl(not_local_host_remote_address), + headers.get_(Headers::get().ForwardedFor)); } TEST_F(ConnectionManagerUtilityTest, UseLocalAddressWhenLocalHostRemoteAddress) { - const std::string local_host_remote_address = "127.0.0.1"; + const std::string local_host_remote_address = "tcp://127.0.0.1:0"; const std::string local_address = "10.3.2.1"; EXPECT_CALL(connection_, remoteAddress()).WillRepeatedly(ReturnRef(local_host_remote_address)); @@ -74,7 +76,7 @@ TEST_F(ConnectionManagerUtilityTest, UseLocalAddressWhenLocalHostRemoteAddress) } TEST_F(ConnectionManagerUtilityTest, UserAgentDontSet) { - const std::string internal_remote_address = "10.0.0.1"; + const std::string internal_remote_address = "tcp://10.0.0.1:0"; EXPECT_CALL(config_, useRemoteAddress()).WillRepeatedly(Return(true)); EXPECT_CALL(connection_, remoteAddress()).WillRepeatedly(ReturnRef(internal_remote_address)); @@ -88,7 +90,7 @@ TEST_F(ConnectionManagerUtilityTest, UserAgentDontSet) { } TEST_F(ConnectionManagerUtilityTest, UserAgentSetWhenIncomingEmpty) { - const std::string internal_remote_address = "10.0.0.1"; + const std::string internal_remote_address = "tcp://10.0.0.1:0"; EXPECT_CALL(config_, useRemoteAddress()).WillRepeatedly(Return(true)); EXPECT_CALL(connection_, remoteAddress()).WillRepeatedly(ReturnRef(internal_remote_address)); @@ -137,7 +139,7 @@ TEST_F(ConnectionManagerUtilityTest, InternalServiceForceTrace) { } TEST_F(ConnectionManagerUtilityTest, EdgeRequestRegenerateRequestIdAndWipeDownstream) { - const std::string external_remote_address = "34.0.0.1"; + const std::string external_remote_address = "tcp://34.0.0.1:0"; const std::string generated_uuid = "f4dca0a9-12c7-4307-8002-969403baf480"; ON_CALL(config_, useRemoteAddress()).WillByDefault(Return(true)); @@ -208,7 +210,7 @@ TEST_F(ConnectionManagerUtilityTest, ExternalRequestPreserveRequestIdAndDownstre } TEST_F(ConnectionManagerUtilityTest, UserAgentSetIncomingUserAgent) { - const std::string internal_remote_address = "10.0.0.1"; + const std::string internal_remote_address = "tcp://10.0.0.1:0"; EXPECT_CALL(config_, useRemoteAddress()).WillRepeatedly(Return(true)); EXPECT_CALL(connection_, remoteAddress()).WillRepeatedly(ReturnRef(internal_remote_address)); @@ -223,7 +225,7 @@ TEST_F(ConnectionManagerUtilityTest, UserAgentSetIncomingUserAgent) { } TEST_F(ConnectionManagerUtilityTest, UserAgentSetNoIncomingUserAgent) { - const std::string internal_remote_address = "10.0.0.1"; + const std::string internal_remote_address = "tcp://10.0.0.1:0"; EXPECT_CALL(config_, useRemoteAddress()).WillRepeatedly(Return(true)); EXPECT_CALL(connection_, remoteAddress()).WillRepeatedly(ReturnRef(internal_remote_address)); @@ -263,7 +265,7 @@ TEST_F(ConnectionManagerUtilityTest, RequestIdGeneratedWhenItsNotPresent) { } TEST_F(ConnectionManagerUtilityTest, DoNotOverrideRequestIdIfPresentWhenInternalRequest) { - std::string local_remote_address = "10.0.0.1"; + std::string local_remote_address = "tcp://10.0.0.1:0"; EXPECT_CALL(config_, useRemoteAddress()).WillOnce(Return(true)); EXPECT_CALL(connection_, remoteAddress()).WillRepeatedly(ReturnRef(local_remote_address)); @@ -275,7 +277,7 @@ TEST_F(ConnectionManagerUtilityTest, DoNotOverrideRequestIdIfPresentWhenInternal } TEST_F(ConnectionManagerUtilityTest, OverrideRequestIdForExternalRequests) { - std::string external_ip = "134.2.2.11"; + std::string external_ip = "tcp://134.2.2.11:0"; EXPECT_CALL(connection_, remoteAddress()).WillRepeatedly(ReturnRef(external_ip)); TestHeaderMapImpl headers{{"x-request-id", "original"}}; @@ -287,7 +289,8 @@ TEST_F(ConnectionManagerUtilityTest, OverrideRequestIdForExternalRequests) { } TEST_F(ConnectionManagerUtilityTest, ExternalAddressExternalRequestUseRemote) { - ON_CALL(connection_, remoteAddress()).WillByDefault(ReturnRefOfCopy(std::string("50.0.0.1"))); + ON_CALL(connection_, remoteAddress()) + .WillByDefault(ReturnRefOfCopy(std::string("tcp://50.0.0.1:0"))); ON_CALL(config_, useRemoteAddress()).WillByDefault(Return(true)); config_.route_config_.internal_only_headers_.push_back(LowerCaseString("custom_header")); @@ -310,7 +313,8 @@ TEST_F(ConnectionManagerUtilityTest, ExternalAddressExternalRequestUseRemote) { } TEST_F(ConnectionManagerUtilityTest, ExternalAddressExternalRequestDontUseRemote) { - ON_CALL(connection_, remoteAddress()).WillByDefault(ReturnRefOfCopy(std::string("50.0.0.1"))); + ON_CALL(connection_, remoteAddress()) + .WillByDefault(ReturnRefOfCopy(std::string("tcp://50.0.0.1:0"))); ON_CALL(config_, useRemoteAddress()).WillByDefault(Return(false)); TestHeaderMapImpl headers{{"x-envoy-external-address", "60.0.0.1"}, @@ -322,7 +326,8 @@ TEST_F(ConnectionManagerUtilityTest, ExternalAddressExternalRequestDontUseRemote } TEST_F(ConnectionManagerUtilityTest, ExternalAddressInternalRequestUseRemote) { - ON_CALL(connection_, remoteAddress()).WillByDefault(ReturnRefOfCopy(std::string("10.0.0.1"))); + ON_CALL(connection_, remoteAddress()) + .WillByDefault(ReturnRefOfCopy(std::string("tcp://10.0.0.1:0"))); ON_CALL(config_, useRemoteAddress()).WillByDefault(Return(true)); TestHeaderMapImpl headers{{"x-envoy-external-address", "60.0.0.1"}, diff --git a/test/common/network/connection_impl_test.cc b/test/common/network/connection_impl_test.cc index 9502140507513..d2a14961129e4 100644 --- a/test/common/network/connection_impl_test.cc +++ b/test/common/network/connection_impl_test.cc @@ -1,4 +1,5 @@ #include "common/buffer/buffer_impl.h" +#include "common/common/empty_string.h" #include "common/event/dispatcher_impl.h" #include "common/network/connection_impl.h" #include "common/network/listen_socket_impl.h" @@ -43,7 +44,8 @@ TEST(ConnectionImplUtility, updateBufferStats) { TEST(ConnectionImplDeathTest, BadFd) { Event::DispatcherImpl dispatcher; - EXPECT_DEATH(ConnectionImpl(dispatcher, -1, "127.0.0.1"), ".*assert failure: fd_ != -1.*"); + EXPECT_DEATH(ConnectionImpl(dispatcher, -1, "tcp://127.0.0.1:0", EMPTY_STRING), + ".*assert failure: fd_ != -1.*"); } struct MockBufferStats { diff --git a/test/common/network/filter_manager_impl_test.cc b/test/common/network/filter_manager_impl_test.cc index fc9a36f7dd52d..7ef187d45dc77 100644 --- a/test/common/network/filter_manager_impl_test.cc +++ b/test/common/network/filter_manager_impl_test.cc @@ -122,8 +122,14 @@ TEST_F(NetworkFilterManagerTest, RateLimitAndTcpProxy) { std::string tcp_proxy_json = R"EOF( { - "cluster": "fake_cluster", - "stat_prefix": "name" + "stat_prefix": "name", + "route_config": { + "routes": [ + { + "cluster": "fake_cluster" + } + ] + } } )EOF"; diff --git a/test/common/network/listener_impl_test.cc b/test/common/network/listener_impl_test.cc index 72bd5020424cc..2f4d3cf6fb1ec 100644 --- a/test/common/network/listener_impl_test.cc +++ b/test/common/network/listener_impl_test.cc @@ -49,11 +49,14 @@ class TestListenerImpl : public ListenerImpl { ~TestListenerImpl() {} MOCK_METHOD1(getAddressPort_, uint16_t(sockaddr*)); - MOCK_METHOD2(newConnection_, void(int, sockaddr*)); + MOCK_METHOD3(newConnection_, void(int, sockaddr*, sockaddr*)); - void newConnection(int fd, sockaddr* addr) override { newConnection_(fd, addr); } - void newConnection(int fd, const std::string& addr) override { - ListenerImpl::newConnection(fd, addr); + void newConnection(int fd, sockaddr* remote_addr, sockaddr* local_addr) override { + newConnection_(fd, remote_addr, local_addr); + } + void newConnection(int fd, const std::string& remote_addr, + const std::string& local_addr) override { + ListenerImpl::newConnection(fd, remote_addr, local_addr); } protected: @@ -79,10 +82,10 @@ TEST(ListenerImplTest, UseOriginalDst) { EXPECT_CALL(listener, getAddressPort_(_)).WillRepeatedly(Return(10001)); EXPECT_CALL(connection_handler, findListener("10001")).WillRepeatedly(Return(&listenerDst)); - EXPECT_CALL(listener, newConnection_(_, _)).Times(0); - EXPECT_CALL(listenerDst, newConnection_(_, _)) + EXPECT_CALL(listener, newConnection_(_, _, _)).Times(0); + EXPECT_CALL(listenerDst, newConnection_(_, _, _)) .Times(1) - .WillOnce(Invoke([&](int, sockaddr*) -> void { + .WillOnce(Invoke([&](int, sockaddr*, sockaddr*) -> void { client_connection->close(ConnectionCloseType::NoFlush); dispatcher.exit(); })); diff --git a/test/common/network/proxy_protocol_test.cc b/test/common/network/proxy_protocol_test.cc index 72d56b523b209..eda21fe0e35a2 100644 --- a/test/common/network/proxy_protocol_test.cc +++ b/test/common/network/proxy_protocol_test.cc @@ -1,6 +1,7 @@ #include "common/buffer/buffer_impl.h" #include "common/event/dispatcher_impl.h" #include "common/network/listener_impl.h" +#include "common/network/utility.h" #include "common/stats/stats_impl.h" #include "test/mocks/buffer/mocks.h" @@ -47,7 +48,7 @@ TEST_F(ProxyProtocolTest, Basic) { EXPECT_CALL(callbacks_, onNewConnection_(_)) .WillOnce(Invoke([&](ConnectionPtr& conn) -> void { - ASSERT_EQ("1.2.3.4", conn->remoteAddress()); + ASSERT_EQ("1.2.3.4", Network::Utility::hostFromUrl(conn->remoteAddress())); conn->addReadFilter(read_filter_); accepted_connection = std::move(conn); })); @@ -71,7 +72,7 @@ TEST_F(ProxyProtocolTest, Fragmented) { EXPECT_CALL(callbacks_, onNewConnection_(_)) .WillOnce(Invoke([&](ConnectionPtr& conn) -> void { - ASSERT_EQ("255.255.255.255", conn->remoteAddress()); + ASSERT_EQ("255.255.255.255", Network::Utility::hostFromUrl(conn->remoteAddress())); read_filter_.reset(new MockReadFilter()); conn->addReadFilter(read_filter_); conn->close(ConnectionCloseType::NoFlush); @@ -87,7 +88,7 @@ TEST_F(ProxyProtocolTest, PartialRead) { EXPECT_CALL(callbacks_, onNewConnection_(_)) .WillOnce(Invoke([&](ConnectionPtr& conn) -> void { - ASSERT_EQ("255.255.255.255", conn->remoteAddress()); + ASSERT_EQ("255.255.255.255", Network::Utility::hostFromUrl(conn->remoteAddress())); read_filter_.reset(new MockReadFilter()); conn->addReadFilter(read_filter_); conn->close(ConnectionCloseType::NoFlush); diff --git a/test/common/network/utility_test.cc b/test/common/network/utility_test.cc index 9b4226a7d3c1a..e51bab21b681c 100644 --- a/test/common/network/utility_test.cc +++ b/test/common/network/utility_test.cc @@ -4,7 +4,7 @@ namespace Network { -TEST(IpWhiteListTest, Errors) { +TEST(IpListTest, Errors) { { std::string json = R"EOF( { @@ -13,7 +13,7 @@ TEST(IpWhiteListTest, Errors) { )EOF"; Json::ObjectPtr loader = Json::Factory::LoadFromString(json); - EXPECT_THROW({ IpWhiteList wl(*loader); }, EnvoyException); + EXPECT_THROW({ IpList wl(*loader, "ip_white_list"); }, EnvoyException); } { @@ -24,7 +24,7 @@ TEST(IpWhiteListTest, Errors) { )EOF"; Json::ObjectPtr loader = Json::Factory::LoadFromString(json); - EXPECT_THROW({ IpWhiteList wl(*loader); }, EnvoyException); + EXPECT_THROW({ IpList wl(*loader, "ip_white_list"); }, EnvoyException); } { @@ -35,7 +35,7 @@ TEST(IpWhiteListTest, Errors) { )EOF"; Json::ObjectPtr loader = Json::Factory::LoadFromString(json); - EXPECT_THROW({ IpWhiteList wl(*loader); }, EnvoyException); + EXPECT_THROW({ IpList wl(*loader, "ip_white_list"); }, EnvoyException); } { @@ -46,11 +46,11 @@ TEST(IpWhiteListTest, Errors) { )EOF"; Json::ObjectPtr loader = Json::Factory::LoadFromString(json); - EXPECT_THROW({ IpWhiteList wl(*loader); }, EnvoyException); + EXPECT_THROW({ IpList wl(*loader, "ip_white_list"); }, EnvoyException); } } -TEST(IpWhiteListTest, Normal) { +TEST(IpListTest, Normal) { std::string json = R"EOF( { "ip_white_list": [ @@ -62,7 +62,7 @@ TEST(IpWhiteListTest, Normal) { )EOF"; Json::ObjectPtr loader = Json::Factory::LoadFromString(json); - IpWhiteList wl(*loader); + IpList wl(*loader, "ip_white_list"); EXPECT_TRUE(wl.contains("192.168.3.0")); EXPECT_TRUE(wl.contains("192.168.3.3")); @@ -83,7 +83,7 @@ TEST(IpWhiteListTest, Normal) { EXPECT_FALSE(wl.contains("")); } -TEST(IpWhiteListTest, MatchAny) { +TEST(IpListTest, MatchAny) { std::string json = R"EOF( { "ip_white_list": [ @@ -93,7 +93,7 @@ TEST(IpWhiteListTest, MatchAny) { )EOF"; Json::ObjectPtr loader = Json::Factory::LoadFromString(json); - IpWhiteList wl(*loader); + IpList wl(*loader, "ip_white_list"); EXPECT_TRUE(wl.contains("192.168.3.3")); EXPECT_TRUE(wl.contains("192.168.3.0")); @@ -138,4 +138,74 @@ TEST(NetworkUtility, loopbackAddress) { } } +TEST(PortRangeListTest, Errors) { + { + std::string port_range_str = "a1"; + std::list port_range_list; + EXPECT_THROW(Utility::parsePortRangeList(port_range_str, port_range_list), EnvoyException); + } + + { + std::string port_range_str = "1A"; + std::list port_range_list; + EXPECT_THROW(Utility::parsePortRangeList(port_range_str, port_range_list), EnvoyException); + } + + { + std::string port_range_str = "1_1"; + std::list port_range_list; + EXPECT_THROW(Utility::parsePortRangeList(port_range_str, port_range_list), EnvoyException); + } + + { + std::string port_range_str = "1,1X1"; + std::list port_range_list; + EXPECT_THROW(Utility::parsePortRangeList(port_range_str, port_range_list), EnvoyException); + } + + { + std::string port_range_str = "1,1*1"; + std::list port_range_list; + EXPECT_THROW(Utility::parsePortRangeList(port_range_str, port_range_list), EnvoyException); + } +} + +TEST(PortRangeListTest, Normal) { + { + std::string port_range_str = "1"; + std::list port_range_list; + + Utility::parsePortRangeList(port_range_str, port_range_list); + EXPECT_TRUE(Utility::portInRangeList(1, port_range_list)); + EXPECT_FALSE(Utility::portInRangeList(2, port_range_list)); + } + + { + std::string port_range_str = "1024-2048"; + std::list port_range_list; + + Utility::parsePortRangeList(port_range_str, port_range_list); + EXPECT_TRUE(Utility::portInRangeList(1024, port_range_list)); + EXPECT_TRUE(Utility::portInRangeList(2048, port_range_list)); + EXPECT_TRUE(Utility::portInRangeList(1536, port_range_list)); + EXPECT_FALSE(Utility::portInRangeList(1023, port_range_list)); + EXPECT_FALSE(Utility::portInRangeList(2049, port_range_list)); + EXPECT_FALSE(Utility::portInRangeList(0, port_range_list)); + } + + { + std::string port_range_str = "1,10-100,1000-10000,65535"; + std::list port_range_list; + + Utility::parsePortRangeList(port_range_str, port_range_list); + EXPECT_TRUE(Utility::portInRangeList(1, port_range_list)); + EXPECT_TRUE(Utility::portInRangeList(50, port_range_list)); + EXPECT_TRUE(Utility::portInRangeList(5000, port_range_list)); + EXPECT_TRUE(Utility::portInRangeList(65535, port_range_list)); + EXPECT_FALSE(Utility::portInRangeList(2, port_range_list)); + EXPECT_FALSE(Utility::portInRangeList(200, port_range_list)); + EXPECT_FALSE(Utility::portInRangeList(20000, port_range_list)); + } +} + } // Network diff --git a/test/config/integration/server.json b/test/config/integration/server.json index 5d19e235c2b52..50cb8b21d4ebd 100644 --- a/test/config/integration/server.json +++ b/test/config/integration/server.json @@ -188,7 +188,16 @@ "filters": [ { "type": "read", "name": "tcp_proxy", - "config": { "cluster": "cluster_1", "stat_prefix": "test_tcp" } + "config": { + "stat_prefix": "test_tcp", + "route_config": { + "routes": [ + { + "cluster": "cluster_1" + } + ] + } + } } ] }], diff --git a/test/config/integration/server_http2.json b/test/config/integration/server_http2.json index 836626eedbf4a..ccdbf686996cb 100644 --- a/test/config/integration/server_http2.json +++ b/test/config/integration/server_http2.json @@ -157,7 +157,16 @@ "filters": [ { "type": "read", "name": "tcp_proxy", - "config": { "cluster": "cluster_1", "stat_prefix": "test_tcp" } + "config": { + "stat_prefix": "test_tcp", + "route_config": { + "routes": [ + { + "cluster": "cluster_1" + } + ] + } + } } ] }], diff --git a/test/mocks/network/mocks.h b/test/mocks/network/mocks.h index f21431bc6eed2..7dcc259bf2d66 100644 --- a/test/mocks/network/mocks.h +++ b/test/mocks/network/mocks.h @@ -52,6 +52,7 @@ class MockConnection : public Connection, public MockConnectionBase { MOCK_METHOD1(readDisable, void(bool disable)); MOCK_METHOD0(readEnabled, bool()); MOCK_METHOD0(remoteAddress, const std::string&()); + MOCK_METHOD0(localAddress, const std::string&()); MOCK_METHOD1(setBufferStats, void(const BufferStats& stats)); MOCK_METHOD0(ssl, Ssl::Connection*()); MOCK_METHOD0(state, State()); @@ -81,6 +82,7 @@ class MockClientConnection : public ClientConnection, public MockConnectionBase MOCK_METHOD1(readDisable, void(bool disable)); MOCK_METHOD0(readEnabled, bool()); MOCK_METHOD0(remoteAddress, const std::string&()); + MOCK_METHOD0(localAddress, const std::string&()); MOCK_METHOD1(setBufferStats, void(const BufferStats& stats)); MOCK_METHOD0(ssl, Ssl::Connection*()); MOCK_METHOD0(state, State()); diff --git a/test/server/config/network/config_test.cc b/test/server/config/network/config_test.cc index c94ba557666f7..b909f2ecd3dea 100644 --- a/test/server/config/network/config_test.cc +++ b/test/server/config/network/config_test.cc @@ -69,7 +69,27 @@ TEST(NetworkFilterConfigTest, TcpProxy) { std::string json_string = R"EOF( { "stat_prefix": "my_stat_prefix", - "cluster" : "fake_cluster" + "route_config": { + "routes": [ + { + "destination_ip_list": [ + "192.168.1.1/32", + "192.168.1.0/24" + ], + "source_ip_list": [ + "192.168.0.0/16", + "192.0.0.0/8", + "127.0.0.0/8" + ], + "destination_ports": "1-1024,2048-4096,12345", + "cluster": "fake_cluster" + }, + { + "source_ports": "23457,23459", + "cluster": "fake_cluster2" + } + ] + } } )EOF";