Skip to content

Commit 9d8b797

Browse files
author
bneradt
committed
HTTP/3 via OpenSSL QUIC
OpenSSL 3.5 can terminate QUIC connections directly, but ATS only had a quiche-backed HTTP/3 listener. Operators who want to use the system OpenSSL QUIC stack needed a separate downstream backend without changing the existing quiche path or origin HTTP/3 scope. This adds an optional ENABLE_OPENSSL_QUIC backend that uses OpenSSL's native QUIC listener and stream APIs for downstream HTTP/3. This keeps the backend mutually exclusive with quiche, exposes TS_HAS_OPENSSL_QUIC, and shares ATS's existing HTTP/3 stream handling above the transport. This also installs native-QUIC TLS callbacks for ALPN and SNI certificate selection before ATS has a QUIC NetVC to bind. OpenSSL native QUIC does not make a selected SSL_CTX certificate active via SSL_set_SSL_CTX alone, so this applies the selected cert, key, and chain to the connection SSL. This also broadens client-side HTTP/3 tests to run with either backend and hardens transaction cleanup when OpenSSL closes stream state before ATS finishes teardown. This caches stream identifiers, declines listener-time QUIC tickets until a NetVC is bound, and adds focused H3 lifecycle and session-ticket coverage.
1 parent 1381172 commit 9d8b797

32 files changed

Lines changed: 2434 additions & 74 deletions

CMakeLists.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ set(ENABLE_TPROXY
215215
'X' where X is a number to use as the IP_TRANSPARENT sockopt,
216216
anything else to enable."
217217
)
218+
option(ENABLE_OPENSSL_QUIC "Use OpenSSL native QUIC (default OFF)")
218219
option(ENABLE_QUICHE "Use quiche (default OFF)")
219220

220221
option(ENABLE_EXAMPLE "Build example directory (default OFF)")
@@ -284,6 +285,7 @@ include(CheckOpenSSLIsBoringSSL)
284285
include(CheckOpenSSLIsQuictls)
285286
include(CheckOpenSSLIsAwsLc)
286287
include(CheckOpenSSLHasQuicTlsCbs)
288+
include(CheckOpenSSLHasNativeQuic)
287289
find_package(OpenSSL REQUIRED)
288290
check_openssl_is_boringssl(SSLLIB_IS_BORINGSSL BORINGSSL_VERSION "${OPENSSL_INCLUDE_DIR}")
289291
check_openssl_is_awslc(SSLLIB_IS_AWSLC AWSLC_VERSION "${OPENSSL_INCLUDE_DIR}")
@@ -323,6 +325,8 @@ if(SSLLIB_HAS_QUIC_TLS_CBS
323325
add_compile_definitions(TS_OPENSSL_QUIC_TLS_CBS_COMPAT)
324326
endif()
325327

328+
check_openssl_has_native_quic(SSLLIB_HAS_NATIVE_QUIC "${OPENSSL_INCLUDE_DIR}")
329+
326330
if(ENABLE_PROFILER)
327331
find_package(profiler REQUIRED)
328332
set(TS_HAS_PROFILER ${profiler_FOUND})
@@ -338,6 +342,25 @@ elseif(TS_HAS_MIMALLOC)
338342
link_libraries(mimalloc)
339343
endif()
340344

345+
if(ENABLE_OPENSSL_QUIC AND ENABLE_QUICHE)
346+
message(FATAL_ERROR "ENABLE_OPENSSL_QUIC and ENABLE_QUICHE are mutually exclusive QUIC backends")
347+
endif()
348+
349+
if(ENABLE_OPENSSL_QUIC)
350+
if(NOT SSLLIB_HAS_NATIVE_QUIC)
351+
message(FATAL_ERROR "OpenSSL native QUIC support requires OpenSSL 3.5 or newer with OSSL_QUIC_server_method")
352+
endif()
353+
if(SSLLIB_IS_BORINGSSL
354+
OR SSLLIB_IS_AWSLC
355+
OR SSLLIB_IS_QUICTLS
356+
)
357+
message(FATAL_ERROR "OpenSSL native QUIC support requires upstream OpenSSL 3.5 or newer")
358+
endif()
359+
set(TS_HAS_OPENSSL_QUIC TRUE)
360+
set(TS_USE_QUIC TRUE)
361+
message(STATUS "Using OpenSSL native QUIC")
362+
endif()
363+
341364
if(ENABLE_QUICHE)
342365
if(TS_OPENSSL_QUIC_TLS_CBS_COMPAT)
343366
set(quiche_USE_STATIC TRUE)

CMakePresets.json

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,10 @@
185185
"inherits": ["ci"],
186186
"cacheVariables": {
187187
"ENABLE_PROBES": "ON",
188-
"OPENSSL_ROOT_DIR": "/opt/openssl-quic",
189188
"opentelemetry_ROOT": "/opt",
190-
"CURL_ROOT": "/opt",
191189
"wamr_ROOT": "/opt",
192-
"ENABLE_CRIPTS": "ON"
190+
"ENABLE_CRIPTS": "ON",
191+
"ENABLE_OPENSSL_QUIC": "ON"
193192
}
194193
},
195194
{
@@ -205,20 +204,19 @@
205204
"name": "ci-fedora-quiche",
206205
"displayName": "CI Fedora Quiche",
207206
"description": "CI Pipeline config for Fedora Linux (quiche build)",
208-
"inherits": ["ci"],
207+
"inherits": ["ci-fedora"],
209208
"cacheVariables": {
210209
"OPENSSL_ROOT_DIR": "/opt/h3-tools-boringssl/boringssl",
211210
"quiche_ROOT": "/opt/h3-tools-boringssl/quiche",
212-
"opentelemetry_ROOT": "/opt",
213211
"CURL_ROOT": "/opt",
214-
"wamr_ROOT": "/opt",
215212
"CMAKE_INSTALL_PREFIX": "/tmp/ats-quiche",
213+
"ENABLE_OPENSSL_QUIC": "OFF",
216214
"ENABLE_QUICHE": "ON"
217215
}
218216
},
219217
{
220218
"name": "ci-fedora-autest",
221-
"displayName": "CI Fedora Quiche Autest",
219+
"displayName": "CI Fedora Autest",
222220
"description": "CI Pipeline config for Fedora Linux (autest build)",
223221
"inherits": ["ci-fedora", "autest"]
224222
},
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#######################
2+
#
3+
# Licensed to the Apache Software Foundation (ASF) under one or more contributor license
4+
# agreements. See the NOTICE file distributed with this work for additional information regarding
5+
# copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0
6+
# (the "License"); you may not use this file except in compliance with the License. You may obtain
7+
# a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software distributed under the License
12+
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13+
# or implied. See the License for the specific language governing permissions and limitations under
14+
# the License.
15+
#
16+
#######################
17+
18+
function(CHECK_OPENSSL_HAS_NATIVE_QUIC OUT_VAR OPENSSL_INCLUDE_DIR)
19+
set(CHECK_PROGRAM
20+
"
21+
#include <openssl/ssl.h>
22+
#include <openssl/quic.h>
23+
#include <cstdint>
24+
25+
int main() {
26+
const SSL_METHOD *method = OSSL_QUIC_server_method();
27+
SSL *ssl = nullptr;
28+
uint64_t value = 0;
29+
return method == nullptr ||
30+
SSL_new_listener == nullptr ||
31+
SSL_listen == nullptr ||
32+
SSL_handle_events == nullptr ||
33+
SSL_accept_connection == nullptr ||
34+
SSL_accept_stream == nullptr ||
35+
SSL_new_stream == nullptr ||
36+
SSL_stream_conclude == nullptr ||
37+
SSL_get_stream_id == nullptr ||
38+
SSL_get_stream_type == nullptr ||
39+
SSL_get_stream_read_state == nullptr ||
40+
SSL_get_stream_write_buf_avail(ssl, &value) ||
41+
SSL_get_conn_close_info == nullptr ||
42+
SSL_shutdown_ex == nullptr ||
43+
SSL_set_default_stream_mode == nullptr ||
44+
SSL_set_blocking_mode == nullptr ||
45+
SSL_set_event_handling_mode(ssl, SSL_VALUE_EVENT_HANDLING_MODE_EXPLICIT) ||
46+
SSL_set_feature_request_uint(ssl, SSL_VALUE_QUIC_IDLE_TIMEOUT, value) ||
47+
SSL_set_incoming_stream_policy == nullptr;
48+
}
49+
"
50+
)
51+
set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}")
52+
set(CMAKE_REQUIRED_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
53+
include(CheckCXXSourceCompiles)
54+
check_cxx_source_compiles("${CHECK_PROGRAM}" ${OUT_VAR})
55+
set(${OUT_VAR}
56+
${${OUT_VAR}}
57+
PARENT_SCOPE
58+
)
59+
endfunction()

include/iocore/net/QUICMultiCertConfigLoader.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525

2626
#include "iocore/net/SSLMultiCertConfigLoader.h"
2727
#include "iocore/eventsystem/ConfigProcessor.h"
28+
#include "tscore/ink_config.h"
29+
30+
#include <string_view>
2831

2932
class QUICCertConfig
3033
{
@@ -40,6 +43,10 @@ class QUICCertConfig
4043
static int _config_id;
4144
};
4245

46+
#if TS_HAS_OPENSSL_QUIC
47+
std::string_view quic_sni_server_name(SSL *ssl);
48+
#endif
49+
4350
class QUICMultiCertConfigLoader : public SSLMultiCertConfigLoader
4451
{
4552
public:
@@ -48,7 +55,12 @@ class QUICMultiCertConfigLoader : public SSLMultiCertConfigLoader
4855
virtual SSL_CTX *default_server_ssl_ctx() override;
4956

5057
private:
51-
const char *_debug_tag() const override;
58+
const char *_debug_tag() const override;
59+
#if TS_HAS_OPENSSL_QUIC
60+
virtual void _set_handshake_callbacks(SSL_CTX *ctx) override;
61+
virtual bool _set_alpn_callback(SSL_CTX *ctx) override;
62+
virtual bool _set_curves(SSL_CTX *ctx) override;
63+
#endif
5264
virtual bool _setup_session_cache(SSL_CTX *ctx) override;
5365
virtual bool _set_cipher_suites_for_legacy_versions(SSL_CTX *ctx) override;
5466
virtual bool _set_info_callback(SSL_CTX *ctx) override;

include/iocore/net/TLSSNISupport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ class TLSSNISupport
122122
* @return True if the servername was set successfully, false otherwise.
123123
*/
124124
bool set_sni_server_name(SSL *ssl, char const *name);
125+
void set_sni_server_name(std::string_view name);
125126

126127
/**
127128
* Get the server name in SNI

include/iocore/net/quic/QUICConfig.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@
2424
#pragma once
2525

2626
#include <openssl/ssl.h>
27+
#include "tscore/ink_config.h"
28+
29+
#if TS_HAS_QUICHE
2730
#include <quiche.h>
31+
#endif
2832

2933
#include "iocore/eventsystem/ConfigProcessor.h"
3034
#include "iocore/net/SSLTypes.h"
@@ -89,7 +93,9 @@ class QUICConfigParams : public ConfigInfo
8993

9094
bool disable_http_0_9() const;
9195

96+
#if TS_HAS_QUICHE
9297
quiche_cc_algorithm get_cc_algorithm() const;
98+
#endif
9399

94100
private:
95101
static int _connection_table_size;
@@ -163,3 +169,4 @@ class QUICConfig
163169
};
164170

165171
SSL_CTX *quic_new_ssl_ctx();
172+
SSL_CTX *quic_new_server_ssl_ctx();

include/iocore/net/quic/QUICStream.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,25 @@
3131
#include "iocore/net/quic/QUICConnection.h"
3232
#include "iocore/net/quic/QUICDebugNames.h"
3333

34-
#include <quiche.h>
34+
#include <cstddef>
35+
#include <cstdint>
3536

3637
class QUICStreamAdapter;
3738
class QUICStreamStateListener;
3839

40+
class QUICStreamIO
41+
{
42+
public:
43+
using ErrorCode = uint64_t;
44+
45+
virtual ~QUICStreamIO() = default;
46+
47+
virtual int64_t read_stream(QUICStreamId stream_id, uint8_t *buf, size_t len, bool &fin, ErrorCode &error_code) = 0;
48+
virtual bool stream_read_finished(QUICStreamId stream_id) = 0;
49+
virtual int64_t stream_write_capacity(QUICStreamId stream_id) = 0;
50+
virtual int64_t write_stream(QUICStreamId stream_id, uint8_t const *buf, size_t len, bool fin, ErrorCode &error_code) = 0;
51+
};
52+
3953
/**
4054
* @brief QUIC Stream
4155
* TODO: This is similar to Http2Stream. Need to think some integration.
@@ -61,8 +75,8 @@ class QUICStream
6175
void stop_sending(QUICStreamErrorUPtr error);
6276
void reset(QUICStreamErrorUPtr error);
6377

64-
void receive_data(quiche_conn *quiche_con);
65-
void send_data(quiche_conn *quiche_con);
78+
void receive_data(QUICStreamIO &stream_io);
79+
int64_t send_data(QUICStreamIO &stream_io);
6680

6781
/*
6882
* QUICApplication need to call one of these functions when it process VC_EVENT_*

include/iocore/net/quic/QUICStreamManager.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ class QUICStreamManager : public QUICStreamStateListener
4848

4949
QUICStream *create_stream(QUICStreamId stream_id, QUICConnectionError &err);
5050

51-
QUICConnectionErrorUPtr create_uni_stream(QUICStreamId new_stream_id);
52-
QUICConnectionErrorUPtr create_bidi_stream(QUICStreamId new_stream_id);
53-
QUICConnectionErrorUPtr delete_stream(QUICStreamId new_stream_id);
54-
void reset_stream(QUICStreamId stream_id, QUICStreamErrorUPtr error);
51+
virtual QUICConnectionErrorUPtr create_uni_stream(QUICStreamId &new_stream_id);
52+
virtual QUICConnectionErrorUPtr create_bidi_stream(QUICStreamId &new_stream_id);
53+
QUICConnectionErrorUPtr delete_stream(QUICStreamId new_stream_id);
54+
void reset_stream(QUICStreamId stream_id, QUICStreamErrorUPtr error);
5555

5656
void set_default_application(QUICApplication *app);
5757

@@ -63,4 +63,6 @@ class QUICStreamManager : public QUICStreamStateListener
6363
protected:
6464
QUICContext *_context = nullptr;
6565
QUICApplicationMap *_app_map = nullptr;
66+
QUICStreamId _next_local_bidi_stream_id{0};
67+
QUICStreamId _next_local_uni_stream_id{0};
6668
};

include/proxy/http3/Http3Frame.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ class Http3DataFrame : public Http3Frame
9898
// Head of IOBufferBlock chain to send
9999
Ptr<IOBufferBlock> _whole_frame;
100100
uint64_t _payload_len = 0;
101+
102+
protected:
103+
bool _parse() override;
101104
};
102105

103106
//

include/proxy/http3/Http3HeaderVIOAdaptor.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
#include "proxy/http3/Http3FrameHandler.h"
2929

3030
class HQTransaction;
31-
3231
class Http3HeaderVIOAdaptor : public Continuation, public Http3FrameHandler
3332
{
3433
public:

0 commit comments

Comments
 (0)