Mercurial > hg > nginx-quic
annotate src/event/quic/ngx_event_quic_socket.c @ 8700:b09f055daa4e quic
QUIC: fixed handling of RETIRE_CONNECTION_ID frame.
Previously, the retired socket was not closed if it didn't match
active or backup.
New sockets could not be created (due to count limit), since retired socket
was not closed before calling ngx_quic_create_sockets().
When replacing retired socket, new socket is only requested after closing
old one, to avoid hitting the limit on the number of active connection ids.
Together with added restrictions, this fixes an issue when a current socket
could be closed during migration, recreated and erroneously reused leading
to null pointer dereference.
author | Vladimir Homutov <vl@nginx.com> |
---|---|
date | Thu, 18 Nov 2021 14:19:36 +0300 |
parents | 6d1488b62dc5 |
children | aae8b91e0280 |
rev | line source |
---|---|
8423 | 1 |
2 /* | |
3 * Copyright (C) Nginx, Inc. | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_event.h> | |
10 #include <ngx_event_quic_connection.h> | |
11 | |
12 | |
13 static ngx_int_t ngx_quic_create_temp_socket(ngx_connection_t *c, | |
14 ngx_quic_connection_t *qc, ngx_str_t *dcid, ngx_quic_path_t *path, | |
15 ngx_quic_client_id_t *cid); | |
16 | |
17 | |
18 ngx_int_t | |
19 ngx_quic_open_sockets(ngx_connection_t *c, ngx_quic_connection_t *qc, | |
20 ngx_quic_header_t *pkt) | |
21 { | |
22 ngx_quic_path_t *path; | |
23 ngx_quic_socket_t *qsock; | |
24 ngx_quic_client_id_t *cid; | |
25 | |
26 /* | |
27 * qc->nclient_ids = 0 | |
28 * qc->nsockets = 0 | |
29 * qc->max_retired_seqnum = 0 | |
30 * qc->client_seqnum = 0 | |
31 */ | |
32 | |
33 ngx_queue_init(&qc->sockets); | |
34 ngx_queue_init(&qc->free_sockets); | |
35 | |
36 ngx_queue_init(&qc->paths); | |
37 ngx_queue_init(&qc->free_paths); | |
38 | |
39 ngx_queue_init(&qc->client_ids); | |
40 ngx_queue_init(&qc->free_client_ids); | |
41 | |
42 qc->tp.original_dcid.len = pkt->odcid.len; | |
43 qc->tp.original_dcid.data = ngx_pstrdup(c->pool, &pkt->odcid); | |
44 if (qc->tp.original_dcid.data == NULL) { | |
45 return NGX_ERROR; | |
46 } | |
47 | |
48 /* socket to use for further processing */ | |
49 qsock = ngx_quic_alloc_socket(c, qc); | |
50 if (qsock == NULL) { | |
51 return NGX_ERROR; | |
52 } | |
53 | |
54 /* socket is listening at new server id (autogenerated) */ | |
55 if (ngx_quic_listen(c, qc, qsock) != NGX_OK) { | |
56 return NGX_ERROR; | |
57 } | |
58 | |
59 qc->tp.initial_scid.len = qsock->sid.len; | |
60 qc->tp.initial_scid.data = ngx_pnalloc(c->pool, qsock->sid.len); | |
61 if (qc->tp.initial_scid.data == NULL) { | |
62 goto failed; | |
63 } | |
64 ngx_memcpy(qc->tp.initial_scid.data, qsock->sid.id, qsock->sid.len); | |
65 | |
66 /* for all packets except first, this is set at udp layer */ | |
67 c->udp = &qsock->udp; | |
68 | |
69 /* ngx_quic_get_connection(c) macro is now usable */ | |
70 | |
71 /* we have a client identified by scid */ | |
72 cid = ngx_quic_create_client_id(c, &pkt->scid, 0, NULL); | |
73 if (cid == NULL) { | |
74 goto failed; | |
75 } | |
76 | |
77 /* the client arrived from this path */ | |
78 path = ngx_quic_add_path(c, c->sockaddr, c->socklen); | |
79 if (path == NULL) { | |
80 goto failed; | |
81 } | |
82 | |
83 if (pkt->validated) { | |
84 path->state = NGX_QUIC_PATH_VALIDATED; | |
85 path->validated_at = ngx_time(); | |
86 } | |
87 | |
88 /* now bind socket to client and path */ | |
89 ngx_quic_connect(c, qsock, path, cid); | |
90 | |
91 if (ngx_quic_create_temp_socket(c, qc, &pkt->odcid, path, cid) != NGX_OK) { | |
92 goto failed; | |
93 } | |
94 | |
95 /* use this socket as default destination */ | |
96 qc->socket = qsock; | |
97 | |
98 ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
99 "quic active socket is #%uL:%uL:%uL (%s)", | |
100 qsock->sid.seqnum, qsock->cid->seqnum, qsock->path->seqnum, | |
101 ngx_quic_path_state_str(qsock->path)); | |
102 | |
103 return NGX_OK; | |
104 | |
105 failed: | |
106 | |
107 ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node); | |
108 c->udp = NULL; | |
109 | |
110 return NGX_ERROR; | |
111 } | |
112 | |
113 | |
114 static ngx_int_t | |
115 ngx_quic_create_temp_socket(ngx_connection_t *c, ngx_quic_connection_t *qc, | |
116 ngx_str_t *dcid, ngx_quic_path_t *path, ngx_quic_client_id_t *cid) | |
117 { | |
118 ngx_str_t id; | |
119 ngx_quic_socket_t *qsock; | |
120 ngx_quic_server_id_t *sid; | |
121 | |
122 qsock = ngx_quic_alloc_socket(c, qc); | |
123 if (qsock == NULL) { | |
124 return NGX_ERROR; | |
125 } | |
126 | |
127 sid = &qsock->sid; | |
128 | |
129 sid->seqnum = NGX_QUIC_UNSET_PN; /* mark socket as temporary */ | |
130 | |
131 sid->len = dcid->len; | |
132 ngx_memcpy(sid->id, dcid->data, dcid->len); | |
133 | |
134 id.len = sid->len; | |
135 id.data = sid->id; | |
136 | |
137 ngx_insert_udp_connection(c, &qsock->udp, &id); | |
138 | |
139 ngx_queue_insert_tail(&qc->sockets, &qsock->queue); | |
140 | |
141 qc->nsockets++; | |
142 qsock->quic = qc; | |
143 | |
144 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
145 "quic socket #%L listening at sid:%xV nsock:%ui", | |
146 (int64_t) sid->seqnum, &id, qc->nsockets); | |
147 | |
148 ngx_quic_connect(c, qsock, path, cid); | |
149 | |
150 return NGX_OK; | |
151 } | |
152 | |
153 | |
154 ngx_quic_socket_t * | |
155 ngx_quic_alloc_socket(ngx_connection_t *c, ngx_quic_connection_t *qc) | |
156 { | |
157 ngx_queue_t *q; | |
158 ngx_quic_socket_t *sock; | |
159 | |
160 if (!ngx_queue_empty(&qc->free_sockets)) { | |
161 | |
162 q = ngx_queue_head(&qc->free_sockets); | |
163 sock = ngx_queue_data(q, ngx_quic_socket_t, queue); | |
164 | |
165 ngx_queue_remove(&sock->queue); | |
166 | |
167 ngx_memzero(sock, sizeof(ngx_quic_socket_t)); | |
168 | |
169 } else { | |
170 | |
171 sock = ngx_pcalloc(c->pool, sizeof(ngx_quic_socket_t)); | |
172 if (sock == NULL) { | |
173 return NULL; | |
174 } | |
175 } | |
176 | |
177 return sock; | |
178 } | |
179 | |
180 | |
181 void | |
182 ngx_quic_close_socket(ngx_connection_t *c, ngx_quic_socket_t *qsock) | |
183 { | |
184 ngx_quic_connection_t *qc; | |
185 | |
186 qc = ngx_quic_get_connection(c); | |
187 | |
188 ngx_queue_remove(&qsock->queue); | |
189 ngx_queue_insert_head(&qc->free_sockets, &qsock->queue); | |
190 | |
191 ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node); | |
192 qc->nsockets--; | |
193 | |
194 if (qsock->path) { | |
195 ngx_quic_unref_path(c, qsock->path); | |
196 } | |
197 | |
198 if (qsock->cid) { | |
199 ngx_quic_unref_client_id(c, qsock->cid); | |
200 } | |
201 | |
202 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
203 "quic socket #%L closed nsock:%ui", | |
204 (int64_t) qsock->sid.seqnum, qc->nsockets); | |
205 } | |
206 | |
207 | |
8700
b09f055daa4e
QUIC: fixed handling of RETIRE_CONNECTION_ID frame.
Vladimir Homutov <vl@nginx.com>
parents:
8650
diff
changeset
|
208 void |
8423 | 209 ngx_quic_unref_path(ngx_connection_t *c, ngx_quic_path_t *path) |
210 { | |
211 ngx_quic_connection_t *qc; | |
212 | |
213 path->refcnt--; | |
214 | |
215 if (path->refcnt) { | |
216 return; | |
217 } | |
218 | |
219 qc = ngx_quic_get_connection(c); | |
220 | |
221 ngx_queue_remove(&path->queue); | |
222 ngx_queue_insert_head(&qc->free_paths, &path->queue); | |
223 | |
224 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
225 "quic path #%uL addr:%V removed", | |
226 path->seqnum, &path->addr_text); | |
227 } | |
228 | |
229 | |
230 ngx_int_t | |
231 ngx_quic_listen(ngx_connection_t *c, ngx_quic_connection_t *qc, | |
232 ngx_quic_socket_t *qsock) | |
233 { | |
234 ngx_str_t id; | |
235 ngx_quic_server_id_t *sid; | |
236 | |
237 sid = &qsock->sid; | |
238 | |
239 sid->len = NGX_QUIC_SERVER_CID_LEN; | |
240 | |
241 if (ngx_quic_create_server_id(c, sid->id) != NGX_OK) { | |
242 return NGX_ERROR; | |
243 } | |
244 | |
245 sid->seqnum = qc->server_seqnum++; | |
246 | |
247 id.data = sid->id; | |
248 id.len = sid->len; | |
249 | |
250 ngx_insert_udp_connection(c, &qsock->udp, &id); | |
251 | |
252 ngx_queue_insert_tail(&qc->sockets, &qsock->queue); | |
253 | |
254 qc->nsockets++; | |
255 qsock->quic = qc; | |
256 | |
257 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
258 "quic socket #%uL listening at sid:%xV nsock:%ui", | |
259 sid->seqnum, &id, qc->nsockets); | |
260 | |
261 return NGX_OK; | |
262 } | |
263 | |
264 | |
265 void | |
266 ngx_quic_connect(ngx_connection_t *c, ngx_quic_socket_t *sock, | |
267 ngx_quic_path_t *path, ngx_quic_client_id_t *cid) | |
268 { | |
269 sock->path = path; | |
270 path->refcnt++; | |
271 | |
272 sock->cid = cid; | |
273 cid->refcnt++; | |
274 | |
275 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
276 "quic socket #%L connected to cid #%uL path:%uL", | |
277 (int64_t) sock->sid.seqnum, | |
278 sock->cid->seqnum, path->seqnum); | |
279 } | |
280 | |
281 | |
282 void | |
283 ngx_quic_close_sockets(ngx_connection_t *c) | |
284 { | |
285 ngx_queue_t *q; | |
286 ngx_quic_socket_t *qsock; | |
287 ngx_quic_connection_t *qc; | |
288 | |
289 qc = ngx_quic_get_connection(c); | |
290 | |
291 ngx_quic_close_socket(c, qc->socket); | |
292 | |
293 if (qc->backup) { | |
294 ngx_quic_close_socket(c, qc->backup); | |
295 } | |
296 | |
297 while (!ngx_queue_empty(&qc->sockets)) { | |
298 q = ngx_queue_head(&qc->sockets); | |
299 qsock = ngx_queue_data(q, ngx_quic_socket_t, queue); | |
300 | |
301 ngx_quic_close_socket(c, qsock); | |
302 } | |
303 } | |
304 | |
305 | |
306 ngx_quic_socket_t * | |
307 ngx_quic_find_socket(ngx_connection_t *c, uint64_t seqnum) | |
308 { | |
309 ngx_queue_t *q; | |
310 ngx_quic_socket_t *qsock; | |
311 ngx_quic_connection_t *qc; | |
312 | |
313 qc = ngx_quic_get_connection(c); | |
314 | |
315 for (q = ngx_queue_head(&qc->sockets); | |
316 q != ngx_queue_sentinel(&qc->sockets); | |
317 q = ngx_queue_next(q)) | |
318 { | |
319 qsock = ngx_queue_data(q, ngx_quic_socket_t, queue); | |
320 | |
321 if (qsock->sid.seqnum == seqnum) { | |
322 return qsock; | |
323 } | |
324 } | |
325 | |
326 return NULL; | |
327 } | |
328 | |
329 | |
330 ngx_quic_socket_t * | |
331 ngx_quic_get_unconnected_socket(ngx_connection_t *c) | |
332 { | |
333 ngx_queue_t *q; | |
334 ngx_quic_socket_t *sock; | |
335 ngx_quic_connection_t *qc; | |
336 | |
337 qc = ngx_quic_get_connection(c); | |
338 | |
339 for (q = ngx_queue_head(&qc->sockets); | |
340 q != ngx_queue_sentinel(&qc->sockets); | |
341 q = ngx_queue_next(q)) | |
342 { | |
343 sock = ngx_queue_data(q, ngx_quic_socket_t, queue); | |
344 | |
345 if (sock->cid == NULL) { | |
346 return sock; | |
347 } | |
348 } | |
349 | |
350 return NULL; | |
8650 | 351 } |