mirror of
https://github.com/nginx/nginx.git
synced 2024-12-21 09:47:48 +08:00
QUIC: reworked migration handling.
The quic connection now holds active, backup and probe paths instead of sockets. The number of migration paths is now limited and cannot be inflated by a bad client or an attacker. The client id is now associated with path rather than socket. This allows to simplify processing of output and connection ids handling. New migration abandons any previously started migrations. This allows to free consumed client ids and request new for use in future migrations and make progress in case when connection id limit is hit during migration. A path now can be revalidated without losing its state. The patch also fixes various issues with NAT rebinding case handling: - paths are now validated (previously, there was no validation and paths were left in limited state) - attempt to reuse id on different path is now again verified (this was broken in 40445fc7c403) - former path is now validated in case of apparent migration
This commit is contained in:
parent
1f97aa71ec
commit
8a4a267d74
@ -131,8 +131,8 @@ ngx_quic_apply_transport_params(ngx_connection_t *c, ngx_quic_tp_t *ctp)
|
|||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
|
|
||||||
scid.data = qc->socket->cid->id;
|
scid.data = qc->path->cid->id;
|
||||||
scid.len = qc->socket->cid->len;
|
scid.len = qc->path->cid->len;
|
||||||
|
|
||||||
if (scid.len != ctp->initial_scid.len
|
if (scid.len != ctp->initial_scid.len
|
||||||
|| ngx_memcmp(scid.data, ctp->initial_scid.data, scid.len) != 0)
|
|| ngx_memcmp(scid.data, ctp->initial_scid.data, scid.len) != 0)
|
||||||
@ -373,7 +373,7 @@ ngx_quic_handle_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
|||||||
{
|
{
|
||||||
cid = ngx_queue_data(q, ngx_quic_client_id_t, queue);
|
cid = ngx_queue_data(q, ngx_quic_client_id_t, queue);
|
||||||
|
|
||||||
if (cid->seqnum == 0 || cid->refcnt == 0) {
|
if (cid->seqnum == 0 || !cid->used) {
|
||||||
/*
|
/*
|
||||||
* No stateless reset token in initial connection id.
|
* No stateless reset token in initial connection id.
|
||||||
* Don't accept a token from an unused connection id.
|
* Don't accept a token from an unused connection id.
|
||||||
@ -673,10 +673,12 @@ ngx_quic_handle_datagram(ngx_connection_t *c, ngx_buf_t *b,
|
|||||||
u_char *p, *start;
|
u_char *p, *start;
|
||||||
ngx_int_t rc;
|
ngx_int_t rc;
|
||||||
ngx_uint_t good;
|
ngx_uint_t good;
|
||||||
|
ngx_quic_path_t *path;
|
||||||
ngx_quic_header_t pkt;
|
ngx_quic_header_t pkt;
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
|
|
||||||
good = 0;
|
good = 0;
|
||||||
|
path = NULL;
|
||||||
|
|
||||||
size = b->last - b->pos;
|
size = b->last - b->pos;
|
||||||
|
|
||||||
@ -690,6 +692,7 @@ ngx_quic_handle_datagram(ngx_connection_t *c, ngx_buf_t *b,
|
|||||||
pkt.len = b->last - p;
|
pkt.len = b->last - p;
|
||||||
pkt.log = c->log;
|
pkt.log = c->log;
|
||||||
pkt.first = (p == start) ? 1 : 0;
|
pkt.first = (p == start) ? 1 : 0;
|
||||||
|
pkt.path = path;
|
||||||
pkt.flags = p[0];
|
pkt.flags = p[0];
|
||||||
pkt.raw->pos++;
|
pkt.raw->pos++;
|
||||||
|
|
||||||
@ -720,6 +723,8 @@ ngx_quic_handle_datagram(ngx_connection_t *c, ngx_buf_t *b,
|
|||||||
good = 1;
|
good = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path = pkt.path; /* preserve packet path from 1st packet */
|
||||||
|
|
||||||
/* NGX_OK || NGX_DECLINED */
|
/* NGX_OK || NGX_DECLINED */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -825,14 +830,15 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pkt->first) {
|
if (pkt->first) {
|
||||||
if (ngx_quic_find_path(c, c->udp->dgram->sockaddr,
|
if (ngx_cmp_sockaddr(c->udp->dgram->sockaddr,
|
||||||
c->udp->dgram->socklen)
|
c->udp->dgram->socklen,
|
||||||
== NULL)
|
qc->path->sockaddr, qc->path->socklen, 1)
|
||||||
|
!= NGX_OK)
|
||||||
{
|
{
|
||||||
/* packet comes from unknown path, possibly migration */
|
/* packet comes from unknown path, possibly migration */
|
||||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||||
"quic too early migration attempt");
|
"quic too early migration attempt");
|
||||||
return NGX_DECLINED;
|
return NGX_DONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -991,9 +997,12 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
|||||||
|
|
||||||
pkt->decrypted = 1;
|
pkt->decrypted = 1;
|
||||||
|
|
||||||
if (pkt->first) {
|
c->log->action = "handling decrypted packet";
|
||||||
if (ngx_quic_update_paths(c, pkt) != NGX_OK) {
|
|
||||||
return NGX_ERROR;
|
if (pkt->path == NULL) {
|
||||||
|
rc = ngx_quic_set_path(c, pkt);
|
||||||
|
if (rc != NGX_OK) {
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1012,9 +1021,10 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
|||||||
*/
|
*/
|
||||||
ngx_quic_discard_ctx(c, ssl_encryption_initial);
|
ngx_quic_discard_ctx(c, ssl_encryption_initial);
|
||||||
|
|
||||||
if (qc->socket->path->state != NGX_QUIC_PATH_VALIDATED) {
|
if (!qc->path->validated) {
|
||||||
qc->socket->path->state = NGX_QUIC_PATH_VALIDATED;
|
qc->path->validated = 1;
|
||||||
qc->socket->path->limited = 0;
|
qc->path->limited = 0;
|
||||||
|
ngx_quic_path_dbg(c, "in handshake", qc->path);
|
||||||
ngx_post_event(&qc->push, &ngx_posted_events);
|
ngx_post_event(&qc->push, &ngx_posted_events);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1153,7 +1163,6 @@ ngx_quic_handle_frames(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
|||||||
ngx_uint_t do_close, nonprobing;
|
ngx_uint_t do_close, nonprobing;
|
||||||
ngx_chain_t chain;
|
ngx_chain_t chain;
|
||||||
ngx_quic_frame_t frame;
|
ngx_quic_frame_t frame;
|
||||||
ngx_quic_socket_t *qsock;
|
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
@ -1335,7 +1344,8 @@ ngx_quic_handle_frames(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
|||||||
|
|
||||||
case NGX_QUIC_FT_PATH_CHALLENGE:
|
case NGX_QUIC_FT_PATH_CHALLENGE:
|
||||||
|
|
||||||
if (ngx_quic_handle_path_challenge_frame(c, &frame.u.path_challenge)
|
if (ngx_quic_handle_path_challenge_frame(c, pkt,
|
||||||
|
&frame.u.path_challenge)
|
||||||
!= NGX_OK)
|
!= NGX_OK)
|
||||||
{
|
{
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
@ -1394,26 +1404,18 @@ ngx_quic_handle_frames(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
|||||||
ngx_quic_close_connection(c, NGX_OK);
|
ngx_quic_close_connection(c, NGX_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
qsock = ngx_quic_get_socket(c);
|
if (pkt->path != qc->path && nonprobing) {
|
||||||
|
|
||||||
if (qsock != qc->socket) {
|
|
||||||
|
|
||||||
if (qsock->path != qc->socket->path && nonprobing) {
|
|
||||||
/*
|
|
||||||
* RFC 9000, 9.2. Initiating Connection Migration
|
|
||||||
*
|
|
||||||
* An endpoint can migrate a connection to a new local
|
|
||||||
* address by sending packets containing non-probing frames
|
|
||||||
* from that address.
|
|
||||||
*/
|
|
||||||
if (ngx_quic_handle_migration(c, pkt) != NGX_OK) {
|
|
||||||
return NGX_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* else: packet arrived via non-default socket;
|
* RFC 9000, 9.2. Initiating Connection Migration
|
||||||
* no reason to change active path
|
*
|
||||||
|
* An endpoint can migrate a connection to a new local
|
||||||
|
* address by sending packets containing non-probing frames
|
||||||
|
* from that address.
|
||||||
*/
|
*/
|
||||||
|
if (ngx_quic_handle_migration(c, pkt) != NGX_OK) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ngx_quic_ack_packet(c, pkt) != NGX_OK) {
|
if (ngx_quic_ack_packet(c, pkt) != NGX_OK) {
|
||||||
|
@ -69,7 +69,7 @@ struct ngx_quic_client_id_s {
|
|||||||
size_t len;
|
size_t len;
|
||||||
u_char id[NGX_QUIC_CID_LEN_MAX];
|
u_char id[NGX_QUIC_CID_LEN_MAX];
|
||||||
u_char sr_token[NGX_QUIC_SR_TOKEN_LEN];
|
u_char sr_token[NGX_QUIC_SR_TOKEN_LEN];
|
||||||
ngx_uint_t refcnt;
|
ngx_uint_t used; /* unsigned used:1; */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -83,20 +83,22 @@ struct ngx_quic_server_id_s {
|
|||||||
struct ngx_quic_path_s {
|
struct ngx_quic_path_s {
|
||||||
ngx_queue_t queue;
|
ngx_queue_t queue;
|
||||||
struct sockaddr *sockaddr;
|
struct sockaddr *sockaddr;
|
||||||
|
ngx_sockaddr_t sa;
|
||||||
socklen_t socklen;
|
socklen_t socklen;
|
||||||
ngx_uint_t state;
|
ngx_quic_client_id_t *cid;
|
||||||
ngx_uint_t limited; /* unsigned limited:1; */
|
|
||||||
ngx_msec_t expires;
|
ngx_msec_t expires;
|
||||||
ngx_msec_t last_seen;
|
|
||||||
ngx_uint_t tries;
|
ngx_uint_t tries;
|
||||||
|
ngx_uint_t tag;
|
||||||
off_t sent;
|
off_t sent;
|
||||||
off_t received;
|
off_t received;
|
||||||
u_char challenge1[8];
|
u_char challenge1[8];
|
||||||
u_char challenge2[8];
|
u_char challenge2[8];
|
||||||
ngx_uint_t refcnt;
|
|
||||||
uint64_t seqnum;
|
uint64_t seqnum;
|
||||||
ngx_str_t addr_text;
|
ngx_str_t addr_text;
|
||||||
u_char text[NGX_SOCKADDR_STRLEN];
|
u_char text[NGX_SOCKADDR_STRLEN];
|
||||||
|
unsigned validated:1;
|
||||||
|
unsigned validating:1;
|
||||||
|
unsigned limited:1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -104,11 +106,8 @@ struct ngx_quic_socket_s {
|
|||||||
ngx_udp_connection_t udp;
|
ngx_udp_connection_t udp;
|
||||||
ngx_quic_connection_t *quic;
|
ngx_quic_connection_t *quic;
|
||||||
ngx_queue_t queue;
|
ngx_queue_t queue;
|
||||||
|
|
||||||
ngx_quic_server_id_t sid;
|
ngx_quic_server_id_t sid;
|
||||||
|
ngx_uint_t used; /* unsigned used:1; */
|
||||||
ngx_quic_path_t *path;
|
|
||||||
ngx_quic_client_id_t *cid;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -184,8 +183,7 @@ struct ngx_quic_send_ctx_s {
|
|||||||
struct ngx_quic_connection_s {
|
struct ngx_quic_connection_s {
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
|
|
||||||
ngx_quic_socket_t *socket;
|
ngx_quic_path_t *path;
|
||||||
ngx_quic_socket_t *backup;
|
|
||||||
|
|
||||||
ngx_queue_t sockets;
|
ngx_queue_t sockets;
|
||||||
ngx_queue_t paths;
|
ngx_queue_t paths;
|
||||||
|
@ -15,13 +15,10 @@
|
|||||||
#if (NGX_QUIC_BPF)
|
#if (NGX_QUIC_BPF)
|
||||||
static ngx_int_t ngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id);
|
static ngx_int_t ngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id);
|
||||||
#endif
|
#endif
|
||||||
static ngx_int_t ngx_quic_send_retire_connection_id(ngx_connection_t *c,
|
static ngx_int_t ngx_quic_retire_client_id(ngx_connection_t *c,
|
||||||
uint64_t seqnum);
|
ngx_quic_client_id_t *cid);
|
||||||
|
|
||||||
static ngx_quic_client_id_t *ngx_quic_alloc_client_id(ngx_connection_t *c,
|
static ngx_quic_client_id_t *ngx_quic_alloc_client_id(ngx_connection_t *c,
|
||||||
ngx_quic_connection_t *qc);
|
ngx_quic_connection_t *qc);
|
||||||
static ngx_int_t ngx_quic_replace_retired_client_id(ngx_connection_t *c,
|
|
||||||
ngx_quic_client_id_t *retired_cid);
|
|
||||||
static ngx_int_t ngx_quic_send_server_id(ngx_connection_t *c,
|
static ngx_int_t ngx_quic_send_server_id(ngx_connection_t *c,
|
||||||
ngx_quic_server_id_t *sid);
|
ngx_quic_server_id_t *sid);
|
||||||
|
|
||||||
@ -77,9 +74,9 @@ ngx_int_t
|
|||||||
ngx_quic_handle_new_connection_id_frame(ngx_connection_t *c,
|
ngx_quic_handle_new_connection_id_frame(ngx_connection_t *c,
|
||||||
ngx_quic_new_conn_id_frame_t *f)
|
ngx_quic_new_conn_id_frame_t *f)
|
||||||
{
|
{
|
||||||
uint64_t seq;
|
|
||||||
ngx_str_t id;
|
ngx_str_t id;
|
||||||
ngx_queue_t *q;
|
ngx_queue_t *q;
|
||||||
|
ngx_quic_frame_t *frame;
|
||||||
ngx_quic_client_id_t *cid, *item;
|
ngx_quic_client_id_t *cid, *item;
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
|
|
||||||
@ -97,10 +94,17 @@ ngx_quic_handle_new_connection_id_frame(ngx_connection_t *c,
|
|||||||
* done so for that sequence number.
|
* done so for that sequence number.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (ngx_quic_send_retire_connection_id(c, f->seqnum) != NGX_OK) {
|
frame = ngx_quic_alloc_frame(c);
|
||||||
|
if (frame == NULL) {
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frame->level = ssl_encryption_application;
|
||||||
|
frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID;
|
||||||
|
frame->u.retire_cid.sequence_number = f->seqnum;
|
||||||
|
|
||||||
|
ngx_quic_queue_frame(qc, frame);
|
||||||
|
|
||||||
goto retire;
|
goto retire;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,20 +177,7 @@ retire:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this connection id must be retired */
|
if (ngx_quic_retire_client_id(c, cid) != NGX_OK) {
|
||||||
seq = cid->seqnum;
|
|
||||||
|
|
||||||
if (cid->refcnt) {
|
|
||||||
/* we are going to retire client id which is in use */
|
|
||||||
if (ngx_quic_replace_retired_client_id(c, cid) != NGX_OK) {
|
|
||||||
return NGX_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
ngx_quic_unref_client_id(c, cid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ngx_quic_send_retire_connection_id(c, seq) != NGX_OK) {
|
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -213,25 +204,47 @@ done:
|
|||||||
|
|
||||||
|
|
||||||
static ngx_int_t
|
static ngx_int_t
|
||||||
ngx_quic_send_retire_connection_id(ngx_connection_t *c, uint64_t seqnum)
|
ngx_quic_retire_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid)
|
||||||
{
|
{
|
||||||
ngx_quic_frame_t *frame;
|
ngx_queue_t *q;
|
||||||
|
ngx_quic_path_t *path;
|
||||||
|
ngx_quic_client_id_t *new_cid;
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
|
|
||||||
frame = ngx_quic_alloc_frame(c);
|
if (!cid->used) {
|
||||||
if (frame == NULL) {
|
return ngx_quic_free_client_id(c, cid);
|
||||||
return NGX_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
frame->level = ssl_encryption_application;
|
/* we are going to retire client id which is in use */
|
||||||
frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID;
|
|
||||||
frame->u.retire_cid.sequence_number = seqnum;
|
|
||||||
|
|
||||||
ngx_quic_queue_frame(qc, frame);
|
q = ngx_queue_head(&qc->paths);
|
||||||
|
|
||||||
/* we are no longer going to use this client id */
|
while (q != ngx_queue_sentinel(&qc->paths)) {
|
||||||
|
|
||||||
|
path = ngx_queue_data(q, ngx_quic_path_t, queue);
|
||||||
|
q = ngx_queue_next(q);
|
||||||
|
|
||||||
|
if (path->cid != cid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path == qc->path) {
|
||||||
|
/* this is the active path: update it with new CID */
|
||||||
|
new_cid = ngx_quic_next_client_id(c);
|
||||||
|
if (new_cid == NULL) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
qc->path->cid = new_cid;
|
||||||
|
new_cid->used = 1;
|
||||||
|
|
||||||
|
return ngx_quic_free_client_id(c, cid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ngx_quic_free_path(c, path);
|
||||||
|
}
|
||||||
|
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
@ -318,7 +331,7 @@ ngx_quic_next_client_id(ngx_connection_t *c)
|
|||||||
{
|
{
|
||||||
cid = ngx_queue_data(q, ngx_quic_client_id_t, queue);
|
cid = ngx_queue_data(q, ngx_quic_client_id_t, queue);
|
||||||
|
|
||||||
if (cid->refcnt == 0) {
|
if (!cid->used) {
|
||||||
return cid;
|
return cid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -327,42 +340,11 @@ ngx_quic_next_client_id(ngx_connection_t *c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ngx_quic_client_id_t *
|
|
||||||
ngx_quic_used_client_id(ngx_connection_t *c, ngx_quic_path_t *path)
|
|
||||||
{
|
|
||||||
ngx_queue_t *q;
|
|
||||||
ngx_quic_socket_t *qsock;
|
|
||||||
ngx_quic_connection_t *qc;
|
|
||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
|
||||||
|
|
||||||
/* best guess: cid used by active path is good for us */
|
|
||||||
if (qc->socket->path == path) {
|
|
||||||
return qc->socket->cid;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (q = ngx_queue_head(&qc->sockets);
|
|
||||||
q != ngx_queue_sentinel(&qc->sockets);
|
|
||||||
q = ngx_queue_next(q))
|
|
||||||
{
|
|
||||||
qsock = ngx_queue_data(q, ngx_quic_socket_t, queue);
|
|
||||||
|
|
||||||
if (qsock->path && qsock->path == path) {
|
|
||||||
return qsock->cid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ngx_int_t
|
ngx_int_t
|
||||||
ngx_quic_handle_retire_connection_id_frame(ngx_connection_t *c,
|
ngx_quic_handle_retire_connection_id_frame(ngx_connection_t *c,
|
||||||
ngx_quic_retire_cid_frame_t *f)
|
ngx_quic_retire_cid_frame_t *f)
|
||||||
{
|
{
|
||||||
ngx_quic_path_t *path;
|
ngx_quic_socket_t *qsock;
|
||||||
ngx_quic_socket_t *qsock, **tmp;
|
|
||||||
ngx_quic_client_id_t *cid;
|
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
@ -408,76 +390,14 @@ ngx_quic_handle_retire_connection_id_frame(ngx_connection_t *c,
|
|||||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||||
"quic socket #%uL is retired", qsock->sid.seqnum);
|
"quic socket #%uL is retired", qsock->sid.seqnum);
|
||||||
|
|
||||||
/* check if client is willing to retire sid we have in use */
|
|
||||||
if (qsock->sid.seqnum == qc->socket->sid.seqnum) {
|
|
||||||
tmp = &qc->socket;
|
|
||||||
|
|
||||||
} else if (qc->backup && qsock->sid.seqnum == qc->backup->sid.seqnum) {
|
|
||||||
tmp = &qc->backup;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
ngx_quic_close_socket(c, qsock);
|
|
||||||
|
|
||||||
/* restore socket count up to a limit after deletion */
|
|
||||||
if (ngx_quic_create_sockets(c) != NGX_OK) {
|
|
||||||
return NGX_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NGX_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* preserve path/cid from retired socket */
|
|
||||||
path = qsock->path;
|
|
||||||
cid = qsock->cid;
|
|
||||||
|
|
||||||
/* ensure that closing_socket will not drop path and cid */
|
|
||||||
path->refcnt++;
|
|
||||||
cid->refcnt++;
|
|
||||||
|
|
||||||
ngx_quic_close_socket(c, qsock);
|
ngx_quic_close_socket(c, qsock);
|
||||||
|
|
||||||
/* restore original values */
|
|
||||||
path->refcnt--;
|
|
||||||
cid->refcnt--;
|
|
||||||
|
|
||||||
/* restore socket count up to a limit after deletion */
|
/* restore socket count up to a limit after deletion */
|
||||||
if (ngx_quic_create_sockets(c) != NGX_OK) {
|
if (ngx_quic_create_sockets(c) != NGX_OK) {
|
||||||
goto failed;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
qsock = ngx_quic_get_unconnected_socket(c);
|
|
||||||
if (qsock == NULL) {
|
|
||||||
qc->error = NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR;
|
|
||||||
qc->error_reason = "not enough server IDs";
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngx_quic_connect(c, qsock, path, cid);
|
|
||||||
|
|
||||||
ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
|
||||||
"quic %s socket is now #%uL:%uL:%uL (%s)",
|
|
||||||
(*tmp) == qc->socket ? "active" : "backup",
|
|
||||||
qsock->sid.seqnum, qsock->cid->seqnum,
|
|
||||||
qsock->path->seqnum,
|
|
||||||
ngx_quic_path_state_str(qsock->path));
|
|
||||||
|
|
||||||
/* restore active/backup pointer in quic connection */
|
|
||||||
*tmp = qsock;
|
|
||||||
|
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
|
|
||||||
failed:
|
|
||||||
|
|
||||||
/*
|
|
||||||
* socket was closed, path and cid were preserved artifically
|
|
||||||
* to be reused, but it didn't happen, thus unref here
|
|
||||||
*/
|
|
||||||
|
|
||||||
ngx_quic_unref_path(c, path);
|
|
||||||
ngx_quic_unref_client_id(c, cid);
|
|
||||||
|
|
||||||
return NGX_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -552,62 +472,31 @@ ngx_quic_send_server_id(ngx_connection_t *c, ngx_quic_server_id_t *sid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static ngx_int_t
|
ngx_int_t
|
||||||
ngx_quic_replace_retired_client_id(ngx_connection_t *c,
|
ngx_quic_free_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid)
|
||||||
ngx_quic_client_id_t *retired_cid)
|
|
||||||
{
|
{
|
||||||
ngx_queue_t *q;
|
ngx_quic_frame_t *frame;
|
||||||
ngx_quic_socket_t *qsock;
|
|
||||||
ngx_quic_client_id_t *cid;
|
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
|
|
||||||
for (q = ngx_queue_head(&qc->sockets);
|
frame = ngx_quic_alloc_frame(c);
|
||||||
q != ngx_queue_sentinel(&qc->sockets);
|
if (frame == NULL) {
|
||||||
q = ngx_queue_next(q))
|
return NGX_ERROR;
|
||||||
{
|
|
||||||
qsock = ngx_queue_data(q, ngx_quic_socket_t, queue);
|
|
||||||
|
|
||||||
if (qsock->cid == retired_cid) {
|
|
||||||
|
|
||||||
cid = ngx_quic_next_client_id(c);
|
|
||||||
if (cid == NULL) {
|
|
||||||
return NGX_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
qsock->cid = cid;
|
|
||||||
cid->refcnt++;
|
|
||||||
|
|
||||||
ngx_quic_unref_client_id(c, retired_cid);
|
|
||||||
|
|
||||||
if (retired_cid->refcnt == 0) {
|
|
||||||
return NGX_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NGX_OK;
|
frame->level = ssl_encryption_application;
|
||||||
}
|
frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID;
|
||||||
|
frame->u.retire_cid.sequence_number = cid->seqnum;
|
||||||
|
|
||||||
|
ngx_quic_queue_frame(qc, frame);
|
||||||
|
|
||||||
void
|
/* we are no longer going to use this client id */
|
||||||
ngx_quic_unref_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid)
|
|
||||||
{
|
|
||||||
ngx_quic_connection_t *qc;
|
|
||||||
|
|
||||||
if (cid->refcnt) {
|
|
||||||
cid->refcnt--;
|
|
||||||
} /* else: unused client id */
|
|
||||||
|
|
||||||
if (cid->refcnt) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
|
||||||
|
|
||||||
ngx_queue_remove(&cid->queue);
|
ngx_queue_remove(&cid->queue);
|
||||||
ngx_queue_insert_head(&qc->free_client_ids, &cid->queue);
|
ngx_queue_insert_head(&qc->free_client_ids, &cid->queue);
|
||||||
|
|
||||||
qc->nclient_ids--;
|
qc->nclient_ids--;
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
|
@ -23,8 +23,7 @@ ngx_int_t ngx_quic_create_server_id(ngx_connection_t *c, u_char *id);
|
|||||||
ngx_quic_client_id_t *ngx_quic_create_client_id(ngx_connection_t *c,
|
ngx_quic_client_id_t *ngx_quic_create_client_id(ngx_connection_t *c,
|
||||||
ngx_str_t *id, uint64_t seqnum, u_char *token);
|
ngx_str_t *id, uint64_t seqnum, u_char *token);
|
||||||
ngx_quic_client_id_t *ngx_quic_next_client_id(ngx_connection_t *c);
|
ngx_quic_client_id_t *ngx_quic_next_client_id(ngx_connection_t *c);
|
||||||
ngx_quic_client_id_t *ngx_quic_used_client_id(ngx_connection_t *c,
|
ngx_int_t ngx_quic_free_client_id(ngx_connection_t *c,
|
||||||
ngx_quic_path_t *path);
|
ngx_quic_client_id_t *cid);
|
||||||
void ngx_quic_unref_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid);
|
|
||||||
|
|
||||||
#endif /* _NGX_EVENT_QUIC_CONNID_H_INCLUDED_ */
|
#endif /* _NGX_EVENT_QUIC_CONNID_H_INCLUDED_ */
|
||||||
|
@ -16,17 +16,14 @@ static ngx_int_t ngx_quic_validate_path(ngx_connection_t *c,
|
|||||||
ngx_quic_path_t *path);
|
ngx_quic_path_t *path);
|
||||||
static ngx_int_t ngx_quic_send_path_challenge(ngx_connection_t *c,
|
static ngx_int_t ngx_quic_send_path_challenge(ngx_connection_t *c,
|
||||||
ngx_quic_path_t *path);
|
ngx_quic_path_t *path);
|
||||||
static ngx_int_t ngx_quic_path_restore(ngx_connection_t *c);
|
static ngx_quic_path_t *ngx_quic_get_path(ngx_connection_t *c, ngx_uint_t tag);
|
||||||
static ngx_quic_path_t *ngx_quic_alloc_path(ngx_connection_t *c);
|
|
||||||
|
|
||||||
|
|
||||||
ngx_int_t
|
ngx_int_t
|
||||||
ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
|
ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
|
||||||
ngx_quic_path_challenge_frame_t *f)
|
ngx_quic_header_t *pkt, ngx_quic_path_challenge_frame_t *f)
|
||||||
{
|
{
|
||||||
ngx_quic_path_t *path;
|
|
||||||
ngx_quic_frame_t frame, *fp;
|
ngx_quic_frame_t frame, *fp;
|
||||||
ngx_quic_socket_t *qsock;
|
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
@ -43,18 +40,16 @@ ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
|
|||||||
* A PATH_RESPONSE frame MUST be sent on the network path where the
|
* A PATH_RESPONSE frame MUST be sent on the network path where the
|
||||||
* PATH_CHALLENGE frame was received.
|
* PATH_CHALLENGE frame was received.
|
||||||
*/
|
*/
|
||||||
qsock = ngx_quic_get_socket(c);
|
|
||||||
path = qsock->path;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* An endpoint MUST expand datagrams that contain a PATH_RESPONSE frame
|
* An endpoint MUST expand datagrams that contain a PATH_RESPONSE frame
|
||||||
* to at least the smallest allowed maximum datagram size of 1200 bytes.
|
* to at least the smallest allowed maximum datagram size of 1200 bytes.
|
||||||
*/
|
*/
|
||||||
if (ngx_quic_frame_sendto(c, &frame, 1200, path) != NGX_OK) {
|
if (ngx_quic_frame_sendto(c, &frame, 1200, pkt->path) != NGX_OK) {
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qsock == qc->socket) {
|
if (pkt->path == qc->path) {
|
||||||
/*
|
/*
|
||||||
* RFC 9000, 9.3.3. Off-Path Packet Forwarding
|
* RFC 9000, 9.3.3. Off-Path Packet Forwarding
|
||||||
*
|
*
|
||||||
@ -101,7 +96,7 @@ ngx_quic_handle_path_response_frame(ngx_connection_t *c,
|
|||||||
{
|
{
|
||||||
path = ngx_queue_data(q, ngx_quic_path_t, queue);
|
path = ngx_queue_data(q, ngx_quic_path_t, queue);
|
||||||
|
|
||||||
if (path->state != NGX_QUIC_PATH_VALIDATING) {
|
if (!path->validating) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +107,7 @@ ngx_quic_handle_path_response_frame(ngx_connection_t *c,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||||
"quic stale PATH_RESPONSE ignored");
|
"quic stale PATH_RESPONSE ignored");
|
||||||
|
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
@ -130,8 +125,9 @@ valid:
|
|||||||
|
|
||||||
rst = 1;
|
rst = 1;
|
||||||
|
|
||||||
if (qc->backup) {
|
prev = ngx_quic_get_path(c, NGX_QUIC_PATH_BACKUP);
|
||||||
prev = qc->backup->path;
|
|
||||||
|
if (prev != NULL) {
|
||||||
|
|
||||||
if (ngx_cmp_sockaddr(prev->sockaddr, prev->socklen,
|
if (ngx_cmp_sockaddr(prev->sockaddr, prev->socklen,
|
||||||
path->sockaddr, path->socklen, 0)
|
path->sockaddr, path->socklen, 0)
|
||||||
@ -164,20 +160,24 @@ valid:
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||||
"quic path #%uL successfully validated", path->seqnum);
|
"quic path #%uL addr:%V successfully validated",
|
||||||
|
path->seqnum, &path->addr_text);
|
||||||
|
|
||||||
path->state = NGX_QUIC_PATH_VALIDATED;
|
ngx_quic_path_dbg(c, "is validated", path);
|
||||||
|
|
||||||
|
path->validated = 1;
|
||||||
|
path->validating = 0;
|
||||||
path->limited = 0;
|
path->limited = 0;
|
||||||
|
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static ngx_quic_path_t *
|
ngx_quic_path_t *
|
||||||
ngx_quic_alloc_path(ngx_connection_t *c)
|
ngx_quic_new_path(ngx_connection_t *c,
|
||||||
|
struct sockaddr *sockaddr, socklen_t socklen, ngx_quic_client_id_t *cid)
|
||||||
{
|
{
|
||||||
ngx_queue_t *q;
|
ngx_queue_t *q;
|
||||||
struct sockaddr *sa;
|
|
||||||
ngx_quic_path_t *path;
|
ngx_quic_path_t *path;
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
|
|
||||||
@ -190,9 +190,7 @@ ngx_quic_alloc_path(ngx_connection_t *c)
|
|||||||
|
|
||||||
ngx_queue_remove(&path->queue);
|
ngx_queue_remove(&path->queue);
|
||||||
|
|
||||||
sa = path->sockaddr;
|
|
||||||
ngx_memzero(path, sizeof(ngx_quic_path_t));
|
ngx_memzero(path, sizeof(ngx_quic_path_t));
|
||||||
path->sockaddr = sa;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@ -200,37 +198,18 @@ ngx_quic_alloc_path(ngx_connection_t *c)
|
|||||||
if (path == NULL) {
|
if (path == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
path->sockaddr = ngx_palloc(c->pool, NGX_SOCKADDRLEN);
|
|
||||||
if (path->sockaddr == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return path;
|
ngx_queue_insert_tail(&qc->paths, &path->queue);
|
||||||
}
|
|
||||||
|
|
||||||
|
path->cid = cid;
|
||||||
|
cid->used = 1;
|
||||||
|
|
||||||
ngx_quic_path_t *
|
|
||||||
ngx_quic_add_path(ngx_connection_t *c, struct sockaddr *sockaddr,
|
|
||||||
socklen_t socklen)
|
|
||||||
{
|
|
||||||
ngx_quic_path_t *path;
|
|
||||||
ngx_quic_connection_t *qc;
|
|
||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
|
||||||
|
|
||||||
path = ngx_quic_alloc_path(c);
|
|
||||||
if (path == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
path->state = NGX_QUIC_PATH_NEW;
|
|
||||||
path->limited = 1;
|
path->limited = 1;
|
||||||
|
|
||||||
path->seqnum = qc->path_seqnum++;
|
path->seqnum = qc->path_seqnum++;
|
||||||
path->last_seen = ngx_current_msec;
|
|
||||||
|
|
||||||
|
path->sockaddr = &path->sa.sockaddr;
|
||||||
path->socklen = socklen;
|
path->socklen = socklen;
|
||||||
ngx_memcpy(path->sockaddr, sockaddr, socklen);
|
ngx_memcpy(path->sockaddr, sockaddr, socklen);
|
||||||
|
|
||||||
@ -238,19 +217,15 @@ ngx_quic_add_path(ngx_connection_t *c, struct sockaddr *sockaddr,
|
|||||||
path->addr_text.len = ngx_sock_ntop(sockaddr, socklen, path->text,
|
path->addr_text.len = ngx_sock_ntop(sockaddr, socklen, path->text,
|
||||||
NGX_SOCKADDR_STRLEN, 1);
|
NGX_SOCKADDR_STRLEN, 1);
|
||||||
|
|
||||||
ngx_queue_insert_tail(&qc->paths, &path->queue);
|
|
||||||
|
|
||||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||||
"quic path #%uL created src:%V",
|
"quic path #%uL created addr:%V",
|
||||||
path->seqnum, &path->addr_text);
|
path->seqnum, &path->addr_text);
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ngx_quic_path_t *
|
static ngx_quic_path_t *
|
||||||
ngx_quic_find_path(ngx_connection_t *c, struct sockaddr *sockaddr,
|
ngx_quic_get_path(ngx_connection_t *c, ngx_uint_t tag)
|
||||||
socklen_t socklen)
|
|
||||||
{
|
{
|
||||||
ngx_queue_t *q;
|
ngx_queue_t *q;
|
||||||
ngx_quic_path_t *path;
|
ngx_quic_path_t *path;
|
||||||
@ -264,10 +239,7 @@ ngx_quic_find_path(ngx_connection_t *c, struct sockaddr *sockaddr,
|
|||||||
{
|
{
|
||||||
path = ngx_queue_data(q, ngx_quic_path_t, queue);
|
path = ngx_queue_data(q, ngx_quic_path_t, queue);
|
||||||
|
|
||||||
if (ngx_cmp_sockaddr(sockaddr, socklen,
|
if (path->tag == tag) {
|
||||||
path->sockaddr, path->socklen, 1)
|
|
||||||
== NGX_OK)
|
|
||||||
{
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,10 +249,11 @@ ngx_quic_find_path(ngx_connection_t *c, struct sockaddr *sockaddr,
|
|||||||
|
|
||||||
|
|
||||||
ngx_int_t
|
ngx_int_t
|
||||||
ngx_quic_update_paths(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
ngx_quic_set_path(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||||
{
|
{
|
||||||
off_t len;
|
off_t len;
|
||||||
ngx_quic_path_t *path;
|
ngx_queue_t *q;
|
||||||
|
ngx_quic_path_t *path, *probe;
|
||||||
ngx_quic_socket_t *qsock;
|
ngx_quic_socket_t *qsock;
|
||||||
ngx_quic_client_id_t *cid;
|
ngx_quic_client_id_t *cid;
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
@ -288,72 +261,69 @@ ngx_quic_update_paths(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
|||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
qsock = ngx_quic_get_socket(c);
|
qsock = ngx_quic_get_socket(c);
|
||||||
|
|
||||||
|
len = pkt->raw->last - pkt->raw->start;
|
||||||
|
|
||||||
if (c->udp->dgram == NULL) {
|
if (c->udp->dgram == NULL) {
|
||||||
/* 1st ever packet in connection, path already exists */
|
/* first ever packet in connection, path already exists */
|
||||||
path = qsock->path;
|
path = qc->path;
|
||||||
goto update;
|
goto update;
|
||||||
}
|
}
|
||||||
|
|
||||||
path = ngx_quic_find_path(c, c->udp->dgram->sockaddr,
|
probe = NULL;
|
||||||
c->udp->dgram->socklen);
|
|
||||||
|
|
||||||
if (path == NULL) {
|
for (q = ngx_queue_head(&qc->paths);
|
||||||
path = ngx_quic_add_path(c, c->udp->dgram->sockaddr,
|
q != ngx_queue_sentinel(&qc->paths);
|
||||||
c->udp->dgram->socklen);
|
q = ngx_queue_next(q))
|
||||||
if (path == NULL) {
|
{
|
||||||
return NGX_ERROR;
|
path = ngx_queue_data(q, ngx_quic_path_t, queue);
|
||||||
}
|
|
||||||
|
|
||||||
if (qsock->path) {
|
|
||||||
/* NAT rebinding case: packet to same CID, but from new address */
|
|
||||||
|
|
||||||
ngx_quic_unref_path(c, qsock->path);
|
|
||||||
|
|
||||||
qsock->path = path;
|
|
||||||
path->refcnt++;
|
|
||||||
|
|
||||||
|
if (ngx_cmp_sockaddr(c->udp->dgram->sockaddr, c->udp->dgram->socklen,
|
||||||
|
path->sockaddr, path->socklen, 1)
|
||||||
|
== NGX_OK)
|
||||||
|
{
|
||||||
goto update;
|
goto update;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (qsock->path) {
|
if (path->tag == NGX_QUIC_PATH_PROBE) {
|
||||||
goto update;
|
probe = path;
|
||||||
}
|
|
||||||
|
|
||||||
/* prefer unused client IDs if available */
|
|
||||||
cid = ngx_quic_next_client_id(c);
|
|
||||||
if (cid == NULL) {
|
|
||||||
|
|
||||||
/* try to reuse connection ID used on the same path */
|
|
||||||
cid = ngx_quic_used_client_id(c, path);
|
|
||||||
if (cid == NULL) {
|
|
||||||
|
|
||||||
qc->error = NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR;
|
|
||||||
qc->error_reason = "no available client ids for new path";
|
|
||||||
|
|
||||||
ngx_log_error(NGX_LOG_ERR, c->log, 0,
|
|
||||||
"no available client ids for new path");
|
|
||||||
|
|
||||||
return NGX_ERROR;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngx_quic_connect(c, qsock, path, cid);
|
/* packet from new path, drop current probe, if any */
|
||||||
|
|
||||||
|
if (probe && ngx_quic_free_path(c, probe) != NGX_OK) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* new path requires new client id */
|
||||||
|
cid = ngx_quic_next_client_id(c);
|
||||||
|
if (cid == NULL) {
|
||||||
|
ngx_log_error(NGX_LOG_ERR, c->log, 0,
|
||||||
|
"quic no available client ids for new path");
|
||||||
|
/* stop processing of this datagram */
|
||||||
|
return NGX_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = ngx_quic_new_path(c, c->udp->dgram->sockaddr,
|
||||||
|
c->udp->dgram->socklen, cid);
|
||||||
|
if (path == NULL) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
path->tag = NGX_QUIC_PATH_PROBE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* client arrived using new path and previously seen DCID,
|
||||||
|
* this indicates NAT rebinding (or bad client)
|
||||||
|
*/
|
||||||
|
if (qsock->used) {
|
||||||
|
pkt->rebound = 1;
|
||||||
|
}
|
||||||
|
|
||||||
update:
|
update:
|
||||||
|
|
||||||
if (path->state != NGX_QUIC_PATH_NEW) {
|
qsock->used = 1;
|
||||||
/* force limits/revalidation for paths that were not seen recently */
|
pkt->path = path;
|
||||||
if (ngx_current_msec - path->last_seen > qc->tp.max_idle_timeout) {
|
|
||||||
path->state = NGX_QUIC_PATH_NEW;
|
|
||||||
path->limited = 1;
|
|
||||||
path->sent = 0;
|
|
||||||
path->received = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
path->last_seen = ngx_current_msec;
|
|
||||||
|
|
||||||
len = pkt->raw->last - pkt->raw->start;
|
|
||||||
|
|
||||||
/* TODO: this may be too late in some cases;
|
/* TODO: this may be too late in some cases;
|
||||||
* for example, if error happens during decrypt(), we cannot
|
* for example, if error happens during decrypt(), we cannot
|
||||||
@ -364,11 +334,38 @@ update:
|
|||||||
*/
|
*/
|
||||||
path->received += len;
|
path->received += len;
|
||||||
|
|
||||||
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||||
"quic packet via #%uL:%uL:%uL"
|
"quic packet len:%O via sock#%uL path#%uL",
|
||||||
" size:%O path recvd:%O sent:%O limited:%ui",
|
len, qsock->sid.seqnum, path->seqnum);
|
||||||
qsock->sid.seqnum, qsock->cid->seqnum, path->seqnum,
|
ngx_quic_path_dbg(c, "status", path);
|
||||||
len, path->received, path->sent, path->limited);
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
ngx_quic_free_path(ngx_connection_t *c, ngx_quic_path_t *path)
|
||||||
|
{
|
||||||
|
ngx_quic_connection_t *qc;
|
||||||
|
|
||||||
|
qc = ngx_quic_get_connection(c);
|
||||||
|
|
||||||
|
ngx_queue_remove(&path->queue);
|
||||||
|
ngx_queue_insert_head(&qc->free_paths, &path->queue);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* invalidate CID that is no longer usable for any other path;
|
||||||
|
* this also requests new CIDs from client
|
||||||
|
*/
|
||||||
|
if (path->cid) {
|
||||||
|
if (ngx_quic_free_client_id(c, path->cid) != NGX_OK) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||||
|
"quic path #%uL addr:%V retired",
|
||||||
|
path->seqnum, &path->addr_text);
|
||||||
|
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
@ -398,35 +395,14 @@ ngx_quic_set_connection_path(ngx_connection_t *c, ngx_quic_path_t *path)
|
|||||||
ngx_int_t
|
ngx_int_t
|
||||||
ngx_quic_handle_migration(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
ngx_quic_handle_migration(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||||
{
|
{
|
||||||
ngx_quic_path_t *next;
|
ngx_quic_path_t *next, *bkp;
|
||||||
ngx_quic_socket_t *qsock;
|
|
||||||
ngx_quic_send_ctx_t *ctx;
|
ngx_quic_send_ctx_t *ctx;
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
|
|
||||||
/* got non-probing packet via non-active socket with different path */
|
/* got non-probing packet via non-active path */
|
||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
|
|
||||||
/* current socket, different from active */
|
|
||||||
qsock = ngx_quic_get_socket(c);
|
|
||||||
|
|
||||||
next = qsock->path; /* going to migrate to this path... */
|
|
||||||
|
|
||||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
|
||||||
"quic migration from #%uL:%uL:%uL (%s)"
|
|
||||||
" to #%uL:%uL:%uL (%s)",
|
|
||||||
qc->socket->sid.seqnum, qc->socket->cid->seqnum,
|
|
||||||
qc->socket->path->seqnum,
|
|
||||||
ngx_quic_path_state_str(qc->socket->path),
|
|
||||||
qsock->sid.seqnum, qsock->cid->seqnum, next->seqnum,
|
|
||||||
ngx_quic_path_state_str(next));
|
|
||||||
|
|
||||||
if (next->state == NGX_QUIC_PATH_NEW) {
|
|
||||||
if (ngx_quic_validate_path(c, qsock->path) != NGX_OK) {
|
|
||||||
return NGX_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = ngx_quic_get_send_ctx(qc, pkt->level);
|
ctx = ngx_quic_get_send_ctx(qc, pkt->level);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -439,39 +415,59 @@ ngx_quic_handle_migration(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
|||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* switching connection to new path */
|
next = pkt->path;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RFC 9000, 9.3.3:
|
||||||
|
*
|
||||||
|
* In response to an apparent migration, endpoints MUST validate the
|
||||||
|
* previously active path using a PATH_CHALLENGE frame.
|
||||||
|
*/
|
||||||
|
if (pkt->rebound) {
|
||||||
|
|
||||||
|
/* NAT rebinding: client uses new path with old SID */
|
||||||
|
if (ngx_quic_validate_path(c, qc->path) != NGX_OK) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qc->path->validated) {
|
||||||
|
|
||||||
|
if (next->tag != NGX_QUIC_PATH_BACKUP) {
|
||||||
|
/* can delete backup path, if any */
|
||||||
|
bkp = ngx_quic_get_path(c, NGX_QUIC_PATH_BACKUP);
|
||||||
|
|
||||||
|
if (bkp && ngx_quic_free_path(c, bkp) != NGX_OK) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qc->path->tag = NGX_QUIC_PATH_BACKUP;
|
||||||
|
ngx_quic_path_dbg(c, "is now backup", qc->path);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (ngx_quic_free_path(c, qc->path) != NGX_OK) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* switch active path to migrated */
|
||||||
|
qc->path = next;
|
||||||
|
qc->path->tag = NGX_QUIC_PATH_ACTIVE;
|
||||||
|
|
||||||
ngx_quic_set_connection_path(c, next);
|
ngx_quic_set_connection_path(c, next);
|
||||||
|
|
||||||
/*
|
if (!next->validated && !next->validating) {
|
||||||
* RFC 9000, 9.5. Privacy Implications of Connection Migration
|
if (ngx_quic_validate_path(c, next) != NGX_OK) {
|
||||||
*
|
return NGX_ERROR;
|
||||||
* An endpoint MUST NOT reuse a connection ID when sending to
|
|
||||||
* more than one destination address.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* preserve valid path we are migrating from */
|
|
||||||
if (qc->socket->path->state == NGX_QUIC_PATH_VALIDATED) {
|
|
||||||
|
|
||||||
if (qc->backup) {
|
|
||||||
ngx_quic_close_socket(c, qc->backup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qc->backup = qc->socket;
|
|
||||||
|
|
||||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
|
||||||
"quic backup socket is now #%uL:%uL:%uL (%s)",
|
|
||||||
qc->backup->sid.seqnum, qc->backup->cid->seqnum,
|
|
||||||
qc->backup->path->seqnum,
|
|
||||||
ngx_quic_path_state_str(qc->backup->path));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qc->socket = qsock;
|
|
||||||
|
|
||||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||||
"quic active socket is now #%uL:%uL:%uL (%s)",
|
"quic migrated to path#%uL addr:%V",
|
||||||
qsock->sid.seqnum, qsock->cid->seqnum,
|
qc->path->seqnum, &qc->path->addr_text);
|
||||||
qsock->path->seqnum, ngx_quic_path_state_str(qsock->path));
|
|
||||||
|
ngx_quic_path_dbg(c, "is now active", qc->path);
|
||||||
|
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
@ -487,10 +483,9 @@ ngx_quic_validate_path(ngx_connection_t *c, ngx_quic_path_t *path)
|
|||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
|
|
||||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||||
"quic initiated validation of new path #%uL",
|
"quic initiated validation of path #%uL", path->seqnum);
|
||||||
path->seqnum);
|
|
||||||
|
|
||||||
path->state = NGX_QUIC_PATH_VALIDATING;
|
path->validating = 1;
|
||||||
|
|
||||||
if (RAND_bytes(path->challenge1, 8) != 1) {
|
if (RAND_bytes(path->challenge1, 8) != 1) {
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
@ -524,7 +519,7 @@ ngx_quic_send_path_challenge(ngx_connection_t *c, ngx_quic_path_t *path)
|
|||||||
ngx_quic_frame_t frame;
|
ngx_quic_frame_t frame;
|
||||||
|
|
||||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||||
"quic path #%uL send path challenge tries:%ui",
|
"quic path #%uL send path_challenge tries:%ui",
|
||||||
path->seqnum, path->tries);
|
path->seqnum, path->tries);
|
||||||
|
|
||||||
ngx_memzero(&frame, sizeof(ngx_quic_frame_t));
|
ngx_memzero(&frame, sizeof(ngx_quic_frame_t));
|
||||||
@ -564,7 +559,7 @@ ngx_quic_path_validation_handler(ngx_event_t *ev)
|
|||||||
ngx_msec_t now;
|
ngx_msec_t now;
|
||||||
ngx_queue_t *q;
|
ngx_queue_t *q;
|
||||||
ngx_msec_int_t left, next, pto;
|
ngx_msec_int_t left, next, pto;
|
||||||
ngx_quic_path_t *path;
|
ngx_quic_path_t *path, *bkp;
|
||||||
ngx_connection_t *c;
|
ngx_connection_t *c;
|
||||||
ngx_quic_send_ctx_t *ctx;
|
ngx_quic_send_ctx_t *ctx;
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
@ -578,13 +573,14 @@ ngx_quic_path_validation_handler(ngx_event_t *ev)
|
|||||||
next = -1;
|
next = -1;
|
||||||
now = ngx_current_msec;
|
now = ngx_current_msec;
|
||||||
|
|
||||||
for (q = ngx_queue_head(&qc->paths);
|
q = ngx_queue_head(&qc->paths);
|
||||||
q != ngx_queue_sentinel(&qc->paths);
|
|
||||||
q = ngx_queue_next(q))
|
|
||||||
{
|
|
||||||
path = ngx_queue_data(q, ngx_quic_path_t, queue);
|
|
||||||
|
|
||||||
if (path->state != NGX_QUIC_PATH_VALIDATING) {
|
while (q != ngx_queue_sentinel(&qc->paths)) {
|
||||||
|
|
||||||
|
path = ngx_queue_data(q, ngx_quic_path_t, queue);
|
||||||
|
q = ngx_queue_next(q);
|
||||||
|
|
||||||
|
if (!path->validating) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -593,7 +589,7 @@ ngx_quic_path_validation_handler(ngx_event_t *ev)
|
|||||||
if (left > 0) {
|
if (left > 0) {
|
||||||
|
|
||||||
if (next == -1 || left < next) {
|
if (next == -1 || left < next) {
|
||||||
next = path->expires;
|
next = left;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@ -617,26 +613,43 @@ ngx_quic_path_validation_handler(ngx_event_t *ev)
|
|||||||
|
|
||||||
/* found expired path */
|
/* found expired path */
|
||||||
|
|
||||||
path->state = NGX_QUIC_PATH_NEW;
|
path->validated = 0;
|
||||||
|
path->validating = 0;
|
||||||
path->limited = 1;
|
path->limited = 1;
|
||||||
|
|
||||||
/*
|
|
||||||
* RFC 9000, 9.4. Loss Detection and Congestion Control
|
/* RFC 9000, 9.3.2. On-Path Address Spoofing
|
||||||
*
|
*
|
||||||
* If the timer fires before the PATH_RESPONSE is received, the
|
* To protect the connection from failing due to such a spurious
|
||||||
* endpoint might send a new PATH_CHALLENGE and restart the timer for
|
* migration, an endpoint MUST revert to using the last validated
|
||||||
* a longer period of time. This timer SHOULD be set as described in
|
* peer address when validation of a new peer address fails.
|
||||||
* Section 6.2.1 of [QUIC-RECOVERY] and MUST NOT be more aggressive.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (qc->socket->path != path) {
|
if (qc->path == path) {
|
||||||
/* the path was not actually used */
|
/* active path validation failed */
|
||||||
continue;
|
|
||||||
|
bkp = ngx_quic_get_path(c, NGX_QUIC_PATH_BACKUP);
|
||||||
|
|
||||||
|
if (bkp == NULL) {
|
||||||
|
qc->error = NGX_QUIC_ERR_NO_VIABLE_PATH;
|
||||||
|
qc->error_reason = "no viable path";
|
||||||
|
ngx_quic_close_connection(c, NGX_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qc->path = bkp;
|
||||||
|
qc->path->tag = NGX_QUIC_PATH_ACTIVE;
|
||||||
|
|
||||||
|
ngx_quic_set_connection_path(c, qc->path);
|
||||||
|
|
||||||
|
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||||
|
"quic path #%uL addr:%V is restored from backup",
|
||||||
|
qc->path->seqnum, &qc->path->addr_text);
|
||||||
|
|
||||||
|
ngx_quic_path_dbg(c, "is active", qc->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ngx_quic_path_restore(c) != NGX_OK) {
|
if (ngx_quic_free_path(c, path) != NGX_OK) {
|
||||||
qc->error = NGX_QUIC_ERR_NO_VIABLE_PATH;
|
|
||||||
qc->error_reason = "no viable path";
|
|
||||||
ngx_quic_close_connection(c, NGX_ERROR);
|
ngx_quic_close_connection(c, NGX_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -646,44 +659,3 @@ ngx_quic_path_validation_handler(ngx_event_t *ev)
|
|||||||
ngx_add_timer(&qc->path_validation, next);
|
ngx_add_timer(&qc->path_validation, next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static ngx_int_t
|
|
||||||
ngx_quic_path_restore(ngx_connection_t *c)
|
|
||||||
{
|
|
||||||
ngx_quic_socket_t *qsock;
|
|
||||||
ngx_quic_connection_t *qc;
|
|
||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* RFC 9000, 9.1. Probing a New Path
|
|
||||||
*
|
|
||||||
* Failure to validate a path does not cause the connection to end
|
|
||||||
*
|
|
||||||
* RFC 9000, 9.3.2. On-Path Address Spoofing
|
|
||||||
*
|
|
||||||
* To protect the connection from failing due to such a spurious
|
|
||||||
* migration, an endpoint MUST revert to using the last validated
|
|
||||||
* peer address when validation of a new peer address fails.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (qc->backup == NULL) {
|
|
||||||
return NGX_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
qc->socket = qc->backup;
|
|
||||||
qc->backup = NULL;
|
|
||||||
|
|
||||||
qsock = qc->socket;
|
|
||||||
|
|
||||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
|
||||||
"quic active socket is restored to #%uL:%uL:%uL"
|
|
||||||
" (%s), no backup",
|
|
||||||
qsock->sid.seqnum, qsock->cid->seqnum, qsock->path->seqnum,
|
|
||||||
ngx_quic_path_state_str(qsock->path));
|
|
||||||
|
|
||||||
ngx_quic_set_connection_path(c, qsock->path);
|
|
||||||
|
|
||||||
return NGX_OK;
|
|
||||||
}
|
|
||||||
|
@ -11,29 +11,29 @@
|
|||||||
#include <ngx_config.h>
|
#include <ngx_config.h>
|
||||||
#include <ngx_core.h>
|
#include <ngx_core.h>
|
||||||
|
|
||||||
#define NGX_QUIC_PATH_RETRIES 3
|
#define NGX_QUIC_PATH_RETRIES 3
|
||||||
|
|
||||||
#define NGX_QUIC_PATH_NEW 0
|
#define NGX_QUIC_PATH_PROBE 0
|
||||||
#define NGX_QUIC_PATH_VALIDATING 1
|
#define NGX_QUIC_PATH_ACTIVE 1
|
||||||
#define NGX_QUIC_PATH_VALIDATED 2
|
#define NGX_QUIC_PATH_BACKUP 2
|
||||||
|
|
||||||
|
|
||||||
#define ngx_quic_path_state_str(p) \
|
|
||||||
((p)->state == NGX_QUIC_PATH_NEW) ? "new" : \
|
|
||||||
(((p)->state == NGX_QUIC_PATH_VALIDATED) ? "validated" : "validating")
|
|
||||||
|
|
||||||
|
#define ngx_quic_path_dbg(c, msg, path) \
|
||||||
|
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, \
|
||||||
|
"quic path#%uL %s sent:%O recvd:%O state:%s%s%s", \
|
||||||
|
path->seqnum, msg, path->sent, path->received, \
|
||||||
|
path->limited ? "L" : "", path->validated ? "V": "N", \
|
||||||
|
path->validating ? "R": "");
|
||||||
|
|
||||||
ngx_int_t ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
|
ngx_int_t ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
|
||||||
ngx_quic_path_challenge_frame_t *f);
|
ngx_quic_header_t *pkt, ngx_quic_path_challenge_frame_t *f);
|
||||||
ngx_int_t ngx_quic_handle_path_response_frame(ngx_connection_t *c,
|
ngx_int_t ngx_quic_handle_path_response_frame(ngx_connection_t *c,
|
||||||
ngx_quic_path_challenge_frame_t *f);
|
ngx_quic_path_challenge_frame_t *f);
|
||||||
|
|
||||||
ngx_quic_path_t *ngx_quic_find_path(ngx_connection_t *c,
|
ngx_quic_path_t *ngx_quic_new_path(ngx_connection_t *c,
|
||||||
struct sockaddr *sockaddr, socklen_t socklen);
|
struct sockaddr *sockaddr, socklen_t socklen, ngx_quic_client_id_t *cid);
|
||||||
ngx_quic_path_t *ngx_quic_add_path(ngx_connection_t *c,
|
ngx_int_t ngx_quic_free_path(ngx_connection_t *c, ngx_quic_path_t *path);
|
||||||
struct sockaddr *sockaddr, socklen_t socklen);
|
|
||||||
|
|
||||||
ngx_int_t ngx_quic_update_paths(ngx_connection_t *c, ngx_quic_header_t *pkt);
|
ngx_int_t ngx_quic_set_path(ngx_connection_t *c, ngx_quic_header_t *pkt);
|
||||||
ngx_int_t ngx_quic_handle_migration(ngx_connection_t *c,
|
ngx_int_t ngx_quic_handle_migration(ngx_connection_t *c,
|
||||||
ngx_quic_header_t *pkt);
|
ngx_quic_header_t *pkt);
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ static ssize_t ngx_quic_send_segments(ngx_connection_t *c, u_char *buf,
|
|||||||
static ssize_t ngx_quic_output_packet(ngx_connection_t *c,
|
static ssize_t ngx_quic_output_packet(ngx_connection_t *c,
|
||||||
ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min);
|
ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min);
|
||||||
static void ngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
static void ngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||||
ngx_quic_header_t *pkt);
|
ngx_quic_header_t *pkt, ngx_quic_path_t *path);
|
||||||
static ngx_uint_t ngx_quic_get_padding_level(ngx_connection_t *c);
|
static ngx_uint_t ngx_quic_get_padding_level(ngx_connection_t *c);
|
||||||
static ssize_t ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len,
|
static ssize_t ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len,
|
||||||
struct sockaddr *sockaddr, socklen_t socklen);
|
struct sockaddr *sockaddr, socklen_t socklen);
|
||||||
@ -131,7 +131,7 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
|
|||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
cg = &qc->congestion;
|
cg = &qc->congestion;
|
||||||
path = qc->socket->path;
|
path = qc->path;
|
||||||
|
|
||||||
while (cg->in_flight < cg->window) {
|
while (cg->in_flight < cg->window) {
|
||||||
|
|
||||||
@ -269,7 +269,7 @@ ngx_quic_allow_segmentation(ngx_connection_t *c)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qc->socket->path->limited) {
|
if (qc->path->limited) {
|
||||||
/* don't even try to be faster on non-validated paths */
|
/* don't even try to be faster on non-validated paths */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -325,7 +325,7 @@ ngx_quic_create_segments(ngx_connection_t *c)
|
|||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
cg = &qc->congestion;
|
cg = &qc->congestion;
|
||||||
path = qc->socket->path;
|
path = qc->path;
|
||||||
|
|
||||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||||
|
|
||||||
@ -505,17 +505,18 @@ static ssize_t
|
|||||||
ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||||
u_char *data, size_t max, size_t min)
|
u_char *data, size_t max, size_t min)
|
||||||
{
|
{
|
||||||
size_t len, pad, min_payload, max_payload;
|
size_t len, pad, min_payload, max_payload;
|
||||||
u_char *p;
|
u_char *p;
|
||||||
ssize_t flen;
|
ssize_t flen;
|
||||||
ngx_str_t res;
|
ngx_str_t res;
|
||||||
ngx_int_t rc;
|
ngx_int_t rc;
|
||||||
ngx_uint_t nframes, expand;
|
ngx_uint_t nframes, expand;
|
||||||
ngx_msec_t now;
|
ngx_msec_t now;
|
||||||
ngx_queue_t *q;
|
ngx_queue_t *q;
|
||||||
ngx_quic_frame_t *f;
|
ngx_quic_frame_t *f;
|
||||||
ngx_quic_header_t pkt;
|
ngx_quic_header_t pkt;
|
||||||
static u_char src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
|
ngx_quic_connection_t *qc;
|
||||||
|
static u_char src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
|
||||||
|
|
||||||
if (ngx_queue_empty(&ctx->frames)) {
|
if (ngx_queue_empty(&ctx->frames)) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -525,7 +526,9 @@ ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
|||||||
"quic output %s packet max:%uz min:%uz",
|
"quic output %s packet max:%uz min:%uz",
|
||||||
ngx_quic_level_name(ctx->level), max, min);
|
ngx_quic_level_name(ctx->level), max, min);
|
||||||
|
|
||||||
ngx_quic_init_packet(c, ctx, &pkt);
|
qc = ngx_quic_get_connection(c);
|
||||||
|
|
||||||
|
ngx_quic_init_packet(c, ctx, &pkt, qc->path);
|
||||||
|
|
||||||
min_payload = ngx_quic_payload_size(&pkt, min);
|
min_payload = ngx_quic_payload_size(&pkt, min);
|
||||||
max_payload = ngx_quic_payload_size(&pkt, max);
|
max_payload = ngx_quic_payload_size(&pkt, max);
|
||||||
@ -668,14 +671,14 @@ ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
ngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
ngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||||
ngx_quic_header_t *pkt)
|
ngx_quic_header_t *pkt, ngx_quic_path_t *path)
|
||||||
{
|
{
|
||||||
ngx_quic_socket_t *qsock;
|
ngx_quic_socket_t *qsock;
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
|
|
||||||
qsock = qc->socket;
|
qsock = ngx_quic_get_socket(c);
|
||||||
|
|
||||||
ngx_memzero(pkt, sizeof(ngx_quic_header_t));
|
ngx_memzero(pkt, sizeof(ngx_quic_header_t));
|
||||||
|
|
||||||
@ -693,8 +696,8 @@ ngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pkt->dcid.data = qsock->cid->id;
|
pkt->dcid.data = path->cid->id;
|
||||||
pkt->dcid.len = qsock->cid->len;
|
pkt->dcid.len = path->cid->len;
|
||||||
|
|
||||||
pkt->scid.data = qsock->sid.id;
|
pkt->scid.data = qsock->sid.id;
|
||||||
pkt->scid.len = qsock->sid.len;
|
pkt->scid.len = qsock->sid.len;
|
||||||
@ -1202,7 +1205,7 @@ ngx_quic_frame_sendto(ngx_connection_t *c, ngx_quic_frame_t *frame,
|
|||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||||
|
|
||||||
ngx_quic_init_packet(c, ctx, &pkt);
|
ngx_quic_init_packet(c, ctx, &pkt, path);
|
||||||
|
|
||||||
min = ngx_quic_path_limit(c, path, min);
|
min = ngx_quic_path_limit(c, path, min);
|
||||||
|
|
||||||
|
@ -14,11 +14,12 @@ ngx_int_t
|
|||||||
ngx_quic_open_sockets(ngx_connection_t *c, ngx_quic_connection_t *qc,
|
ngx_quic_open_sockets(ngx_connection_t *c, ngx_quic_connection_t *qc,
|
||||||
ngx_quic_header_t *pkt)
|
ngx_quic_header_t *pkt)
|
||||||
{
|
{
|
||||||
ngx_quic_path_t *path;
|
|
||||||
ngx_quic_socket_t *qsock, *tmp;
|
ngx_quic_socket_t *qsock, *tmp;
|
||||||
ngx_quic_client_id_t *cid;
|
ngx_quic_client_id_t *cid;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* qc->path = NULL
|
||||||
|
*
|
||||||
* qc->nclient_ids = 0
|
* qc->nclient_ids = 0
|
||||||
* qc->nsockets = 0
|
* qc->nsockets = 0
|
||||||
* qc->max_retired_seqnum = 0
|
* qc->max_retired_seqnum = 0
|
||||||
@ -51,6 +52,8 @@ ngx_quic_open_sockets(ngx_connection_t *c, ngx_quic_connection_t *qc,
|
|||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qsock->used = 1;
|
||||||
|
|
||||||
qc->tp.initial_scid.len = qsock->sid.len;
|
qc->tp.initial_scid.len = qsock->sid.len;
|
||||||
qc->tp.initial_scid.data = ngx_pnalloc(c->pool, qsock->sid.len);
|
qc->tp.initial_scid.data = ngx_pnalloc(c->pool, qsock->sid.len);
|
||||||
if (qc->tp.initial_scid.data == NULL) {
|
if (qc->tp.initial_scid.data == NULL) {
|
||||||
@ -69,19 +72,20 @@ ngx_quic_open_sockets(ngx_connection_t *c, ngx_quic_connection_t *qc,
|
|||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* the client arrived from this path */
|
/* path of the first packet is our initial active path */
|
||||||
path = ngx_quic_add_path(c, c->sockaddr, c->socklen);
|
qc->path = ngx_quic_new_path(c, c->sockaddr, c->socklen, cid);
|
||||||
if (path == NULL) {
|
if (qc->path == NULL) {
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qc->path->tag = NGX_QUIC_PATH_ACTIVE;
|
||||||
|
|
||||||
if (pkt->validated) {
|
if (pkt->validated) {
|
||||||
path->state = NGX_QUIC_PATH_VALIDATED;
|
qc->path->validated = 1;
|
||||||
path->limited = 0;
|
qc->path->limited = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* now bind socket to client and path */
|
ngx_quic_path_dbg(c, "set active", qc->path);
|
||||||
ngx_quic_connect(c, qsock, path, cid);
|
|
||||||
|
|
||||||
tmp = ngx_pcalloc(c->pool, sizeof(ngx_quic_socket_t));
|
tmp = ngx_pcalloc(c->pool, sizeof(ngx_quic_socket_t));
|
||||||
if (tmp == NULL) {
|
if (tmp == NULL) {
|
||||||
@ -97,16 +101,6 @@ ngx_quic_open_sockets(ngx_connection_t *c, ngx_quic_connection_t *qc,
|
|||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngx_quic_connect(c, tmp, path, cid);
|
|
||||||
|
|
||||||
/* use this socket as default destination */
|
|
||||||
qc->socket = qsock;
|
|
||||||
|
|
||||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
|
||||||
"quic active socket is #%uL:%uL:%uL (%s)",
|
|
||||||
qsock->sid.seqnum, qsock->cid->seqnum, qsock->path->seqnum,
|
|
||||||
ngx_quic_path_state_str(qsock->path));
|
|
||||||
|
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
|
|
||||||
failed:
|
failed:
|
||||||
@ -165,42 +159,12 @@ ngx_quic_close_socket(ngx_connection_t *c, ngx_quic_socket_t *qsock)
|
|||||||
ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node);
|
ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node);
|
||||||
qc->nsockets--;
|
qc->nsockets--;
|
||||||
|
|
||||||
if (qsock->path) {
|
|
||||||
ngx_quic_unref_path(c, qsock->path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qsock->cid) {
|
|
||||||
ngx_quic_unref_client_id(c, qsock->cid);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||||
"quic socket #%L closed nsock:%ui",
|
"quic socket #%L closed nsock:%ui",
|
||||||
(int64_t) qsock->sid.seqnum, qc->nsockets);
|
(int64_t) qsock->sid.seqnum, qc->nsockets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
ngx_quic_unref_path(ngx_connection_t *c, ngx_quic_path_t *path)
|
|
||||||
{
|
|
||||||
ngx_quic_connection_t *qc;
|
|
||||||
|
|
||||||
path->refcnt--;
|
|
||||||
|
|
||||||
if (path->refcnt) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
|
||||||
|
|
||||||
ngx_queue_remove(&path->queue);
|
|
||||||
ngx_queue_insert_head(&qc->free_paths, &path->queue);
|
|
||||||
|
|
||||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
|
||||||
"quic path #%uL addr:%V removed",
|
|
||||||
path->seqnum, &path->addr_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ngx_int_t
|
ngx_int_t
|
||||||
ngx_quic_listen(ngx_connection_t *c, ngx_quic_connection_t *qc,
|
ngx_quic_listen(ngx_connection_t *c, ngx_quic_connection_t *qc,
|
||||||
ngx_quic_socket_t *qsock)
|
ngx_quic_socket_t *qsock)
|
||||||
@ -228,23 +192,6 @@ ngx_quic_listen(ngx_connection_t *c, ngx_quic_connection_t *qc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
ngx_quic_connect(ngx_connection_t *c, ngx_quic_socket_t *sock,
|
|
||||||
ngx_quic_path_t *path, ngx_quic_client_id_t *cid)
|
|
||||||
{
|
|
||||||
sock->path = path;
|
|
||||||
path->refcnt++;
|
|
||||||
|
|
||||||
sock->cid = cid;
|
|
||||||
cid->refcnt++;
|
|
||||||
|
|
||||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
|
||||||
"quic socket #%L connected to cid #%uL path:%uL",
|
|
||||||
(int64_t) sock->sid.seqnum,
|
|
||||||
sock->cid->seqnum, path->seqnum);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ngx_quic_close_sockets(ngx_connection_t *c)
|
ngx_quic_close_sockets(ngx_connection_t *c)
|
||||||
{
|
{
|
||||||
@ -285,27 +232,3 @@ ngx_quic_find_socket(ngx_connection_t *c, uint64_t seqnum)
|
|||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ngx_quic_socket_t *
|
|
||||||
ngx_quic_get_unconnected_socket(ngx_connection_t *c)
|
|
||||||
{
|
|
||||||
ngx_queue_t *q;
|
|
||||||
ngx_quic_socket_t *sock;
|
|
||||||
ngx_quic_connection_t *qc;
|
|
||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
|
||||||
|
|
||||||
for (q = ngx_queue_head(&qc->sockets);
|
|
||||||
q != ngx_queue_sentinel(&qc->sockets);
|
|
||||||
q = ngx_queue_next(q))
|
|
||||||
{
|
|
||||||
sock = ngx_queue_data(q, ngx_quic_socket_t, queue);
|
|
||||||
|
|
||||||
if (sock->cid == NULL) {
|
|
||||||
return sock;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
@ -22,10 +22,6 @@ ngx_int_t ngx_quic_listen(ngx_connection_t *c, ngx_quic_connection_t *qc,
|
|||||||
ngx_quic_socket_t *qsock);
|
ngx_quic_socket_t *qsock);
|
||||||
void ngx_quic_close_socket(ngx_connection_t *c, ngx_quic_socket_t *qsock);
|
void ngx_quic_close_socket(ngx_connection_t *c, ngx_quic_socket_t *qsock);
|
||||||
|
|
||||||
void ngx_quic_unref_path(ngx_connection_t *c, ngx_quic_path_t *path);
|
|
||||||
void ngx_quic_connect(ngx_connection_t *c, ngx_quic_socket_t *qsock,
|
|
||||||
ngx_quic_path_t *path, ngx_quic_client_id_t *cid);
|
|
||||||
|
|
||||||
ngx_quic_socket_t *ngx_quic_find_socket(ngx_connection_t *c, uint64_t seqnum);
|
ngx_quic_socket_t *ngx_quic_find_socket(ngx_connection_t *c, uint64_t seqnum);
|
||||||
ngx_quic_socket_t *ngx_quic_get_unconnected_socket(ngx_connection_t *c);
|
ngx_quic_socket_t *ngx_quic_get_unconnected_socket(ngx_connection_t *c);
|
||||||
|
|
||||||
|
@ -497,7 +497,7 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data)
|
|||||||
ngx_quic_queue_frame(qc, frame);
|
ngx_quic_queue_frame(qc, frame);
|
||||||
|
|
||||||
if (qc->conf->retry) {
|
if (qc->conf->retry) {
|
||||||
if (ngx_quic_send_new_token(c, qc->socket->path) != NGX_OK) {
|
if (ngx_quic_send_new_token(c, qc->path) != NGX_OK) {
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -541,6 +541,7 @@ ngx_quic_init_connection(ngx_connection_t *c)
|
|||||||
ssize_t len;
|
ssize_t len;
|
||||||
ngx_str_t dcid;
|
ngx_str_t dcid;
|
||||||
ngx_ssl_conn_t *ssl_conn;
|
ngx_ssl_conn_t *ssl_conn;
|
||||||
|
ngx_quic_socket_t *qsock;
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
@ -569,8 +570,10 @@ ngx_quic_init_connection(ngx_connection_t *c)
|
|||||||
SSL_set_quic_use_legacy_codepoint(ssl_conn, qc->version != 1);
|
SSL_set_quic_use_legacy_codepoint(ssl_conn, qc->version != 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
dcid.data = qc->socket->sid.id;
|
qsock = ngx_quic_get_socket(c);
|
||||||
dcid.len = qc->socket->sid.len;
|
|
||||||
|
dcid.data = qsock->sid.id;
|
||||||
|
dcid.len = qsock->sid.len;
|
||||||
|
|
||||||
if (ngx_quic_new_sr_token(c, &dcid, qc->conf->sr_token_key, qc->tp.sr_token)
|
if (ngx_quic_new_sr_token(c, &dcid, qc->conf->sr_token_key, qc->tp.sr_token)
|
||||||
!= NGX_OK)
|
!= NGX_OK)
|
||||||
|
@ -300,6 +300,7 @@ struct ngx_quic_frame_s {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
ngx_log_t *log;
|
ngx_log_t *log;
|
||||||
|
ngx_quic_path_t *path;
|
||||||
|
|
||||||
ngx_quic_keys_t *keys;
|
ngx_quic_keys_t *keys;
|
||||||
|
|
||||||
@ -335,6 +336,7 @@ typedef struct {
|
|||||||
unsigned validated:1;
|
unsigned validated:1;
|
||||||
unsigned retried:1;
|
unsigned retried:1;
|
||||||
unsigned first:1;
|
unsigned first:1;
|
||||||
|
unsigned rebound:1;
|
||||||
} ngx_quic_header_t;
|
} ngx_quic_header_t;
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user