diff --git a/doc/admin-guide/files/records.config.en.rst b/doc/admin-guide/files/records.config.en.rst index 62504b4b88a..600925ea7ff 100644 --- a/doc/admin-guide/files/records.config.en.rst +++ b/doc/admin-guide/files/records.config.en.rst @@ -3935,6 +3935,15 @@ TLS v1.3 0-RTT Configuration Set to ``1`` to allow HTTP parameters on early data requests. +SNI Routing +----------- + +.. ts:cv:: CONFIG proxy.config.tunnel.activity_check_period INT 0 + :units: seconds + + Frequency of checking the activity of SNI Routing Tunnel. Set to ``0`` to disable monitoring of the activity of the SNI tunnels. + The feature is disabled by default. + OCSP Stapling Configuration =========================== diff --git a/doc/admin-guide/monitoring/statistics/core/ssl.en.rst b/doc/admin-guide/monitoring/statistics/core/ssl.en.rst index 3cd4622729e..4f51c6c79e3 100644 --- a/doc/admin-guide/monitoring/statistics/core/ssl.en.rst +++ b/doc/admin-guide/monitoring/statistics/core/ssl.en.rst @@ -232,3 +232,8 @@ SSL/TLS Incoming client SSL connections terminated due to an unsupported or disabled version of SSL/TLS, since statistics collection began. + +.. ts:stat:: global proxy.process.tunnel.current_active_connections integer + :type: gauge + + A gauge of current active SNI Routing Tunnels. diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc index e3429bbbbe6..fd00ca027fd 100644 --- a/mgmt/RecordsConfig.cc +++ b/mgmt/RecordsConfig.cc @@ -980,6 +980,14 @@ static const RecordElement RecordsConfig[] = , {RECT_CONFIG, "proxy.config.hostdb.host_file.interval", RECD_INT, "86400", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL} , + //########################################################################## + //# + //# SNI Routing + //# + //########################################################################## + {RECT_CONFIG, "proxy.config.tunnel.activity_check_period", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-100]", RECA_NULL} + , + //########################################################################## //# //# HTTP diff --git a/proxy/http/HttpConfig.cc b/proxy/http/HttpConfig.cc index 52674cbf049..685eca3a56d 100644 --- a/proxy/http/HttpConfig.cc +++ b/proxy/http/HttpConfig.cc @@ -310,6 +310,12 @@ register_stat_callbacks() RecRegisterRawStat(http_rsb, RECT_PROCESS, "proxy.process.http.websocket.current_active_client_connections", RECD_INT, RECP_NON_PERSISTENT, (int)http_websocket_current_active_client_connections_stat, RecRawStatSyncSum); HTTP_CLEAR_DYN_STAT(http_websocket_current_active_client_connections_stat); + + // Tunnel Stats + RecRegisterRawStat(http_rsb, RECT_PROCESS, "proxy.process.tunnel.current_active_connections", RECD_INT, RECP_NON_PERSISTENT, + (int)tunnel_current_active_connections_stat, RecRawStatSyncSum); + HTTP_CLEAR_DYN_STAT(tunnel_current_active_connections_stat); + // Current Transaction Stats RecRegisterRawStat(http_rsb, RECT_PROCESS, "proxy.process.http.current_client_transactions", RECD_INT, RECP_NON_PERSISTENT, (int)http_current_client_transactions_stat, RecRawStatSyncSum); @@ -1146,6 +1152,8 @@ HttpConfig::startup() HttpEstablishStaticConfigByte(c.oride.attach_server_session_to_client, "proxy.config.http.attach_server_session_to_client"); HttpEstablishStaticConfigLongLong(c.oride.max_proxy_cycles, "proxy.config.http.max_proxy_cycles"); + HttpEstablishStaticConfigLongLong(c.oride.tunnel_activity_check_period, "proxy.config.tunnel.activity_check_period"); + HttpEstablishStaticConfigLongLong(c.http_request_line_max_size, "proxy.config.http.request_line_max_size"); HttpEstablishStaticConfigLongLong(c.http_hdr_field_max_size, "proxy.config.http.header_field_max_size"); @@ -1447,6 +1455,7 @@ HttpConfig::reconfigure() } params->oride.attach_server_session_to_client = m_master.oride.attach_server_session_to_client; params->oride.max_proxy_cycles = m_master.oride.max_proxy_cycles; + params->oride.tunnel_activity_check_period = m_master.oride.tunnel_activity_check_period; params->http_request_line_max_size = m_master.http_request_line_max_size; params->http_hdr_field_max_size = m_master.http_hdr_field_max_size; diff --git a/proxy/http/HttpConfig.h b/proxy/http/HttpConfig.h index f977ea5fecb..5f77ece86c0 100644 --- a/proxy/http/HttpConfig.h +++ b/proxy/http/HttpConfig.h @@ -62,6 +62,7 @@ enum { http_current_client_connections_stat, http_current_active_client_connections_stat, http_websocket_current_active_client_connections_stat, + tunnel_current_active_connections_stat, http_current_client_transactions_stat, http_total_incoming_connections_stat, http_current_server_transactions_stat, @@ -507,6 +508,7 @@ struct OverridableHttpConfigParams { MgmtByte uncacheable_requests_bypass_parent = 1; MgmtByte attach_server_session_to_client = 0; MgmtInt max_proxy_cycles = 0; + MgmtInt tunnel_activity_check_period = 0; MgmtByte forward_connect_method = 0; diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc index 2c82924a744..5e983271071 100644 --- a/proxy/http/HttpSM.cc +++ b/proxy/http/HttpSM.cc @@ -1236,8 +1236,7 @@ HttpSM::state_raw_http_server_open(int event, void *data) do_http_server_open(true); return 0; - case NET_EVENT_OPEN: - + case NET_EVENT_OPEN: { // Record the VC in our table server_entry = vc_table.new_entry(); server_entry->vc = netvc = static_cast(data); @@ -1248,8 +1247,13 @@ HttpSM::state_raw_http_server_open(int event, void *data) netvc->set_inactivity_timeout(get_server_inactivity_timeout()); netvc->set_active_timeout(get_server_active_timeout()); t_state.current.server->clear_connect_fail(); - break; + if (get_tunnel_type() != SNIRoutingType::NONE) { + tunnel.mark_tls_tunnel_active(); + } + + break; + } case VC_EVENT_ERROR: case NET_EVENT_OPEN_FAILED: t_state.current.state = HttpTransact::OPEN_RAW_ERROR; @@ -5281,7 +5285,7 @@ HttpSM::do_http_server_open(bool raw) SSLNetVConnection *ssl_vc = dynamic_cast(ua_txn->get_netvc()); if (ssl_vc && raw) { tls_upstream = ssl_vc->upstream_tls(); - + _tunnel_type = ssl_vc->tunnel_type(); // ALPN on TLS Partial Blind Tunnel - set negotiated ALPN id if (ssl_vc->tunnel_type() == SNIRoutingType::PARTIAL_BLIND) { int pid = ssl_vc->get_negotiated_protocol_id(); @@ -8260,6 +8264,12 @@ HttpSM::is_redirect_required() return redirect_required; } +SNIRoutingType +HttpSM::get_tunnel_type() const +{ + return _tunnel_type; +} + // Fill in the client protocols used. Return the number of entries populated. int HttpSM::populate_client_protocol(std::string_view *result, int n) const diff --git a/proxy/http/HttpSM.h b/proxy/http/HttpSM.h index ac7c74512eb..d6545f9a551 100644 --- a/proxy/http/HttpSM.h +++ b/proxy/http/HttpSM.h @@ -403,6 +403,7 @@ class HttpSM : public Continuation, public PluginUserArgs // See if we should allow the transaction // based on sni and host name header values void check_sni_host(); + SNIRoutingType get_tunnel_type() const; protected: int reentrancy_count = 0; @@ -701,7 +702,8 @@ class HttpSM : public Continuation, public PluginUserArgs PostDataBuffers _postbuf; int _client_connection_id = -1, _client_transaction_id = -1; int _client_transaction_priority_weight = -1, _client_transaction_priority_dependence = -1; - bool _from_early_data = false; + bool _from_early_data = false; + SNIRoutingType _tunnel_type = SNIRoutingType::NONE; }; // Function to get the cache_sm object - YTS Team, yamsat diff --git a/proxy/http/HttpTunnel.cc b/proxy/http/HttpTunnel.cc index 3f7a3b53e47..08837c6290e 100644 --- a/proxy/http/HttpTunnel.cc +++ b/proxy/http/HttpTunnel.cc @@ -494,6 +494,7 @@ HttpTunnel::kill_tunnel() ink_assert(producer.alive == false); } active = false; + this->mark_tls_tunnel_inactive(); this->deallocate_buffers(); this->reset(); } @@ -1169,6 +1170,10 @@ HttpTunnel::producer_handler(int event, HttpTunnelProducer *p) switch (event) { case VC_EVENT_READ_READY: + if (sm->get_tunnel_type() != SNIRoutingType::NONE) { + mark_tls_tunnel_active(); + } + // Data read from producer, reenable consumers for (c = p->consumer_list.head; c; c = c->link.next) { if (c->alive && c->write_vio) { @@ -1629,6 +1634,14 @@ HttpTunnel::close_vc(HttpTunnelConsumer *c) int HttpTunnel::main_handler(int event, void *data) { + if (event == HTTP_TUNNEL_EVENT_ACTIVITY_CHECK) { + if (!_is_tls_tunnel_active()) { + mark_tls_tunnel_inactive(); + } + + return EVENT_DONE; + } + HttpTunnelProducer *p = nullptr; HttpTunnelConsumer *c = nullptr; bool sm_callback = false; @@ -1689,3 +1702,70 @@ void HttpTunnel::internal_error() { } + +void +HttpTunnel::mark_tls_tunnel_active() +{ + _tls_tunnel_last_update = Thread::get_hrtime(); + + if (_tls_tunnel_active) { + return; + } + + _tls_tunnel_active = true; + HTTP_INCREMENT_DYN_STAT(tunnel_current_active_connections_stat); + + _schedule_tls_tunnel_activity_check_event(); +} + +void +HttpTunnel::mark_tls_tunnel_inactive() +{ + if (!_tls_tunnel_active) { + return; + } + + _tls_tunnel_active = false; + HTTP_DECREMENT_DYN_STAT(tunnel_current_active_connections_stat); + + if (_tls_tunnel_activity_check_event) { + _tls_tunnel_activity_check_event->cancel(); + _tls_tunnel_activity_check_event = nullptr; + } +} + +void +HttpTunnel::_schedule_tls_tunnel_activity_check_event() +{ + if (_tls_tunnel_activity_check_event) { + return; + } + + ink_hrtime period = HRTIME_SECONDS(sm->t_state.txn_conf->tunnel_activity_check_period); + + if (period > 0) { + EThread *ethread = this_ethread(); + _tls_tunnel_activity_check_event = ethread->schedule_every_local(this, period, HTTP_TUNNEL_EVENT_ACTIVITY_CHECK); + } +} + +bool +HttpTunnel::_is_tls_tunnel_active() const +{ + ink_hrtime period = HRTIME_SECONDS(sm->t_state.txn_conf->tunnel_activity_check_period); + + // This should not be called if period is 0 + ink_release_assert(period > 0); + + ink_hrtime now = Thread::get_hrtime(); + + Debug("http_tunnel", "now=%" PRId64 " last_update=%" PRId64, now, _tls_tunnel_last_update); + + // In some cases, m_tls_tunnel_last_update could be larger than now, because it's using cached current time. + // - e.g. comparing Thread::cur_time of different threads. + if (_tls_tunnel_last_update >= now || now - _tls_tunnel_last_update <= period) { + return true; + } + + return false; +} diff --git a/proxy/http/HttpTunnel.h b/proxy/http/HttpTunnel.h index f4c8888ee61..146cfdacd02 100644 --- a/proxy/http/HttpTunnel.h +++ b/proxy/http/HttpTunnel.h @@ -48,6 +48,7 @@ #define HTTP_TUNNEL_EVENT_DONE (HTTP_TUNNEL_EVENTS_START + 1) #define HTTP_TUNNEL_EVENT_PRECOMPLETE (HTTP_TUNNEL_EVENTS_START + 2) #define HTTP_TUNNEL_EVENT_CONSUMER_DETACH (HTTP_TUNNEL_EVENTS_START + 3) +#define HTTP_TUNNEL_EVENT_ACTIVITY_CHECK (HTTP_TUNNEL_EVENTS_START + 4) #define HTTP_TUNNEL_STATIC_PRODUCER (VConnection *)!0 @@ -284,6 +285,10 @@ class HttpTunnel : public Continuation bool has_cache_writer() const; bool has_consumer_besides_client() const; + // CAVEAT: This is different from the HttpTunnel::active flag + void mark_tls_tunnel_active(); + void mark_tls_tunnel_inactive(); + HttpTunnelProducer *add_producer(VConnection *vc, int64_t nbytes, IOBufferReader *reader_start, HttpProducerHandler sm_handler, HttpTunnelType_t vc_type, const char *name); @@ -334,6 +339,8 @@ class HttpTunnel : public Continuation void finish_all_internal(HttpTunnelProducer *p, bool chain); void update_stats_after_abort(HttpTunnelType_t t); void producer_run(HttpTunnelProducer *p); + void _schedule_tls_tunnel_activity_check_event(); + bool _is_tls_tunnel_active() const; HttpTunnelProducer *get_producer(VIO *vio); HttpTunnelConsumer *get_consumer(VIO *vio); @@ -349,6 +356,11 @@ class HttpTunnel : public Continuation bool active = false; + // Activity check for SNI Routing Tunnel + bool _tls_tunnel_active = false; + Event *_tls_tunnel_activity_check_event = nullptr; + ink_hrtime _tls_tunnel_last_update = 0; + /// State data about flow control. FlowControl flow_state;