-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Add routing capabilities to tcp_proxy #377
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
09dd326
Extend and customize listener behavior
ed44be2
Incorporate review comments (except ConnectionHandler interface)
60e72a6
Create an interface for ConnectionHandler
2ff9595
Merge remote-tracking branch 'upstream/master'
14eaed4
Fix style and typo
f655994
Add regular expression support in header match (#335)
rshriram 8641d2a
Replace openssl with boringssl as the official ssl provider (#339)
RomanDzhabarov aa20a22
stats: fix scope timer prefix (#340)
mattklein123 fa5bbd9
Move ConnectionHandler interface to namespace Network and incorporate…
fa5aa3c
Merge remote-tracking branch 'upstream/master'
987bc7a
Fix bad merge
d1c75f6
Incorporate review comment
7315ce3
Merge remote-tracking branch 'upstream/master'
8e746bf
Merge remote-tracking branch 'upstream/master'
5e2c2b1
Fix handling of subnet "0.0.0.0/0" in whitelist.
becb250
Add a comment regarding the fix for "0.0.0.0/0" subnet
2729237
Add a comment regarding the fix for "0.0.0.0/0" subnet
d4b80ac
Merge branch 'master' of https://github.com/enricoschiattarella/envoy
d833ec8
Add routing capabilities to tcp_proxy
d877b8a
Add some comments and cosmetic fixes.
0b1cc69
Merge remote-tracking branch 'upstream/master'
91bd2a6
Add missing file
8b53016
Incorporate review comments
5e6fbc4
Merge remote-tracking branch 'upstream/master'
8bc1d82
Fix TCP_PROXY_NETWORK_FILTER_SCHEMA error.
2dffce8
Chande destinationAddress() to localAddress() and populate at constru…
6eecce2
Make local_addr part of connection and pass it down from listeners
435ac47
TCP proxy routing: config validation logic, test for non-routable con…
enricoschiattarella 5e46ef9
Merge remote-tracking branch 'upstream/master'
enricoschiattarella 1548bce
Merge remote-tracking branch 'upstream/master'
enricoschiattarella 87d9a90
Fix include order
enricoschiattarella File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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()))) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if localAddress() is empty, this will throw an exception, so I think for now need to check if it's not empty.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
| 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_); | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'd probably put here those optional config settings here as well, so one checking this template file can quickly get a sense of all options.
will leave up to you.
These configs are loaded as part of Envoy tests as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, I had the impression that these templates were used for generating actual configs. If they are just loaded during Envoy tests I will add them so that the parsing logic gets exercised.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't find a good real-world example for this because in configgen.py the tcp_proxy instances and the clusters are coupled (since there used to be a 1:1 mapping). Let me think a bit more about this while you guys review the new version.