Skip to content

Commit f60002d

Browse files
committed
Add TLS receive read-ahead support
Add WOLFSSL_TLS_READ_AHEAD (--enable-readahead), toggled at runtime via wolfSSL_set_read_ahead(). When enabled, the record-header read pulls a full record in one recv() so the body arrives without a second syscall. The receive window is configurable with wolfSSL_CTX/SSL_set_default_read_buffer_len() (OpenSSL-compatible): 0 keeps the one-record default, a larger value coalesces several records per recv(), a smaller value caps the per-connection buffer footprint. Records exceeding the window are still received correctly, the buffer grows on demand and is reallocated back down to the window afterwards so the retained footprint stays bounded. Includes docs, API tests, and a benchmark toggle.
1 parent f5ace71 commit f60002d

12 files changed

Lines changed: 927 additions & 12 deletions

File tree

configure.ac

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2467,6 +2467,19 @@ then
24672467
AM_CFLAGS="$AM_CFLAGS -DSHOW_SECRETS -DHAVE_SECRET_CALLBACK -DWOLFSSL_SSLKEYLOGFILE -DWOLFSSL_KEYLOG_EXPORT_WARNED"
24682468
fi
24692469

2470+
# TLS receive read-ahead: when enabled at runtime via wolfSSL_set_read_ahead(),
2471+
# the record-header read pulls in up to a full record in one recv(), avoiding a
2472+
# second syscall for the body. Opt-in (default: disabled).
2473+
AC_ARG_ENABLE([readahead],
2474+
[AS_HELP_STRING([--enable-readahead],[Enable TLS receive read-ahead support, activated at runtime with wolfSSL_set_read_ahead() (default: disabled)])],
2475+
[ ENABLED_READAHEAD=$enableval ],
2476+
[ ENABLED_READAHEAD=no ]
2477+
)
2478+
if test "$ENABLED_READAHEAD" = "yes"
2479+
then
2480+
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_TLS_READ_AHEAD"
2481+
fi
2482+
24702483
# TLS v1.3 Draft 18 (Note: only final TLS v1.3 supported, here for backwards build compatibility)
24712484
AC_ARG_ENABLE([tls13-draft18],
24722485
[AS_HELP_STRING([--enable-tls13-draft18],[Enable wolfSSL TLS v1.3 Draft 18 (default: disabled)])],
@@ -13718,6 +13731,7 @@ echo " * FrodoKEM decapsulate: $ENABLED_FRODOKEM_DECAPSULATE"
1371813731
echo " * ERR Queues per Thread: $ENABLED_ERRORQUEUEPERTHREAD"
1371913732
echo " * rwlock: $ENABLED_RWLOCK"
1372013733
echo " * keylog export: $ENABLED_KEYLOG_EXPORT"
13734+
echo " * TLS receive read-ahead: $ENABLED_READAHEAD"
1372113735
echo " * AutoSAR : $ENABLED_AUTOSAR"
1372213736
echo " * ML-KEM standalone: $ENABLED_MLKEM_STANDALONE"
1372313737
echo " * PQ/T hybrids: $ENABLED_PQC_HYBRIDS"

doc/dox_comments/header_files-ja/ssl.h

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4530,6 +4530,138 @@ int wolfSSL_CTX_get_read_ahead(WOLFSSL_CTX* ctx);
45304530
*/
45314531
int wolfSSL_CTX_set_read_ahead(WOLFSSL_CTX* ctx, int v);
45324532

4533+
/*!
4534+
\ingroup Setup
4535+
4536+
\brief この関数は、このWOLFSSL_CTXから作成されるセッションに対して、リードアヘッドの
4537+
受信ウィンドウサイズを設定します。リードアヘッドが有効な場合
4538+
(wolfSSL_CTX_set_read_ahead())、1回のrecv()で最大 \p len バイトを読み込みます。
4539+
- \p len が0の場合、ウィンドウは1レコード分のデフォルトにリセットされます。これは
4540+
新しく作成されたコンテキストがすでに持つウィンドウでもあり、レコードのボディが
4541+
ヘッダーと一緒に読み込まれるため、2回目のシステムコールを必要としません。
4542+
- \p len が1レコードより大きい場合、1回のrecv()で連続する複数のレコードをまとめて
4543+
読み込むことができ、レコードごとに1回ではなく1回のシステムコールで済みます。
4544+
- \p len が1レコードより小さい場合、ピアのレコードが小さいと分かっているとき
4545+
(例:4KB)に受信バッファのフットプリントを抑え、1レコード分のデフォルトと比べて
4546+
ヒープを節約します。
4547+
\p len は投機的な先読みウィンドウであり、上限ではありません。\p len より大きな
4548+
レコードも正しく受信されます。入力バッファはそのレコードの実際のサイズまでオンデマンドで
4549+
拡張され(そのレコードについては追加のシステムコールと再割り当てのコストがかかります)、
4550+
その後ウィンドウサイズまで縮小されます。そのため、保持されるフットプリントは、観測された
4551+
最大のレコードではなく \p len によって制限されたままになります。この設定は、ライブラリが
4552+
リードアヘッドサポート付き(--enable-readahead / WOLFSSL_TLS_READ_AHEAD)で
4553+
ビルドされている場合にのみI/Oに影響します。それ以外の場合、値は保存されますが効果は
4554+
ありません。これはOpenSSLのSSL_CTX_set_default_read_buffer_len()に相当するwolfSSLの
4555+
関数ですが、OpenSSLと異なりステータスコードを返し(戻り値を無視する呼び出し元も
4556+
ソース互換のままです)、1レコードより小さいサイズも尊重します(OpenSSLはバッファを
4557+
拡大することしかしません)。\p len がWOLFSSL_MAX_READ_AHEAD_SZ(16MB)を超える場合、
4558+
その最大値に制限されます。0と過大な値はどちらも正規化されるため、
4559+
wolfSSL_CTX_get_default_read_buffer_len()は生の引数ではなく、実効ウィンドウ(未設定の
4560+
場合は0ではなく1レコード分のデフォルト)を報告します。
4561+
4562+
\note これはメモリとシステムコールのトレードオフです。リードアヘッドが有効な間、入力
4563+
バッファは接続の存続期間中保持されます(\p len に制限されます)。そのため、大きな
4564+
\p len に多数の同時接続を掛け合わせると恒常的なメモリ消費となり、一方で小さな \p len は
4565+
接続ごとのフットプリントを抑えますが、それを超えるレコードではより多くのシステムコールが
4566+
必要になります。
4567+
4568+
\return SSL_SUCCESS バッファ長が設定された場合。
4569+
\return SSL_FAILURE ctxがNULLの場合。
4570+
4571+
\param ctx リードアヘッドバッファ長を設定するWOLFSSL_CTX構造体。
4572+
\param len リードアヘッドの結合バッファサイズ(バイト単位、0 = 1レコード)。
4573+
4574+
_Example_
4575+
\code
4576+
WOLFSSL_CTX* ctx;
4577+
// ctxをセットアップ
4578+
wolfSSL_CTX_set_read_ahead(ctx, 1);
4579+
// 1回のrecv()で最大4つの最大サイズレコードをまとめて読み込む
4580+
wolfSSL_CTX_set_default_read_buffer_len(ctx, 4 * 16384);
4581+
\endcode
4582+
4583+
\sa wolfSSL_CTX_set_read_ahead
4584+
\sa wolfSSL_set_default_read_buffer_len
4585+
\sa wolfSSL_has_pending
4586+
*/
4587+
int wolfSSL_CTX_set_default_read_buffer_len(WOLFSSL_CTX* ctx, size_t len);
4588+
4589+
/*!
4590+
\ingroup Setup
4591+
4592+
\brief この関数は、単一のWOLFSSLセッションにリードアヘッドの結合バッファサイズを設定し、
4593+
そのWOLFSSL_CTXから継承した値を上書きします。詳しい説明、1レコード分のデフォルト、および
4594+
メモリとシステムコールのトレードオフについては、
4595+
wolfSSL_CTX_set_default_read_buffer_len()を参照してください。
4596+
4597+
\return SSL_SUCCESS バッファ長が設定された場合。
4598+
\return SSL_FAILURE sslがNULLの場合。
4599+
4600+
\param ssl リードアヘッドバッファ長を設定するWOLFSSL構造体。
4601+
\param len リードアヘッドの結合バッファサイズ(バイト単位、0 = 1レコード)。
4602+
4603+
\sa wolfSSL_CTX_set_default_read_buffer_len
4604+
\sa wolfSSL_set_read_ahead
4605+
\sa wolfSSL_has_pending
4606+
*/
4607+
int wolfSSL_set_default_read_buffer_len(WOLFSSL* ssl, size_t len);
4608+
4609+
/*!
4610+
\ingroup Setup
4611+
4612+
\brief この関数は、wolfSSL_CTX_set_default_read_buffer_len()によってWOLFSSL_CTXに
4613+
設定されたリードアヘッドの結合バッファサイズを返します。設定時に長さ0と過大な値は
4614+
正規化されるため、報告される値は生の引数ではなく、実際に使用されている実効ウィンドウ
4615+
(長さが一度も変更されていない場合は0ではなく1レコード分のデフォルト)です。
4616+
4617+
\return len 成功時にはリードアヘッドバッファ長(バイト単位)を返します。
4618+
\return SSL_FAILURE ctxがNULLの場合。
4619+
4620+
\param ctx リードアヘッドバッファ長を取得するWOLFSSL_CTX構造体。
4621+
4622+
_Example_
4623+
\code
4624+
WOLFSSL_CTX* ctx;
4625+
long len;
4626+
// ctxをセットアップ
4627+
len = wolfSSL_CTX_get_default_read_buffer_len(ctx);
4628+
// lenを確認
4629+
\endcode
4630+
4631+
\sa wolfSSL_CTX_set_default_read_buffer_len
4632+
\sa wolfSSL_get_default_read_buffer_len
4633+
\sa wolfSSL_CTX_set_read_ahead
4634+
*/
4635+
long wolfSSL_CTX_get_default_read_buffer_len(WOLFSSL_CTX* ctx);
4636+
4637+
/*!
4638+
\ingroup Setup
4639+
4640+
\brief この関数は、単一のWOLFSSLセッションで有効なリードアヘッドの結合バッファサイズを
4641+
返します。この値は、そのWOLFSSL_CTXから継承されたものか、
4642+
wolfSSL_set_default_read_buffer_len()で上書きされたもののいずれかです。WOLFSSL_CTXの
4643+
ゲッターと同様に、報告される値は生の引数ではなく、実際に使用されている実効ウィンドウです。
4644+
4645+
\return len 成功時にはリードアヘッドバッファ長(バイト単位)を返します。
4646+
\return SSL_FAILURE sslがNULLの場合。
4647+
4648+
\param ssl リードアヘッドバッファ長を取得するWOLFSSL構造体。
4649+
4650+
_Example_
4651+
\code
4652+
WOLFSSL* ssl;
4653+
long len;
4654+
// sslをセットアップ
4655+
len = wolfSSL_get_default_read_buffer_len(ssl);
4656+
// lenを確認
4657+
\endcode
4658+
4659+
\sa wolfSSL_set_default_read_buffer_len
4660+
\sa wolfSSL_CTX_get_default_read_buffer_len
4661+
\sa wolfSSL_set_read_ahead
4662+
*/
4663+
long wolfSSL_get_default_read_buffer_len(const WOLFSSL* ssl);
4664+
45334665
/*!
45344666
\ingroup Setup
45354667

doc/dox_comments/header_files/ssl.h

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5486,6 +5486,21 @@ int wolfSSL_CTX_get_read_ahead(WOLFSSL_CTX* ctx);
54865486
\ingroup Setup
54875487

54885488
\brief This function sets the read ahead flag in the WOLFSSL_CTX structure.
5489+
When enabled, the record-header read pulls in up to a full TLS record in a
5490+
single recv(), so the body (and any following buffered records) is obtained
5491+
without a second syscall. The flag only changes I/O behaviour when the
5492+
library is built with read-ahead support (--enable-readahead /
5493+
WOLFSSL_TLS_READ_AHEAD); otherwise it is stored but inert.
5494+
5495+
\note With read-ahead enabled, undecrypted data can remain buffered
5496+
internally while the socket has no more data to read. wolfSSL_has_pending()
5497+
reports whether any such data is buffered, but a non-zero return does not
5498+
guarantee a full record is available: wolfSSL_read() may still return
5499+
WANT_READ when only a partial record is buffered. Event-driven applications
5500+
should therefore drain the connection by calling wolfSSL_read() until it
5501+
returns WANT_READ (or an error) before returning to select()/poll(), rather
5502+
than looping on wolfSSL_has_pending() alone; otherwise buffered data could
5503+
be missed or the loop could spin.
54895504

54905505
\return SSL_SUCCESS If ctx read ahead flag set.
54915506
\return SSL_FAILURE If ctx is NULL then SSL_FAILURE is returned.
@@ -5506,9 +5521,147 @@ int wolfSSL_CTX_get_read_ahead(WOLFSSL_CTX* ctx);
55065521
\sa wolfSSL_CTX_new
55075522
\sa wolfSSL_CTX_free
55085523
\sa wolfSSL_CTX_get_read_ahead
5524+
\sa wolfSSL_has_pending
55095525
*/
55105526
int wolfSSL_CTX_set_read_ahead(WOLFSSL_CTX* ctx, int v);
55115527

5528+
/*!
5529+
\ingroup Setup
5530+
5531+
\brief This function sets the read-ahead receive window size for contexts
5532+
created from this WOLFSSL_CTX. When read-ahead is enabled
5533+
(wolfSSL_CTX_set_read_ahead()), a single recv() pulls in up to \p len bytes:
5534+
- \p len of 0 resets the window to the one-record default. This is also
5535+
the window a freshly created context already carries, so the record body
5536+
is read together with its header without a second syscall.
5537+
- A \p len larger than one record lets a single recv() coalesce several
5538+
back-to-back records, one syscall instead of one per record.
5539+
- A \p len smaller than one record caps the receive buffer's footprint
5540+
when the peer's records are known to be small (e.g. 4 KB), saving heap
5541+
versus the one-record default.
5542+
\p len is only a speculative read window, not a hard limit: a record larger
5543+
than \p len is still received correctly, with the input buffer grown to the
5544+
record's actual size on demand (costing an extra syscall and reallocation
5545+
for that record) and then reallocated back down to the window once the
5546+
oversized record is consumed, so the retained footprint stays bounded by
5547+
\p len rather than by the largest record seen. The setting only affects I/O
5548+
when the library is built with read-ahead support (--enable-readahead /
5549+
WOLFSSL_TLS_READ_AHEAD); otherwise it is stored but inert. This is the
5550+
wolfSSL equivalent of OpenSSL's SSL_CTX_set_default_read_buffer_len(); unlike
5551+
OpenSSL it returns a status code (callers that ignore the return remain
5552+
source-compatible) and it honours sizes below one record, whereas OpenSSL
5553+
only ever enlarges the buffer. A \p len above WOLFSSL_MAX_READ_AHEAD_SZ
5554+
(16 MB) is clamped to that maximum. Because 0 and oversized values are both
5555+
normalised, wolfSSL_CTX_get_default_read_buffer_len() reports the effective
5556+
window (the one-record default rather than 0 when unset), not the raw
5557+
argument.
5558+
5559+
\note This is a memory-vs-syscall trade-off. While read-ahead is enabled the
5560+
input buffer is retained (bounded to \p len) for the connection's lifetime,
5561+
so a large \p len multiplied by many concurrent connections is persistent
5562+
memory, while a small \p len bounds per-connection footprint at the cost of
5563+
more syscalls for records that exceed it.
5564+
5565+
\return SSL_SUCCESS If the buffer length was set.
5566+
\return SSL_FAILURE If ctx is NULL.
5567+
5568+
\param ctx WOLFSSL_CTX structure to set the read-ahead buffer length on.
5569+
\param len read-ahead coalescing buffer size in bytes (0 = one record).
5570+
5571+
_Example_
5572+
\code
5573+
WOLFSSL_CTX* ctx;
5574+
// setup ctx
5575+
wolfSSL_CTX_set_read_ahead(ctx, 1);
5576+
// coalesce up to four max-size records per recv()
5577+
wolfSSL_CTX_set_default_read_buffer_len(ctx, 4 * 16384);
5578+
\endcode
5579+
5580+
\sa wolfSSL_CTX_set_read_ahead
5581+
\sa wolfSSL_set_default_read_buffer_len
5582+
\sa wolfSSL_has_pending
5583+
*/
5584+
int wolfSSL_CTX_set_default_read_buffer_len(WOLFSSL_CTX* ctx, size_t len);
5585+
5586+
/*!
5587+
\ingroup Setup
5588+
5589+
\brief This function sets the read-ahead coalescing buffer size on a single
5590+
WOLFSSL session, overriding the value inherited from its WOLFSSL_CTX. See
5591+
wolfSSL_CTX_set_default_read_buffer_len() for the full description, the
5592+
one-record default, and the memory-vs-syscall trade-off.
5593+
5594+
\return SSL_SUCCESS If the buffer length was set.
5595+
\return SSL_FAILURE If ssl is NULL.
5596+
5597+
\param ssl WOLFSSL structure to set the read-ahead buffer length on.
5598+
\param len read-ahead coalescing buffer size in bytes (0 = one record).
5599+
5600+
\sa wolfSSL_CTX_set_default_read_buffer_len
5601+
\sa wolfSSL_set_read_ahead
5602+
\sa wolfSSL_has_pending
5603+
*/
5604+
int wolfSSL_set_default_read_buffer_len(WOLFSSL* ssl, size_t len);
5605+
5606+
/*!
5607+
\ingroup Setup
5608+
5609+
\brief This function returns the read-ahead coalescing buffer size
5610+
configured on a WOLFSSL_CTX by wolfSSL_CTX_set_default_read_buffer_len().
5611+
Because a length of 0 and oversized values are normalised when set, the
5612+
value reported is the effective window actually in use (the one-record
5613+
default rather than 0 when the length was never changed), not the raw
5614+
argument last passed.
5615+
5616+
\return len On success returns the read-ahead buffer length in bytes.
5617+
\return SSL_FAILURE If ctx is NULL.
5618+
5619+
\param ctx WOLFSSL_CTX structure to get the read-ahead buffer length from.
5620+
5621+
_Example_
5622+
\code
5623+
WOLFSSL_CTX* ctx;
5624+
long len;
5625+
// setup ctx
5626+
len = wolfSSL_CTX_get_default_read_buffer_len(ctx);
5627+
// check len
5628+
\endcode
5629+
5630+
\sa wolfSSL_CTX_set_default_read_buffer_len
5631+
\sa wolfSSL_get_default_read_buffer_len
5632+
\sa wolfSSL_CTX_set_read_ahead
5633+
*/
5634+
long wolfSSL_CTX_get_default_read_buffer_len(WOLFSSL_CTX* ctx);
5635+
5636+
/*!
5637+
\ingroup Setup
5638+
5639+
\brief This function returns the read-ahead coalescing buffer size in
5640+
effect for a single WOLFSSL session, whether inherited from its
5641+
WOLFSSL_CTX or overridden by wolfSSL_set_default_read_buffer_len(). As with
5642+
the WOLFSSL_CTX getter, the value reported is the effective window in use,
5643+
not the raw argument last passed.
5644+
5645+
\return len On success returns the read-ahead buffer length in bytes.
5646+
\return SSL_FAILURE If ssl is NULL.
5647+
5648+
\param ssl WOLFSSL structure to get the read-ahead buffer length from.
5649+
5650+
_Example_
5651+
\code
5652+
WOLFSSL* ssl;
5653+
long len;
5654+
// setup ssl
5655+
len = wolfSSL_get_default_read_buffer_len(ssl);
5656+
// check len
5657+
\endcode
5658+
5659+
\sa wolfSSL_set_default_read_buffer_len
5660+
\sa wolfSSL_CTX_get_default_read_buffer_len
5661+
\sa wolfSSL_set_read_ahead
5662+
*/
5663+
long wolfSSL_get_default_read_buffer_len(const WOLFSSL* ssl);
5664+
55125665
/*!
55135666
\ingroup Setup
55145667

examples/benchmark/tls_bench.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,13 @@ static int bench_tls_client(info_t* info)
11001100
#endif
11011101
wolfSSL_SetIOReadCtx(cli_ssl, info);
11021102
wolfSSL_SetIOWriteCtx(cli_ssl, info);
1103+
#if defined(WOLFSSL_TLS_READ_AHEAD) && !defined(NO_FILESYSTEM) && \
1104+
!defined(NO_STDIO_FILESYSTEM)
1105+
/* Optional A/B benchmark toggle for TLS receive read-ahead. Gated on
1106+
* filesystem support since XGETENV is only defined there. */
1107+
if (XGETENV("WOLF_BENCH_READ_AHEAD") != NULL)
1108+
wolfSSL_set_read_ahead(cli_ssl, 1);
1109+
#endif
11031110

11041111
#if !defined(SINGLE_THREADED) && defined(WOLFSSL_DTLS)
11051112
/* synchronize with server */
@@ -1557,6 +1564,13 @@ static int bench_tls_server(info_t* info)
15571564

15581565
wolfSSL_SetIOReadCtx(srv_ssl, info);
15591566
wolfSSL_SetIOWriteCtx(srv_ssl, info);
1567+
#if defined(WOLFSSL_TLS_READ_AHEAD) && !defined(NO_FILESYSTEM) && \
1568+
!defined(NO_STDIO_FILESYSTEM)
1569+
/* Optional A/B benchmark toggle for TLS receive read-ahead. Gated on
1570+
* filesystem support since XGETENV is only defined there. */
1571+
if (XGETENV("WOLF_BENCH_READ_AHEAD") != NULL)
1572+
wolfSSL_set_read_ahead(srv_ssl, 1);
1573+
#endif
15601574
#ifndef NO_DH
15611575
wolfSSL_SetTmpDH(srv_ssl, dhp, sizeof(dhp), dhg, sizeof(dhg));
15621576
#endif

examples/client/client.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,15 @@ static int ClientBenchmarkThroughput(WOLFSSL_CTX* ctx, char* host, word16 port,
790790
if (ssl == NULL)
791791
err_sys("unable to get SSL object");
792792

793+
#if defined(WOLFSSL_TLS_READ_AHEAD) && !defined(NO_FILESYSTEM) && \
794+
!defined(NO_STDIO_FILESYSTEM)
795+
/* Optional A/B toggle: enable TLS receive read-ahead for the throughput
796+
* benchmark when WOLF_BENCH_READ_AHEAD is set in the environment. Gated on
797+
* filesystem support since XGETENV is only defined there. */
798+
if (XGETENV("WOLF_BENCH_READ_AHEAD") != NULL)
799+
wolfSSL_set_read_ahead(ssl, 1);
800+
#endif
801+
793802
tcp_connect(&sockfd, host, port, dtlsUDP, dtlsSCTP, ssl);
794803
if (wolfSSL_set_fd(ssl, sockfd) != WOLFSSL_SUCCESS) {
795804
err_sys("error in setting fd");

0 commit comments

Comments
 (0)