LCOV - code coverage report
Current view: top level - source/extensions/common/matcher - trie_matcher.h (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 4 91 4.4 %
Date: 2024-01-05 06:35:25 Functions: 5 27 18.5 %

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include "envoy/matcher/matcher.h"
       4             : #include "envoy/network/filter.h"
       5             : #include "envoy/server/factory_context.h"
       6             : 
       7             : #include "source/common/matcher/matcher.h"
       8             : #include "source/common/network/lc_trie.h"
       9             : #include "source/common/network/utility.h"
      10             : 
      11             : #include "xds/type/matcher/v3/ip.pb.h"
      12             : #include "xds/type/matcher/v3/ip.pb.validate.h"
      13             : 
      14             : namespace Envoy {
      15             : namespace Extensions {
      16             : namespace Common {
      17             : namespace Matcher {
      18             : 
      19             : using ::Envoy::Matcher::DataInputFactoryCb;
      20             : using ::Envoy::Matcher::DataInputGetResult;
      21             : using ::Envoy::Matcher::DataInputPtr;
      22             : using ::Envoy::Matcher::evaluateMatch;
      23             : using ::Envoy::Matcher::MatchState;
      24             : using ::Envoy::Matcher::MatchTree;
      25             : using ::Envoy::Matcher::OnMatch;
      26             : using ::Envoy::Matcher::OnMatchFactory;
      27             : using ::Envoy::Matcher::OnMatchFactoryCb;
      28             : 
      29             : template <class DataType> struct TrieNode {
      30             :   size_t index_;
      31             :   uint32_t prefix_len_;
      32             :   bool exclusive_;
      33             :   std::shared_ptr<OnMatch<DataType>> on_match_;
      34             : 
      35           0 :   friend bool operator==(const TrieNode<DataType>& lhs, const TrieNode<DataType>& rhs) {
      36           0 :     return lhs.index_ == rhs.index_ && lhs.prefix_len_ == rhs.prefix_len_ &&
      37           0 :            lhs.exclusive_ == rhs.exclusive_ && lhs.on_match_ == rhs.on_match_;
      38           0 :   }
      39             :   template <typename H>
      40             :   friend H AbslHashValue(H h, // NOLINT(readability-identifier-naming)
      41           0 :                          const TrieNode<DataType>& node) {
      42           0 :     return H::combine(std::move(h), node.index_, node.prefix_len_, node.exclusive_, node.on_match_);
      43           0 :   }
      44             : };
      45             : 
      46             : template <class DataType> struct TrieNodeComparator {
      47           0 :   inline bool operator()(const TrieNode<DataType>& lhs, const TrieNode<DataType>& rhs) const {
      48           0 :     if (lhs.prefix_len_ > rhs.prefix_len_) {
      49           0 :       return true;
      50           0 :     }
      51           0 :     if (lhs.prefix_len_ == rhs.prefix_len_ && lhs.index_ < rhs.index_) {
      52           0 :       return true;
      53           0 :     }
      54           0 :     return false;
      55           0 :   }
      56             : };
      57             : 
      58             : /**
      59             :  * Implementation of a `sublinear` LC-trie matcher.
      60             :  */
      61             : template <class DataType> class TrieMatcher : public MatchTree<DataType> {
      62             : public:
      63             :   TrieMatcher(DataInputPtr<DataType>&& data_input, absl::optional<OnMatch<DataType>> on_no_match,
      64             :               const std::shared_ptr<Network::LcTrie::LcTrie<TrieNode<DataType>>>& trie)
      65           0 :       : data_input_(std::move(data_input)), on_no_match_(std::move(on_no_match)), trie_(trie) {
      66           0 :     auto input_type = data_input_->dataInputType();
      67           0 :     if (input_type != Envoy::Matcher::DefaultMatchingDataType) {
      68           0 :       throw EnvoyException(
      69           0 :           absl::StrCat("Unsupported data input type: ", input_type,
      70           0 :                        ", currently only string type is supported in trie matcher"));
      71           0 :     }
      72           0 :   }
      73             : 
      74           0 :   typename MatchTree<DataType>::MatchResult match(const DataType& data) override {
      75           0 :     const auto input = data_input_->get(data);
      76           0 :     if (input.data_availability_ != DataInputGetResult::DataAvailability::AllDataAvailable) {
      77           0 :       return {MatchState::UnableToMatch, absl::nullopt};
      78           0 :     }
      79           0 :     if (absl::holds_alternative<absl::monostate>(input.data_)) {
      80           0 :       return {MatchState::MatchComplete, on_no_match_};
      81           0 :     }
      82           0 :     const Network::Address::InstanceConstSharedPtr addr =
      83           0 :         Network::Utility::parseInternetAddressNoThrow(absl::get<std::string>(input.data_));
      84           0 :     if (!addr) {
      85           0 :       return {MatchState::MatchComplete, on_no_match_};
      86           0 :     }
      87           0 :     auto values = trie_->getData(addr);
      88             :     // The candidates returned by the LC trie are not in any specific order, so we
      89             :     // sort them by the prefix length first (longest first), and the order of declaration second.
      90           0 :     std::sort(values.begin(), values.end(), TrieNodeComparator<DataType>());
      91           0 :     bool first = true;
      92           0 :     for (const auto& node : values) {
      93           0 :       if (!first && node.exclusive_) {
      94           0 :         continue;
      95           0 :       }
      96           0 :       if (node.on_match_->action_cb_) {
      97           0 :         return {MatchState::MatchComplete, OnMatch<DataType>{node.on_match_->action_cb_, nullptr}};
      98           0 :       }
      99             :       // Resume any subtree matching to preserve backtracking progress.
     100           0 :       auto matched = evaluateMatch(*node.on_match_->matcher_, data);
     101           0 :       if (matched.match_state_ == MatchState::UnableToMatch) {
     102           0 :         return {MatchState::UnableToMatch, absl::nullopt};
     103           0 :       }
     104           0 :       if (matched.match_state_ == MatchState::MatchComplete && matched.result_) {
     105           0 :         return {MatchState::MatchComplete, OnMatch<DataType>{matched.result_, nullptr}};
     106           0 :       }
     107           0 :       if (first) {
     108           0 :         first = false;
     109           0 :       }
     110           0 :     }
     111           0 :     return {MatchState::MatchComplete, on_no_match_};
     112           0 :   }
     113             : 
     114             : private:
     115             :   const DataInputPtr<DataType> data_input_;
     116             :   const absl::optional<OnMatch<DataType>> on_no_match_;
     117             :   std::shared_ptr<Network::LcTrie::LcTrie<TrieNode<DataType>>> trie_;
     118             : };
     119             : 
     120             : template <class DataType>
     121             : class TrieMatcherFactoryBase : public ::Envoy::Matcher::CustomMatcherFactory<DataType> {
     122             : public:
     123             :   ::Envoy::Matcher::MatchTreeFactoryCb<DataType>
     124             :   createCustomMatcherFactoryCb(const Protobuf::Message& config,
     125             :                                Server::Configuration::ServerFactoryContext& factory_context,
     126             :                                DataInputFactoryCb<DataType> data_input,
     127             :                                absl::optional<OnMatchFactoryCb<DataType>> on_no_match,
     128           0 :                                OnMatchFactory<DataType>& on_match_factory) override {
     129           0 :     const auto& typed_config =
     130           0 :         MessageUtil::downcastAndValidate<const xds::type::matcher::v3::IPMatcher&>(
     131           0 :             config, factory_context.messageValidationVisitor());
     132           0 :     std::vector<OnMatchFactoryCb<DataType>> match_children;
     133           0 :     match_children.reserve(typed_config.range_matchers().size());
     134           0 :     for (const auto& range_matcher : typed_config.range_matchers()) {
     135           0 :       match_children.push_back(*on_match_factory.createOnMatch(range_matcher.on_match()));
     136           0 :     }
     137           0 :     std::vector<std::pair<TrieNode<DataType>, std::vector<Network::Address::CidrRange>>> data;
     138           0 :     data.reserve(match_children.size());
     139           0 :     size_t i = 0;
     140             :     // Ranges might have variable prefix length so we cannot combine them into one node because
     141             :     // then the matched prefix length cannot be determined.
     142           0 :     for (const auto& range_matcher : typed_config.range_matchers()) {
     143           0 :       auto on_match = std::make_shared<OnMatch<DataType>>(match_children[i++]());
     144           0 :       for (const auto& range : range_matcher.ranges()) {
     145           0 :         TrieNode<DataType> node = {i, range.prefix_len().value(), range_matcher.exclusive(),
     146           0 :                                    on_match};
     147           0 :         data.push_back({node, {Network::Address::CidrRange::create(range)}});
     148           0 :       }
     149           0 :     }
     150           0 :     auto lc_trie = std::make_shared<Network::LcTrie::LcTrie<TrieNode<DataType>>>(data);
     151           0 :     return [data_input, lc_trie, on_no_match]() {
     152           0 :       return std::make_unique<TrieMatcher<DataType>>(
     153           0 :           data_input(), on_no_match ? absl::make_optional(on_no_match.value()()) : absl::nullopt,
     154           0 :           lc_trie);
     155           0 :     };
     156           0 :   };
     157           2 :   ProtobufTypes::MessagePtr createEmptyConfigProto() override {
     158           2 :     return std::make_unique<xds::type::matcher::v3::IPMatcher>();
     159           2 :   }
     160          78 :   std::string name() const override { return "envoy.matching.custom_matchers.trie_matcher"; }
     161             : };
     162             : 
     163             : class NetworkTrieMatcherFactory : public TrieMatcherFactoryBase<Network::MatchingData> {};
     164             : class UdpNetworkTrieMatcherFactory : public TrieMatcherFactoryBase<Network::UdpMatchingData> {};
     165             : class HttpTrieMatcherFactory : public TrieMatcherFactoryBase<Http::HttpMatchingData> {};
     166             : 
     167             : } // namespace Matcher
     168             : } // namespace Common
     169             : } // namespace Extensions
     170             : } // namespace Envoy

Generated by: LCOV version 1.15