-
Notifications
You must be signed in to change notification settings - Fork 5.3k
udp: add router for UDP proxy #17864
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
Closed
Closed
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
4cfa9c5
udp: add route proto
zhxie 9891c45
udp: add router
zhxie 367db00
udp: route data to clusters
zhxie 3bd3085
udp: fix format
zhxie 4c1b126
udp: minor type change
zhxie 3f762f7
udp: clean code
zhxie 95e1094
test: add test for routing in UDP proxy
zhxie b592612
test: add test for route matching of UDP proxy
zhxie 804aedd
test: fix format
zhxie 37def08
udp: deprecate api in favor of router
zhxie 77fb547
test: prioritize router
zhxie c804aca
docs: prioritize router
zhxie 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| syntax = "proto3"; | ||
|
|
||
| package envoy.extensions.filters.udp.udp_proxy.v3; | ||
|
|
||
| import "envoy/config/core/v3/address.proto"; | ||
|
|
||
| import "udpa/annotations/status.proto"; | ||
| import "udpa/annotations/versioning.proto"; | ||
| import "validate/validate.proto"; | ||
|
|
||
| option java_package = "io.envoyproxy.envoy.extensions.filters.udp.udp_proxy.v3"; | ||
| option java_outer_classname = "RouteProto"; | ||
| option java_multiple_files = true; | ||
| option (udpa.annotations.file_status).package_version_status = ACTIVE; | ||
|
|
||
| // [#protodoc-title: UDP proxy route configuration] | ||
| // UDP proxy :ref:`configuration overview <config_udp_listener_filters_udp_proxy>`. | ||
|
|
||
| message RouteConfiguration { | ||
| // The list of routes that will be matched, in order, against incoming requests. The first route | ||
| // that matches will be used. | ||
| repeated Route routes = 1; | ||
| } | ||
|
|
||
| message Route { | ||
| // Route matching parameters. | ||
| RouteMatch match = 1 [(validate.rules).message = {required: true}]; | ||
|
|
||
| // Route request to some upstream cluster. | ||
| RouteAction route = 2 [(validate.rules).message = {required: true}]; | ||
| } | ||
|
|
||
| message RouteMatch { | ||
| // 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. | ||
| repeated config.core.v3.CidrRange source_prefix_ranges = 1; | ||
| } | ||
|
|
||
| message RouteAction { | ||
| oneof cluster_specifier { | ||
| option (validate.required) = true; | ||
|
|
||
| // Indicates the upstream cluster to which the request should be routed. | ||
| string cluster = 1; | ||
| } | ||
| } | ||
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
1 change: 1 addition & 0 deletions
1
generated_api_shadow/envoy/extensions/filters/udp/udp_proxy/v3/BUILD
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
48 changes: 48 additions & 0 deletions
48
generated_api_shadow/envoy/extensions/filters/udp/udp_proxy/v3/route.proto
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
13 changes: 11 additions & 2 deletions
13
generated_api_shadow/envoy/extensions/filters/udp/udp_proxy/v3/udp_proxy.proto
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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 |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| load( | ||
| "//bazel:envoy_build_system.bzl", | ||
| "envoy_cc_library", | ||
| "envoy_extension_package", | ||
| ) | ||
|
|
||
| licenses(["notice"]) # Apache 2 | ||
|
|
||
| envoy_extension_package() | ||
|
|
||
| envoy_cc_library( | ||
| name = "router_interface", | ||
| hdrs = ["router.h"], | ||
| ) | ||
|
|
||
| envoy_cc_library( | ||
| name = "router_lib", | ||
| srcs = ["router_impl.cc"], | ||
| hdrs = ["router_impl.h"], | ||
| deps = [ | ||
| ":router_interface", | ||
| "//source/common/network:cidr_range_lib", | ||
| "//source/common/network:lc_trie_lib", | ||
| "@envoy_api//envoy/extensions/filters/udp/udp_proxy/v3:pkg_cc_proto", | ||
| ], | ||
| ) |
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 |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| #pragma once | ||
|
|
||
| #include <memory> | ||
| #include <string> | ||
| #include <vector> | ||
|
|
||
| #include "envoy/common/pure.h" | ||
| #include "envoy/network/address.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Extensions { | ||
| namespace UdpFilters { | ||
| namespace UdpProxy { | ||
| namespace Router { | ||
|
|
||
| /** | ||
| * RouteEntry is an individual resolved route entry. | ||
| */ | ||
| class RouteEntry { | ||
| public: | ||
| virtual ~RouteEntry() = default; | ||
|
|
||
| /** | ||
| * @return const std::string& the upstream cluster that owns the route. | ||
| */ | ||
| virtual const std::string& clusterName() const PURE; | ||
| }; | ||
|
|
||
| using RouteEntryConstSharedPtr = std::shared_ptr<const RouteEntry>; | ||
|
|
||
| /** | ||
| * Route holds the RouteEntry for a request. | ||
| */ | ||
| class Route { | ||
| public: | ||
| virtual ~Route() = default; | ||
|
|
||
| /** | ||
| * @return the route entry or nullptr if there is no matching route for the request. | ||
| */ | ||
| virtual const RouteEntry* routeEntry() const PURE; | ||
| }; | ||
|
|
||
| using RouteConstSharedPtr = std::shared_ptr<const Route>; | ||
|
|
||
| /** | ||
| * The router configuration. | ||
| */ | ||
| class Config { | ||
| public: | ||
| virtual ~Config() = default; | ||
|
|
||
| virtual RouteConstSharedPtr route(Network::Address::InstanceConstSharedPtr address) const PURE; | ||
| virtual const std::vector<RouteEntryConstSharedPtr>& entries() const PURE; | ||
| }; | ||
|
|
||
| using ConfigConstSharedPtr = std::shared_ptr<const Config>; | ||
|
|
||
| } // namespace Router | ||
| } // namespace UdpProxy | ||
| } // namespace UdpFilters | ||
| } // namespace Extensions | ||
| } // namespace Envoy |
90 changes: 90 additions & 0 deletions
90
source/extensions/filters/udp/udp_proxy/router/router_impl.cc
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 |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| #include "source/extensions/filters/udp/udp_proxy/router/router_impl.h" | ||
|
|
||
| #include "absl/container/flat_hash_set.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Extensions { | ||
| namespace UdpFilters { | ||
| namespace UdpProxy { | ||
| namespace Router { | ||
|
|
||
| ClusterRouteEntry::ClusterRouteEntry( | ||
| const envoy::extensions::filters::udp::udp_proxy::v3::Route& route) | ||
| : cluster_name_(route.route().cluster()) {} | ||
|
|
||
| ClusterRouteEntry::ClusterRouteEntry(const std::string& cluster) : cluster_name_(cluster) {} | ||
|
|
||
| ConfigImpl::ConfigImpl(const envoy::extensions::filters::udp::udp_proxy::v3::UdpProxyConfig& config) | ||
| : cluster_(std::make_shared<ClusterRouteEntry>(config.cluster())), | ||
| source_ips_trie_(buildRouteTrie(config.route_config())), | ||
| entries_(buildEntryList(config.cluster(), config.route_config())) {} | ||
|
|
||
| RouteConstSharedPtr ConfigImpl::route(Network::Address::InstanceConstSharedPtr address) const { | ||
| if (!cluster_->routeEntry()->clusterName().empty()) { | ||
| return cluster_; | ||
| } | ||
|
|
||
| const auto& data = source_ips_trie_.getData(address); | ||
| if (!data.empty()) { | ||
| ASSERT(data.size() == 1); | ||
| return data.back(); | ||
| } | ||
|
|
||
| return nullptr; | ||
| } | ||
|
|
||
| ConfigImpl::SourceIPsTrie ConfigImpl::buildRouteTrie(const RouteConfiguration& config) { | ||
| std::vector<std::pair<RouteConstSharedPtr, std::vector<Network::Address::CidrRange>>> | ||
| source_ips_list; | ||
| source_ips_list.reserve(config.routes().size()); | ||
|
|
||
| auto convertAddress = [](const auto& prefix_ranges) -> std::vector<Network::Address::CidrRange> { | ||
| std::vector<Network::Address::CidrRange> ips; | ||
| ips.reserve(prefix_ranges.size()); | ||
| for (const auto& ip : prefix_ranges) { | ||
| const auto& cidr_range = Network::Address::CidrRange::create(ip); | ||
| ips.push_back(cidr_range); | ||
| } | ||
| return ips; | ||
| }; | ||
|
|
||
| for (auto& route : config.routes()) { | ||
| auto ranges = route.match().source_prefix_ranges(); | ||
| auto route_entry = std::make_shared<ClusterRouteEntry>(route); | ||
|
|
||
| source_ips_list.push_back(make_pair(route_entry, convertAddress(ranges))); | ||
| } | ||
|
|
||
| return {source_ips_list, true}; | ||
| } | ||
|
|
||
| std::vector<RouteEntryConstSharedPtr> ConfigImpl::buildEntryList(const std::string& cluster, | ||
| const RouteConfiguration& config) { | ||
| auto set = absl::flat_hash_set<RouteEntryConstSharedPtr>(); | ||
|
|
||
| if (!cluster.empty()) { | ||
| set.emplace(std::make_shared<ClusterRouteEntry>(cluster)); | ||
| } | ||
|
|
||
| for (const auto& route : config.routes()) { | ||
| auto route_entry = std::make_shared<ClusterRouteEntry>(route); | ||
| auto cluster_name = route_entry->routeEntry()->clusterName(); | ||
| if (!cluster_name.empty()) { | ||
| set.emplace(std::make_shared<ClusterRouteEntry>(cluster_name)); | ||
| } | ||
| } | ||
|
|
||
| auto list = std::vector<RouteEntryConstSharedPtr>(); | ||
| list.reserve(set.size()); | ||
| for (const auto& entry : set) { | ||
| list.push_back(entry); | ||
| } | ||
|
|
||
| return list; | ||
| } | ||
|
|
||
| } // namespace Router | ||
| } // namespace UdpProxy | ||
| } // namespace UdpFilters | ||
| } // namespace Extensions | ||
| } // namespace Envoy |
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 think for this we should go straight to using the new style matchers as there is no reason here not to support sub-linear matching for CIDR ranges, etc. Can you take a look at #17633 and replicate that here? Obviously some matchers won't be supported and we will have to deal with that, but many would be. cc @snowp who can help with questions.
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 have noticed these components, but they focus on HTTP and may be not suitable for UDP. In fact, network filters have their own route matchers like RocketMQ route configuration for RocketMQ proxy and Thrift route configuration for Thrift proxy. UDP proxy is a UDP listener filter which is similar to network filters, so I am not sure if we can use these new style matchers.
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.
The new matching API was developed in order to avoid each protocol from having to define their own matching APIs - the API itself should not be HTTP specific, only loosely coupled with HTTP through extensions and the context in which they are used. Happy to chat more about how we can make use of the new API in this context.
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.
That sounds great! A unified matcher API will bring convenience to development. In UDP, we mainly consider the source prefix and source port.
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.
Yup as Snow said, the new API was designed for this purpose, so let's please switch to it, even if we only support a small subset of matchers.
/wait