1
#include "source/common/upstream/transport_socket_match_impl.h"
2

            
3
#include "envoy/config/cluster/v3/cluster.pb.h"
4
#include "envoy/config/core/v3/base.pb.h"
5
#include "envoy/registry/registry.h"
6
#include "envoy/server/factory_context.h"
7
#include "envoy/server/transport_socket_config.h"
8

            
9
#include "source/common/config/utility.h"
10
#include "source/common/matcher/exact_map_matcher.h"
11
#include "source/common/matcher/matcher.h"
12
#include "source/common/matcher/prefix_map_matcher.h"
13
#include "source/common/stream_info/filter_state_impl.h"
14

            
15
#include "xds/type/matcher/v3/matcher.pb.h"
16

            
17
namespace Envoy {
18
namespace Upstream {
19

            
20
absl::StatusOr<std::unique_ptr<TransportSocketMatcherImpl>> TransportSocketMatcherImpl::create(
21
    const Protobuf::RepeatedPtrField<envoy::config::cluster::v3::Cluster::TransportSocketMatch>&
22
        socket_matches,
23
    Server::Configuration::TransportSocketFactoryContext& factory_context,
24
14
    Network::UpstreamTransportSocketFactoryPtr& default_factory, Stats::Scope& stats_scope) {
25
14
  absl::Status creation_status = absl::OkStatus();
26
14
  auto ret = std::unique_ptr<TransportSocketMatcherImpl>(new TransportSocketMatcherImpl(
27
14
      socket_matches, factory_context, default_factory, stats_scope, creation_status));
28
14
  RETURN_IF_NOT_OK(creation_status);
29
14
  return ret;
30
14
}
31

            
32
absl::StatusOr<std::unique_ptr<TransportSocketMatcherImpl>> TransportSocketMatcherImpl::create(
33
    const Protobuf::RepeatedPtrField<envoy::config::cluster::v3::Cluster::TransportSocketMatch>&
34
        socket_matches,
35
    OptRef<const xds::type::matcher::v3::Matcher> transport_socket_matcher,
36
    Server::Configuration::TransportSocketFactoryContext& factory_context,
37
18200
    Network::UpstreamTransportSocketFactoryPtr& default_factory, Stats::Scope& stats_scope) {
38
18200
  absl::Status creation_status = absl::OkStatus();
39
18200
  auto ret = std::unique_ptr<TransportSocketMatcherImpl>(
40
18200
      new TransportSocketMatcherImpl(socket_matches, transport_socket_matcher, factory_context,
41
18200
                                     default_factory, stats_scope, creation_status));
42
18200
  RETURN_IF_NOT_OK(creation_status);
43
18198
  return ret;
44
18200
}
45

            
46
TransportSocketMatcherImpl::TransportSocketMatcherImpl(
47
    const Protobuf::RepeatedPtrField<envoy::config::cluster::v3::Cluster::TransportSocketMatch>&
48
        socket_matches,
49
    Server::Configuration::TransportSocketFactoryContext& factory_context,
50
    Network::UpstreamTransportSocketFactoryPtr& default_factory, Stats::Scope& stats_scope,
51
    absl::Status& creation_status)
52
14
    : stats_scope_(stats_scope),
53
14
      default_match_("default", std::move(default_factory), generateStats("default")) {
54
14
  setupLegacySocketMatches(socket_matches, factory_context, creation_status);
55
14
}
56

            
57
TransportSocketMatcherImpl::TransportSocketMatcherImpl(
58
    const Protobuf::RepeatedPtrField<envoy::config::cluster::v3::Cluster::TransportSocketMatch>&
59
        socket_matches,
60
    OptRef<const xds::type::matcher::v3::Matcher> transport_socket_matcher,
61
    Server::Configuration::TransportSocketFactoryContext& factory_context,
62
    Network::UpstreamTransportSocketFactoryPtr& default_factory, Stats::Scope& stats_scope,
63
    absl::Status& creation_status)
64
18200
    : stats_scope_(stats_scope),
65
18200
      default_match_("default", std::move(default_factory), generateStats("default")) {
66
18200
  if (transport_socket_matcher.has_value()) {
67
8
    setupTransportSocketMatcher(transport_socket_matcher.value(), socket_matches, factory_context,
68
8
                                creation_status);
69
18200
  } else {
70
18192
    setupLegacySocketMatches(socket_matches, factory_context, creation_status);
71
18192
  }
72
18200
}
73

            
74
TransportSocketMatchStats
75
18254
TransportSocketMatcherImpl::generateStats(const std::string& prefix) const {
76
18254
  return {ALL_TRANSPORT_SOCKET_MATCH_STATS(POOL_COUNTER_PREFIX(stats_scope_, prefix))};
77
18254
}
78

            
79
TransportSocketMatcher::MatchData TransportSocketMatcherImpl::resolve(
80
    const envoy::config::core::v3::Metadata* endpoint_metadata,
81
    const envoy::config::core::v3::Metadata* locality_metadata,
82
19187
    Network::TransportSocketOptionsConstSharedPtr transport_socket_options) const {
83
  // If matcher is available, use matcher-based resolution.
84
19187
  if (matcher_) {
85
11
    return resolveUsingMatcher(endpoint_metadata, locality_metadata, transport_socket_options);
86
11
  }
87

            
88
  // Fall back to legacy metadata-based matching.
89
  // We want to check for a match in the endpoint metadata first, since that will always take
90
  // precedence for transport socket matching.
91
19179
  for (const auto& match : matches_) {
92
33
    if (Config::Metadata::metadataLabelMatch(
93
33
            match.label_set, endpoint_metadata,
94
33
            Envoy::Config::MetadataFilters::get().ENVOY_TRANSPORT_SOCKET_MATCH, false)) {
95
20
      return {*match.factory, match.stats, match.name};
96
20
    }
97
33
  }
98

            
99
  // If we didn't match on any endpoint-specific metadata, let's check the locality-level metadata.
100
19157
  for (const auto& match : matches_) {
101
10
    if (Config::Metadata::metadataLabelMatch(
102
10
            match.label_set, locality_metadata,
103
10
            Envoy::Config::MetadataFilters::get().ENVOY_TRANSPORT_SOCKET_MATCH, false)) {
104
1
      return {*match.factory, match.stats, match.name};
105
1
    }
106
10
  }
107

            
108
19155
  return {*default_match_.factory, default_match_.stats, default_match_.name};
109
19156
}
110

            
111
void TransportSocketMatcherImpl::setupLegacySocketMatches(
112
    const Protobuf::RepeatedPtrField<envoy::config::cluster::v3::Cluster::TransportSocketMatch>&
113
        socket_matches,
114
    Server::Configuration::TransportSocketFactoryContext& factory_context,
115
18206
    absl::Status& creation_status) {
116
18210
  for (const auto& socket_match : socket_matches) {
117
29
    const auto& socket_config = socket_match.transport_socket();
118
29
    auto& config_factory = Config::Utility::getAndCheckFactory<
119
29
        Server::Configuration::UpstreamTransportSocketConfigFactory>(socket_config);
120
29
    ProtobufTypes::MessagePtr message = Config::Utility::translateToFactoryConfig(
121
29
        socket_config, factory_context.messageValidationVisitor(), config_factory);
122
29
    auto factory_or_error = config_factory.createTransportSocketFactory(*message, factory_context);
123
29
    SET_AND_RETURN_IF_NOT_OK(factory_or_error.status(), creation_status);
124
29
    FactoryMatch factory_match(socket_match.name(), std::move(factory_or_error.value()),
125
29
                               generateStats(absl::StrCat(socket_match.name(), ".")));
126
29
    for (const auto& kv : socket_match.match().fields()) {
127
23
      factory_match.label_set.emplace_back(kv.first, kv.second);
128
23
    }
129
29
    matches_.emplace_back(std::move(factory_match));
130
29
  }
131
18206
}
132

            
133
TransportSocketMatcher::MatchData TransportSocketMatcherImpl::resolveUsingMatcher(
134
    const envoy::config::core::v3::Metadata* endpoint_metadata,
135
    const envoy::config::core::v3::Metadata* locality_metadata,
136
11
    Network::TransportSocketOptionsConstSharedPtr transport_socket_options) const {
137
  // Extract filter state from transport socket options if available.
138
11
  StreamInfo::FilterStateSharedPtr filter_state;
139
11
  if (transport_socket_options) {
140
3
    const auto& shared_objects = transport_socket_options->downstreamSharedFilterStateObjects();
141
3
    if (!shared_objects.empty()) {
142
      // Create a temporary filter state to hold the shared objects for matching.
143
2
      filter_state = std::make_shared<StreamInfo::FilterStateImpl>(
144
2
          StreamInfo::FilterState::LifeSpan::Connection);
145
2
      for (const auto& object : shared_objects) {
146
2
        filter_state->setData(object.name_, object.data_, object.state_type_,
147
2
                              StreamInfo::FilterState::LifeSpan::Connection,
148
2
                              object.stream_sharing_);
149
2
      }
150
2
    }
151
3
  }
152

            
153
11
  Upstream::TransportSocketMatchingData data(endpoint_metadata, locality_metadata,
154
11
                                             filter_state.get());
155
11
  auto on_match = Matcher::evaluateMatch(*matcher_, data);
156
11
  if (on_match.isMatch()) {
157
10
    const auto action = on_match.action();
158
10
    if (action) {
159
10
      const auto& name_action = action->getTyped<TransportSocketNameAction>();
160
10
      const std::string& transport_socket_name = name_action.name();
161
10
      const auto it = transport_sockets_by_name_.find(transport_socket_name);
162
10
      if (it != transport_sockets_by_name_.end()) {
163
        // Stats should already be pre-generated during configuration.
164
10
        auto it_stats = matcher_stats_by_name_.find(transport_socket_name);
165
10
        ASSERT(it_stats != matcher_stats_by_name_.end(),
166
10
               "Stats should be pre-generated for all transport sockets");
167
10
        return {*it->second, it_stats->second, transport_socket_name};
168
10
      }
169
      ENVOY_LOG(warn, "Transport socket '{}' not found, using default", transport_socket_name);
170
    }
171
10
  }
172

            
173
  // Fall back to default if no match or action found.
174
1
  return {*default_match_.factory, default_match_.stats, default_match_.name};
175
11
}
176

            
177
namespace {
178
constexpr absl::string_view kFilterStateInputName =
179
    "envoy.matching.inputs.transport_socket_filter_state";
180
} // namespace
181

            
182
void TransportSocketMatcherImpl::setupTransportSocketMatcher(
183
    const xds::type::matcher::v3::Matcher& transport_socket_matcher,
184
    const Protobuf::RepeatedPtrField<envoy::config::cluster::v3::Cluster::TransportSocketMatch>&
185
        socket_matches,
186
    Server::Configuration::TransportSocketFactoryContext& factory_context,
187
8
    absl::Status& creation_status) {
188
  // Build the transport socket factories by name only if using matcher-based selection.
189
13
  for (const auto& socket_match : socket_matches) {
190
13
    if (socket_match.name().empty()) {
191
1
      creation_status = absl::InvalidArgumentError(
192
1
          "Transport socket name is required when using transport_socket_matcher");
193
1
      return;
194
1
    }
195

            
196
12
    const auto& socket_config = socket_match.transport_socket();
197
12
    auto& config_factory = Config::Utility::getAndCheckFactory<
198
12
        Server::Configuration::UpstreamTransportSocketConfigFactory>(socket_config);
199
12
    ProtobufTypes::MessagePtr message = Config::Utility::translateToFactoryConfig(
200
12
        socket_config, factory_context.messageValidationVisitor(), config_factory);
201
12
    auto factory_or_error = config_factory.createTransportSocketFactory(*message, factory_context);
202
12
    SET_AND_RETURN_IF_NOT_OK(factory_or_error.status(), creation_status);
203

            
204
12
    const std::string& socket_name = socket_match.name();
205
12
    auto [_, inserted] =
206
12
        transport_sockets_by_name_.try_emplace(socket_name, std::move(factory_or_error.value()));
207
12
    if (!inserted) {
208
1
      creation_status = absl::InvalidArgumentError(fmt::format(
209
1
          "Duplicate transport socket name '{}' found in transport_socket_matches", socket_name));
210
1
      return;
211
1
    }
212

            
213
    // Pre-generate stats at config time to avoid mutex contention in the hot path.
214
11
    matcher_stats_by_name_.emplace(socket_name, generateStats(absl::StrCat(socket_name, ".")));
215
11
  }
216

            
217
  // Create a validation visitor that detects if the filter state input is used.
218
6
  class FilterStateDetectionVisitor
219
6
      : public Matcher::MatchTreeValidationVisitor<TransportSocketMatchingData> {
220
6
  public:
221
6
    explicit FilterStateDetectionVisitor(bool& uses_filter_state)
222
6
        : uses_filter_state_(uses_filter_state) {}
223

            
224
6
  private:
225
6
    absl::Status performDataInputValidation(
226
6
        const Matcher::DataInputFactory<TransportSocketMatchingData>& data_input_factory,
227
6
        absl::string_view) override {
228
5
      if (data_input_factory.name() == kFilterStateInputName) {
229
1
        uses_filter_state_ = true;
230
1
      }
231
5
      return absl::OkStatus();
232
5
    }
233

            
234
6
    bool& uses_filter_state_;
235
6
  };
236

            
237
6
  uses_filter_state_ = false;
238
6
  FilterStateDetectionVisitor validation_visitor(uses_filter_state_);
239

            
240
6
  Matcher::MatchTreeFactory<TransportSocketMatchingData, TransportSocketActionFactoryContext>
241
6
      factory(factory_context.serverFactoryContext(), factory_context.serverFactoryContext(),
242
6
              validation_visitor);
243
6
  auto factory_cb = factory.create(transport_socket_matcher);
244
  // Create the matcher instance from the factory callback.
245
6
  matcher_ = factory_cb();
246

            
247
6
  creation_status = absl::OkStatus();
248
6
}
249

            
250
} // namespace Upstream
251
} // namespace Envoy