Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ proto_library(
"//envoy/extensions/internal_redirect/safe_cross_scheme/v3:pkg",
"//envoy/extensions/matching/common_inputs/environment_variable/v3:pkg",
"//envoy/extensions/matching/input_matchers/consistent_hashing/v3:pkg",
"//envoy/extensions/matching/input_matchers/ip/v3:pkg",
"//envoy/extensions/network/socket_interface/v3:pkg",
"//envoy/extensions/rate_limit_descriptors/expr/v3:pkg",
"//envoy/extensions/request_id/uuid/v3:pkg",
Expand Down
12 changes: 12 additions & 0 deletions api/envoy/extensions/matching/input_matchers/ip/v3/BUILD
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",
],
)
27 changes: 27 additions & 0 deletions api/envoy/extensions/matching/input_matchers/ip/v3/ip.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
syntax = "proto3";

package envoy.extensions.matching.input_matchers.ip.v3;

import "envoy/config/core/v3/address.proto";

import "udpa/annotations/status.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 at least 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.
Comment thread
aguinet marked this conversation as resolved.
Outdated
message Ip {
// Match if the IP belongs to any of these CIDR ranges.
repeated config.core.v3.CidrRange cidr_ranges = 1 [(validate.rules).repeated = {min_items: 1}];
}
1 change: 1 addition & 0 deletions api/versioning/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ proto_library(
"//envoy/extensions/internal_redirect/safe_cross_scheme/v3:pkg",
"//envoy/extensions/matching/common_inputs/environment_variable/v3:pkg",
"//envoy/extensions/matching/input_matchers/consistent_hashing/v3:pkg",
"//envoy/extensions/matching/input_matchers/ip/v3:pkg",
"//envoy/extensions/network/socket_interface/v3:pkg",
"//envoy/extensions/rate_limit_descriptors/expr/v3:pkg",
"//envoy/extensions/request_id/uuid/v3:pkg",
Expand Down
1 change: 1 addition & 0 deletions generated_api_shadow/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ proto_library(
"//envoy/extensions/internal_redirect/safe_cross_scheme/v3:pkg",
"//envoy/extensions/matching/common_inputs/environment_variable/v3:pkg",
"//envoy/extensions/matching/input_matchers/consistent_hashing/v3:pkg",
"//envoy/extensions/matching/input_matchers/ip/v3:pkg",
"//envoy/extensions/network/socket_interface/v3:pkg",
"//envoy/extensions/rate_limit_descriptors/expr/v3:pkg",
"//envoy/extensions/request_id/uuid/v3:pkg",
Expand Down

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.

1 change: 1 addition & 0 deletions source/extensions/extensions_build_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ EXTENSIONS = {
#

"envoy.matching.input_matchers.consistent_hashing": "//source/extensions/matching/input_matchers/consistent_hashing:config",
"envoy.matching.input_matchers.ip": "//source/extensions/matching/input_matchers/ip:config",

#
# Generic Inputs
Expand Down
33 changes: 33 additions & 0 deletions source/extensions/matching/input_matchers/ip/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
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"],
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",
],
)
40 changes: 40 additions & 0 deletions source/extensions/matching/input_matchers/ip/config.cc
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
34 changes: 34 additions & 0 deletions source/extensions/matching/input_matchers/ip/config.h
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 {
Comment thread
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
33 changes: 33 additions & 0 deletions source/extensions/matching/input_matchers/ip/matcher.cc
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)}}) {}
Comment thread
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;
Comment thread
aguinet marked this conversation as resolved.
Outdated
if (ip_str.empty()) {
return false;
}
const auto ip = Network::Utility::parseInternetAddress(std::string{ip_str});
Comment thread
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
29 changes: 29 additions & 0 deletions source/extensions/matching/input_matchers/ip/matcher.h
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_;
Comment thread
aguinet marked this conversation as resolved.
Outdated
};

} // namespace IP
} // namespace InputMatchers
} // namespace Matching
} // namespace Extensions
} // namespace Envoy
31 changes: 31 additions & 0 deletions test/extensions/matching/input_matchers/ip/BUILD
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",
],
)
60 changes: 60 additions & 0 deletions test/extensions/matching/input_matchers/ip/config_test.cc
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
Loading