Skip to content

Commit 1fc61b7

Browse files
committed
SSL: SSL_sendfile() support with kernel TLS.
Requires OpenSSL 3.0 compiled with "enable-ktls" option. Further, KTLS needs to be enabled in kernel, and in OpenSSL, either via OpenSSL configuration file or with "ssl_conf_command Options KTLS;" in nginx configuration. On FreeBSD, kernel TLS is available starting with FreeBSD 13.0, and can be enabled with "sysctl kern.ipc.tls.enable=1" and "kldload ktls_ocf" to load a software backend, see man ktls(4) for details. On Linux, kernel TLS is available starting with kernel 4.13 (at least 5.2 is recommended), and needs kernel compiled with CONFIG_TLS=y (with CONFIG_TLS=m, which is used at least on Ubuntu 21.04 by default, the tls module needs to be loaded with "modprobe tls").
1 parent 3ab1b64 commit 1fc61b7

File tree

4 files changed

+211
-9
lines changed

4 files changed

+211
-9
lines changed

src/event/ngx_event_openssl.c

+204-5
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ static void ngx_ssl_write_handler(ngx_event_t *wev);
4747
static ssize_t ngx_ssl_write_early(ngx_connection_t *c, u_char *data,
4848
size_t size);
4949
#endif
50+
static ssize_t ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file,
51+
size_t size);
5052
static void ngx_ssl_read_handler(ngx_event_t *rev);
5153
static void ngx_ssl_shutdown_handler(ngx_event_t *ev);
5254
static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr,
@@ -1762,6 +1764,16 @@ ngx_ssl_handshake(ngx_connection_t *c)
17621764

17631765
#endif
17641766
#endif
1767+
#endif
1768+
1769+
#ifdef BIO_get_ktls_send
1770+
1771+
if (BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection)) == 1) {
1772+
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
1773+
"BIO_get_ktls_send(): 1");
1774+
c->ssl->sendfile = 1;
1775+
}
1776+
17651777
#endif
17661778

17671779
rc = ngx_ssl_ocsp_validate(c);
@@ -1899,6 +1911,16 @@ ngx_ssl_try_early_data(ngx_connection_t *c)
18991911
c->read->ready = 1;
19001912
c->write->ready = 1;
19011913

1914+
#ifdef BIO_get_ktls_send
1915+
1916+
if (BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection)) == 1) {
1917+
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
1918+
"BIO_get_ktls_send(): 1");
1919+
c->ssl->sendfile = 1;
1920+
}
1921+
1922+
#endif
1923+
19021924
rc = ngx_ssl_ocsp_validate(c);
19031925

19041926
if (rc == NGX_ERROR) {
@@ -2502,10 +2524,11 @@ ngx_ssl_write_handler(ngx_event_t *wev)
25022524
ngx_chain_t *
25032525
ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
25042526
{
2505-
int n;
2506-
ngx_uint_t flush;
2507-
ssize_t send, size;
2508-
ngx_buf_t *buf;
2527+
int n;
2528+
ngx_uint_t flush;
2529+
ssize_t send, size, file_size;
2530+
ngx_buf_t *buf;
2531+
ngx_chain_t *cl;
25092532

25102533
if (!c->ssl->buffer) {
25112534

@@ -2579,6 +2602,11 @@ ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
25792602
continue;
25802603
}
25812604

2605+
if (in->buf->in_file && c->ssl->sendfile) {
2606+
flush = 1;
2607+
break;
2608+
}
2609+
25822610
size = in->buf->last - in->buf->pos;
25832611

25842612
if (size > buf->end - buf->last) {
@@ -2610,8 +2638,35 @@ ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
26102638
size = buf->last - buf->pos;
26112639

26122640
if (size == 0) {
2641+
2642+
if (in && in->buf->in_file && send < limit) {
2643+
2644+
/* coalesce the neighbouring file bufs */
2645+
2646+
cl = in;
2647+
file_size = (size_t) ngx_chain_coalesce_file(&cl, limit - send);
2648+
2649+
n = ngx_ssl_sendfile(c, in->buf, file_size);
2650+
2651+
if (n == NGX_ERROR) {
2652+
return NGX_CHAIN_ERROR;
2653+
}
2654+
2655+
if (n == NGX_AGAIN) {
2656+
break;
2657+
}
2658+
2659+
in = ngx_chain_update_sent(in, n);
2660+
2661+
send += n;
2662+
flush = 0;
2663+
2664+
continue;
2665+
}
2666+
26132667
buf->flush = 0;
26142668
c->buffered &= ~NGX_SSL_BUFFERED;
2669+
26152670
return in;
26162671
}
26172672

@@ -2636,7 +2691,7 @@ ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
26362691
buf->pos = buf->start;
26372692
buf->last = buf->start;
26382693

2639-
if (in == NULL || send == limit) {
2694+
if (in == NULL || send >= limit) {
26402695
break;
26412696
}
26422697
}
@@ -2882,6 +2937,150 @@ ngx_ssl_write_early(ngx_connection_t *c, u_char *data, size_t size)
28822937
#endif
28832938

28842939

2940+
static ssize_t
2941+
ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size)
2942+
{
2943+
#ifdef BIO_get_ktls_send
2944+
2945+
int sslerr;
2946+
ssize_t n;
2947+
ngx_err_t err;
2948+
2949+
ngx_ssl_clear_error(c->log);
2950+
2951+
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
2952+
"SSL to sendfile: @%O %uz",
2953+
file->file_pos, size);
2954+
2955+
ngx_set_errno(0);
2956+
2957+
n = SSL_sendfile(c->ssl->connection, file->file->fd, file->file_pos,
2958+
size, 0);
2959+
2960+
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_sendfile: %d", n);
2961+
2962+
if (n > 0) {
2963+
2964+
if (c->ssl->saved_read_handler) {
2965+
2966+
c->read->handler = c->ssl->saved_read_handler;
2967+
c->ssl->saved_read_handler = NULL;
2968+
c->read->ready = 1;
2969+
2970+
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
2971+
return NGX_ERROR;
2972+
}
2973+
2974+
ngx_post_event(c->read, &ngx_posted_events);
2975+
}
2976+
2977+
c->sent += n;
2978+
2979+
return n;
2980+
}
2981+
2982+
if (n == 0) {
2983+
2984+
/*
2985+
* if sendfile returns zero, then someone has truncated the file,
2986+
* so the offset became beyond the end of the file
2987+
*/
2988+
2989+
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
2990+
"SSL_sendfile() reported that \"%s\" was truncated at %O",
2991+
file->file->name.data, file->file_pos);
2992+
2993+
return NGX_ERROR;
2994+
}
2995+
2996+
sslerr = SSL_get_error(c->ssl->connection, n);
2997+
2998+
if (sslerr == SSL_ERROR_ZERO_RETURN) {
2999+
3000+
/*
3001+
* OpenSSL fails to return SSL_ERROR_SYSCALL if an error
3002+
* happens during writing after close_notify alert from the
3003+
* peer, and returns SSL_ERROR_ZERO_RETURN instead
3004+
*/
3005+
3006+
sslerr = SSL_ERROR_SYSCALL;
3007+
}
3008+
3009+
if (sslerr == SSL_ERROR_SSL
3010+
&& ERR_GET_REASON(ERR_peek_error()) == SSL_R_UNINITIALIZED
3011+
&& ngx_errno != 0)
3012+
{
3013+
/*
3014+
* OpenSSL fails to return SSL_ERROR_SYSCALL if an error
3015+
* happens in sendfile(), and returns SSL_ERROR_SSL with
3016+
* SSL_R_UNINITIALIZED reason instead
3017+
*/
3018+
3019+
sslerr = SSL_ERROR_SYSCALL;
3020+
}
3021+
3022+
err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
3023+
3024+
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
3025+
3026+
if (sslerr == SSL_ERROR_WANT_WRITE) {
3027+
3028+
if (c->ssl->saved_read_handler) {
3029+
3030+
c->read->handler = c->ssl->saved_read_handler;
3031+
c->ssl->saved_read_handler = NULL;
3032+
c->read->ready = 1;
3033+
3034+
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
3035+
return NGX_ERROR;
3036+
}
3037+
3038+
ngx_post_event(c->read, &ngx_posted_events);
3039+
}
3040+
3041+
c->write->ready = 0;
3042+
return NGX_AGAIN;
3043+
}
3044+
3045+
if (sslerr == SSL_ERROR_WANT_READ) {
3046+
3047+
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
3048+
"SSL_sendfile: want read");
3049+
3050+
c->read->ready = 0;
3051+
3052+
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
3053+
return NGX_ERROR;
3054+
}
3055+
3056+
/*
3057+
* we do not set the timer because there is already
3058+
* the write event timer
3059+
*/
3060+
3061+
if (c->ssl->saved_read_handler == NULL) {
3062+
c->ssl->saved_read_handler = c->read->handler;
3063+
c->read->handler = ngx_ssl_read_handler;
3064+
}
3065+
3066+
return NGX_AGAIN;
3067+
}
3068+
3069+
c->ssl->no_wait_shutdown = 1;
3070+
c->ssl->no_send_shutdown = 1;
3071+
c->write->error = 1;
3072+
3073+
ngx_ssl_connection_error(c, sslerr, err, "SSL_sendfile() failed");
3074+
3075+
#else
3076+
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
3077+
"SSL_sendfile() not available");
3078+
#endif
3079+
3080+
return NGX_ERROR;
3081+
}
3082+
3083+
28853084
static void
28863085
ngx_ssl_read_handler(ngx_event_t *rev)
28873086
{

src/event/ngx_event_openssl.h

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ struct ngx_ssl_connection_s {
109109
unsigned handshake_rejected:1;
110110
unsigned renegotiation:1;
111111
unsigned buffer:1;
112+
unsigned sendfile:1;
112113
unsigned no_wait_shutdown:1;
113114
unsigned no_send_shutdown:1;
114115
unsigned shutdown_without_free:1;

src/http/ngx_http_request.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ ngx_http_alloc_request(ngx_connection_t *c)
607607
}
608608

609609
#if (NGX_HTTP_SSL)
610-
if (c->ssl) {
610+
if (c->ssl && !c->ssl->sendfile) {
611611
r->main_filter_need_in_memory = 1;
612612
}
613613
#endif

src/http/ngx_http_upstream.c

+5-3
Original file line numberDiff line numberDiff line change
@@ -1683,9 +1683,6 @@ ngx_http_upstream_ssl_init_connection(ngx_http_request_t *r,
16831683
return;
16841684
}
16851685

1686-
c->sendfile = 0;
1687-
u->output.sendfile = 0;
1688-
16891686
if (u->conf->ssl_server_name || u->conf->ssl_verify) {
16901687
if (ngx_http_upstream_ssl_name(r, u, c) != NGX_OK) {
16911688
ngx_http_upstream_finalize_request(r, u,
@@ -1791,6 +1788,11 @@ ngx_http_upstream_ssl_handshake(ngx_http_request_t *r, ngx_http_upstream_t *u,
17911788
}
17921789
}
17931790

1791+
if (!c->ssl->sendfile) {
1792+
c->sendfile = 0;
1793+
u->output.sendfile = 0;
1794+
}
1795+
17941796
c->write->handler = ngx_http_upstream_handler;
17951797
c->read->handler = ngx_http_upstream_handler;
17961798

0 commit comments

Comments
 (0)