comparison src/event/quic/ngx_event_quic_migration.c @ 8763:4117aa7fa38e quic

QUIC: connection migration. The patch adds proper transitions between multiple networking addresses that can be used by a single quic connection. New networking paths are validated using PATH_CHALLENGE/PATH_RESPONSE frames.
author Vladimir Homutov <vl@nginx.com>
date Thu, 29 Apr 2021 15:35:02 +0300
parents c8bda5e1e662
children d5f93733c17d
comparison
equal deleted inserted replaced
8762:12f18e0bca09 8763:4117aa7fa38e
8 #include <ngx_core.h> 8 #include <ngx_core.h>
9 #include <ngx_event.h> 9 #include <ngx_event.h>
10 #include <ngx_event_quic_connection.h> 10 #include <ngx_event_quic_connection.h>
11 11
12 12
13 static void ngx_quic_set_connection_path(ngx_connection_t *c,
14 ngx_quic_path_t *path);
15 static ngx_int_t ngx_quic_validate_path(ngx_connection_t *c,
16 ngx_quic_socket_t *qsock);
17 static ngx_int_t ngx_quic_send_path_challenge(ngx_connection_t *c,
18 ngx_quic_path_t *path);
19 static ngx_int_t ngx_quic_path_restore(ngx_connection_t *c);
20 static ngx_quic_path_t *ngx_quic_alloc_path(ngx_connection_t *c);
21
22
13 ngx_int_t 23 ngx_int_t
14 ngx_quic_handle_path_challenge_frame(ngx_connection_t *c, 24 ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
15 ngx_quic_header_t *pkt, ngx_quic_path_challenge_frame_t *f) 25 ngx_quic_header_t *pkt, ngx_quic_path_challenge_frame_t *f)
16 { 26 {
17 ngx_quic_frame_t *frame; 27 off_t max, pad;
18 ngx_quic_connection_t *qc; 28 ssize_t sent;
19 29 ngx_quic_path_t *path;
20 qc = ngx_quic_get_connection(c); 30 ngx_quic_frame_t frame, *fp;
21 31 ngx_quic_socket_t *qsock;
22 frame = ngx_quic_alloc_frame(c); 32 ngx_quic_connection_t *qc;
23 if (frame == NULL) { 33
24 return NGX_ERROR; 34 qc = ngx_quic_get_connection(c);
25 } 35
26 36 frame.level = pkt->level;
27 frame->level = pkt->level; 37 frame.type = NGX_QUIC_FT_PATH_RESPONSE;
28 frame->type = NGX_QUIC_FT_PATH_RESPONSE; 38 frame.u.path_response = *f;
29 frame->u.path_response = *f; 39
30 40 /*
31 ngx_quic_queue_frame(qc, frame); 41 * A PATH_RESPONSE frame MUST be sent on the network path where the
42 * PATH_CHALLENGE was received.
43 */
44 qsock = ngx_quic_get_socket(c);
45 path = qsock->path;
46
47 /*
48 * An endpoint MUST NOT expand the datagram containing the PATH_RESPONSE
49 * if the resulting data exceeds the anti-amplification limit.
50 */
51 max = path->received * 3;
52 max = (path->sent >= max) ? 0 : max - path->sent;
53 pad = ngx_min(1200, max);
54
55 sent = ngx_quic_frame_sendto(c, &frame, pad, path->sockaddr, path->socklen);
56 if (sent == -1) {
57 return NGX_ERROR;
58 }
59
60 path->sent += sent;
61
62 if (qsock == qc->socket) {
63 /*
64 * An endpoint that receives a PATH_CHALLENGE on an active path SHOULD
65 * send a non-probing packet in response.
66 */
67
68 fp = ngx_quic_alloc_frame(c);
69 if (fp == NULL) {
70 return NGX_ERROR;
71 }
72
73 fp->level = pkt->level;
74 fp->type = NGX_QUIC_FT_PING;
75
76 ngx_quic_queue_frame(qc, fp);
77 }
32 78
33 return NGX_OK; 79 return NGX_OK;
34 } 80 }
35 81
36 82
37 ngx_int_t 83 ngx_int_t
38 ngx_quic_handle_path_response_frame(ngx_connection_t *c, 84 ngx_quic_handle_path_response_frame(ngx_connection_t *c,
39 ngx_quic_header_t *pkt, ngx_quic_path_challenge_frame_t *f) 85 ngx_quic_header_t *pkt, ngx_quic_path_challenge_frame_t *f)
40 { 86 {
41 /* TODO */ 87 ngx_queue_t *q;
88 ngx_quic_path_t *path, *prev;
89 ngx_quic_connection_t *qc;
90
91 qc = ngx_quic_get_connection(c);
92
93 /*
94 * A PATH_RESPONSE frame received on any network path validates the path
95 * on which the PATH_CHALLENGE was sent.
96 */
97
98 for (q = ngx_queue_head(&qc->paths);
99 q != ngx_queue_sentinel(&qc->paths);
100 q = ngx_queue_next(q))
101 {
102 path = ngx_queue_data(q, ngx_quic_path_t, queue);
103
104 if (path->state != NGX_QUIC_PATH_VALIDATING) {
105 continue;
106 }
107
108 if (ngx_memcmp(path->challenge1, f->data, sizeof(f->data)) == 0
109 || ngx_memcmp(path->challenge2, f->data, sizeof(f->data)) == 0)
110 {
111 goto valid;
112 }
113 }
114
115 ngx_log_error(NGX_LOG_INFO, c->log, 0,
116 "quic stale PATH_RESPONSE ignored");
117
42 return NGX_OK; 118 return NGX_OK;
43 } 119
44 120 valid:
121
122 /*
123 * On confirming a peer's ownership of its new address,
124 * an endpoint MUST immediately reset the congestion controller
125 * and round-trip time estimator for the new path
126 * to initial values
127 * ...unless the only change in the peer's address is its port number.
128 */
129
130 prev = qc->backup->path;
131
132 if (ngx_cmp_sockaddr(prev->sockaddr, prev->socklen,
133 path->sockaddr, path->socklen, 0)
134 != NGX_OK)
135 {
136 /* address has changed */
137 ngx_memzero(&qc->congestion, sizeof(ngx_quic_congestion_t));
138
139 qc->congestion.window = ngx_min(10 * qc->tp.max_udp_payload_size,
140 ngx_max(2 * qc->tp.max_udp_payload_size,
141 14720));
142 qc->congestion.ssthresh = (size_t) -1;
143 qc->congestion.recovery_start = ngx_current_msec;
144 }
145
146 /*
147 * After verifying a new client address, the server SHOULD
148 * send new address validation tokens (Section 8) to the client.
149 */
150
151 if (ngx_quic_send_new_token(c, path) != NGX_OK) {
152 return NGX_ERROR;
153 }
154
155 ngx_log_error(NGX_LOG_INFO, c->log, 0,
156 "quic path #%uL successfully validated", path->seqnum);
157
158 path->state = NGX_QUIC_PATH_VALIDATED;
159 path->validated_at = ngx_time();
160
161 return NGX_OK;
162 }
163
164
165 static ngx_quic_path_t *
166 ngx_quic_alloc_path(ngx_connection_t *c)
167 {
168 ngx_queue_t *q;
169 struct sockaddr *sa;
170 ngx_quic_path_t *path;
171 ngx_quic_connection_t *qc;
172
173 qc = ngx_quic_get_connection(c);
174
175 if (!ngx_queue_empty(&qc->free_paths)) {
176
177 q = ngx_queue_head(&qc->free_paths);
178 path = ngx_queue_data(q, ngx_quic_path_t, queue);
179
180 ngx_queue_remove(&path->queue);
181
182 sa = path->sockaddr;
183 ngx_memzero(path, sizeof(ngx_quic_path_t));
184 path->sockaddr = sa;
185
186 } else {
187
188 path = ngx_pcalloc(c->pool, sizeof(ngx_quic_path_t));
189 if (path == NULL) {
190 return NULL;
191 }
192
193 path->sockaddr = ngx_palloc(c->pool, NGX_SOCKADDRLEN);
194 if (path->sockaddr == NULL) {
195 return NULL;
196 }
197 }
198
199 return path;
200 }
201
202
203 ngx_quic_path_t *
204 ngx_quic_add_path(ngx_connection_t *c, struct sockaddr *sockaddr,
205 socklen_t socklen)
206 {
207 ngx_quic_path_t *path;
208 ngx_quic_connection_t *qc;
209
210 qc = ngx_quic_get_connection(c);
211
212 path = ngx_quic_alloc_path(c);
213 if (path == NULL) {
214 return NULL;
215 }
216
217 path->seqnum = qc->path_seqnum++;
218
219 path->socklen = socklen;
220 ngx_memcpy(path->sockaddr, sockaddr, socklen);
221
222 path->addr_text.data = path->text;
223 path->addr_text.len = ngx_sock_ntop(sockaddr, socklen, path->text,
224 NGX_SOCKADDR_STRLEN, 1);
225
226 ngx_queue_insert_tail(&qc->paths, &path->queue);
227
228 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
229 "quic path #%uL created src:%V",
230 path->seqnum, &path->addr_text);
231
232 return path;
233 }
234
235
236 ngx_quic_path_t *
237 ngx_quic_find_path(ngx_connection_t *c, struct sockaddr *sockaddr,
238 socklen_t socklen)
239 {
240 ngx_queue_t *q;
241 ngx_quic_path_t *path;
242 ngx_quic_connection_t *qc;
243
244 qc = ngx_quic_get_connection(c);
245
246 for (q = ngx_queue_head(&qc->paths);
247 q != ngx_queue_sentinel(&qc->paths);
248 q = ngx_queue_next(q))
249 {
250 path = ngx_queue_data(q, ngx_quic_path_t, queue);
251
252 if (ngx_cmp_sockaddr(sockaddr, socklen,
253 path->sockaddr, path->socklen, 1)
254 == NGX_OK)
255 {
256 return path;
257 }
258 }
259
260 return NULL;
261 }
262
263
264 ngx_int_t
265 ngx_quic_check_migration(ngx_connection_t *c, ngx_quic_header_t *pkt)
266 {
267 ngx_quic_path_t *path;
268 ngx_quic_socket_t *qsock;
269 ngx_quic_connection_t *qc;
270
271 qc = ngx_quic_get_connection(c);
272
273 qsock = ngx_quic_get_socket(c);
274
275 if (c->udp->dgram == NULL) {
276 /* 2nd QUIC packet in first UDP datagram */
277 return NGX_OK;
278 }
279
280 path = ngx_quic_find_path(c, c->udp->dgram->sockaddr,
281 c->udp->dgram->socklen);
282 if (path == NULL) {
283 /* packet comes from unknown path, possibly migration */
284
285 if (qc->tp.disable_active_migration) {
286 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
287 "quic migration disabled, dropping packet "
288 "from unknown path");
289 return NGX_DECLINED;
290 }
291
292 if (pkt->level != ssl_encryption_application) {
293 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
294 "quic too early migration attempt");
295 return NGX_DECLINED;
296 }
297
298 return NGX_OK;
299 }
300
301 /* packet from known path */
302
303 if (qsock->path == NULL) {
304 /* client switched to previously unused server id */
305 return NGX_OK;
306 }
307
308 if (path == qsock->path) {
309 /* regular packet to expected path */
310 return NGX_OK;
311 }
312
313 /* client is trying to use server id already used on other path */
314
315 ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
316 "quic attempt to use socket #%uL:%uL:%uL with path #%uL",
317 qsock->sid.seqnum, qsock->cid->seqnum,
318 qsock->path->seqnum, path->seqnum);
319
320 return NGX_DECLINED;
321 }
322
323
324 ngx_int_t
325 ngx_quic_update_paths(ngx_connection_t *c, ngx_quic_header_t *pkt)
326 {
327 off_t len;
328 ngx_quic_path_t *path;
329 ngx_quic_socket_t *qsock;
330 ngx_quic_client_id_t *cid;
331 ngx_quic_connection_t *qc;
332
333 qsock = ngx_quic_get_socket(c);
334 path = qsock->path;
335
336 if (path) {
337 goto update;
338 }
339
340 path = ngx_quic_find_path(c, c->udp->dgram->sockaddr,
341 c->udp->dgram->socklen);
342
343 if (path == NULL) {
344 path = ngx_quic_add_path(c, c->udp->dgram->sockaddr,
345 c->udp->dgram->socklen);
346 if (path == NULL) {
347 return NGX_ERROR;
348 }
349 }
350
351 cid = ngx_quic_next_client_id(c);
352 if (cid == NULL) {
353 qc = ngx_quic_get_connection(c);
354 qc->error = NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR;
355 qc->error_reason = "no available client ids for new path";
356
357 ngx_log_error(NGX_LOG_ERR, c->log, 0,
358 "no available client ids for new path");
359
360 return NGX_ERROR;
361 }
362
363 ngx_quic_connect(c, qsock, path, cid);
364
365 update:
366
367 if (pkt->raw->start == pkt->data) {
368 len = pkt->raw->last - pkt->raw->start;
369
370 } else {
371 len = 0;
372 }
373
374 /* TODO: this may be too late in some cases;
375 * for example, if error happens during decrypt(), we cannot
376 * send CC, if error happens in 1st packet, due to amplification
377 * limit, because path->received = 0
378 *
379 * should we account garbage as received or only decrypting packets?
380 */
381 path->received += len;
382
383 ngx_log_debug6(NGX_LOG_DEBUG_EVENT, c->log, 0,
384 "quic packet via #%uL:%uL:%uL"
385 " size:%O path recvd:%O sent:%O",
386 qsock->sid.seqnum, qsock->cid->seqnum, path->seqnum,
387 len, path->received, path->sent);
388
389 return NGX_OK;
390 }
391
392
393 static void
394 ngx_quic_set_connection_path(ngx_connection_t *c, ngx_quic_path_t *path)
395 {
396 size_t len;
397
398 ngx_memcpy(c->sockaddr, path->sockaddr, path->socklen);
399 c->socklen = path->socklen;
400
401 if (c->addr_text.data) {
402 len = ngx_min(c->addr_text.len, path->addr_text.len);
403
404 ngx_memcpy(c->addr_text.data, path->addr_text.data, len);
405 c->addr_text.len = len;
406 }
407
408 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
409 "quic send path set to #%uL addr:%V",
410 path->seqnum, &path->addr_text);
411 }
412
413
414 ngx_int_t
415 ngx_quic_handle_migration(ngx_connection_t *c, ngx_quic_header_t *pkt)
416 {
417 ngx_quic_path_t *next;
418 ngx_quic_socket_t *qsock;
419 ngx_quic_send_ctx_t *ctx;
420 ngx_quic_connection_t *qc;
421
422 /* got non-probing packet via non-active socket with different path */
423
424 qc = ngx_quic_get_connection(c);
425
426 /* current socket, different from active */
427 qsock = ngx_quic_get_socket(c);
428
429 next = qsock->path; /* going to migrate to this path... */
430
431 ngx_log_error(NGX_LOG_INFO, c->log, 0,
432 "quic migration from #%uL:%uL:%uL (%s)"
433 " to #%uL:%uL:%uL (%s)",
434 qc->socket->sid.seqnum, qc->socket->cid->seqnum,
435 qc->socket->path->seqnum,
436 ngx_quic_path_state_str(qc->socket->path),
437 qsock->sid.seqnum, qsock->cid->seqnum, next->seqnum,
438 ngx_quic_path_state_str(next));
439
440 switch (next->state) {
441 case NGX_QUIC_PATH_NEW:
442 if (ngx_quic_validate_path(c, qsock) != NGX_OK) {
443 return NGX_ERROR;
444 }
445 break;
446
447 /* migration to previously known path */
448
449 case NGX_QUIC_PATH_VALIDATING:
450 /* alredy validating, nothing to do */
451 break;
452
453 case NGX_QUIC_PATH_VALIDATED:
454 /* if path is old enough, revalidate */
455 if (ngx_time() - next->validated_at > NGX_QUIC_PATH_VALID_TIME) {
456
457 next->state = NGX_QUIC_PATH_NEW;
458
459 if (ngx_quic_validate_path(c, qsock) != NGX_OK) {
460 return NGX_ERROR;
461 }
462 }
463
464 break;
465 }
466
467 ctx = ngx_quic_get_send_ctx(qc, pkt->level);
468
469 /*
470 * An endpoint only changes the address to which it sends packets in
471 * response to the highest-numbered non-probing packet.
472 */
473 if (pkt->pn != ctx->largest_pn) {
474 return NGX_OK;
475 }
476
477 /* switching connection to new path */
478
479 ngx_quic_set_connection_path(c, next);
480
481 /*
482 * An endpoint MUST NOT reuse a connection ID when sending to
483 * more than one destination address.
484 */
485
486 /* preserve valid path we are migrating from */
487 if (qc->socket->path->state == NGX_QUIC_PATH_VALIDATED) {
488
489 if (qc->backup) {
490 ngx_quic_close_socket(c, qc->backup);
491 }
492
493 qc->backup = qc->socket;
494
495 ngx_log_error(NGX_LOG_INFO, c->log, 0,
496 "quic backup socket is now #%uL:%uL:%uL (%s)",
497 qc->backup->sid.seqnum, qc->backup->cid->seqnum,
498 qc->backup->path->seqnum,
499 ngx_quic_path_state_str(qc->backup->path));
500 }
501
502 qc->socket = qsock;
503
504 ngx_log_error(NGX_LOG_INFO, c->log, 0,
505 "quic active socket is now #%uL:%uL:%uL (%s)",
506 qsock->sid.seqnum, qsock->cid->seqnum,
507 qsock->path->seqnum, ngx_quic_path_state_str(qsock->path));
508
509 return NGX_OK;
510 }
511
512
513 static ngx_int_t
514 ngx_quic_validate_path(ngx_connection_t *c, ngx_quic_socket_t *qsock)
515 {
516 ngx_msec_t pto;
517 ngx_quic_path_t *path;
518 ngx_quic_send_ctx_t *ctx;
519 ngx_quic_connection_t *qc;
520
521 qc = ngx_quic_get_connection(c);
522
523 path = qsock->path;
524
525 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
526 "quic initiated validation of new path #%uL",
527 path->seqnum);
528
529 path->state = NGX_QUIC_PATH_VALIDATING;
530
531 if (RAND_bytes(path->challenge1, 8) != 1) {
532 return NGX_ERROR;
533 }
534
535 if (RAND_bytes(path->challenge2, 8) != 1) {
536 return NGX_ERROR;
537 }
538
539 if (ngx_quic_send_path_challenge(c, path) != NGX_OK) {
540 return NGX_ERROR;
541 }
542
543 ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
544 pto = ngx_quic_pto(c, ctx);
545
546 path->expires = ngx_current_msec + pto;
547 path->tries = NGX_QUIC_PATH_RETRIES;
548
549 if (!qc->path_validation.timer_set) {
550 ngx_add_timer(&qc->path_validation, pto);
551 }
552
553
554 return NGX_OK;
555 }
556
557
558 static ngx_int_t
559 ngx_quic_send_path_challenge(ngx_connection_t *c, ngx_quic_path_t *path)
560 {
561 off_t max, pad;
562 ssize_t sent;
563 ngx_quic_frame_t frame;
564
565 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
566 "quic path #%uL send path challenge tries:%ui",
567 path->seqnum, path->tries);
568
569 frame.level = ssl_encryption_application;
570 frame.type = NGX_QUIC_FT_PATH_CHALLENGE;
571
572 ngx_memcpy(frame.u.path_challenge.data, path->challenge1, 8);
573
574 /*
575 * An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame
576 * to at least the smallest allowed maximum datagram size of 1200 bytes,
577 * unless the anti-amplification limit for the path does not permit
578 * sending a datagram of this size.
579 */
580
581 /* same applies to PATH_RESPONSE frames */
582
583 max = path->received * 3;
584 max = (path->sent >= max) ? 0 : max - path->sent;
585 pad = ngx_min(1200, max);
586
587 sent = ngx_quic_frame_sendto(c, &frame, pad, path->sockaddr, path->socklen);
588 if (sent == -1) {
589 return NGX_ERROR;
590 }
591
592 path->sent += sent;
593
594 ngx_memcpy(frame.u.path_challenge.data, path->challenge2, 8);
595
596 max = (path->sent >= max) ? 0 : max - path->sent;
597 pad = ngx_min(1200, max);
598
599 sent = ngx_quic_frame_sendto(c, &frame, pad, path->sockaddr, path->socklen);
600 if (sent == -1) {
601 return NGX_ERROR;
602 }
603
604 path->sent += sent;
605
606 return NGX_OK;
607 }
608
609
610 void
611 ngx_quic_path_validation_handler(ngx_event_t *ev)
612 {
613 ngx_msec_t now;
614 ngx_queue_t *q;
615 ngx_msec_int_t left, next, pto;
616 ngx_quic_path_t *path;
617 ngx_connection_t *c;
618 ngx_quic_send_ctx_t *ctx;
619 ngx_quic_connection_t *qc;
620
621 c = ev->data;
622 qc = ngx_quic_get_connection(c);
623
624 ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
625 pto = ngx_quic_pto(c, ctx);
626
627 next = -1;
628 now = ngx_current_msec;
629
630 for (q = ngx_queue_head(&qc->paths);
631 q != ngx_queue_sentinel(&qc->paths);
632 q = ngx_queue_next(q))
633 {
634 path = ngx_queue_data(q, ngx_quic_path_t, queue);
635
636 if (path->state != NGX_QUIC_PATH_VALIDATING) {
637 continue;
638 }
639
640 left = path->expires - now;
641
642 if (left > 0) {
643
644 if (next == -1 || left < next) {
645 next = path->expires;
646 }
647
648 continue;
649 }
650
651 if (--path->tries) {
652 path->expires = ngx_current_msec + pto;
653
654 if (next == -1 || pto < next) {
655 next = pto;
656 }
657
658 /* retransmit */
659 (void) ngx_quic_send_path_challenge(c, path);
660
661 continue;
662 }
663
664 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,
665 "quic path #%uL validation failed", path->seqnum);
666
667 /* found expired path */
668
669 path->state = NGX_QUIC_PATH_NEW;
670
671 /*
672 * If the timer fires before the PATH_RESPONSE is received, the
673 * endpoint might send a new PATH_CHALLENGE, and restart the timer for
674 * a longer period of time. This timer SHOULD be set as described in
675 * Section 6.2.1 of [QUIC-RECOVERY] and MUST NOT be more aggressive.
676 */
677
678 if (qc->socket->path != path) {
679 /* the path was not actually used */
680 continue;
681 }
682
683 if (ngx_quic_path_restore(c) != NGX_OK) {
684 qc->error = NGX_QUIC_ERR_NO_VIABLE_PATH;
685 qc->error_reason = "no viable path";
686 ngx_quic_close_connection(c, NGX_ERROR);
687 return;
688 }
689 }
690
691 if (next != -1) {
692 ngx_add_timer(&qc->path_validation, next);
693 }
694 }
695
696
697 static ngx_int_t
698 ngx_quic_path_restore(ngx_connection_t *c)
699 {
700 ngx_quic_socket_t *qsock;
701 ngx_quic_connection_t *qc;
702
703 qc = ngx_quic_get_connection(c);
704
705 /* Failure to validate a path does not cause the connection to end */
706
707 /*
708 * To protect the connection from failing due to such a spurious
709 * migration, an endpoint MUST revert to using the last validated
710 * peer address when validation of a new peer address fails.
711 */
712
713 if (qc->backup == NULL) {
714 return NGX_ERROR;
715 }
716
717 qc->socket = qc->backup;
718 qc->backup = NULL;
719
720 qsock = qc->socket;
721
722 ngx_log_error(NGX_LOG_INFO, c->log, 0,
723 "quic active socket is restored to #%uL:%uL:%uL"
724 " (%s), no backup",
725 qsock->sid.seqnum, qsock->cid->seqnum, qsock->path->seqnum,
726 ngx_quic_path_state_str(qsock->path));
727
728 ngx_quic_set_connection_path(c, qsock->path);
729
730 return NGX_OK;
731 }