Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,5 @@ extensions/filters/http/oauth2 @rgs1 @derekargueta @snowp
/*/extensions/filters/http/set_metadata @aguinet @snowp
# Formatters
/*/extensions/formatter/req_without_query @dio @tsaarni
# IP address input matcher
/*/extensions/matching/input_matchers/ip @aguinet @snowp
1 change: 1 addition & 0 deletions api/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,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/quic/crypto_stream/v3:pkg",
"//envoy/extensions/quic/proof_source/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",
],
)
38 changes: 38 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,38 @@
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. For "big" lists of IPs, this matcher is more
// efficient than multiple single IP matcher, that would have a linear cost.
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}];

// The human readable prefix to use when emitting statistics for the IP input
// matcher. Names in the table below are concatenated to this prefix.
//
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: clarify the the following counters will be concatenated to the stat_prefix

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 5aaaa86

// .. csv-table::
// :header: Name, Type, Description
// :widths: 1, 1, 2
//
// ip_parsing_failed, Counter, Total number of IP addresses the matcher was unable to parse
string stat_prefix = 2 [(validate.rules).string = {min_len: 1}];
}
1 change: 1 addition & 0 deletions api/versioning/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,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/quic/crypto_stream/v3:pkg",
"//envoy/extensions/quic/proof_source/v3:pkg",
Expand Down
1 change: 1 addition & 0 deletions docs/root/api-v3/common_messages/common_messages.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ Common messages
../extensions/filters/common/dependency/v3/dependency.proto
../extensions/filters/common/matcher/action/v3/skip_action.proto
../extensions/matching/input_matchers/consistent_hashing/v3/consistent_hashing.proto
../extensions/matching/input_matchers/ip/v3/ip.proto
../extensions/matching/common_inputs/environment_variable/v3/input.proto
1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ New Features
* http: added a new option to upstream HTTP/2 :ref:`keepalive <envoy_v3_api_field_config.core.v3.Http2ProtocolOptions.connection_keepalive>` to send a PING ahead of a new stream if the connection has been idle for a sufficient duration.
* http: added the ability to :ref:`unescape slash sequences<envoy_v3_api_field_extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.path_with_escaped_slashes_action>` in the path. Requests with unescaped slashes can be proxied, rejected or redirected to the new unescaped path. By default this feature is disabled. The default behavior can be overridden through :ref:`http_connection_manager.path_with_escaped_slashes_action<config_http_conn_man_runtime_path_with_escaped_slashes_action>` runtime variable. This action can be selectively enabled for a portion of requests by setting the :ref:`http_connection_manager.path_with_escaped_slashes_action_sampling<config_http_conn_man_runtime_path_with_escaped_slashes_action_enabled>` runtime variable.
* http: added upstream and downstream alpha HTTP/3 support! See :ref:`quic_options <envoy_v3_api_field_config.listener.v3.UdpListenerConfig.quic_options>` for downstream and the new http3_protocol_options in :ref:`http_protocol_options <envoy_v3_api_msg_extensions.upstreams.http.v3.HttpProtocolOptions>` for upstream HTTP/3.
* input matcher: a new input matcher that :ref:`matches an IP address against a list of CIDR ranges <envoy_v3_api_file_envoy/extensions/matching/input_matchers/ip/v3/ip.proto>`.
* jwt_authn: added support to fetch remote jwks asynchronously specified by :ref:`async_fetch <envoy_v3_api_field_extensions.filters.http.jwt_authn.v3.RemoteJwks.async_fetch>`.
* listener: added ability to change an existing listener's address.
* local_rate_limit_filter: added suppoort for locally rate limiting http requests on a per connection basis. This can be enabled by setting the :ref:`local_rate_limit_per_downstream_connection <envoy_v3_api_field_extensions.filters.http.local_ratelimit.v3.LocalRateLimit.local_rate_limit_per_downstream_connection>` field to true.
Expand Down
1 change: 1 addition & 0 deletions generated_api_shadow/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,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/quic/crypto_stream/v3:pkg",
"//envoy/extensions/quic/proof_source/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
5 changes: 5 additions & 0 deletions source/extensions/extensions_metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,11 @@ envoy.matching.input_matchers.consistent_hashing:
- envoy.matching.input_matchers
security_posture: robust_to_untrusted_downstream
status: stable
envoy.matching.input_matchers.ip:
categories:
- envoy.matching.input_matchers
security_posture: robust_to_untrusted_downstream_and_upstream
status: stable
envoy.quic.proof_source.filter_chain:
categories:
- envoy.quic.proof_source
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 = [
"//envoy/matcher:matcher_interface",
"//source/common/network:lc_trie_lib",
],
)

envoy_cc_extension(
name = "config",
srcs = ["config.cc"],
hdrs = ["config.h"],
deps = [
":ip_lib",
"//envoy/matcher:matcher_interface",
"//envoy/registry",
"//envoy/server:factory_context_interface",
"@envoy_api//envoy/extensions/matching/input_matchers/ip/v3:pkg_cc_proto",
],
)
47 changes: 47 additions & 0 deletions source/extensions/matching/input_matchers/ip/config.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "source/extensions/matching/input_matchers/ip/config.h"

namespace Envoy {
namespace Extensions {
namespace Matching {
namespace InputMatchers {
namespace IP {

Envoy::Matcher::InputMatcherFactoryCb
Config::createInputMatcherFactoryCb(const Protobuf::Message& config,
Server::Configuration::FactoryContext& context) {
const auto& ip_config = MessageUtil::downcastAndValidate<
const envoy::extensions::matching::input_matchers::ip::v3::Ip&>(
config, 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);
// We only assert that the range is valid because:
// * if "address" can't be parsed, it will throw an EnvoyException
// * prefix_len can't be < 0 as per the protobuf definition as an uint32_t
// * if prefix_len is too big, CidrRange::create clamps it to a valid value
// => it is thus not possible to create an invalid range.
ASSERT(range.isValid(), "address range should be valid!");
ranges.emplace_back(std::move(range));
}

const std::string& stat_prefix = ip_config.stat_prefix();
Stats::Scope& scope = context.scope();
return [ranges, stat_prefix, &scope]() {
return std::make_unique<Matcher>(ranges, stat_prefix, scope);
};
}
/**
* Static registration for the consistent hashing matcher. @see RegisterFactory.
*/
REGISTER_FACTORY(Config, Envoy::Matcher::InputMatcherFactory);

} // namespace IP
} // namespace InputMatchers
} // namespace Matching
} // namespace Extensions
} // namespace Envoy
33 changes: 33 additions & 0 deletions source/extensions/matching/input_matchers/ip/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#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 "source/common/protobuf/utility.h"
#include "source/extensions/matching/input_matchers/ip/matcher.h"

namespace Envoy {
namespace Extensions {
namespace Matching {
namespace InputMatchers {
namespace IP {

class Config : public Envoy::Matcher::InputMatcherFactory {
public:
Envoy::Matcher::InputMatcherFactoryCb
createInputMatcherFactoryCb(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
49 changes: 49 additions & 0 deletions source/extensions/matching/input_matchers/ip/matcher.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include "source/extensions/matching/input_matchers/ip/matcher.h"

#include "source/common/network/utility.h"

namespace Envoy {
namespace Extensions {
namespace Matching {
namespace InputMatchers {
namespace IP {

namespace {

MatcherStats generateStats(absl::string_view prefix, Stats::Scope& scope) {
return MatcherStats{IP_MATCHER_STATS(POOL_COUNTER_PREFIX(scope, prefix))};
}

} // namespace

Matcher::Matcher(std::vector<Network::Address::CidrRange> const& ranges,
absl::string_view stat_prefix,
Stats::Scope& stat_scope)
: // We could put "false" instead of "true". What matters is that the IP
// belongs to the trie. We could further optimize the storage of LcTrie in
// this case by implementing an LcTrie<void> specialization that doesn't
// store any associated data.
trie_({{true, ranges}}), stats_(generateStats(stat_prefix, stat_scope)) {}

bool Matcher::match(absl::optional<absl::string_view> input) {
if (!input) {
return false;
}
const absl::string_view ip_str = *input;
if (ip_str.empty()) {
return false;
}
const auto ip = Network::Utility::parseInternetAddressNoThrow(std::string{ip_str});
if (!ip) {
stats_.ip_parsing_failed_.inc();
ENVOY_LOG(debug, "IP matcher: unable to parse address '{}'", ip_str);
return false;
}
return !trie_.getData(ip).empty();
}

} // namespace IP
} // namespace InputMatchers
} // namespace Matching
} // namespace Extensions
} // namespace Envoy
39 changes: 39 additions & 0 deletions source/extensions/matching/input_matchers/ip/matcher.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once

#include <vector>

#include "envoy/matcher/matcher.h"
#include "envoy/network/address.h"
#include "envoy/stats/stats_macros.h"

#include "source/common/network/lc_trie.h"

namespace Envoy {
namespace Extensions {
namespace Matching {
namespace InputMatchers {
namespace IP {

#define IP_MATCHER_STATS(COUNTER) COUNTER(ip_parsing_failed)

struct MatcherStats {
IP_MATCHER_STATS(GENERATE_COUNTER_STRUCT);
};

class Matcher : public Envoy::Matcher::InputMatcher, Logger::Loggable<Logger::Id::filter> {
public:
Matcher(std::vector<Network::Address::CidrRange> const& ranges, absl::string_view stat_prefix,
Stats::Scope& stat_scope);
bool match(absl::optional<absl::string_view> input) override;
absl::optional<const MatcherStats> stats() const { return stats_; }

private:
const Network::LcTrie::LcTrie<bool> trie_;
MatcherStats stats_;
};

} // namespace IP
} // namespace InputMatchers
} // namespace Matching
} // namespace Extensions
} // namespace Envoy
Loading