Latest update - GitHub 020b1db7e
This commit is contained in:
@@ -5079,7 +5079,8 @@ ngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
{
|
||||
#ifdef SSL_get_negotiated_group
|
||||
|
||||
int nid;
|
||||
int nid;
|
||||
const char *name;
|
||||
|
||||
nid = SSL_get_negotiated_group(c->ssl->connection);
|
||||
|
||||
@@ -5091,14 +5092,24 @@ ngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
s->len = sizeof("0x0000") - 1;
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x3000000fL)
|
||||
name = SSL_group_to_name(c->ssl->connection, nid);
|
||||
#else
|
||||
name = NULL;
|
||||
#endif
|
||||
|
||||
s->len = name ? ngx_strlen(name) : sizeof("0x0000") - 1;
|
||||
s->data = ngx_pnalloc(pool, s->len);
|
||||
if (s->data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_sprintf(s->data, "0x%04xd", nid & 0xffff);
|
||||
if (name) {
|
||||
ngx_memcpy(s->data, name, s->len);
|
||||
|
||||
} else {
|
||||
ngx_sprintf(s->data, "0x%04xd", nid & 0xffff);
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -5118,6 +5129,7 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
int *curves, n, i, nid;
|
||||
u_char *p;
|
||||
size_t len;
|
||||
const char *name;
|
||||
|
||||
n = SSL_get1_curves(c->ssl->connection, NULL);
|
||||
|
||||
@@ -5138,7 +5150,13 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
nid = curves[i];
|
||||
|
||||
if (nid & TLSEXT_nid_unknown) {
|
||||
len += sizeof("0x0000") - 1;
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x3000000fL)
|
||||
name = SSL_group_to_name(c->ssl->connection, nid);
|
||||
#else
|
||||
name = NULL;
|
||||
#endif
|
||||
|
||||
len += name ? ngx_strlen(name) : sizeof("0x0000") - 1;
|
||||
|
||||
} else {
|
||||
len += ngx_strlen(OBJ_nid2sn(nid));
|
||||
@@ -5158,7 +5176,14 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
nid = curves[i];
|
||||
|
||||
if (nid & TLSEXT_nid_unknown) {
|
||||
p = ngx_sprintf(p, "0x%04xd", nid & 0xffff);
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x3000000fL)
|
||||
name = SSL_group_to_name(c->ssl->connection, nid);
|
||||
#else
|
||||
name = NULL;
|
||||
#endif
|
||||
|
||||
p = name ? ngx_cpymem(p, name, ngx_strlen(name))
|
||||
: ngx_sprintf(p, "0x%04xd", nid & 0xffff);
|
||||
|
||||
} else {
|
||||
p = ngx_sprintf(p, "%s", OBJ_nid2sn(nid));
|
||||
|
||||
@@ -308,11 +308,16 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf,
|
||||
qc->streams.client_max_streams_uni = qc->tp.initial_max_streams_uni;
|
||||
qc->streams.client_max_streams_bidi = qc->tp.initial_max_streams_bidi;
|
||||
|
||||
qc->congestion.window = ngx_min(10 * qc->tp.max_udp_payload_size,
|
||||
ngx_max(2 * qc->tp.max_udp_payload_size,
|
||||
qc->congestion.window = ngx_min(10 * NGX_QUIC_MIN_INITIAL_SIZE,
|
||||
ngx_max(2 * NGX_QUIC_MIN_INITIAL_SIZE,
|
||||
14720));
|
||||
qc->congestion.ssthresh = (size_t) -1;
|
||||
qc->congestion.recovery_start = ngx_current_msec;
|
||||
qc->congestion.mtu = NGX_QUIC_MIN_INITIAL_SIZE;
|
||||
qc->congestion.recovery_start = ngx_current_msec - 1;
|
||||
|
||||
qc->max_frames = (conf->max_concurrent_streams_uni
|
||||
+ conf->max_concurrent_streams_bidi)
|
||||
* conf->stream_buffer_size / 2000;
|
||||
|
||||
if (pkt->validated && pkt->retried) {
|
||||
qc->tp.retry_scid.len = pkt->dcid.len;
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
/* RFC 9002, 7.6.1. Duration: kPersistentCongestionThreshold */
|
||||
#define NGX_QUIC_PERSISTENT_CONGESTION_THR 3
|
||||
|
||||
/* CUBIC parameters x10 */
|
||||
#define NGX_QUIC_CUBIC_BETA 7
|
||||
#define MGX_QUIC_CUBIC_C 4
|
||||
|
||||
|
||||
/* send time of ACK'ed packets */
|
||||
typedef struct {
|
||||
@@ -29,18 +33,22 @@ typedef struct {
|
||||
} ngx_quic_ack_stat_t;
|
||||
|
||||
|
||||
static ngx_inline ngx_msec_t ngx_quic_lost_threshold(ngx_quic_connection_t *qc);
|
||||
static ngx_inline ngx_msec_t ngx_quic_time_threshold(ngx_quic_connection_t *qc);
|
||||
static uint64_t ngx_quic_packet_threshold(ngx_quic_send_ctx_t *ctx);
|
||||
static void ngx_quic_rtt_sample(ngx_connection_t *c, ngx_quic_ack_frame_t *ack,
|
||||
enum ssl_encryption_level_t level, ngx_msec_t send_time);
|
||||
static ngx_int_t ngx_quic_handle_ack_frame_range(ngx_connection_t *c,
|
||||
ngx_quic_send_ctx_t *ctx, uint64_t min, uint64_t max,
|
||||
ngx_quic_ack_stat_t *st);
|
||||
static size_t ngx_quic_congestion_cubic(ngx_connection_t *c);
|
||||
static void ngx_quic_drop_ack_ranges(ngx_connection_t *c,
|
||||
ngx_quic_send_ctx_t *ctx, uint64_t pn);
|
||||
static ngx_int_t ngx_quic_detect_lost(ngx_connection_t *c,
|
||||
ngx_quic_ack_stat_t *st);
|
||||
static ngx_msec_t ngx_quic_congestion_cubic_time(ngx_connection_t *c);
|
||||
static ngx_msec_t ngx_quic_pcg_duration(ngx_connection_t *c);
|
||||
static void ngx_quic_persistent_congestion(ngx_connection_t *c);
|
||||
static ngx_msec_t ngx_quic_oldest_sent_packet(ngx_connection_t *c);
|
||||
static void ngx_quic_congestion_lost(ngx_connection_t *c,
|
||||
ngx_quic_frame_t *frame);
|
||||
static void ngx_quic_lost_handler(ngx_event_t *ev);
|
||||
@@ -48,7 +56,7 @@ static void ngx_quic_lost_handler(ngx_event_t *ev);
|
||||
|
||||
/* RFC 9002, 6.1.2. Time Threshold: kTimeThreshold, kGranularity */
|
||||
static ngx_inline ngx_msec_t
|
||||
ngx_quic_lost_threshold(ngx_quic_connection_t *qc)
|
||||
ngx_quic_time_threshold(ngx_quic_connection_t *qc)
|
||||
{
|
||||
ngx_msec_t thr;
|
||||
|
||||
@@ -59,6 +67,29 @@ ngx_quic_lost_threshold(ngx_quic_connection_t *qc)
|
||||
}
|
||||
|
||||
|
||||
static uint64_t
|
||||
ngx_quic_packet_threshold(ngx_quic_send_ctx_t *ctx)
|
||||
{
|
||||
uint64_t pkt_thr;
|
||||
ngx_queue_t *q;
|
||||
ngx_quic_frame_t *f;
|
||||
|
||||
if (ngx_queue_empty(&ctx->sent)) {
|
||||
return NGX_QUIC_PKT_THR;
|
||||
}
|
||||
|
||||
q = ngx_queue_head(&ctx->sent);
|
||||
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
|
||||
pkt_thr = (ctx->pnum - f->pnum) / 2;
|
||||
|
||||
if (pkt_thr <= NGX_QUIC_PKT_THR) {
|
||||
return NGX_QUIC_PKT_THR;
|
||||
}
|
||||
|
||||
return pkt_thr;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
|
||||
ngx_quic_frame_t *f)
|
||||
@@ -313,8 +344,9 @@ ngx_quic_handle_ack_frame_range(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
void
|
||||
ngx_quic_congestion_ack(ngx_connection_t *c, ngx_quic_frame_t *f)
|
||||
{
|
||||
size_t w_cubic;
|
||||
ngx_uint_t blocked;
|
||||
ngx_msec_t timer;
|
||||
ngx_msec_t now, timer;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
@@ -329,16 +361,34 @@ ngx_quic_congestion_ack(ngx_connection_t *c, ngx_quic_frame_t *f)
|
||||
return;
|
||||
}
|
||||
|
||||
now = ngx_current_msec;
|
||||
|
||||
blocked = (cg->in_flight >= cg->window) ? 1 : 0;
|
||||
|
||||
cg->in_flight -= f->plen;
|
||||
|
||||
/* prevent recovery_start from wrapping */
|
||||
|
||||
timer = now - cg->recovery_start;
|
||||
|
||||
if ((ngx_msec_int_t) timer < 0) {
|
||||
cg->recovery_start = ngx_quic_oldest_sent_packet(c) - 1;
|
||||
}
|
||||
|
||||
timer = f->send_time - cg->recovery_start;
|
||||
|
||||
if ((ngx_msec_int_t) timer <= 0) {
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack recovery win:%uz ss:%z if:%uz",
|
||||
cg->window, cg->ssthresh, cg->in_flight);
|
||||
"quic congestion ack rec t:%M win:%uz if:%uz",
|
||||
now, cg->window, cg->in_flight);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (cg->idle) {
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack idle t:%M win:%uz if:%uz",
|
||||
now, cg->window, cg->in_flight);
|
||||
|
||||
goto done;
|
||||
}
|
||||
@@ -346,24 +396,51 @@ ngx_quic_congestion_ack(ngx_connection_t *c, ngx_quic_frame_t *f)
|
||||
if (cg->window < cg->ssthresh) {
|
||||
cg->window += f->plen;
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion slow start win:%uz ss:%z if:%uz",
|
||||
cg->window, cg->ssthresh, cg->in_flight);
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack ss t:%M win:%uz ss:%z if:%uz",
|
||||
now, cg->window, cg->ssthresh, cg->in_flight);
|
||||
|
||||
} else {
|
||||
cg->window += qc->tp.max_udp_payload_size * f->plen / cg->window;
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion avoidance win:%uz ss:%z if:%uz",
|
||||
cg->window, cg->ssthresh, cg->in_flight);
|
||||
}
|
||||
/* RFC 9438, 4.2. Window Increase Function */
|
||||
|
||||
/* prevent recovery_start from wrapping */
|
||||
w_cubic = ngx_quic_congestion_cubic(c);
|
||||
|
||||
timer = cg->recovery_start - ngx_current_msec + qc->tp.max_idle_timeout * 2;
|
||||
if (cg->window < cg->w_prior) {
|
||||
cg->w_est += (uint64_t) cg->mtu * f->plen
|
||||
* 3 * (10 - NGX_QUIC_CUBIC_BETA)
|
||||
/ (10 + NGX_QUIC_CUBIC_BETA) / cg->window;
|
||||
|
||||
if ((ngx_msec_int_t) timer < 0) {
|
||||
cg->recovery_start = ngx_current_msec - qc->tp.max_idle_timeout * 2;
|
||||
} else {
|
||||
cg->w_est += (uint64_t) cg->mtu * f->plen / cg->window;
|
||||
}
|
||||
|
||||
if (w_cubic < cg->w_est) {
|
||||
cg->window = cg->w_est;
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack reno t:%M win:%uz c:%uz if:%uz",
|
||||
now, cg->window, w_cubic, cg->in_flight);
|
||||
|
||||
} else if (w_cubic > cg->window) {
|
||||
|
||||
if (w_cubic >= cg->window * 3 / 2) {
|
||||
cg->window += cg->mtu / 2;
|
||||
|
||||
} else {
|
||||
cg->window += (uint64_t) cg->mtu * (w_cubic - cg->window)
|
||||
/ cg->window;
|
||||
}
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack cubic t:%M win:%uz c:%uz if:%uz",
|
||||
now, cg->window, w_cubic, cg->in_flight);
|
||||
|
||||
} else {
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack skip t:%M win:%uz c:%uz if:%uz",
|
||||
now, cg->window, w_cubic, cg->in_flight);
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
@@ -374,6 +451,87 @@ done:
|
||||
}
|
||||
|
||||
|
||||
static size_t
|
||||
ngx_quic_congestion_cubic(ngx_connection_t *c)
|
||||
{
|
||||
int64_t w, t, cc;
|
||||
ngx_msec_t now;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
cg = &qc->congestion;
|
||||
|
||||
ngx_quic_congestion_idle(c, cg->idle);
|
||||
|
||||
now = ngx_current_msec;
|
||||
t = (ngx_msec_int_t) (now - cg->k);
|
||||
|
||||
if (t > 1000000) {
|
||||
w = NGX_MAX_SIZE_T_VALUE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (t < -1000000) {
|
||||
w = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* RFC 9438, Figure 1
|
||||
*
|
||||
* w_cubic = C * (t_msec / 1000) ^ 3 * mtu + w_max
|
||||
*/
|
||||
|
||||
cc = 10000000000ll / (int64_t) cg->mtu / MGX_QUIC_CUBIC_C;
|
||||
w = t * t * t / cc + (int64_t) cg->w_max;
|
||||
|
||||
if (w > NGX_MAX_SIZE_T_VALUE) {
|
||||
w = NGX_MAX_SIZE_T_VALUE;
|
||||
}
|
||||
|
||||
if (w < 0) {
|
||||
w = 0;
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic cubic t:%L w:%L wm:%uz", t, w, cg->w_max);
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_quic_congestion_idle(ngx_connection_t *c, ngx_uint_t idle)
|
||||
{
|
||||
ngx_msec_t now;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
cg = &qc->congestion;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion idle:%ui", idle);
|
||||
|
||||
if (cg->window >= cg->ssthresh) {
|
||||
/* RFC 9438, 5.8. Behavior for Application-Limited Flows */
|
||||
|
||||
now = ngx_current_msec;
|
||||
|
||||
if (cg->idle) {
|
||||
cg->k += now - cg->idle_start;
|
||||
}
|
||||
|
||||
cg->idle_start = now;
|
||||
}
|
||||
|
||||
cg->idle = idle;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_quic_drop_ack_ranges(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
uint64_t pn)
|
||||
@@ -435,6 +593,7 @@ ngx_quic_drop_ack_ranges(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
static ngx_int_t
|
||||
ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
|
||||
{
|
||||
uint64_t pkt_thr;
|
||||
ngx_uint_t i, nlost;
|
||||
ngx_msec_t now, wait, thr, oldest, newest;
|
||||
ngx_queue_t *q;
|
||||
@@ -444,11 +603,12 @@ ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
now = ngx_current_msec;
|
||||
thr = ngx_quic_lost_threshold(qc);
|
||||
thr = ngx_quic_time_threshold(qc);
|
||||
|
||||
/* send time of lost packets across all send contexts */
|
||||
oldest = NGX_TIMER_INFINITE;
|
||||
newest = NGX_TIMER_INFINITE;
|
||||
#if (NGX_SUPPRESS_WARN)
|
||||
oldest = now;
|
||||
newest = now;
|
||||
#endif
|
||||
|
||||
nlost = 0;
|
||||
|
||||
@@ -460,6 +620,8 @@ ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
|
||||
continue;
|
||||
}
|
||||
|
||||
pkt_thr = ngx_quic_packet_threshold(ctx);
|
||||
|
||||
while (!ngx_queue_empty(&ctx->sent)) {
|
||||
|
||||
q = ngx_queue_head(&ctx->sent);
|
||||
@@ -471,23 +633,27 @@ ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
|
||||
|
||||
wait = start->send_time + thr - now;
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic detect_lost pnum:%uL thr:%M wait:%i level:%d",
|
||||
start->pnum, thr, (ngx_int_t) wait, start->level);
|
||||
ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic detect_lost pnum:%uL thr:%M pthr:%uL wait:%i level:%d",
|
||||
start->pnum, thr, pkt_thr, (ngx_int_t) wait, start->level);
|
||||
|
||||
if ((ngx_msec_int_t) wait > 0
|
||||
&& ctx->largest_ack - start->pnum < NGX_QUIC_PKT_THR)
|
||||
&& ctx->largest_ack - start->pnum < pkt_thr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (start->send_time > qc->first_rtt) {
|
||||
if ((ngx_msec_int_t) (start->send_time - qc->first_rtt) > 0) {
|
||||
|
||||
if (oldest == NGX_TIMER_INFINITE || start->send_time < oldest) {
|
||||
if (nlost == 0
|
||||
|| (ngx_msec_int_t) (start->send_time - oldest) < 0)
|
||||
{
|
||||
oldest = start->send_time;
|
||||
}
|
||||
|
||||
if (newest == NGX_TIMER_INFINITE || start->send_time > newest) {
|
||||
if (nlost == 0
|
||||
|| (ngx_msec_int_t) (start->send_time - newest) > 0)
|
||||
{
|
||||
newest = start->send_time;
|
||||
}
|
||||
|
||||
@@ -508,8 +674,9 @@ ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
|
||||
* latest ACK frame.
|
||||
*/
|
||||
|
||||
if (st && nlost >= 2 && (st->newest < oldest || st->oldest > newest)) {
|
||||
|
||||
if (st && nlost >= 2 && ((ngx_msec_int_t) (st->newest - oldest) < 0
|
||||
|| (ngx_msec_int_t) (st->oldest - newest) > 0))
|
||||
{
|
||||
if (newest - oldest > ngx_quic_pcg_duration(c)) {
|
||||
ngx_quic_persistent_congestion(c);
|
||||
}
|
||||
@@ -547,11 +714,43 @@ ngx_quic_persistent_congestion(ngx_connection_t *c)
|
||||
qc = ngx_quic_get_connection(c);
|
||||
cg = &qc->congestion;
|
||||
|
||||
cg->recovery_start = ngx_current_msec;
|
||||
cg->window = qc->tp.max_udp_payload_size * 2;
|
||||
cg->mtu = qc->path->mtu;
|
||||
cg->recovery_start = ngx_quic_oldest_sent_packet(c) - 1;
|
||||
cg->window = cg->mtu * 2;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic persistent congestion win:%uz", cg->window);
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion persistent t:%M win:%uz",
|
||||
ngx_current_msec, cg->window);
|
||||
}
|
||||
|
||||
|
||||
static ngx_msec_t
|
||||
ngx_quic_oldest_sent_packet(ngx_connection_t *c)
|
||||
{
|
||||
ngx_msec_t oldest;
|
||||
ngx_uint_t i;
|
||||
ngx_queue_t *q;
|
||||
ngx_quic_frame_t *start;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
oldest = ngx_current_msec;
|
||||
|
||||
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
|
||||
ctx = &qc->send_ctx[i];
|
||||
|
||||
if (!ngx_queue_empty(&ctx->sent)) {
|
||||
q = ngx_queue_head(&ctx->sent);
|
||||
start = ngx_queue_data(q, ngx_quic_frame_t, queue);
|
||||
|
||||
if ((ngx_msec_int_t) (start->send_time - oldest) < 0) {
|
||||
oldest = start->send_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return oldest;
|
||||
}
|
||||
|
||||
|
||||
@@ -659,7 +858,7 @@ static void
|
||||
ngx_quic_congestion_lost(ngx_connection_t *c, ngx_quic_frame_t *f)
|
||||
{
|
||||
ngx_uint_t blocked;
|
||||
ngx_msec_t timer;
|
||||
ngx_msec_t now, timer;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
@@ -681,26 +880,41 @@ ngx_quic_congestion_lost(ngx_connection_t *c, ngx_quic_frame_t *f)
|
||||
|
||||
timer = f->send_time - cg->recovery_start;
|
||||
|
||||
now = ngx_current_msec;
|
||||
|
||||
if ((ngx_msec_int_t) timer <= 0) {
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion lost recovery win:%uz ss:%z if:%uz",
|
||||
cg->window, cg->ssthresh, cg->in_flight);
|
||||
"quic congestion lost rec t:%M win:%uz if:%uz",
|
||||
now, cg->window, cg->in_flight);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
cg->recovery_start = ngx_current_msec;
|
||||
cg->window /= 2;
|
||||
if (f->ignore_loss) {
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion lost ignore t:%M win:%uz if:%uz",
|
||||
now, cg->window, cg->in_flight);
|
||||
|
||||
if (cg->window < qc->tp.max_udp_payload_size * 2) {
|
||||
cg->window = qc->tp.max_udp_payload_size * 2;
|
||||
goto done;
|
||||
}
|
||||
|
||||
cg->ssthresh = cg->window;
|
||||
/* RFC 9438, 4.6. Multiplicative Decrease */
|
||||
|
||||
cg->mtu = qc->path->mtu;
|
||||
cg->recovery_start = now;
|
||||
cg->w_prior = cg->window;
|
||||
/* RFC 9438, 4.7. Fast Convergence */
|
||||
cg->w_max = (cg->window < cg->w_max)
|
||||
? cg->window * (10 + NGX_QUIC_CUBIC_BETA) / 20 : cg->window;
|
||||
cg->ssthresh = cg->in_flight * NGX_QUIC_CUBIC_BETA / 10;
|
||||
cg->window = ngx_max(cg->ssthresh, cg->mtu * 2);
|
||||
cg->w_est = cg->window;
|
||||
cg->k = now + ngx_quic_congestion_cubic_time(c);
|
||||
cg->idle_start = now;
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion lost win:%uz ss:%z if:%uz",
|
||||
cg->window, cg->ssthresh, cg->in_flight);
|
||||
"quic congestion lost t:%M win:%uz if:%uz",
|
||||
now, cg->window, cg->in_flight);
|
||||
|
||||
done:
|
||||
|
||||
@@ -710,9 +924,62 @@ done:
|
||||
}
|
||||
|
||||
|
||||
static ngx_msec_t
|
||||
ngx_quic_congestion_cubic_time(ngx_connection_t *c)
|
||||
{
|
||||
int64_t v, x, d, cc;
|
||||
ngx_uint_t n;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
cg = &qc->congestion;
|
||||
|
||||
/*
|
||||
* RFC 9438, Figure 2
|
||||
*
|
||||
* k_msec = ((w_max - cwnd_epoch) / C / mtu) ^ 1/3 * 1000
|
||||
*/
|
||||
|
||||
if (cg->w_max <= cg->window) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
cc = 10000000000ll / (int64_t) cg->mtu / MGX_QUIC_CUBIC_C;
|
||||
v = (int64_t) (cg->w_max - cg->window) * cc;
|
||||
|
||||
/*
|
||||
* Newton-Raphson method for x ^ 3 = v:
|
||||
*
|
||||
* x_next = (2 * x_prev + v / x_prev ^ 2) / 3
|
||||
*/
|
||||
|
||||
x = 5000;
|
||||
|
||||
for (n = 1; n <= 10; n++) {
|
||||
d = (v / x / x - x) / 3;
|
||||
x += d;
|
||||
|
||||
if (ngx_abs(d) <= 100) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (x > NGX_MAX_SIZE_T_VALUE) {
|
||||
return NGX_MAX_SIZE_T_VALUE;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic cubic time:%L n:%ui", x, n);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_quic_set_lost_timer(ngx_connection_t *c)
|
||||
{
|
||||
uint64_t pkt_thr;
|
||||
ngx_uint_t i;
|
||||
ngx_msec_t now;
|
||||
ngx_queue_t *q;
|
||||
@@ -738,10 +1005,12 @@ ngx_quic_set_lost_timer(ngx_connection_t *c)
|
||||
q = ngx_queue_head(&ctx->sent);
|
||||
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
|
||||
w = (ngx_msec_int_t)
|
||||
(f->send_time + ngx_quic_lost_threshold(qc) - now);
|
||||
(f->send_time + ngx_quic_time_threshold(qc) - now);
|
||||
|
||||
if (f->pnum <= ctx->largest_ack) {
|
||||
if (w < 0 || ctx->largest_ack - f->pnum >= NGX_QUIC_PKT_THR) {
|
||||
pkt_thr = ngx_quic_packet_threshold(ctx);
|
||||
|
||||
if (w < 0 || ctx->largest_ack - f->pnum >= pkt_thr) {
|
||||
w = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c,
|
||||
|
||||
void ngx_quic_congestion_ack(ngx_connection_t *c,
|
||||
ngx_quic_frame_t *frame);
|
||||
void ngx_quic_congestion_idle(ngx_connection_t *c, ngx_uint_t idle);
|
||||
void ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx);
|
||||
void ngx_quic_set_lost_timer(ngx_connection_t *c);
|
||||
void ngx_quic_pto_handler(ngx_event_t *ev);
|
||||
|
||||
@@ -168,7 +168,14 @@ typedef struct {
|
||||
size_t in_flight;
|
||||
size_t window;
|
||||
size_t ssthresh;
|
||||
size_t w_max;
|
||||
size_t w_est;
|
||||
size_t w_prior;
|
||||
size_t mtu;
|
||||
ngx_msec_t recovery_start;
|
||||
ngx_msec_t idle_start;
|
||||
ngx_msec_t k;
|
||||
ngx_uint_t idle; /* unsigned idle:1; */
|
||||
} ngx_quic_congestion_t;
|
||||
|
||||
|
||||
@@ -254,6 +261,7 @@ struct ngx_quic_connection_s {
|
||||
ngx_buf_t *free_shadow_bufs;
|
||||
|
||||
ngx_uint_t nframes;
|
||||
ngx_uint_t max_frames;
|
||||
#ifdef NGX_QUIC_DEBUG_ALLOC
|
||||
ngx_uint_t nbufs;
|
||||
ngx_uint_t nshadowbufs;
|
||||
|
||||
@@ -214,7 +214,7 @@ ngx_quic_alloc_frame(ngx_connection_t *c)
|
||||
"quic reuse frame n:%ui", qc->nframes);
|
||||
#endif
|
||||
|
||||
} else if (qc->nframes < 10000) {
|
||||
} else if (qc->nframes < qc->max_frames) {
|
||||
frame = ngx_palloc(c->pool, sizeof(ngx_quic_frame_t));
|
||||
if (frame == NULL) {
|
||||
return NULL;
|
||||
|
||||
@@ -182,11 +182,12 @@ valid:
|
||||
|
||||
ngx_memzero(&qc->congestion, sizeof(ngx_quic_congestion_t));
|
||||
|
||||
qc->congestion.window = ngx_min(10 * qc->tp.max_udp_payload_size,
|
||||
ngx_max(2 * qc->tp.max_udp_payload_size,
|
||||
qc->congestion.window = ngx_min(10 * NGX_QUIC_MIN_INITIAL_SIZE,
|
||||
ngx_max(2 * NGX_QUIC_MIN_INITIAL_SIZE,
|
||||
14720));
|
||||
qc->congestion.ssthresh = (size_t) -1;
|
||||
qc->congestion.recovery_start = ngx_current_msec;
|
||||
qc->congestion.mtu = NGX_QUIC_MIN_INITIAL_SIZE;
|
||||
qc->congestion.recovery_start = ngx_current_msec - 1;
|
||||
|
||||
ngx_quic_init_rtt(qc);
|
||||
}
|
||||
@@ -923,6 +924,8 @@ ngx_quic_send_path_mtu_probe(ngx_connection_t *c, ngx_quic_path_t *path)
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->type = NGX_QUIC_FT_PING;
|
||||
frame->ignore_loss = 1;
|
||||
frame->ignore_congestion = 1;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||
|
||||
@@ -45,9 +45,9 @@
|
||||
|
||||
|
||||
static ngx_int_t ngx_quic_create_datagrams(ngx_connection_t *c);
|
||||
static void ngx_quic_commit_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx);
|
||||
static void ngx_quic_revert_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
uint64_t pnum);
|
||||
static void ngx_quic_commit_send(ngx_connection_t *c);
|
||||
static void ngx_quic_revert_send(ngx_connection_t *c,
|
||||
uint64_t preserved_pnum[NGX_QUIC_SEND_CTX_LAST]);
|
||||
#if ((NGX_HAVE_UDP_SEGMENT) && (NGX_HAVE_MSGHDR_MSG_CONTROL))
|
||||
static ngx_uint_t ngx_quic_allow_segmentation(ngx_connection_t *c);
|
||||
static ngx_int_t ngx_quic_create_segments(ngx_connection_t *c);
|
||||
@@ -127,6 +127,10 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
|
||||
cg = &qc->congestion;
|
||||
path = qc->path;
|
||||
|
||||
#if (NGX_SUPPRESS_WARN)
|
||||
ngx_memzero(preserved_pnum, sizeof(preserved_pnum));
|
||||
#endif
|
||||
|
||||
while (cg->in_flight < cg->window) {
|
||||
|
||||
p = dst;
|
||||
@@ -150,12 +154,7 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
|
||||
|
||||
if (min > len) {
|
||||
/* padding can't be applied - avoid sending the packet */
|
||||
|
||||
while (i-- > 0) {
|
||||
ctx = &qc->send_ctx[i];
|
||||
ngx_quic_revert_send(c, ctx, preserved_pnum[i]);
|
||||
}
|
||||
|
||||
ngx_quic_revert_send(c, preserved_pnum);
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
@@ -180,17 +179,12 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
|
||||
}
|
||||
|
||||
if (n == NGX_AGAIN) {
|
||||
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
|
||||
ngx_quic_revert_send(c, &qc->send_ctx[i], preserved_pnum[i]);
|
||||
}
|
||||
|
||||
ngx_quic_revert_send(c, preserved_pnum);
|
||||
ngx_add_timer(&qc->push, NGX_QUIC_SOCKET_RETRY_DELAY);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
|
||||
ngx_quic_commit_send(c, &qc->send_ctx[i]);
|
||||
}
|
||||
ngx_quic_commit_send(c);
|
||||
|
||||
path->sent += len;
|
||||
}
|
||||
@@ -200,53 +194,79 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
|
||||
|
||||
|
||||
static void
|
||||
ngx_quic_commit_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
|
||||
ngx_quic_commit_send(ngx_connection_t *c)
|
||||
{
|
||||
ngx_uint_t i, idle;
|
||||
ngx_queue_t *q;
|
||||
ngx_quic_frame_t *f;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
cg = &qc->congestion;
|
||||
|
||||
while (!ngx_queue_empty(&ctx->sending)) {
|
||||
idle = 1;
|
||||
|
||||
q = ngx_queue_head(&ctx->sending);
|
||||
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
|
||||
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
|
||||
ctx = &qc->send_ctx[i];
|
||||
|
||||
ngx_queue_remove(q);
|
||||
if (!ngx_queue_empty(&ctx->frames)) {
|
||||
idle = 0;
|
||||
}
|
||||
|
||||
if (f->pkt_need_ack && !qc->closing) {
|
||||
ngx_queue_insert_tail(&ctx->sent, q);
|
||||
while (!ngx_queue_empty(&ctx->sending)) {
|
||||
|
||||
cg->in_flight += f->plen;
|
||||
q = ngx_queue_head(&ctx->sending);
|
||||
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
|
||||
|
||||
} else {
|
||||
ngx_quic_free_frame(c, f);
|
||||
ngx_queue_remove(q);
|
||||
|
||||
if (f->pkt_need_ack && !qc->closing) {
|
||||
ngx_queue_insert_tail(&ctx->sent, q);
|
||||
|
||||
cg->in_flight += f->plen;
|
||||
|
||||
} else {
|
||||
ngx_quic_free_frame(c, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion send if:%uz", cg->in_flight);
|
||||
|
||||
ngx_quic_congestion_idle(c, idle);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_quic_revert_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
uint64_t pnum)
|
||||
ngx_quic_revert_send(ngx_connection_t *c, uint64_t pnum[NGX_QUIC_SEND_CTX_LAST])
|
||||
{
|
||||
ngx_queue_t *q;
|
||||
ngx_uint_t i;
|
||||
ngx_queue_t *q;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
while (!ngx_queue_empty(&ctx->sending)) {
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
q = ngx_queue_last(&ctx->sending);
|
||||
ngx_queue_remove(q);
|
||||
ngx_queue_insert_head(&ctx->frames, q);
|
||||
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
|
||||
ctx = &qc->send_ctx[i];
|
||||
|
||||
if (ngx_queue_empty(&ctx->sending)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
do {
|
||||
q = ngx_queue_last(&ctx->sending);
|
||||
ngx_queue_remove(q);
|
||||
ngx_queue_insert_head(&ctx->frames, q);
|
||||
} while (!ngx_queue_empty(&ctx->sending));
|
||||
|
||||
ctx->pnum = pnum[i];
|
||||
}
|
||||
|
||||
ctx->pnum = pnum;
|
||||
ngx_quic_congestion_idle(c, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -311,13 +331,13 @@ ngx_quic_create_segments(ngx_connection_t *c)
|
||||
size_t len, segsize;
|
||||
ssize_t n;
|
||||
u_char *p, *end;
|
||||
uint64_t preserved_pnum;
|
||||
ngx_uint_t nseg;
|
||||
ngx_uint_t nseg, level;
|
||||
ngx_quic_path_t *path;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
static u_char dst[NGX_QUIC_MAX_UDP_SEGMENT_BUF];
|
||||
static uint64_t preserved_pnum[NGX_QUIC_SEND_CTX_LAST];
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
cg = &qc->congestion;
|
||||
@@ -335,7 +355,8 @@ ngx_quic_create_segments(ngx_connection_t *c)
|
||||
|
||||
nseg = 0;
|
||||
|
||||
preserved_pnum = ctx->pnum;
|
||||
level = ctx - qc->send_ctx;
|
||||
preserved_pnum[level] = ctx->pnum;
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
@@ -369,19 +390,18 @@ ngx_quic_create_segments(ngx_connection_t *c)
|
||||
}
|
||||
|
||||
if (n == NGX_AGAIN) {
|
||||
ngx_quic_revert_send(c, ctx, preserved_pnum);
|
||||
|
||||
ngx_quic_revert_send(c, preserved_pnum);
|
||||
ngx_add_timer(&qc->push, NGX_QUIC_SOCKET_RETRY_DELAY);
|
||||
break;
|
||||
}
|
||||
|
||||
ngx_quic_commit_send(c, ctx);
|
||||
ngx_quic_commit_send(c);
|
||||
|
||||
path->sent += n;
|
||||
|
||||
p = dst;
|
||||
nseg = 0;
|
||||
preserved_pnum = ctx->pnum;
|
||||
preserved_pnum[level] = ctx->pnum;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -125,9 +125,10 @@ ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys, ngx_str_t *secret,
|
||||
ngx_quic_secret_t *client, *server;
|
||||
ngx_quic_ciphers_t ciphers;
|
||||
|
||||
static const uint8_t salt[20] =
|
||||
"\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17"
|
||||
"\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb\x7f\x0a";
|
||||
static const uint8_t salt[20] = {
|
||||
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17,
|
||||
0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a
|
||||
};
|
||||
|
||||
client = &keys->secrets[ssl_encryption_initial].client;
|
||||
server = &keys->secrets[ssl_encryption_initial].server;
|
||||
@@ -958,8 +959,9 @@ ngx_quic_create_retry_packet(ngx_quic_header_t *pkt, ngx_str_t *res)
|
||||
/* 5.8. Retry Packet Integrity */
|
||||
static ngx_quic_md_t key = ngx_quic_md(
|
||||
"\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e");
|
||||
static const u_char nonce[NGX_QUIC_IV_LEN] =
|
||||
"\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb";
|
||||
static const u_char nonce[NGX_QUIC_IV_LEN] = {
|
||||
0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb
|
||||
};
|
||||
static ngx_str_t in = ngx_string("");
|
||||
|
||||
ad.data = res->data;
|
||||
|
||||
@@ -1773,7 +1773,7 @@ ngx_quic_parse_transport_params(u_char *p, u_char *end, ngx_quic_tp_t *tp,
|
||||
}
|
||||
|
||||
if (rc == NGX_DECLINED) {
|
||||
ngx_log_error(NGX_LOG_INFO, log, 0,
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,
|
||||
"quic %s transport param id:0x%xL, skipped",
|
||||
(id % 31 == 27) ? "reserved" : "unknown", id);
|
||||
}
|
||||
|
||||
@@ -271,6 +271,7 @@ struct ngx_quic_frame_s {
|
||||
unsigned need_ack:1;
|
||||
unsigned pkt_need_ack:1;
|
||||
unsigned ignore_congestion:1;
|
||||
unsigned ignore_loss:1;
|
||||
|
||||
ngx_chain_t *data;
|
||||
union {
|
||||
|
||||
Reference in New Issue
Block a user