Mercurial > hg > nginx-quic
comparison src/event/quic/ngx_event_quic_connid.c @ 8408:e0cb1e58ca13 quic
QUIC: separate files for connection id related processing.
author | Vladimir Homutov <vl@nginx.com> |
---|---|
date | Tue, 13 Apr 2021 14:37:41 +0300 |
parents | |
children | 4117aa7fa38e |
comparison
equal
deleted
inserted
replaced
8407:c8bda5e1e662 | 8408:e0cb1e58ca13 |
---|---|
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 #define NGX_QUIC_MAX_SERVER_IDS 8 | |
14 | |
15 | |
16 static ngx_int_t ngx_quic_create_server_id(ngx_connection_t *c, u_char *id); | |
17 #if (NGX_QUIC_BPF) | |
18 static ngx_int_t ngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id); | |
19 #endif | |
20 static ngx_int_t ngx_quic_retire_connection_id(ngx_connection_t *c, | |
21 enum ssl_encryption_level_t level, uint64_t seqnum); | |
22 static ngx_quic_server_id_t *ngx_quic_insert_server_id(ngx_connection_t *c, | |
23 ngx_quic_connection_t *qc, ngx_str_t *id); | |
24 static ngx_quic_client_id_t *ngx_quic_alloc_client_id(ngx_connection_t *c, | |
25 ngx_quic_connection_t *qc); | |
26 static ngx_quic_server_id_t *ngx_quic_alloc_server_id(ngx_connection_t *c, | |
27 ngx_quic_connection_t *qc); | |
28 | |
29 | |
30 ngx_int_t | |
31 ngx_quic_setup_connection_ids(ngx_connection_t *c, ngx_quic_connection_t *qc, | |
32 ngx_quic_header_t *pkt) | |
33 { | |
34 ngx_quic_server_id_t *sid, *osid; | |
35 ngx_quic_client_id_t *cid; | |
36 | |
37 /* | |
38 * qc->nclient_ids = 0 | |
39 * qc->nserver_ids = 0 | |
40 * qc->max_retired_seqnum = 0 | |
41 */ | |
42 | |
43 ngx_queue_init(&qc->client_ids); | |
44 ngx_queue_init(&qc->server_ids); | |
45 ngx_queue_init(&qc->free_client_ids); | |
46 ngx_queue_init(&qc->free_server_ids); | |
47 | |
48 qc->odcid.len = pkt->odcid.len; | |
49 qc->odcid.data = ngx_pstrdup(c->pool, &pkt->odcid); | |
50 if (qc->odcid.data == NULL) { | |
51 return NGX_ERROR; | |
52 } | |
53 | |
54 qc->tp.original_dcid = qc->odcid; | |
55 | |
56 qc->scid.len = pkt->scid.len; | |
57 qc->scid.data = ngx_pstrdup(c->pool, &pkt->scid); | |
58 if (qc->scid.data == NULL) { | |
59 return NGX_ERROR; | |
60 } | |
61 | |
62 qc->dcid.len = NGX_QUIC_SERVER_CID_LEN; | |
63 qc->dcid.data = ngx_pnalloc(c->pool, qc->dcid.len); | |
64 if (qc->dcid.data == NULL) { | |
65 return NGX_ERROR; | |
66 } | |
67 | |
68 if (ngx_quic_create_server_id(c, qc->dcid.data) != NGX_OK) { | |
69 return NGX_ERROR; | |
70 } | |
71 | |
72 qc->tp.initial_scid = qc->dcid; | |
73 | |
74 cid = ngx_quic_alloc_client_id(c, qc); | |
75 if (cid == NULL) { | |
76 return NGX_ERROR; | |
77 } | |
78 | |
79 cid->seqnum = 0; | |
80 cid->len = pkt->scid.len; | |
81 ngx_memcpy(cid->id, pkt->scid.data, pkt->scid.len); | |
82 | |
83 ngx_queue_insert_tail(&qc->client_ids, &cid->queue); | |
84 qc->nclient_ids++; | |
85 qc->client_seqnum = 0; | |
86 | |
87 qc->server_seqnum = NGX_QUIC_UNSET_PN; | |
88 | |
89 osid = ngx_quic_insert_server_id(c, qc, &qc->odcid); | |
90 if (osid == NULL) { | |
91 return NGX_ERROR; | |
92 } | |
93 | |
94 qc->server_seqnum = 0; | |
95 | |
96 sid = ngx_quic_insert_server_id(c, qc, &qc->dcid); | |
97 if (sid == NULL) { | |
98 ngx_rbtree_delete(&c->listening->rbtree, &osid->udp.node); | |
99 return NGX_ERROR; | |
100 } | |
101 | |
102 c->udp = &sid->udp; | |
103 | |
104 return NGX_OK; | |
105 } | |
106 | |
107 | |
108 static ngx_int_t | |
109 ngx_quic_create_server_id(ngx_connection_t *c, u_char *id) | |
110 { | |
111 if (RAND_bytes(id, NGX_QUIC_SERVER_CID_LEN) != 1) { | |
112 return NGX_ERROR; | |
113 } | |
114 | |
115 #if (NGX_QUIC_BPF) | |
116 if (ngx_quic_bpf_attach_id(c, id) != NGX_OK) { | |
117 ngx_log_error(NGX_LOG_ERR, c->log, 0, | |
118 "quic bpf failed to generate socket key"); | |
119 /* ignore error, things still may work */ | |
120 } | |
121 #endif | |
122 | |
123 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
124 "quic create server id %*xs", | |
125 (size_t) NGX_QUIC_SERVER_CID_LEN, id); | |
126 return NGX_OK; | |
127 } | |
128 | |
129 | |
130 #if (NGX_QUIC_BPF) | |
131 | |
132 static ngx_int_t | |
133 ngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id) | |
134 { | |
135 int fd; | |
136 uint64_t cookie; | |
137 socklen_t optlen; | |
138 | |
139 fd = c->listening->fd; | |
140 | |
141 optlen = sizeof(cookie); | |
142 | |
143 if (getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &optlen) == -1) { | |
144 ngx_log_error(NGX_LOG_ERR, c->log, ngx_socket_errno, | |
145 "quic getsockopt(SO_COOKIE) failed"); | |
146 | |
147 return NGX_ERROR; | |
148 } | |
149 | |
150 ngx_quic_dcid_encode_key(id, cookie); | |
151 | |
152 return NGX_OK; | |
153 } | |
154 | |
155 #endif | |
156 | |
157 | |
158 | |
159 | |
160 ngx_int_t | |
161 ngx_quic_handle_new_connection_id_frame(ngx_connection_t *c, | |
162 ngx_quic_header_t *pkt, ngx_quic_new_conn_id_frame_t *f) | |
163 { | |
164 ngx_queue_t *q; | |
165 ngx_quic_client_id_t *cid, *item; | |
166 ngx_quic_connection_t *qc; | |
167 | |
168 qc = ngx_quic_get_connection(c); | |
169 | |
170 if (f->seqnum < qc->max_retired_seqnum) { | |
171 /* | |
172 * An endpoint that receives a NEW_CONNECTION_ID frame with | |
173 * a sequence number smaller than the Retire Prior To field | |
174 * of a previously received NEW_CONNECTION_ID frame MUST send | |
175 * a corresponding RETIRE_CONNECTION_ID frame that retires | |
176 * the newly received connection ID, unless it has already | |
177 * done so for that sequence number. | |
178 */ | |
179 | |
180 if (ngx_quic_retire_connection_id(c, pkt->level, f->seqnum) != NGX_OK) { | |
181 return NGX_ERROR; | |
182 } | |
183 | |
184 goto retire; | |
185 } | |
186 | |
187 cid = NULL; | |
188 | |
189 for (q = ngx_queue_head(&qc->client_ids); | |
190 q != ngx_queue_sentinel(&qc->client_ids); | |
191 q = ngx_queue_next(q)) | |
192 { | |
193 item = ngx_queue_data(q, ngx_quic_client_id_t, queue); | |
194 | |
195 if (item->seqnum == f->seqnum) { | |
196 cid = item; | |
197 break; | |
198 } | |
199 } | |
200 | |
201 if (cid) { | |
202 /* | |
203 * Transmission errors, timeouts and retransmissions might cause the | |
204 * same NEW_CONNECTION_ID frame to be received multiple times | |
205 */ | |
206 | |
207 if (cid->len != f->len | |
208 || ngx_strncmp(cid->id, f->cid, f->len) != 0 | |
209 || ngx_strncmp(cid->sr_token, f->srt, NGX_QUIC_SR_TOKEN_LEN) != 0) | |
210 { | |
211 /* | |
212 * ..a sequence number is used for different connection IDs, | |
213 * the endpoint MAY treat that receipt as a connection error | |
214 * of type PROTOCOL_VIOLATION. | |
215 */ | |
216 qc->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION; | |
217 qc->error_reason = "seqnum refers to different connection id/token"; | |
218 return NGX_ERROR; | |
219 } | |
220 | |
221 } else { | |
222 | |
223 cid = ngx_quic_alloc_client_id(c, qc); | |
224 if (cid == NULL) { | |
225 return NGX_ERROR; | |
226 } | |
227 | |
228 cid->seqnum = f->seqnum; | |
229 cid->len = f->len; | |
230 ngx_memcpy(cid->id, f->cid, f->len); | |
231 | |
232 ngx_memcpy(cid->sr_token, f->srt, NGX_QUIC_SR_TOKEN_LEN); | |
233 | |
234 ngx_queue_insert_tail(&qc->client_ids, &cid->queue); | |
235 qc->nclient_ids++; | |
236 | |
237 /* always use latest available connection id */ | |
238 if (f->seqnum > qc->client_seqnum) { | |
239 qc->scid.len = cid->len; | |
240 qc->scid.data = cid->id; | |
241 qc->client_seqnum = f->seqnum; | |
242 } | |
243 } | |
244 | |
245 retire: | |
246 | |
247 if (qc->max_retired_seqnum && f->retire <= qc->max_retired_seqnum) { | |
248 /* | |
249 * Once a sender indicates a Retire Prior To value, smaller values sent | |
250 * in subsequent NEW_CONNECTION_ID frames have no effect. A receiver | |
251 * MUST ignore any Retire Prior To fields that do not increase the | |
252 * largest received Retire Prior To value. | |
253 */ | |
254 goto done; | |
255 } | |
256 | |
257 qc->max_retired_seqnum = f->retire; | |
258 | |
259 q = ngx_queue_head(&qc->client_ids); | |
260 | |
261 while (q != ngx_queue_sentinel(&qc->client_ids)) { | |
262 | |
263 cid = ngx_queue_data(q, ngx_quic_client_id_t, queue); | |
264 q = ngx_queue_next(q); | |
265 | |
266 if (cid->seqnum >= f->retire) { | |
267 continue; | |
268 } | |
269 | |
270 /* this connection id must be retired */ | |
271 | |
272 if (ngx_quic_retire_connection_id(c, pkt->level, cid->seqnum) | |
273 != NGX_OK) | |
274 { | |
275 return NGX_ERROR; | |
276 } | |
277 | |
278 ngx_queue_remove(&cid->queue); | |
279 ngx_queue_insert_head(&qc->free_client_ids, &cid->queue); | |
280 qc->nclient_ids--; | |
281 } | |
282 | |
283 done: | |
284 | |
285 if (qc->nclient_ids > qc->tp.active_connection_id_limit) { | |
286 /* | |
287 * After processing a NEW_CONNECTION_ID frame and | |
288 * adding and retiring active connection IDs, if the number of active | |
289 * connection IDs exceeds the value advertised in its | |
290 * active_connection_id_limit transport parameter, an endpoint MUST | |
291 * close the connection with an error of type CONNECTION_ID_LIMIT_ERROR. | |
292 */ | |
293 qc->error = NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR; | |
294 qc->error_reason = "too many connection ids received"; | |
295 return NGX_ERROR; | |
296 } | |
297 | |
298 return NGX_OK; | |
299 } | |
300 | |
301 | |
302 static ngx_int_t | |
303 ngx_quic_retire_connection_id(ngx_connection_t *c, | |
304 enum ssl_encryption_level_t level, uint64_t seqnum) | |
305 { | |
306 ngx_quic_frame_t *frame; | |
307 ngx_quic_connection_t *qc; | |
308 | |
309 qc = ngx_quic_get_connection(c); | |
310 | |
311 frame = ngx_quic_alloc_frame(c); | |
312 if (frame == NULL) { | |
313 return NGX_ERROR; | |
314 } | |
315 | |
316 frame->level = level; | |
317 frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID; | |
318 frame->u.retire_cid.sequence_number = seqnum; | |
319 | |
320 ngx_quic_queue_frame(qc, frame); | |
321 | |
322 return NGX_OK; | |
323 } | |
324 | |
325 | |
326 ngx_int_t | |
327 ngx_quic_handle_retire_connection_id_frame(ngx_connection_t *c, | |
328 ngx_quic_header_t *pkt, ngx_quic_retire_cid_frame_t *f) | |
329 { | |
330 ngx_queue_t *q; | |
331 ngx_quic_server_id_t *sid; | |
332 ngx_quic_connection_t *qc; | |
333 | |
334 qc = ngx_quic_get_connection(c); | |
335 | |
336 for (q = ngx_queue_head(&qc->server_ids); | |
337 q != ngx_queue_sentinel(&qc->server_ids); | |
338 q = ngx_queue_next(q)) | |
339 { | |
340 sid = ngx_queue_data(q, ngx_quic_server_id_t, queue); | |
341 | |
342 if (sid->seqnum == f->sequence_number) { | |
343 ngx_queue_remove(q); | |
344 ngx_queue_insert_tail(&qc->free_server_ids, &sid->queue); | |
345 ngx_rbtree_delete(&c->listening->rbtree, &sid->udp.node); | |
346 qc->nserver_ids--; | |
347 break; | |
348 } | |
349 } | |
350 | |
351 return ngx_quic_issue_server_ids(c); | |
352 } | |
353 | |
354 | |
355 ngx_int_t | |
356 ngx_quic_issue_server_ids(ngx_connection_t *c) | |
357 { | |
358 ngx_str_t dcid; | |
359 ngx_uint_t n; | |
360 ngx_quic_frame_t *frame; | |
361 ngx_quic_server_id_t *sid; | |
362 ngx_quic_connection_t *qc; | |
363 u_char id[NGX_QUIC_SERVER_CID_LEN]; | |
364 | |
365 qc = ngx_quic_get_connection(c); | |
366 | |
367 n = ngx_min(NGX_QUIC_MAX_SERVER_IDS, qc->ctp.active_connection_id_limit); | |
368 | |
369 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
370 "quic issue server ids has:%ui max:%ui", qc->nserver_ids, n); | |
371 | |
372 while (qc->nserver_ids < n) { | |
373 if (ngx_quic_create_server_id(c, id) != NGX_OK) { | |
374 return NGX_ERROR; | |
375 } | |
376 | |
377 dcid.len = NGX_QUIC_SERVER_CID_LEN; | |
378 dcid.data = id; | |
379 | |
380 sid = ngx_quic_insert_server_id(c, qc, &dcid); | |
381 if (sid == NULL) { | |
382 return NGX_ERROR; | |
383 } | |
384 | |
385 frame = ngx_quic_alloc_frame(c); | |
386 if (frame == NULL) { | |
387 return NGX_ERROR; | |
388 } | |
389 | |
390 frame->level = ssl_encryption_application; | |
391 frame->type = NGX_QUIC_FT_NEW_CONNECTION_ID; | |
392 frame->u.ncid.seqnum = sid->seqnum; | |
393 frame->u.ncid.retire = 0; | |
394 frame->u.ncid.len = NGX_QUIC_SERVER_CID_LEN; | |
395 ngx_memcpy(frame->u.ncid.cid, id, NGX_QUIC_SERVER_CID_LEN); | |
396 | |
397 if (ngx_quic_new_sr_token(c, &dcid, qc->conf->sr_token_key, | |
398 frame->u.ncid.srt) | |
399 != NGX_OK) | |
400 { | |
401 return NGX_ERROR; | |
402 } | |
403 | |
404 ngx_quic_queue_frame(qc, frame); | |
405 } | |
406 | |
407 return NGX_OK; | |
408 } | |
409 | |
410 | |
411 void | |
412 ngx_quic_clear_temp_server_ids(ngx_connection_t *c) | |
413 { | |
414 ngx_queue_t *q, *next; | |
415 ngx_quic_server_id_t *sid; | |
416 ngx_quic_connection_t *qc; | |
417 | |
418 qc = ngx_quic_get_connection(c); | |
419 | |
420 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
421 "quic clear temp server ids"); | |
422 | |
423 for (q = ngx_queue_head(&qc->server_ids); | |
424 q != ngx_queue_sentinel(&qc->server_ids); | |
425 q = next) | |
426 { | |
427 next = ngx_queue_next(q); | |
428 sid = ngx_queue_data(q, ngx_quic_server_id_t, queue); | |
429 | |
430 if (sid->seqnum != NGX_QUIC_UNSET_PN) { | |
431 continue; | |
432 } | |
433 | |
434 ngx_queue_remove(q); | |
435 ngx_queue_insert_tail(&qc->free_server_ids, &sid->queue); | |
436 ngx_rbtree_delete(&c->listening->rbtree, &sid->udp.node); | |
437 qc->nserver_ids--; | |
438 } | |
439 } | |
440 | |
441 | |
442 static ngx_quic_server_id_t * | |
443 ngx_quic_insert_server_id(ngx_connection_t *c, ngx_quic_connection_t *qc, | |
444 ngx_str_t *id) | |
445 { | |
446 ngx_str_t dcid; | |
447 ngx_quic_server_id_t *sid; | |
448 | |
449 sid = ngx_quic_alloc_server_id(c, qc); | |
450 if (sid == NULL) { | |
451 return NULL; | |
452 } | |
453 | |
454 sid->quic = qc; | |
455 | |
456 sid->seqnum = qc->server_seqnum; | |
457 | |
458 if (qc->server_seqnum != NGX_QUIC_UNSET_PN) { | |
459 qc->server_seqnum++; | |
460 } | |
461 | |
462 sid->len = id->len; | |
463 ngx_memcpy(sid->id, id->data, id->len); | |
464 | |
465 ngx_queue_insert_tail(&qc->server_ids, &sid->queue); | |
466 qc->nserver_ids++; | |
467 | |
468 dcid.data = sid->id; | |
469 dcid.len = sid->len; | |
470 | |
471 ngx_insert_udp_connection(c, &sid->udp, &dcid); | |
472 | |
473 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
474 "quic insert server id seqnum:%uL id len:%uz %xV", | |
475 sid->seqnum, id->len, id); | |
476 | |
477 return sid; | |
478 } | |
479 | |
480 | |
481 static ngx_quic_client_id_t * | |
482 ngx_quic_alloc_client_id(ngx_connection_t *c, ngx_quic_connection_t *qc) | |
483 { | |
484 ngx_queue_t *q; | |
485 ngx_quic_client_id_t *cid; | |
486 | |
487 if (!ngx_queue_empty(&qc->free_client_ids)) { | |
488 | |
489 q = ngx_queue_head(&qc->free_client_ids); | |
490 cid = ngx_queue_data(q, ngx_quic_client_id_t, queue); | |
491 | |
492 ngx_queue_remove(&cid->queue); | |
493 | |
494 ngx_memzero(cid, sizeof(ngx_quic_client_id_t)); | |
495 | |
496 } else { | |
497 | |
498 cid = ngx_pcalloc(c->pool, sizeof(ngx_quic_client_id_t)); | |
499 if (cid == NULL) { | |
500 return NULL; | |
501 } | |
502 } | |
503 | |
504 return cid; | |
505 } | |
506 | |
507 | |
508 static ngx_quic_server_id_t * | |
509 ngx_quic_alloc_server_id(ngx_connection_t *c, ngx_quic_connection_t *qc) | |
510 { | |
511 ngx_queue_t *q; | |
512 ngx_quic_server_id_t *sid; | |
513 | |
514 if (!ngx_queue_empty(&qc->free_server_ids)) { | |
515 | |
516 q = ngx_queue_head(&qc->free_server_ids); | |
517 sid = ngx_queue_data(q, ngx_quic_server_id_t, queue); | |
518 | |
519 ngx_queue_remove(&sid->queue); | |
520 | |
521 ngx_memzero(sid, sizeof(ngx_quic_server_id_t)); | |
522 | |
523 } else { | |
524 | |
525 sid = ngx_pcalloc(c->pool, sizeof(ngx_quic_server_id_t)); | |
526 if (sid == NULL) { | |
527 return NULL; | |
528 } | |
529 } | |
530 | |
531 return sid; | |
532 } |