comparison src/event/quic/ngx_event_quic_connid.c @ 8748: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
8747:c8bda5e1e662 8748: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 }