-
Notifications
You must be signed in to change notification settings - Fork 5.5k
IP Matcher on a list of CIDR ranges #16592
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
Changes from 1 commit
cd38be7
16c5477
ef8fe28
bdc5274
6d7f90b
e940002
a0360a7
e027a05
04b5afe
c4b0f24
354ac87
d565e78
3b2391d
7960aa3
5fac922
551ddcb
6365fea
ff91bae
ea28e0d
700187a
ff53fe4
5aaaa86
13fd781
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. | ||
|
|
||
| load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") | ||
|
|
||
| licenses(["notice"]) # Apache 2 | ||
|
|
||
| api_proto_package( | ||
| deps = [ | ||
| "//envoy/config/core/v3:pkg", | ||
| "@com_github_cncf_udpa//udpa/annotations:pkg", | ||
| ], | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| syntax = "proto3"; | ||
|
|
||
| package envoy.extensions.matching.input_matchers.ip.v3; | ||
|
|
||
| import "envoy/config/core/v3/address.proto"; | ||
|
|
||
| import "google/protobuf/wrappers.proto"; | ||
|
|
||
| import "udpa/annotations/status.proto"; | ||
| import "udpa/annotations/versioning.proto"; | ||
| import "validate/validate.proto"; | ||
|
|
||
| option java_package = "io.envoyproxy.envoy.extensions.matching.input_matchers.ip.v3"; | ||
| option java_outer_classname = "IpProto"; | ||
| option java_multiple_files = true; | ||
| option (udpa.annotations.file_status).package_version_status = ACTIVE; | ||
|
|
||
| // [#protodoc-title: IP matcher] | ||
| // [#extension: envoy.matching.input_matchers.ip] | ||
|
|
||
| // This input matcher matches IPv4 or IPv6 addresses against a list of CIDR | ||
| // ranges. It returns true if and only if the input IP belongs to one of these | ||
| // CIDR ranges. Internally, it uses a Level-Compressed trie, as described in | ||
| // the paper `IP-address lookup using LC-tries | ||
| // <https://www.nada.kth.se/~snilsson/publications/IP-address-lookup-using-LC-tries/>`_ | ||
| // by S. Nilsson and G. Karlsson. | ||
| message IP { | ||
|
Contributor
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. Naming convention suggests this should be
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. Fixed in 16c5477 |
||
| // Match if the IP belongs to any of these CIDR ranges | ||
|
Contributor
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. nit: end comments with a period.
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. Fixed in 16c5477 . Thanks for the review! |
||
| repeated config.core.v3.CidrRange cidr_ranges = 1 [(validate.rules).repeated = {min_items: 1}]; | ||
| } | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| load( | ||
| "//bazel:envoy_build_system.bzl", | ||
| "envoy_cc_extension", | ||
| "envoy_cc_library", | ||
| "envoy_extension_package", | ||
| ) | ||
|
|
||
| licenses(["notice"]) # Apache 2 | ||
|
|
||
| envoy_extension_package() | ||
|
|
||
| envoy_cc_library( | ||
| name = "ip_lib", | ||
| srcs = ["matcher.cc"], | ||
| hdrs = ["matcher.h"], | ||
| deps = [ | ||
| "//include/envoy/matcher:matcher_interface", | ||
| "//source/common/network:lc_trie_lib", | ||
| ], | ||
| ) | ||
|
|
||
| envoy_cc_extension( | ||
| name = "config", | ||
| srcs = ["config.cc"], | ||
| hdrs = ["config.h"], | ||
| category = "envoy.matching.input_matchers", | ||
| security_posture = "robust_to_untrusted_downstream_and_upstream", | ||
| deps = [ | ||
| ":ip_lib", | ||
| "//include/envoy/matcher:matcher_interface", | ||
| "//include/envoy/registry", | ||
| "//include/envoy/server:factory_context_interface", | ||
| "@envoy_api//envoy/extensions/matching/input_matchers/ip/v3:pkg_cc_proto", | ||
| ], | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| #include "extensions/matching/input_matchers/ip/config.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Extensions { | ||
| namespace Matching { | ||
| namespace InputMatchers { | ||
| namespace IP { | ||
|
|
||
| Envoy::Matcher::InputMatcherPtr | ||
| IPConfig::createInputMatcher(const Protobuf::Message& config, | ||
| Server::Configuration::FactoryContext& factory_context) { | ||
| const auto& ip_config = MessageUtil::downcastAndValidate< | ||
| const envoy::extensions::matching::input_matchers::ip::v3::IP&>( | ||
| config, factory_context.messageValidationVisitor()); | ||
|
|
||
| const auto& cidr_ranges = ip_config.cidr_ranges(); | ||
| std::vector<Network::Address::CidrRange> ranges; | ||
| ranges.reserve(cidr_ranges.size()); | ||
| for (const auto& cidr_range : cidr_ranges) { | ||
| const std::string& address = cidr_range.address_prefix(); | ||
| const uint32_t prefix_len = cidr_range.prefix_len().value(); | ||
| const auto range = Network::Address::CidrRange::create(address, prefix_len); | ||
| if (!range.isValid()) { | ||
| throw EnvoyException(fmt::format("ip range {}/{} is invalid", address, prefix_len)); | ||
| } | ||
| ranges.emplace_back(std::move(range)); | ||
| } | ||
|
|
||
| return std::make_unique<Matcher>(std::move(ranges)); | ||
| } | ||
| /** | ||
| * Static registration for the consistent hashing matcher. @see RegisterFactory. | ||
| */ | ||
| REGISTER_FACTORY(IPConfig, Envoy::Matcher::InputMatcherFactory); | ||
|
|
||
| } // namespace IP | ||
| } // namespace InputMatchers | ||
| } // namespace Matching | ||
| } // namespace Extensions | ||
| } // namespace Envoy |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| #pragma once | ||
|
|
||
| #include "envoy/extensions/matching/input_matchers/ip/v3/ip.pb.h" | ||
| #include "envoy/extensions/matching/input_matchers/ip/v3/ip.pb.validate.h" | ||
| #include "envoy/matcher/matcher.h" | ||
| #include "envoy/server/factory_context.h" | ||
|
|
||
| #include "common/protobuf/utility.h" | ||
|
|
||
| #include "extensions/matching/input_matchers/ip/matcher.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Extensions { | ||
| namespace Matching { | ||
| namespace InputMatchers { | ||
| namespace IP { | ||
|
|
||
| class IPConfig : public Envoy::Matcher::InputMatcherFactory { | ||
|
aguinet marked this conversation as resolved.
Outdated
|
||
| public: | ||
| Envoy::Matcher::InputMatcherPtr | ||
| createInputMatcher(const Protobuf::Message& config, | ||
| Server::Configuration::FactoryContext& factory_context) override; | ||
|
|
||
| std::string name() const override { return "envoy.matching.matchers.ip"; } | ||
|
|
||
| ProtobufTypes::MessagePtr createEmptyConfigProto() override { | ||
| return std::make_unique<envoy::extensions::matching::input_matchers::ip::v3::IP>(); | ||
| } | ||
| }; | ||
| } // namespace IP | ||
| } // namespace InputMatchers | ||
| } // namespace Matching | ||
| } // namespace Extensions | ||
| } // namespace Envoy | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| #include "extensions/matching/input_matchers/ip/matcher.h" | ||
|
|
||
| #include "common/network/utility.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Extensions { | ||
| namespace Matching { | ||
| namespace InputMatchers { | ||
| namespace IP { | ||
|
|
||
| Matcher::Matcher(std::vector<Network::Address::CidrRange>&& ranges) | ||
| : trie_({{true, std::move(ranges)}}) {} | ||
|
aguinet marked this conversation as resolved.
Outdated
|
||
|
|
||
| bool Matcher::match(absl::optional<absl::string_view> input) { | ||
| if (!input) { | ||
| return false; | ||
| } | ||
| const absl::string_view& ip_str = *input; | ||
|
aguinet marked this conversation as resolved.
Outdated
|
||
| if (ip_str.empty()) { | ||
| return false; | ||
| } | ||
| const auto ip = Network::Utility::parseInternetAddress(std::string{ip_str}); | ||
|
aguinet marked this conversation as resolved.
Outdated
|
||
| if (!ip) { | ||
| return false; | ||
| } | ||
| return !trie_.getData(ip).empty(); | ||
| } | ||
|
|
||
| } // namespace IP | ||
| } // namespace InputMatchers | ||
| } // namespace Matching | ||
| } // namespace Extensions | ||
| } // namespace Envoy | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| #pragma once | ||
|
|
||
| #include <vector> | ||
|
|
||
| #include "envoy/matcher/matcher.h" | ||
| #include "envoy/network/address.h" | ||
|
|
||
| #include "common/network/lc_trie.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Extensions { | ||
| namespace Matching { | ||
| namespace InputMatchers { | ||
| namespace IP { | ||
|
|
||
| class Matcher : public Envoy::Matcher::InputMatcher { | ||
| public: | ||
| Matcher(std::vector<Network::Address::CidrRange>&& ranges); | ||
| bool match(absl::optional<absl::string_view> input) override; | ||
|
|
||
| private: | ||
| Network::LcTrie::LcTrie<bool> trie_; | ||
|
aguinet marked this conversation as resolved.
Outdated
|
||
| }; | ||
|
|
||
| } // namespace IP | ||
| } // namespace InputMatchers | ||
| } // namespace Matching | ||
| } // namespace Extensions | ||
| } // namespace Envoy | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| load( | ||
| "//bazel:envoy_build_system.bzl", | ||
| "envoy_package", | ||
| ) | ||
| load( | ||
| "//test/extensions:extensions_build_system.bzl", | ||
| "envoy_extension_cc_test", | ||
| ) | ||
|
|
||
| licenses(["notice"]) # Apache 2 | ||
|
|
||
| envoy_package() | ||
|
|
||
| envoy_extension_cc_test( | ||
| name = "config_test", | ||
| srcs = ["config_test.cc"], | ||
| extension_name = "envoy.matching.input_matchers.ip", | ||
| deps = [ | ||
| "//source/extensions/matching/input_matchers/ip:config", | ||
| "//test/mocks/server:factory_context_mocks", | ||
| ], | ||
| ) | ||
|
|
||
| envoy_extension_cc_test( | ||
| name = "matcher_test", | ||
| srcs = ["matcher_test.cc"], | ||
| extension_name = "envoy.matching.input_matchers.ip", | ||
| deps = [ | ||
| "//source/extensions/matching/input_matchers/ip:ip_lib", | ||
| ], | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| #include "extensions/matching/input_matchers/ip/config.h" | ||
|
|
||
| #include "test/mocks/server/factory_context.h" | ||
|
|
||
| #include "gtest/gtest.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Extensions { | ||
| namespace Matching { | ||
| namespace InputMatchers { | ||
| namespace IP { | ||
|
|
||
| TEST(ConfigTest, TestConfig) { | ||
| NiceMock<Server::Configuration::MockFactoryContext> context; | ||
|
|
||
| const std::string yaml_string = R"EOF( | ||
| name: ip | ||
| typed_config: | ||
| "@type": type.googleapis.com/envoy.extensions.matching.input_matchers.ip.v3.IP | ||
| cidr_ranges: | ||
| - address_prefix: 192.0.2.0 | ||
| prefix_len: 24 | ||
| )EOF"; | ||
|
|
||
| envoy::config::core::v3::TypedExtensionConfig config; | ||
| TestUtility::loadFromYaml(yaml_string, config); | ||
|
|
||
| IPConfig factory; | ||
| auto message = Config::Utility::translateAnyToFactoryConfig( | ||
| config.typed_config(), ProtobufMessage::getStrictValidationVisitor(), factory); | ||
| auto matcher = factory.createInputMatcher(*message, context); | ||
| EXPECT_NE(nullptr, matcher); | ||
| } | ||
|
|
||
| TEST(ConfigTest, InvalidConfig) { | ||
| NiceMock<Server::Configuration::MockFactoryContext> context; | ||
|
|
||
| const std::string yaml_string = R"EOF( | ||
| name: ip | ||
| typed_config: | ||
| "@type": type.googleapis.com/envoy.extensions.matching.input_matchers.ip.v3.IP | ||
| cidr_ranges: | ||
| - address_prefix: foo | ||
| prefix_len: 10 | ||
| )EOF"; | ||
|
|
||
| envoy::config::core::v3::TypedExtensionConfig config; | ||
| TestUtility::loadFromYaml(yaml_string, config); | ||
|
|
||
| IPConfig factory; | ||
| auto message = Config::Utility::translateAnyToFactoryConfig( | ||
| config.typed_config(), ProtobufMessage::getStrictValidationVisitor(), factory); | ||
| EXPECT_THROW_WITH_MESSAGE(factory.createInputMatcher(*message, context), EnvoyException, | ||
| "malformed IP address: foo"); | ||
| } | ||
| } // namespace IP | ||
| } // namespace InputMatchers | ||
| } // namespace Matching | ||
| } // namespace Extensions | ||
| } // namespace Envoy |
Uh oh!
There was an error while loading. Please reload this page.