comparison src/event/quic/ngx_event_quic_socket.c @ 8797:1e2f4e9c8195 quic

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
author Vladimir Homutov <vl@nginx.com>
date Wed, 19 Jan 2022 22:39:24 +0300
parents 32daba3aabb2
children fab36e4abf83
comparison
equal deleted inserted replaced
8796:7106a918a277 8797:1e2f4e9c8195
12 12
13 ngx_int_t 13 ngx_int_t
14 ngx_quic_open_sockets(ngx_connection_t *c, ngx_quic_connection_t *qc, 14 ngx_quic_open_sockets(ngx_connection_t *c, ngx_quic_connection_t *qc,
15 ngx_quic_header_t *pkt) 15 ngx_quic_header_t *pkt)
16 { 16 {
17 ngx_quic_path_t *path;
18 ngx_quic_socket_t *qsock, *tmp; 17 ngx_quic_socket_t *qsock, *tmp;
19 ngx_quic_client_id_t *cid; 18 ngx_quic_client_id_t *cid;
20 19
21 /* 20 /*
21 * qc->path = NULL
22 *
22 * qc->nclient_ids = 0 23 * qc->nclient_ids = 0
23 * qc->nsockets = 0 24 * qc->nsockets = 0
24 * qc->max_retired_seqnum = 0 25 * qc->max_retired_seqnum = 0
25 * qc->client_seqnum = 0 26 * qc->client_seqnum = 0
26 */ 27 */
49 /* socket is listening at new server id */ 50 /* socket is listening at new server id */
50 if (ngx_quic_listen(c, qc, qsock) != NGX_OK) { 51 if (ngx_quic_listen(c, qc, qsock) != NGX_OK) {
51 return NGX_ERROR; 52 return NGX_ERROR;
52 } 53 }
53 54
55 qsock->used = 1;
56
54 qc->tp.initial_scid.len = qsock->sid.len; 57 qc->tp.initial_scid.len = qsock->sid.len;
55 qc->tp.initial_scid.data = ngx_pnalloc(c->pool, qsock->sid.len); 58 qc->tp.initial_scid.data = ngx_pnalloc(c->pool, qsock->sid.len);
56 if (qc->tp.initial_scid.data == NULL) { 59 if (qc->tp.initial_scid.data == NULL) {
57 goto failed; 60 goto failed;
58 } 61 }
67 cid = ngx_quic_create_client_id(c, &pkt->scid, 0, NULL); 70 cid = ngx_quic_create_client_id(c, &pkt->scid, 0, NULL);
68 if (cid == NULL) { 71 if (cid == NULL) {
69 goto failed; 72 goto failed;
70 } 73 }
71 74
72 /* the client arrived from this path */ 75 /* path of the first packet is our initial active path */
73 path = ngx_quic_add_path(c, c->sockaddr, c->socklen); 76 qc->path = ngx_quic_new_path(c, c->sockaddr, c->socklen, cid);
74 if (path == NULL) { 77 if (qc->path == NULL) {
75 goto failed; 78 goto failed;
76 } 79 }
80
81 qc->path->tag = NGX_QUIC_PATH_ACTIVE;
77 82
78 if (pkt->validated) { 83 if (pkt->validated) {
79 path->state = NGX_QUIC_PATH_VALIDATED; 84 qc->path->validated = 1;
80 path->limited = 0; 85 qc->path->limited = 0;
81 } 86 }
82 87
83 /* now bind socket to client and path */ 88 ngx_quic_path_dbg(c, "set active", qc->path);
84 ngx_quic_connect(c, qsock, path, cid);
85 89
86 tmp = ngx_pcalloc(c->pool, sizeof(ngx_quic_socket_t)); 90 tmp = ngx_pcalloc(c->pool, sizeof(ngx_quic_socket_t));
87 if (tmp == NULL) { 91 if (tmp == NULL) {
88 goto failed; 92 goto failed;
89 } 93 }
94 tmp->sid.len = pkt->odcid.len; 98 tmp->sid.len = pkt->odcid.len;
95 99
96 if (ngx_quic_listen(c, qc, tmp) != NGX_OK) { 100 if (ngx_quic_listen(c, qc, tmp) != NGX_OK) {
97 goto failed; 101 goto failed;
98 } 102 }
99
100 ngx_quic_connect(c, tmp, path, cid);
101
102 /* use this socket as default destination */
103 qc->socket = qsock;
104
105 ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
106 "quic active socket is #%uL:%uL:%uL (%s)",
107 qsock->sid.seqnum, qsock->cid->seqnum, qsock->path->seqnum,
108 ngx_quic_path_state_str(qsock->path));
109 103
110 return NGX_OK; 104 return NGX_OK;
111 105
112 failed: 106 failed:
113 107
163 ngx_queue_insert_head(&qc->free_sockets, &qsock->queue); 157 ngx_queue_insert_head(&qc->free_sockets, &qsock->queue);
164 158
165 ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node); 159 ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node);
166 qc->nsockets--; 160 qc->nsockets--;
167 161
168 if (qsock->path) {
169 ngx_quic_unref_path(c, qsock->path);
170 }
171
172 if (qsock->cid) {
173 ngx_quic_unref_client_id(c, qsock->cid);
174 }
175
176 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, 162 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
177 "quic socket #%L closed nsock:%ui", 163 "quic socket #%L closed nsock:%ui",
178 (int64_t) qsock->sid.seqnum, qc->nsockets); 164 (int64_t) qsock->sid.seqnum, qc->nsockets);
179 } 165 }
180 166
181 167
182 void
183 ngx_quic_unref_path(ngx_connection_t *c, ngx_quic_path_t *path)
184 {
185 ngx_quic_connection_t *qc;
186
187 path->refcnt--;
188
189 if (path->refcnt) {
190 return;
191 }
192
193 qc = ngx_quic_get_connection(c);
194
195 ngx_queue_remove(&path->queue);
196 ngx_queue_insert_head(&qc->free_paths, &path->queue);
197
198 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
199 "quic path #%uL addr:%V removed",
200 path->seqnum, &path->addr_text);
201 }
202
203
204 ngx_int_t 168 ngx_int_t
205 ngx_quic_listen(ngx_connection_t *c, ngx_quic_connection_t *qc, 169 ngx_quic_listen(ngx_connection_t *c, ngx_quic_connection_t *qc,
206 ngx_quic_socket_t *qsock) 170 ngx_quic_socket_t *qsock)
207 { 171 {
208 ngx_str_t id; 172 ngx_str_t id;
223 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, 187 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
224 "quic socket #%L listening at sid:%xV nsock:%ui", 188 "quic socket #%L listening at sid:%xV nsock:%ui",
225 (int64_t) sid->seqnum, &id, qc->nsockets); 189 (int64_t) sid->seqnum, &id, qc->nsockets);
226 190
227 return NGX_OK; 191 return NGX_OK;
228 }
229
230
231 void
232 ngx_quic_connect(ngx_connection_t *c, ngx_quic_socket_t *sock,
233 ngx_quic_path_t *path, ngx_quic_client_id_t *cid)
234 {
235 sock->path = path;
236 path->refcnt++;
237
238 sock->cid = cid;
239 cid->refcnt++;
240
241 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
242 "quic socket #%L connected to cid #%uL path:%uL",
243 (int64_t) sock->sid.seqnum,
244 sock->cid->seqnum, path->seqnum);
245 } 192 }
246 193
247 194
248 void 195 void
249 ngx_quic_close_sockets(ngx_connection_t *c) 196 ngx_quic_close_sockets(ngx_connection_t *c)
283 } 230 }
284 } 231 }
285 232
286 return NULL; 233 return NULL;
287 } 234 }
288
289
290 ngx_quic_socket_t *
291 ngx_quic_get_unconnected_socket(ngx_connection_t *c)
292 {
293 ngx_queue_t *q;
294 ngx_quic_socket_t *sock;
295 ngx_quic_connection_t *qc;
296
297 qc = ngx_quic_get_connection(c);
298
299 for (q = ngx_queue_head(&qc->sockets);
300 q != ngx_queue_sentinel(&qc->sockets);
301 q = ngx_queue_next(q))
302 {
303 sock = ngx_queue_data(q, ngx_quic_socket_t, queue);
304
305 if (sock->cid == NULL) {
306 return sock;
307 }
308 }
309
310 return NULL;
311 }