# HG changeset patch # User Roman Arutyunyan # Date 1605095870 0 # Node ID 45db1b5c1706387aa40ea3b7266fc340a4263225 # Parent 405b6e8eb523961ca7e41e4e97e892be65773f2b QUIC: connection multiplexing per port. Also, connection migration within a single worker is implemented. diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h --- a/src/event/ngx_event.h +++ b/src/event/ngx_event.h @@ -167,6 +167,18 @@ struct ngx_event_aio_s { #endif +#if !(NGX_WIN32) + +struct ngx_udp_connection_s { + ngx_rbtree_node_t node; + ngx_connection_t *connection; + ngx_str_t key; + ngx_buf_t *buffer; +}; + +#endif + + typedef struct { ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); @@ -501,6 +513,8 @@ void ngx_event_accept(ngx_event_t *ev); void ngx_event_recvmsg(ngx_event_t *ev); void ngx_udp_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +void ngx_insert_udp_connection(ngx_connection_t *c, ngx_udp_connection_t *udp, + ngx_str_t *key); #endif void ngx_delete_udp_connection(void *data); ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle); diff --git a/src/event/ngx_event_quic.c b/src/event/ngx_event_quic.c --- a/src/event/ngx_event_quic.c +++ b/src/event/ngx_event_quic.c @@ -121,11 +121,18 @@ struct ngx_quic_connection_s { ngx_str_t odcid; /* original server ID */ ngx_str_t token; + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_queue_t client_ids; + ngx_queue_t server_ids; ngx_queue_t free_client_ids; + ngx_queue_t free_server_ids; ngx_uint_t nclient_ids; + ngx_uint_t nserver_ids; uint64_t max_retired_seqnum; uint64_t client_seqnum; + uint64_t server_seqnum; ngx_uint_t client_tp_done; ngx_quic_tp_t tp; @@ -185,6 +192,15 @@ typedef struct { } ngx_quic_client_id_t; +typedef struct { + ngx_udp_connection_t udp; + ngx_queue_t queue; + uint64_t seqnum; + size_t len; + u_char id[NGX_QUIC_CID_LEN_MAX]; +} ngx_quic_server_id_t; + + typedef ngx_int_t (*ngx_quic_frame_handler_pt)(ngx_connection_t *c, ngx_quic_frame_t *frame, void *data); @@ -217,8 +233,7 @@ static ngx_int_t ngx_quic_process_statel ngx_quic_header_t *pkt); static ngx_int_t ngx_quic_negotiate_version(ngx_connection_t *c, ngx_quic_header_t *inpkt); -static ngx_int_t ngx_quic_new_dcid(ngx_connection_t *c, - ngx_quic_connection_t *qc, ngx_str_t *odcid); +static ngx_int_t ngx_quic_create_server_id(ngx_connection_t *c, u_char *id); static ngx_int_t ngx_quic_send_retry(ngx_connection_t *c); static ngx_int_t ngx_quic_new_token(ngx_connection_t *c, ngx_str_t *token); static ngx_int_t ngx_quic_validate_token(ngx_connection_t *c, @@ -304,8 +319,16 @@ static ngx_int_t ngx_quic_handle_new_con ngx_quic_header_t *pkt, ngx_quic_new_conn_id_frame_t *f); static ngx_int_t ngx_quic_retire_connection_id(ngx_connection_t *c, enum ssl_encryption_level_t level, uint64_t seqnum); +static ngx_int_t ngx_quic_handle_retire_connection_id_frame(ngx_connection_t *c, + ngx_quic_header_t *pkt, ngx_quic_retire_cid_frame_t *f); +static ngx_int_t ngx_quic_issue_server_ids(ngx_connection_t *c); +static void ngx_quic_clear_temp_server_ids(ngx_connection_t *c); +static ngx_quic_server_id_t *ngx_quic_insert_server_id(ngx_connection_t *c, + ngx_str_t *id); static ngx_quic_client_id_t *ngx_quic_alloc_client_id(ngx_connection_t *c, ngx_quic_connection_t *qc); +static ngx_quic_server_id_t *ngx_quic_alloc_server_id(ngx_connection_t *c, + ngx_quic_connection_t *qc); static void ngx_quic_queue_frame(ngx_quic_connection_t *qc, ngx_quic_frame_t *frame); @@ -439,7 +462,8 @@ ngx_quic_log_frame(ngx_log_t *log, ngx_q break; case NGX_QUIC_FT_NEW_CONNECTION_ID: - p = ngx_slprintf(p, last, "NCID seq:%uL retire:%uL len:%ud", + p = ngx_slprintf(p, last, + "NEW_CONNECTION_ID seq:%uL retire:%uL len:%ud", f->u.ncid.seqnum, f->u.ncid.retire, f->u.ncid.len); break; @@ -983,7 +1007,9 @@ ngx_quic_new_connection(ngx_connection_t ngx_queue_init(&qc->free_frames); ngx_queue_init(&qc->client_ids); + ngx_queue_init(&qc->server_ids); ngx_queue_init(&qc->free_client_ids); + ngx_queue_init(&qc->free_server_ids); qc->avg_rtt = NGX_QUIC_INITIAL_RTT; qc->rttvar = NGX_QUIC_INITIAL_RTT / 2; @@ -992,6 +1018,7 @@ ngx_quic_new_connection(ngx_connection_t /* * qc->latest_rtt = 0 * qc->nclient_ids = 0 + * qc->nserver_ids = 0 * qc->max_retired_seqnum = 0 */ @@ -1010,6 +1037,16 @@ ngx_quic_new_connection(ngx_connection_t qc->conf = conf; qc->tp = conf->tp; + if (qc->tp.disable_active_migration) { + qc->sockaddr = ngx_palloc(c->pool, c->socklen); + if (qc->sockaddr == NULL) { + return NULL; + } + + ngx_memcpy(qc->sockaddr, c->sockaddr, c->socklen); + qc->socklen = c->socklen; + } + ctp = &qc->ctp; ctp->max_udp_payload_size = ngx_quic_max_udp_payload(c); ctp->ack_delay_exponent = NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT; @@ -1026,7 +1063,19 @@ ngx_quic_new_connection(ngx_connection_t qc->congestion.ssthresh = (size_t) -1; qc->congestion.recovery_start = ngx_current_msec; - if (ngx_quic_new_dcid(c, qc, &pkt->dcid) != NGX_OK) { + qc->odcid.len = pkt->dcid.len; + qc->odcid.data = ngx_pstrdup(c->pool, &pkt->dcid); + if (qc->odcid.data == NULL) { + return NULL; + } + + qc->dcid.len = NGX_QUIC_SERVER_CID_LEN; + qc->dcid.data = ngx_pnalloc(c->pool, qc->dcid.len); + if (qc->dcid.data == NULL) { + return NULL; + } + + if (ngx_quic_create_server_id(c, qc->dcid.data) != NGX_OK) { return NULL; } @@ -1055,6 +1104,8 @@ ngx_quic_new_connection(ngx_connection_t qc->nclient_ids++; qc->client_seqnum = 0; + qc->server_seqnum = NGX_QUIC_UNSET_PN; + return qc; } @@ -1186,26 +1237,14 @@ ngx_quic_negotiate_version(ngx_connectio static ngx_int_t -ngx_quic_new_dcid(ngx_connection_t *c, ngx_quic_connection_t *qc, - ngx_str_t *odcid) +ngx_quic_create_server_id(ngx_connection_t *c, u_char *id) { - qc->dcid.len = NGX_QUIC_SERVER_CID_LEN; - qc->dcid.data = ngx_pnalloc(c->pool, NGX_QUIC_SERVER_CID_LEN); - if (qc->dcid.data == NULL) { + if (RAND_bytes(id, NGX_QUIC_SERVER_CID_LEN) != 1) { return NGX_ERROR; } - if (RAND_bytes(qc->dcid.data, NGX_QUIC_SERVER_CID_LEN) != 1) { - return NGX_ERROR; - } - - ngx_quic_hexdump(c->log, "quic server CID", qc->dcid.data, qc->dcid.len); - - qc->odcid.len = odcid->len; - qc->odcid.data = ngx_pstrdup(c->pool, odcid); - if (qc->odcid.data == NULL) { - return NGX_ERROR; - } + ngx_quic_hexdump(c->log, "quic create server id", + id, NGX_QUIC_SERVER_CID_LEN); return NGX_OK; } @@ -1254,6 +1293,10 @@ ngx_quic_send_retry(ngx_connection_t *c) c->quic->tp.retry_scid = c->quic->dcid; c->quic->in_retry = 1; + if (ngx_quic_insert_server_id(c, &c->quic->dcid) == NULL) { + return NGX_ERROR; + } + return NGX_OK; } @@ -1629,6 +1672,16 @@ ngx_quic_input_handler(ngx_event_t *rev) return; } + if (qc->tp.disable_active_migration) { + if (c->socklen != qc->socklen + || ngx_memcmp(c->sockaddr, qc->sockaddr, c->socklen) != 0) + { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic dropping packet from new address"); + return; + } + } + b.last += n; qc->received += n; @@ -1694,7 +1747,9 @@ static ngx_int_t ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc) { ngx_uint_t i; + ngx_queue_t *q; ngx_quic_send_ctx_t *ctx; + ngx_quic_server_id_t *sid; ngx_quic_connection_t *qc; qc = c->quic; @@ -1801,6 +1856,15 @@ ngx_quic_close_quic(ngx_connection_t *c, ngx_quic_free_frames(c, &qc->send_ctx[i].sent); } + while (!ngx_queue_empty(&qc->server_ids)) { + q = ngx_queue_head(&qc->server_ids); + sid = ngx_queue_data(q, ngx_quic_server_id_t, queue); + + ngx_queue_remove(q); + ngx_rbtree_delete(&c->listening->rbtree, &sid->udp.node); + qc->nserver_ids--; + } + if (qc->close.timer_set) { return NGX_AGAIN; } @@ -2065,7 +2129,21 @@ ngx_quic_process_packet(ngx_connection_t return NGX_DECLINED; } - if (ngx_quic_new_dcid(c, qc, &pkt->dcid) != NGX_OK) { + qc->odcid.len = pkt->dcid.len; + qc->odcid.data = ngx_pstrdup(c->pool, &pkt->dcid); + if (qc->odcid.data == NULL) { + return NGX_ERROR; + } + + ngx_quic_clear_temp_server_ids(c); + + if (ngx_quic_create_server_id(c, qc->dcid.data) != NGX_OK) { + return NGX_ERROR; + } + + qc->server_seqnum = 0; + + if (ngx_quic_insert_server_id(c, &qc->dcid) == NULL) { return NGX_ERROR; } @@ -2137,6 +2215,16 @@ ngx_quic_process_packet(ngx_connection_t return NGX_ERROR; } + if (ngx_quic_insert_server_id(c, &qc->odcid) == NULL) { + return NGX_ERROR; + } + + qc->server_seqnum = 0; + + if (ngx_quic_insert_server_id(c, &qc->dcid) == NULL) { + return NGX_ERROR; + } + } else if (pkt->level == ssl_encryption_application) { return ngx_quic_send_stateless_reset(c, conf, pkt); @@ -2270,6 +2358,10 @@ ngx_quic_discard_ctx(ngx_connection_t *c ngx_quic_free_frame(c, f); } + if (level == ssl_encryption_initial) { + ngx_quic_clear_temp_server_ids(c); + } + ctx->send_ack = 0; } @@ -2277,43 +2369,12 @@ ngx_quic_discard_ctx(ngx_connection_t *c static ngx_int_t ngx_quic_check_peer(ngx_quic_connection_t *qc, ngx_quic_header_t *pkt) { - ngx_str_t *dcid; ngx_queue_t *q; - ngx_quic_send_ctx_t *ctx; ngx_quic_client_id_t *cid; - dcid = (pkt->level == ssl_encryption_early_data) ? &qc->odcid : &qc->dcid; - - if (pkt->dcid.len == dcid->len - && ngx_memcmp(pkt->dcid.data, dcid->data, dcid->len) == 0) - { - if (pkt->level == ssl_encryption_application) { - return NGX_OK; - } - - goto found; - } - - /* - * a packet sent in response to an initial client packet might be lost, - * thus check also for old dcid - */ - ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_initial); - - if (pkt->level == ssl_encryption_initial - && ctx->largest_ack == NGX_QUIC_UNSET_PN) - { - if (pkt->dcid.len == qc->odcid.len - && ngx_memcmp(pkt->dcid.data, qc->odcid.data, qc->odcid.len) == 0) - { - goto found; - } - } - - ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "quic unexpected quic dcid"); - return NGX_ERROR; - -found: + if (pkt->level == ssl_encryption_application) { + return NGX_OK; + } for (q = ngx_queue_head(&qc->client_ids); q != ngx_queue_sentinel(&qc->client_ids); @@ -2533,6 +2594,16 @@ ngx_quic_payload_handler(ngx_connection_ break; case NGX_QUIC_FT_RETIRE_CONNECTION_ID: + + if (ngx_quic_handle_retire_connection_id_frame(c, pkt, + &frame.u.retire_cid) + != NGX_OK) + { + return NGX_ERROR; + } + + break; + case NGX_QUIC_FT_PATH_RESPONSE: /* TODO: handle */ @@ -3638,6 +3709,10 @@ ngx_quic_crypto_input(ngx_connection_t * */ ngx_quic_discard_ctx(c, ssl_encryption_handshake); + if (ngx_quic_issue_server_ids(c) != NGX_OK) { + return NGX_ERROR; + } + return NGX_OK; } @@ -4265,6 +4340,173 @@ ngx_quic_retire_connection_id(ngx_connec } +static ngx_int_t +ngx_quic_handle_retire_connection_id_frame(ngx_connection_t *c, + ngx_quic_header_t *pkt, ngx_quic_retire_cid_frame_t *f) +{ + ngx_queue_t *q; + ngx_quic_server_id_t *sid; + ngx_quic_connection_t *qc; + + qc = c->quic; + + for (q = ngx_queue_head(&qc->server_ids); + q != ngx_queue_sentinel(&qc->server_ids); + q = ngx_queue_next(q)) + { + sid = ngx_queue_data(q, ngx_quic_server_id_t, queue); + + if (sid->seqnum == f->sequence_number) { + ngx_queue_remove(q); + ngx_rbtree_delete(&c->listening->rbtree, &sid->udp.node); + qc->nserver_ids--; + + if (c->udp != &sid->udp) { + ngx_queue_insert_tail(&qc->free_server_ids, &sid->queue); + } + + break; + } + } + + return ngx_quic_issue_server_ids(c); +} + + +static ngx_int_t +ngx_quic_issue_server_ids(ngx_connection_t *c) +{ + ngx_str_t dcid; + ngx_uint_t n; + ngx_quic_frame_t *frame; + ngx_quic_server_id_t *sid; + ngx_quic_connection_t *qc; + u_char id[NGX_QUIC_SERVER_CID_LEN]; + + qc = c->quic; + + n = ngx_min(NGX_QUIC_MAX_SERVER_IDS, qc->ctp.active_connection_id_limit); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic issue server ids has:%ui max:%ui", qc->nserver_ids, n); + + while (qc->nserver_ids < n) { + if (ngx_quic_create_server_id(c, id) != NGX_OK) { + return NGX_ERROR; + } + + dcid.len = NGX_QUIC_SERVER_CID_LEN; + dcid.data = id; + + sid = ngx_quic_insert_server_id(c, &dcid); + if (sid == NULL) { + return NGX_ERROR; + } + + frame = ngx_quic_alloc_frame(c, 0); + if (frame == NULL) { + return NGX_ERROR; + } + + frame->level = ssl_encryption_application; + frame->type = NGX_QUIC_FT_NEW_CONNECTION_ID; + frame->u.ncid.seqnum = sid->seqnum; + frame->u.ncid.retire = 0; + frame->u.ncid.len = NGX_QUIC_SERVER_CID_LEN; + ngx_memcpy(frame->u.ncid.cid, id, NGX_QUIC_SERVER_CID_LEN); + + if (ngx_quic_new_sr_token(c, &dcid, &qc->conf->sr_token_key, + frame->u.ncid.srt) + != NGX_OK) + { + return NGX_ERROR; + } + + ngx_quic_queue_frame(c->quic, frame); + } + + return NGX_OK; +} + + +static void +ngx_quic_clear_temp_server_ids(ngx_connection_t *c) +{ + ngx_queue_t *q, *next; + ngx_quic_server_id_t *sid; + ngx_quic_connection_t *qc; + + qc = c->quic; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic clear temp server ids"); + + for (q = ngx_queue_head(&qc->server_ids); + q != ngx_queue_sentinel(&qc->server_ids); + q = next) + { + next = ngx_queue_next(q); + sid = ngx_queue_data(q, ngx_quic_server_id_t, queue); + + if (sid->seqnum != NGX_QUIC_UNSET_PN) { + continue; + } + + ngx_queue_remove(q); + ngx_rbtree_delete(&c->listening->rbtree, &sid->udp.node); + qc->nserver_ids--; + + if (c->udp != &sid->udp) { + ngx_queue_insert_tail(&qc->free_server_ids, &sid->queue); + } + } +} + + +static ngx_quic_server_id_t * +ngx_quic_insert_server_id(ngx_connection_t *c, ngx_str_t *id) +{ + ngx_str_t dcid; + ngx_quic_server_id_t *sid; + ngx_quic_connection_t *qc; + + qc = c->quic; + + sid = ngx_quic_alloc_server_id(c, qc); + if (sid == NULL) { + return NULL; + } + + sid->seqnum = qc->server_seqnum; + + if (qc->server_seqnum != NGX_QUIC_UNSET_PN) { + qc->server_seqnum++; + } + + sid->len = id->len; + ngx_memcpy(sid->id, id->data, id->len); + + ngx_queue_insert_tail(&qc->server_ids, &sid->queue); + qc->nserver_ids++; + + dcid.data = sid->id; + dcid.len = sid->len; + + ngx_insert_udp_connection(c, &sid->udp, &dcid); + + if (c->udp == NULL) { + c->udp = &sid->udp; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic insert server id seqnum:%uL", sid->seqnum); + + ngx_quic_hexdump(c->log, "quic server id", id->data, id->len); + + return sid; +} + + static ngx_quic_client_id_t * ngx_quic_alloc_client_id(ngx_connection_t *c, ngx_quic_connection_t *qc) { @@ -4292,6 +4534,33 @@ ngx_quic_alloc_client_id(ngx_connection_ } +static ngx_quic_server_id_t * +ngx_quic_alloc_server_id(ngx_connection_t *c, ngx_quic_connection_t *qc) +{ + ngx_queue_t *q; + ngx_quic_server_id_t *sid; + + if (!ngx_queue_empty(&qc->free_server_ids)) { + + q = ngx_queue_head(&qc->free_server_ids); + sid = ngx_queue_data(q, ngx_quic_server_id_t, queue); + + ngx_queue_remove(&sid->queue); + + ngx_memzero(sid, sizeof(ngx_quic_server_id_t)); + + } else { + + sid = ngx_pcalloc(c->pool, sizeof(ngx_quic_server_id_t)); + if (sid == NULL) { + return NULL; + } + } + + return sid; +} + + static void ngx_quic_queue_frame(ngx_quic_connection_t *qc, ngx_quic_frame_t *frame) { diff --git a/src/event/ngx_event_quic.h b/src/event/ngx_event_quic.h --- a/src/event/ngx_event_quic.h +++ b/src/event/ngx_event_quic.h @@ -58,6 +58,8 @@ #define NGX_QUIC_SR_TOKEN_LEN 16 +#define NGX_QUIC_MAX_SERVER_IDS 8 + typedef struct { /* configurable */ @@ -72,8 +74,8 @@ typedef struct { ngx_uint_t initial_max_streams_bidi; ngx_uint_t initial_max_streams_uni; ngx_uint_t ack_delay_exponent; - ngx_uint_t disable_active_migration; ngx_uint_t active_connection_id_limit; + ngx_flag_t disable_active_migration; ngx_str_t original_dcid; ngx_str_t initial_scid; ngx_str_t retry_scid; @@ -123,6 +125,8 @@ ngx_connection_t *ngx_quic_open_stream(n void ngx_quic_finalize_connection(ngx_connection_t *c, ngx_uint_t err, const char *reason); uint32_t ngx_quic_version(ngx_connection_t *c); +ngx_int_t ngx_quic_get_packet_dcid(ngx_log_t *log, u_char *data, size_t len, + ngx_str_t *dcid); /********************************* DEBUG *************************************/ diff --git a/src/event/ngx_event_quic_transport.c b/src/event/ngx_event_quic_transport.c --- a/src/event/ngx_event_quic_transport.c +++ b/src/event/ngx_event_quic_transport.c @@ -10,6 +10,11 @@ #include +#define NGX_QUIC_LONG_DCID_LEN_OFFSET 5 +#define NGX_QUIC_LONG_DCID_OFFSET 6 +#define NGX_QUIC_SHORT_DCID_OFFSET 1 + + #if (NGX_HAVE_NONALIGNED) #define ngx_quic_parse_uint16(p) ntohs(*(uint16_t *) (p)) @@ -95,6 +100,8 @@ static size_t ngx_quic_create_max_data(u ngx_quic_max_data_frame_t *md); static size_t ngx_quic_create_path_response(u_char *p, ngx_quic_path_challenge_frame_t *pc); +static size_t ngx_quic_create_new_connection_id(u_char *p, + ngx_quic_new_conn_id_frame_t *rcid); static size_t ngx_quic_create_retire_connection_id(u_char *p, ngx_quic_retire_cid_frame_t *rcid); static size_t ngx_quic_create_close(u_char *p, ngx_quic_close_frame_t *cl); @@ -321,6 +328,46 @@ ngx_quic_parse_packet(ngx_quic_header_t } +ngx_int_t +ngx_quic_get_packet_dcid(ngx_log_t *log, u_char *data, size_t n, + ngx_str_t *dcid) +{ + size_t len, offset; + + if (n == 0) { + goto failed; + } + + if (ngx_quic_long_pkt(*data)) { + if (n < NGX_QUIC_LONG_DCID_LEN_OFFSET + 1) { + goto failed; + } + + len = data[NGX_QUIC_LONG_DCID_LEN_OFFSET]; + offset = NGX_QUIC_LONG_DCID_OFFSET; + + } else { + len = NGX_QUIC_SERVER_CID_LEN; + offset = NGX_QUIC_SHORT_DCID_OFFSET; + } + + if (n < len + offset) { + goto failed; + } + + dcid->len = len; + dcid->data = &data[offset]; + + return NGX_OK; + +failed: + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, log, 0, "quic malformed packet"); + + return NGX_ERROR; +} + + static ngx_int_t ngx_quic_parse_long_header(ngx_quic_header_t *pkt) { @@ -1222,6 +1269,9 @@ ngx_quic_create_frame(u_char *p, ngx_qui case NGX_QUIC_FT_PATH_RESPONSE: return ngx_quic_create_path_response(p, &f->u.path_response); + case NGX_QUIC_FT_NEW_CONNECTION_ID: + return ngx_quic_create_new_connection_id(p, &f->u.ncid); + case NGX_QUIC_FT_RETIRE_CONNECTION_ID: return ngx_quic_create_retire_connection_id(p, &f->u.retire_cid); @@ -1705,6 +1755,35 @@ ngx_quic_create_path_response(u_char *p, static size_t +ngx_quic_create_new_connection_id(u_char *p, ngx_quic_new_conn_id_frame_t *ncid) +{ + size_t len; + u_char *start; + + if (p == NULL) { + len = ngx_quic_varint_len(NGX_QUIC_FT_NEW_CONNECTION_ID); + len += ngx_quic_varint_len(ncid->seqnum); + len += ngx_quic_varint_len(ncid->retire); + len++; + len += ncid->len; + len += NGX_QUIC_SR_TOKEN_LEN; + return len; + } + + start = p; + + ngx_quic_build_int(&p, NGX_QUIC_FT_NEW_CONNECTION_ID); + ngx_quic_build_int(&p, ncid->seqnum); + ngx_quic_build_int(&p, ncid->retire); + *p++ = ncid->len; + p = ngx_cpymem(p, ncid->cid, ncid->len); + p = ngx_cpymem(p, ncid->srt, NGX_QUIC_SR_TOKEN_LEN); + + return p - start; +} + + +static size_t ngx_quic_create_retire_connection_id(u_char *p, ngx_quic_retire_cid_frame_t *rcid) { @@ -1783,6 +1862,11 @@ ngx_quic_create_transport_params(u_char *clen = len; } + if (tp->disable_active_migration) { + len += ngx_quic_varint_len(NGX_QUIC_TP_DISABLE_ACTIVE_MIGRATION); + len += ngx_quic_varint_len(0); + } + len += ngx_quic_tp_len(NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT, tp->active_connection_id_limit); @@ -1830,6 +1914,11 @@ ngx_quic_create_transport_params(u_char ngx_quic_tp_vint(NGX_QUIC_TP_MAX_IDLE_TIMEOUT, tp->max_idle_timeout); + if (tp->disable_active_migration) { + ngx_quic_build_int(&p, NGX_QUIC_TP_DISABLE_ACTIVE_MIGRATION); + ngx_quic_build_int(&p, 0); + } + ngx_quic_tp_vint(NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT, tp->active_connection_id_limit); diff --git a/src/event/ngx_event_udp.c b/src/event/ngx_event_udp.c --- a/src/event/ngx_event_udp.c +++ b/src/event/ngx_event_udp.c @@ -12,19 +12,12 @@ #if !(NGX_WIN32) -struct ngx_udp_connection_s { - ngx_rbtree_node_t node; - ngx_connection_t *connection; - ngx_buf_t *buffer; -}; - - static void ngx_close_accepted_udp_connection(ngx_connection_t *c); static ssize_t ngx_udp_shared_recv(ngx_connection_t *c, u_char *buf, size_t size); -static ngx_int_t ngx_insert_udp_connection(ngx_connection_t *c); +static ngx_int_t ngx_create_udp_connection(ngx_connection_t *c); static ngx_connection_t *ngx_lookup_udp_connection(ngx_listening_t *ls, - struct sockaddr *sockaddr, socklen_t socklen, + ngx_str_t *key, struct sockaddr *sockaddr, socklen_t socklen, struct sockaddr *local_sockaddr, socklen_t local_socklen); @@ -32,6 +25,7 @@ void ngx_event_recvmsg(ngx_event_t *ev) { ssize_t n; + ngx_str_t key; ngx_buf_t buf; ngx_log_t *log; ngx_err_t err; @@ -229,8 +223,18 @@ ngx_event_recvmsg(ngx_event_t *ev) #endif - c = ngx_lookup_udp_connection(ls, sockaddr, socklen, local_sockaddr, - local_socklen); + ngx_str_null(&key); + +#if (NGX_QUIC) + if (ls->quic) { + if (ngx_quic_get_packet_dcid(ev->log, buffer, n, &key) != NGX_OK) { + goto next; + } + } +#endif + + c = ngx_lookup_udp_connection(ls, &key, sockaddr, socklen, + local_sockaddr, local_socklen); if (c) { @@ -403,7 +407,7 @@ ngx_event_recvmsg(ngx_event_t *ev) } #endif - if (ngx_insert_udp_connection(c) != NGX_OK) { + if (ngx_create_udp_connection(c) != NGX_OK) { ngx_close_accepted_udp_connection(c); return; } @@ -492,8 +496,13 @@ ngx_udp_rbtree_insert_value(ngx_rbtree_n udpt = (ngx_udp_connection_t *) temp; ct = udpt->connection; - rc = ngx_cmp_sockaddr(c->sockaddr, c->socklen, - ct->sockaddr, ct->socklen, 1); + rc = ngx_memn2cmp(udp->key.data, udpt->key.data, + udp->key.len, udpt->key.len); + + if (rc == 0 && udp->key.len == 0) { + rc = ngx_cmp_sockaddr(c->sockaddr, c->socklen, + ct->sockaddr, ct->socklen, 1); + } if (rc == 0 && c->listening->wildcard) { rc = ngx_cmp_sockaddr(c->local_sockaddr, c->local_socklen, @@ -519,12 +528,18 @@ ngx_udp_rbtree_insert_value(ngx_rbtree_n static ngx_int_t -ngx_insert_udp_connection(ngx_connection_t *c) +ngx_create_udp_connection(ngx_connection_t *c) { - uint32_t hash; + ngx_str_t key; ngx_pool_cleanup_t *cln; ngx_udp_connection_t *udp; +#if (NGX_QUIC) + if (c->listening->quic) { + return NGX_OK; + } +#endif + if (c->udp) { return NGX_OK; } @@ -534,19 +549,6 @@ ngx_insert_udp_connection(ngx_connection return NGX_ERROR; } - udp->connection = c; - - ngx_crc32_init(hash); - ngx_crc32_update(&hash, (u_char *) c->sockaddr, c->socklen); - - if (c->listening->wildcard) { - ngx_crc32_update(&hash, (u_char *) c->local_sockaddr, c->local_socklen); - } - - ngx_crc32_final(hash); - - udp->node.key = hash; - cln = ngx_pool_cleanup_add(c->pool, 0); if (cln == NULL) { return NGX_ERROR; @@ -555,7 +557,9 @@ ngx_insert_udp_connection(ngx_connection cln->data = c; cln->handler = ngx_delete_udp_connection; - ngx_rbtree_insert(&c->listening->rbtree, &udp->node); + key.len = 0; + + ngx_insert_udp_connection(c, udp, &key); c->udp = udp; @@ -564,6 +568,34 @@ ngx_insert_udp_connection(ngx_connection void +ngx_insert_udp_connection(ngx_connection_t *c, ngx_udp_connection_t *udp, + ngx_str_t *key) +{ + uint32_t hash; + + ngx_crc32_init(hash); + + ngx_crc32_update(&hash, key->data, key->len); + + if (key->len == 0) { + ngx_crc32_update(&hash, (u_char *) c->sockaddr, c->socklen); + } + + if (c->listening->wildcard) { + ngx_crc32_update(&hash, (u_char *) c->local_sockaddr, c->local_socklen); + } + + ngx_crc32_final(hash); + + udp->connection = c; + udp->key = *key; + udp->node.key = hash; + + ngx_rbtree_insert(&c->listening->rbtree, &udp->node); +} + + +void ngx_delete_udp_connection(void *data) { ngx_connection_t *c = data; @@ -579,8 +611,9 @@ ngx_delete_udp_connection(void *data) static ngx_connection_t * -ngx_lookup_udp_connection(ngx_listening_t *ls, struct sockaddr *sockaddr, - socklen_t socklen, struct sockaddr *local_sockaddr, socklen_t local_socklen) +ngx_lookup_udp_connection(ngx_listening_t *ls, ngx_str_t *key, + struct sockaddr *sockaddr, socklen_t socklen, + struct sockaddr *local_sockaddr, socklen_t local_socklen) { uint32_t hash; ngx_int_t rc; @@ -608,7 +641,12 @@ ngx_lookup_udp_connection(ngx_listening_ sentinel = ls->rbtree.sentinel; ngx_crc32_init(hash); - ngx_crc32_update(&hash, (u_char *) sockaddr, socklen); + + ngx_crc32_update(&hash, key->data, key->len); + + if (key->len == 0) { + ngx_crc32_update(&hash, (u_char *) sockaddr, socklen); + } if (ls->wildcard) { ngx_crc32_update(&hash, (u_char *) local_sockaddr, local_socklen); @@ -634,8 +672,12 @@ ngx_lookup_udp_connection(ngx_listening_ c = udp->connection; - rc = ngx_cmp_sockaddr(sockaddr, socklen, - c->sockaddr, c->socklen, 1); + rc = ngx_memn2cmp(key->data, udp->key.data, key->len, udp->key.len); + + if (rc == 0 && key->len == 0) { + rc = ngx_cmp_sockaddr(sockaddr, socklen, + c->sockaddr, c->socklen, 1); + } if (rc == 0 && ls->wildcard) { rc = ngx_cmp_sockaddr(local_sockaddr, local_socklen, @@ -643,6 +685,37 @@ ngx_lookup_udp_connection(ngx_listening_ } if (rc == 0) { + if (key->len) { + rc = ngx_cmp_sockaddr(sockaddr, socklen, + c->sockaddr, c->socklen, 1); + + if (rc) { +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + ngx_str_t addr; + u_char text[NGX_SOCKADDR_STRLEN]; + + addr.data = text; + addr.len = ngx_sock_ntop(sockaddr, socklen, text, + NGX_SOCKADDR_STRLEN, 1); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "client migrated to %V", &addr); + } +#endif + + if (c->socklen < socklen) { + c->sockaddr = ngx_palloc(c->pool, socklen); + if (c->sockaddr == NULL) { + return c; + } + } + + ngx_memcpy(c->sockaddr, sockaddr, socklen); + c->socklen = socklen; + } + } + return c; } diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -104,9 +104,9 @@ static ngx_command_t ngx_http_quic_comm offsetof(ngx_quic_conf_t, tp.ack_delay_exponent), &ngx_http_quic_ack_delay_exponent_bounds }, - { ngx_string("quic_active_migration"), + { ngx_string("quic_disable_active_migration"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_num_slot, + ngx_conf_set_flag_slot, NGX_HTTP_SRV_CONF_OFFSET, offsetof(ngx_quic_conf_t, tp.disable_active_migration), NULL }, @@ -246,7 +246,7 @@ ngx_http_quic_create_srv_conf(ngx_conf_t conf->tp.initial_max_streams_bidi = NGX_CONF_UNSET_UINT; conf->tp.initial_max_streams_uni = NGX_CONF_UNSET_UINT; conf->tp.ack_delay_exponent = NGX_CONF_UNSET_UINT; - conf->tp.disable_active_migration = NGX_CONF_UNSET_UINT; + conf->tp.disable_active_migration = NGX_CONF_UNSET; conf->tp.active_connection_id_limit = NGX_CONF_UNSET_UINT; conf->retry = NGX_CONF_UNSET; @@ -301,8 +301,8 @@ ngx_http_quic_merge_srv_conf(ngx_conf_t prev->tp.ack_delay_exponent, NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT); - ngx_conf_merge_uint_value(conf->tp.disable_active_migration, - prev->tp.disable_active_migration, 1); + ngx_conf_merge_value(conf->tp.disable_active_migration, + prev->tp.disable_active_migration, 0); ngx_conf_merge_uint_value(conf->tp.active_connection_id_limit, prev->tp.active_connection_id_limit, 2); diff --git a/src/stream/ngx_stream_quic_module.c b/src/stream/ngx_stream_quic_module.c --- a/src/stream/ngx_stream_quic_module.c +++ b/src/stream/ngx_stream_quic_module.c @@ -105,9 +105,9 @@ static ngx_command_t ngx_stream_quic_co offsetof(ngx_quic_conf_t, tp.ack_delay_exponent), &ngx_stream_quic_ack_delay_exponent_bounds }, - { ngx_string("quic_active_migration"), + { ngx_string("quic_disable_active_migration"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_num_slot, + ngx_conf_set_flag_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_quic_conf_t, tp.disable_active_migration), NULL }, @@ -236,7 +236,7 @@ ngx_stream_quic_create_srv_conf(ngx_conf conf->tp.initial_max_streams_bidi = NGX_CONF_UNSET_UINT; conf->tp.initial_max_streams_uni = NGX_CONF_UNSET_UINT; conf->tp.ack_delay_exponent = NGX_CONF_UNSET_UINT; - conf->tp.disable_active_migration = NGX_CONF_UNSET_UINT; + conf->tp.disable_active_migration = NGX_CONF_UNSET; conf->tp.active_connection_id_limit = NGX_CONF_UNSET_UINT; conf->retry = NGX_CONF_UNSET; @@ -290,8 +290,8 @@ ngx_stream_quic_merge_srv_conf(ngx_conf_ prev->tp.ack_delay_exponent, NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT); - ngx_conf_merge_uint_value(conf->tp.disable_active_migration, - prev->tp.disable_active_migration, 1); + ngx_conf_merge_value(conf->tp.disable_active_migration, + prev->tp.disable_active_migration, 0); ngx_conf_merge_uint_value(conf->tp.active_connection_id_limit, prev->tp.active_connection_id_limit, 2);