1
#include "source/common/runtime/runtime_features.h"
2

            
3
#include "envoy/http/codec_runtime_overrides.h"
4

            
5
#include "source/common/singleton/const_singleton.h"
6

            
7
#include "absl/flags/commandlineflag.h"
8
#include "absl/flags/flag.h"
9
#include "absl/flags/reflection.h"
10
#include "absl/strings/match.h"
11
#include "absl/strings/str_replace.h"
12

            
13
#define RUNTIME_GUARD(name) ABSL_FLAG(bool, name, true, "");        // NOLINT
14
#define FALSE_RUNTIME_GUARD(name) ABSL_FLAG(bool, name, false, ""); // NOLINT
15

            
16
// Add additional features here to enable the new code paths by default.
17
//
18
// Per documentation in CONTRIBUTING.md is expected that new high risk code paths be guarded
19
// by runtime feature guards. If you add a guard of the form
20
// RUNTIME_GUARD(envoy_reloadable_features_my_feature_name)
21
// here you can guard code checking against "envoy.reloadable_features.my_feature_name".
22
// Please note the swap of envoy_reloadable_features_ to envoy.reloadable_features.!
23
//
24
// if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.my_feature_name")) {
25
//   [new code path]
26
// else {
27
//   [old_code_path]
28
// }
29
//
30
// Runtime features are true by default, so the new code path is exercised.
31
// To make a runtime feature false by default, use FALSE_RUNTIME_GUARD, and add
32
// a TODO to change it to true.
33
//
34
// If issues are found that require a runtime feature to be disabled, it should be reported
35
// ASAP by filing a bug on github. Overriding non-buggy code is strongly discouraged to avoid the
36
// problem of the bugs being found after the old code path has been removed.
37
RUNTIME_GUARD(envoy_reloadable_features_async_host_selection);
38
RUNTIME_GUARD(envoy_reloadable_features_cel_message_serialize_text_format);
39
RUNTIME_GUARD(envoy_reloadable_features_coalesce_lb_rebuilds_on_batch_update);
40
RUNTIME_GUARD(envoy_reloadable_features_codec_client_enable_idle_timer_only_when_connected);
41
RUNTIME_GUARD(envoy_reloadable_features_decouple_explicit_drain_pools_and_dns_refresh);
42
RUNTIME_GUARD(envoy_reloadable_features_dfp_cluster_resolves_hosts);
43
RUNTIME_GUARD(envoy_reloadable_features_disallow_quic_client_udp_mmsg);
44
RUNTIME_GUARD(envoy_reloadable_features_enable_cel_regex_precompilation);
45
RUNTIME_GUARD(envoy_reloadable_features_enable_cel_response_path_matching);
46
RUNTIME_GUARD(envoy_reloadable_features_enable_compression_bomb_protection);
47
// TODO(adisuissa): enable once the LRS server-self-ads support is fully
48
// implemented, and tested.
49
FALSE_RUNTIME_GUARD(envoy_reloadable_features_enable_lrs_server_self_ads);
50
RUNTIME_GUARD(envoy_reloadable_features_enable_new_query_param_present_match_behavior);
51
RUNTIME_GUARD(envoy_reloadable_features_ext_authz_http_client_retries_respect_user_retry_on);
52
// Ignore the automated "remove this flag" issue: we should keep this for 1 year. Confirm with
53
// @yanjunxiang-google before removing.
54
RUNTIME_GUARD(envoy_reloadable_features_ext_proc_fail_close_spurious_resp);
55
RUNTIME_GUARD(envoy_reloadable_features_ext_proc_inject_data_with_state_update);
56
RUNTIME_GUARD(envoy_reloadable_features_ext_proc_stream_close_optimization);
57
RUNTIME_GUARD(envoy_reloadable_features_generic_proxy_codec_buffer_limit);
58
RUNTIME_GUARD(envoy_reloadable_features_get_header_tag_from_header_map);
59
RUNTIME_GUARD(envoy_reloadable_features_grpc_side_stream_flow_control);
60
RUNTIME_GUARD(envoy_reloadable_features_happy_eyeballs_sort_non_ip_addresses);
61
RUNTIME_GUARD(envoy_reloadable_features_header_mutation_url_encode_query_params);
62
RUNTIME_GUARD(envoy_reloadable_features_http1_close_connection_on_zombie_stream_complete);
63
RUNTIME_GUARD(envoy_reloadable_features_http2_discard_host_header);
64
RUNTIME_GUARD(envoy_reloadable_features_http_async_client_retry_respect_buffer_limits);
65
// Delay deprecation and decommission until UHV is enabled.
66
RUNTIME_GUARD(envoy_reloadable_features_http_reject_path_with_fragment);
67
RUNTIME_GUARD(envoy_reloadable_features_mcp_filter_use_new_metadata_namespace);
68
RUNTIME_GUARD(envoy_reloadable_features_mobile_use_network_observer_registry);
69
RUNTIME_GUARD(envoy_reloadable_features_no_extension_lookup_by_name);
70
RUNTIME_GUARD(envoy_reloadable_features_oauth2_cleanup_cookies);
71
RUNTIME_GUARD(envoy_reloadable_features_oauth2_encrypt_tokens);
72
RUNTIME_GUARD(envoy_reloadable_features_odcds_over_ads_fix);
73
RUNTIME_GUARD(envoy_reloadable_features_on_demand_cluster_no_recreate_stream);
74
RUNTIME_GUARD(envoy_reloadable_features_on_demand_track_end_stream);
75
RUNTIME_GUARD(envoy_reloadable_features_original_dst_rely_on_idle_timeout);
76
RUNTIME_GUARD(envoy_reloadable_features_prefix_map_matcher_resume_after_subtree_miss);
77
RUNTIME_GUARD(envoy_reloadable_features_proxy_protocol_allow_duplicate_tlvs);
78
RUNTIME_GUARD(envoy_reloadable_features_quic_defer_logging_to_ack_listener);
79
RUNTIME_GUARD(envoy_reloadable_features_quic_fix_defer_logging_miss_for_half_closed_stream);
80
// Ignore the automated "remove this flag" issue: we should keep this for 1 year. Confirm with
81
// @danzh2010 or @RyanTheOptimist before removing.
82
RUNTIME_GUARD(envoy_reloadable_features_quic_send_server_preferred_address_to_all_clients);
83
RUNTIME_GUARD(envoy_reloadable_features_quic_signal_headers_only_to_http1_backend);
84
RUNTIME_GUARD(envoy_reloadable_features_quic_upstream_reads_fixed_number_packets);
85
RUNTIME_GUARD(envoy_reloadable_features_quic_upstream_socket_use_address_cache_for_read);
86
RUNTIME_GUARD(envoy_reloadable_features_rbac_match_headers_individually);
87
RUNTIME_GUARD(envoy_reloadable_features_reject_empty_trusted_ca_file);
88
RUNTIME_GUARD(envoy_reloadable_features_report_load_when_rq_active_is_non_zero);
89
RUNTIME_GUARD(envoy_reloadable_features_reset_ignore_upstream_reason);
90
RUNTIME_GUARD(envoy_reloadable_features_reset_with_error);
91
RUNTIME_GUARD(envoy_reloadable_features_safe_http2_options);
92
RUNTIME_GUARD(envoy_reloadable_features_skip_dns_lookup_for_proxied_requests);
93
RUNTIME_GUARD(envoy_reloadable_features_tcp_proxy_odcds_over_ads_fix);
94
RUNTIME_GUARD(envoy_reloadable_features_tcp_proxy_set_idle_timer_immediately_on_new_connection);
95
RUNTIME_GUARD(envoy_reloadable_features_test_feature_true);
96
RUNTIME_GUARD(envoy_reloadable_features_tls_certificate_compression_brotli);
97
RUNTIME_GUARD(envoy_reloadable_features_trace_refresh_after_route_refresh);
98
RUNTIME_GUARD(envoy_reloadable_features_udp_set_do_not_fragment);
99
RUNTIME_GUARD(envoy_reloadable_features_uhv_allow_malformed_url_encoding);
100
RUNTIME_GUARD(envoy_reloadable_features_uri_template_match_on_asterisk);
101
RUNTIME_GUARD(envoy_reloadable_features_use_migration_in_quiche);
102
RUNTIME_GUARD(envoy_reloadable_features_use_response_decoder_handle);
103
RUNTIME_GUARD(envoy_reloadable_features_validate_connect);
104
RUNTIME_GUARD(envoy_reloadable_features_validate_upstream_headers);
105
RUNTIME_GUARD(envoy_reloadable_features_wasm_use_effective_ctx_for_foreign_functions);
106
RUNTIME_GUARD(envoy_reloadable_features_websocket_allow_4xx_5xx_through_filter_chain);
107
RUNTIME_GUARD(envoy_reloadable_features_websocket_enable_timeout_on_upgrade_response);
108
RUNTIME_GUARD(envoy_reloadable_features_xds_failover_to_primary_enabled);
109
RUNTIME_GUARD(envoy_reloadable_features_xds_legacy_delta_skip_subsequent_node);
110

            
111
RUNTIME_GUARD(envoy_restart_features_move_locality_schedulers_to_lb);
112
RUNTIME_GUARD(envoy_restart_features_raise_file_limits);
113
RUNTIME_GUARD(envoy_restart_features_use_eds_cache_for_ads);
114
RUNTIME_GUARD(envoy_restart_features_validate_http3_pseudo_headers);
115
RUNTIME_GUARD(envoy_restart_features_worker_threads_watchdog_fix);
116

            
117
// Begin false flags. Most of them should come with a TODO to flip true.
118

            
119
// Sentinel and test flag.
120
FALSE_RUNTIME_GUARD(envoy_reloadable_features_test_feature_false);
121
// TODO: Flip to true after sufficient testing to enable formatter support for rate limit action
122
// descriptor_value fields by default.
123
FALSE_RUNTIME_GUARD(
124
    envoy_reloadable_features_enable_formatter_for_ratelimit_action_descriptor_value);
125
// TODO(adisuissa) reset to true to enable unified mux by default
126
FALSE_RUNTIME_GUARD(envoy_reloadable_features_unified_mux);
127
// Used to track if runtime is initialized.
128
FALSE_RUNTIME_GUARD(envoy_reloadable_features_runtime_initialized);
129
// TODO(alyssawilk, renjietang) figure out what to do with this for optimal defaults
130
#if defined(__ANDROID_API__)
131
ABSL_FLAG(bool, envoy_reloadable_features_always_use_v6, true, "");
132
#else
133
FALSE_RUNTIME_GUARD(envoy_reloadable_features_always_use_v6);
134
#endif
135
// TODO(vikaschoudhary16) flip this to true only after all the
136
// TcpProxy::Filter::HttpStreamDecoderFilterCallbacks are implemented or commented as unnecessary
137
FALSE_RUNTIME_GUARD(envoy_restart_features_upstream_http_filters_with_tcp_proxy);
138
// TODO(danzh) false deprecate it once QUICHE has its own enable/disable flag.
139
FALSE_RUNTIME_GUARD(envoy_reloadable_features_quic_reject_all);
140
// TODO(#10646) change to true when UHV is sufficiently tested
141
// For more information about Universal Header Validation, please see
142
// https://github.com/envoyproxy/envoy/issues/10646
143
FALSE_RUNTIME_GUARD(envoy_reloadable_features_enable_universal_header_validator);
144
// TODO(alyssar) evaluate and either make this a config knob or remove.
145
FALSE_RUNTIME_GUARD(envoy_reloadable_features_reresolve_null_addresses);
146
// TODO(alyssar) evaluate and either make this a config knob or remove.
147
FALSE_RUNTIME_GUARD(envoy_reloadable_features_reresolve_if_no_connections);
148
// TODO(adisuissa): flip to true after this is out of alpha mode.
149
FALSE_RUNTIME_GUARD(envoy_restart_features_xds_failover_support);
150
// TODO(abeyad): evaluate and either make this a config knob or remove.
151
FALSE_RUNTIME_GUARD(envoy_reloadable_features_dns_cache_set_ip_version_to_remove);
152
// TODO(fredyw): evaluate and either make this a config knob or remove.
153
FALSE_RUNTIME_GUARD(envoy_reloadable_features_quic_no_tcp_delay);
154
// Adding runtime flag to use balsa_parser for http_inspector.
155
FALSE_RUNTIME_GUARD(envoy_reloadable_features_http_inspector_use_balsa_parser);
156
// TODO(danzh) re-enable it when the issue of preferring TCP over v6 rather than QUIC over v4 is
157
// fixed.
158
FALSE_RUNTIME_GUARD(envoy_reloadable_features_http3_happy_eyeballs);
159
// TODO(renjietang): Evaluate and make this a config knob or remove.
160
FALSE_RUNTIME_GUARD(envoy_reloadable_features_use_canonical_suffix_for_quic_brokenness);
161
// TODO(abeyad): Evaluate and make this a config knob or remove.
162
FALSE_RUNTIME_GUARD(envoy_reloadable_features_use_canonical_suffix_for_srtt);
163
// TODO(abeyad): Evaluate and make this a config knob or remove.
164
FALSE_RUNTIME_GUARD(envoy_reloadable_features_use_canonical_suffix_for_initial_rtt_estimate);
165
// TODO(abeyad): Flip to true after prod testing. Advanced filtering applies to all IP reserved
166
// range addresses.
167
FALSE_RUNTIME_GUARD(envoy_reloadable_features_mobile_ipv6_probe_advanced_filtering);
168
// TODO(botengyao): flip to true after canarying the feature internally without problems.
169
FALSE_RUNTIME_GUARD(envoy_reloadable_features_connection_close_through_filter_manager);
170
// TODO(adisuissa): flip to true after all xDS types use the new subscription
171
// method, and this is tested extensively.
172
FALSE_RUNTIME_GUARD(envoy_reloadable_features_xdstp_based_config_singleton_subscriptions);
173
// TODO(abeyad): Flip to true after prod testing.
174
FALSE_RUNTIME_GUARD(envoy_reloadable_features_disable_quic_rx_queue_overflow_socket_options);
175
// TODO(abeyad): Flip to true after prod testing.
176
FALSE_RUNTIME_GUARD(envoy_reloadable_features_disable_quic_ip_packet_info_socket_options);
177

            
178
// A flag to set the maximum TLS version for google_grpc client to TLS1.2, when needed for
179
// compliance restrictions.
180
FALSE_RUNTIME_GUARD(envoy_reloadable_features_google_grpc_disable_tls_13);
181

            
182
// TODO(yanavlasov): Flip to true after prod testing.
183
// Controls whether a stream stays open when HTTP/2 or HTTP/3 upstream half closes
184
// before downstream.
185
FALSE_RUNTIME_GUARD(envoy_reloadable_features_allow_multiplexed_upstream_half_close);
186

            
187
FALSE_RUNTIME_GUARD(envoy_reloadable_features_ext_proc_graceful_grpc_close);
188

            
189
FALSE_RUNTIME_GUARD(envoy_reloadable_features_getaddrinfo_no_ai_flags);
190

            
191
// Flag to remove legacy route formatter support in header parser
192
// Flip to true after two release periods.
193
FALSE_RUNTIME_GUARD(envoy_reloadable_features_remove_legacy_route_formatter);
194

            
195
// TODO(grnmeira):
196
// Enables the new DNS implementation, a merged implementation of
197
// strict and logical DNS clusters. This new implementation will
198
// take over the split ones, and will be used as a base for the
199
// implementation of on-demand DNS.
200
FALSE_RUNTIME_GUARD(envoy_reloadable_features_enable_new_dns_implementation);
201
// Force a local reply from upstream envoy for reverse connections.
202
FALSE_RUNTIME_GUARD(envoy_reloadable_features_reverse_conn_force_local_reply);
203
// RELEASE_ASSERT when upstream stream detects UAF of downstream response decoder instance.
204
FALSE_RUNTIME_GUARD(envoy_reloadable_features_abort_when_accessing_dead_decoder);
205
// TODO(pradeepcrao): Create a config option to enable this instead after
206
// testing.
207
FALSE_RUNTIME_GUARD(envoy_restart_features_use_cached_grpc_client_for_xds);
208
// Runtime guard to revert back to old non-RFC-compliant CONNECT behavior without Host header.
209
// TODO(vinaykul): Drop this false-runtime-guard when deemed safe with RFC 9110 compliant CONNECT.
210
FALSE_RUNTIME_GUARD(envoy_reloadable_features_http_11_proxy_connect_legacy_format);
211
// TODO(tsaarni): Flip to true after prod testing or remove.
212
FALSE_RUNTIME_GUARD(envoy_reloadable_features_fixed_heap_use_allocated);
213
// Flip back to true once performance aligns with nghttp2 and
214
// https://github.com/envoyproxy/envoy/issues/40070 is resolved.
215
FALSE_RUNTIME_GUARD(envoy_reloadable_features_http2_use_oghttp2);
216
// When enabled, dynamic modules metrics will be registered as custom stat namespaces, causing
217
// the namespace prefix to be stripped from prometheus output and no envoy_ prefix added.
218
// This is the legacy behavior. When disabled which is the default, metrics appear with the
219
// standard envoy_ prefix followed by the namespace.
220
FALSE_RUNTIME_GUARD(envoy_reloadable_features_dynamic_modules_strip_custom_stat_prefix);
221
// TODO(haoyuewang): Flip true after prod testing.
222
FALSE_RUNTIME_GUARD(envoy_reloadable_features_quic_disable_data_read_immediately);
223

            
224
// Block of non-boolean flags. Use of int flags is deprecated. Do not add more.
225
ABSL_FLAG(uint64_t, re2_max_program_size_error_level, 100, ""); // NOLINT
226
ABSL_FLAG(uint64_t, re2_max_program_size_warn_level,            // NOLINT
227
          std::numeric_limits<uint32_t>::max(), "");            // NOLINT
228

            
229
namespace Envoy {
230
namespace Runtime {
231
namespace {
232

            
233
134136
std::string swapPrefix(std::string name) {
234
134136
  return absl::StrReplaceAll(name, {{"envoy_", "envoy."}, {"features_", "features."}});
235
134136
}
236

            
237
} // namespace
238

            
239
1242
RuntimeFeatures::RuntimeFeatures() {
240
1242
  absl::flat_hash_map<absl::string_view, absl::CommandLineFlag*> flags = absl::GetAllFlags();
241
341124
  for (auto& it : flags) {
242
341124
    absl::string_view name = it.second->Name();
243
341124
    if ((!absl::StartsWith(name, "envoy_reloadable_features_") &&
244
341124
         !absl::StartsWith(name, "envoy_restart_features_")) ||
245
341124
        !it.second->TryGet<bool>().has_value()) {
246
206988
      continue;
247
206988
    }
248
134136
    std::string envoy_name = swapPrefix(std::string(name));
249
134136
    all_features_.emplace(envoy_name, it.second);
250
134136
  }
251
1242
}
252

            
253
2352257
absl::CommandLineFlag* RuntimeFeatures::getFlag(absl::string_view feature) const {
254
2352257
  auto it = all_features_.find(feature);
255
2352257
  if (it == all_features_.end()) {
256
437
    return nullptr;
257
437
  }
258
2351820
  return it->second;
259
2352257
}
260

            
261
29311
bool hasRuntimePrefix(absl::string_view feature) {
262
  // Track Envoy reloadable and restart features, excluding synthetic QUIC flags
263
  // which are not tracked in the list below.
264
29311
  return (absl::StartsWith(feature, "envoy.reloadable_features.") &&
265
29311
          !absl::StartsWith(feature, "envoy.reloadable_features.FLAGS_envoy_quic")) ||
266
29311
         absl::StartsWith(feature, "envoy.restart_features.");
267
29311
}
268

            
269
63103
bool isRuntimeFeature(absl::string_view feature) {
270
63103
  return RuntimeFeaturesDefaults::get().getFlag(feature) != nullptr;
271
63103
}
272

            
273
6
bool isLegacyRuntimeFeature(absl::string_view feature) {
274
6
  return feature == Http::MaxRequestHeadersCountOverrideKey ||
275
6
         feature == Http::MaxResponseHeadersCountOverrideKey ||
276
6
         feature == Http::MaxRequestHeadersSizeOverrideKey ||
277
6
         feature == Http::MaxResponseHeadersSizeOverrideKey;
278
6
}
279

            
280
2237973
bool runtimeFeatureEnabled(absl::string_view feature) {
281
2237973
  absl::CommandLineFlag* flag = RuntimeFeaturesDefaults::get().getFlag(feature);
282
2237973
  if (flag == nullptr) {
283
    IS_ENVOY_BUG(absl::StrCat("Unable to find runtime feature ", feature));
284
    return false;
285
  }
286
  // We validate in map creation that the flag is a boolean.
287
2237973
  return flag->TryGet<bool>().value();
288
2237973
}
289

            
290
1761
uint64_t getInteger(absl::string_view feature, uint64_t default_value) {
291
  // DO NOT ADD MORE FLAGS HERE. This function deprecated.
292
1761
  if (absl::StartsWith(feature, "re2.")) {
293
1760
    if (feature == "re2.max_program_size.error_level") {
294
880
      return absl::GetFlag(FLAGS_re2_max_program_size_error_level);
295
880
    } else if (feature == "re2.max_program_size.warn_level") {
296
880
      return absl::GetFlag(FLAGS_re2_max_program_size_warn_level);
297
880
    }
298
1760
  }
299
1
  IS_ENVOY_BUG(absl::StrCat("requested an unsupported integer ", feature));
300
1
  return default_value;
301
1761
}
302

            
303
17704
void markRuntimeInitialized() {
304
17704
  maybeSetRuntimeGuard("envoy.reloadable_features.runtime_initialized", true);
305
17704
}
306

            
307
2156
bool isRuntimeInitialized() {
308
2156
  return runtimeFeatureEnabled("envoy.reloadable_features.runtime_initialized");
309
2156
}
310

            
311
51169
void maybeSetRuntimeGuard(absl::string_view name, bool value) {
312
51169
  absl::CommandLineFlag* flag = RuntimeFeaturesDefaults::get().getFlag(name);
313
51169
  if (flag == nullptr) {
314
    IS_ENVOY_BUG(absl::StrCat("Unable to find runtime feature ", name));
315
    return;
316
  }
317
51169
  std::string err;
318
51169
  flag->ParseFrom(value ? "true" : "false", &err);
319
51169
}
320

            
321
340
void maybeSetDeprecatedInts(absl::string_view name, uint32_t value) {
322
340
  if (!absl::StartsWith(name, "envoy.") && !absl::StartsWith(name, "re2.")) {
323
315
    return;
324
315
  }
325

            
326
  // DO NOT ADD MORE FLAGS HERE. This function deprecated.
327
25
  else if (name == "re2.max_program_size.error_level") {
328
3
    absl::SetFlag(&FLAGS_re2_max_program_size_error_level, value);
329
24
  } else if (name == "re2.max_program_size.warn_level") {
330
1
    absl::SetFlag(&FLAGS_re2_max_program_size_warn_level, value);
331
1
  }
332
340
}
333

            
334
} // namespace Runtime
335
} // namespace Envoy