Mercurial > hg > nginx
comparison src/event/quic/ngx_event_quic_migration.c @ 9147:58afcd72446f
QUIC: path MTU discovery.
MTU selection starts by doubling the initial MTU until the first failure.
Then binary search is used to find the path MTU.
author | Roman Arutyunyan <arut@nginx.com> |
---|---|
date | Mon, 14 Aug 2023 09:21:27 +0400 |
parents | f3412ec3b6d1 |
children | f6b6f3dd7ca0 |
comparison
equal
deleted
inserted
replaced
9146:f3412ec3b6d1 | 9147:58afcd72446f |
---|---|
6 | 6 |
7 #include <ngx_config.h> | 7 #include <ngx_config.h> |
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 | |
12 | |
13 #define NGX_QUIC_PATH_MTU_DELAY 100 | |
14 #define NGX_QUIC_PATH_MTU_PRECISION 16 | |
11 | 15 |
12 | 16 |
13 static void ngx_quic_set_connection_path(ngx_connection_t *c, | 17 static void ngx_quic_set_connection_path(ngx_connection_t *c, |
14 ngx_quic_path_t *path); | 18 ngx_quic_path_t *path); |
15 static ngx_int_t ngx_quic_validate_path(ngx_connection_t *c, | 19 static ngx_int_t ngx_quic_validate_path(ngx_connection_t *c, |
16 ngx_quic_path_t *path); | 20 ngx_quic_path_t *path); |
17 static ngx_int_t ngx_quic_send_path_challenge(ngx_connection_t *c, | 21 static ngx_int_t ngx_quic_send_path_challenge(ngx_connection_t *c, |
18 ngx_quic_path_t *path); | 22 ngx_quic_path_t *path); |
19 static void ngx_quic_set_path_timer(ngx_connection_t *c); | 23 static void ngx_quic_set_path_timer(ngx_connection_t *c); |
24 static ngx_int_t ngx_quic_expire_path_validation(ngx_connection_t *c, | |
25 ngx_quic_path_t *path); | |
26 static ngx_int_t ngx_quic_expire_path_mtu_delay(ngx_connection_t *c, | |
27 ngx_quic_path_t *path); | |
28 static ngx_int_t ngx_quic_expire_path_mtu_discovery(ngx_connection_t *c, | |
29 ngx_quic_path_t *path); | |
20 static ngx_quic_path_t *ngx_quic_get_path(ngx_connection_t *c, ngx_uint_t tag); | 30 static ngx_quic_path_t *ngx_quic_get_path(ngx_connection_t *c, ngx_uint_t tag); |
31 static ngx_int_t ngx_quic_send_path_mtu_probe(ngx_connection_t *c, | |
32 ngx_quic_path_t *path); | |
21 | 33 |
22 | 34 |
23 ngx_int_t | 35 ngx_int_t |
24 ngx_quic_handle_path_challenge_frame(ngx_connection_t *c, | 36 ngx_quic_handle_path_challenge_frame(ngx_connection_t *c, |
25 ngx_quic_header_t *pkt, ngx_quic_path_challenge_frame_t *f) | 37 ngx_quic_header_t *pkt, ngx_quic_path_challenge_frame_t *f) |
95 q != ngx_queue_sentinel(&qc->paths); | 107 q != ngx_queue_sentinel(&qc->paths); |
96 q = ngx_queue_next(q)) | 108 q = ngx_queue_next(q)) |
97 { | 109 { |
98 path = ngx_queue_data(q, ngx_quic_path_t, queue); | 110 path = ngx_queue_data(q, ngx_quic_path_t, queue); |
99 | 111 |
100 if (!path->validating) { | 112 if (path->state != NGX_QUIC_PATH_VALIDATING) { |
101 continue; | 113 continue; |
102 } | 114 } |
103 | 115 |
104 if (ngx_memcmp(path->challenge1, f->data, sizeof(f->data)) == 0 | 116 if (ngx_memcmp(path->challenge1, f->data, sizeof(f->data)) == 0 |
105 || ngx_memcmp(path->challenge2, f->data, sizeof(f->data)) == 0) | 117 || ngx_memcmp(path->challenge2, f->data, sizeof(f->data)) == 0) |
134 path->sockaddr, path->socklen, 0) | 146 path->sockaddr, path->socklen, 0) |
135 == NGX_OK) | 147 == NGX_OK) |
136 { | 148 { |
137 /* address did not change */ | 149 /* address did not change */ |
138 rst = 0; | 150 rst = 0; |
151 | |
152 path->mtu = prev->mtu; | |
153 path->max_mtu = prev->max_mtu; | |
139 } | 154 } |
140 } | 155 } |
141 | 156 |
142 if (rst) { | 157 if (rst) { |
143 ngx_memzero(&qc->congestion, sizeof(ngx_quic_congestion_t)); | 158 ngx_memzero(&qc->congestion, sizeof(ngx_quic_congestion_t)); |
165 path->seqnum, &path->addr_text); | 180 path->seqnum, &path->addr_text); |
166 | 181 |
167 ngx_quic_path_dbg(c, "is validated", path); | 182 ngx_quic_path_dbg(c, "is validated", path); |
168 | 183 |
169 path->validated = 1; | 184 path->validated = 1; |
170 path->validating = 0; | 185 |
171 | 186 ngx_quic_discover_path_mtu(c, path); |
172 ngx_quic_set_path_timer(c); | |
173 | 187 |
174 return NGX_OK; | 188 return NGX_OK; |
175 } | 189 } |
176 | 190 |
177 | 191 |
214 ngx_memcpy(path->sockaddr, sockaddr, socklen); | 228 ngx_memcpy(path->sockaddr, sockaddr, socklen); |
215 | 229 |
216 path->addr_text.data = path->text; | 230 path->addr_text.data = path->text; |
217 path->addr_text.len = ngx_sock_ntop(sockaddr, socklen, path->text, | 231 path->addr_text.len = ngx_sock_ntop(sockaddr, socklen, path->text, |
218 NGX_SOCKADDR_STRLEN, 1); | 232 NGX_SOCKADDR_STRLEN, 1); |
233 | |
234 path->mtu = NGX_QUIC_MIN_INITIAL_SIZE; | |
219 | 235 |
220 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | 236 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, |
221 "quic path seq:%uL created addr:%V", | 237 "quic path seq:%uL created addr:%V", |
222 path->seqnum, &path->addr_text); | 238 path->seqnum, &path->addr_text); |
223 return path; | 239 return path; |
462 qc->path = next; | 478 qc->path = next; |
463 qc->path->tag = NGX_QUIC_PATH_ACTIVE; | 479 qc->path->tag = NGX_QUIC_PATH_ACTIVE; |
464 | 480 |
465 ngx_quic_set_connection_path(c, next); | 481 ngx_quic_set_connection_path(c, next); |
466 | 482 |
467 if (!next->validated && !next->validating) { | 483 if (!next->validated && next->state != NGX_QUIC_PATH_VALIDATING) { |
468 if (ngx_quic_validate_path(c, next) != NGX_OK) { | 484 if (ngx_quic_validate_path(c, next) != NGX_OK) { |
469 return NGX_ERROR; | 485 return NGX_ERROR; |
470 } | 486 } |
471 } | 487 } |
472 | 488 |
490 qc = ngx_quic_get_connection(c); | 506 qc = ngx_quic_get_connection(c); |
491 | 507 |
492 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, | 508 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, |
493 "quic initiated validation of path seq:%uL", path->seqnum); | 509 "quic initiated validation of path seq:%uL", path->seqnum); |
494 | 510 |
495 path->validating = 1; | |
496 path->tries = 0; | 511 path->tries = 0; |
497 | 512 |
498 if (RAND_bytes(path->challenge1, 8) != 1) { | 513 if (RAND_bytes(path->challenge1, 8) != 1) { |
499 return NGX_ERROR; | 514 return NGX_ERROR; |
500 } | 515 } |
509 | 524 |
510 ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application); | 525 ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application); |
511 pto = ngx_max(ngx_quic_pto(c, ctx), 1000); | 526 pto = ngx_max(ngx_quic_pto(c, ctx), 1000); |
512 | 527 |
513 path->expires = ngx_current_msec + pto; | 528 path->expires = ngx_current_msec + pto; |
529 path->state = NGX_QUIC_PATH_VALIDATING; | |
514 | 530 |
515 ngx_quic_set_path_timer(c); | 531 ngx_quic_set_path_timer(c); |
516 | 532 |
517 return NGX_OK; | 533 return NGX_OK; |
518 } | 534 } |
556 | 572 |
557 return NGX_OK; | 573 return NGX_OK; |
558 } | 574 } |
559 | 575 |
560 | 576 |
577 void | |
578 ngx_quic_discover_path_mtu(ngx_connection_t *c, ngx_quic_path_t *path) | |
579 { | |
580 ngx_quic_connection_t *qc; | |
581 | |
582 qc = ngx_quic_get_connection(c); | |
583 | |
584 if (path->max_mtu) { | |
585 if (path->max_mtu - path->mtu <= NGX_QUIC_PATH_MTU_PRECISION) { | |
586 path->state = NGX_QUIC_PATH_IDLE; | |
587 ngx_quic_set_path_timer(c); | |
588 return; | |
589 } | |
590 | |
591 path->mtud = (path->mtu + path->max_mtu) / 2; | |
592 | |
593 } else { | |
594 path->mtud = path->mtu * 2; | |
595 | |
596 if (path->mtud >= qc->ctp.max_udp_payload_size) { | |
597 path->mtud = qc->ctp.max_udp_payload_size; | |
598 path->max_mtu = qc->ctp.max_udp_payload_size; | |
599 } | |
600 } | |
601 | |
602 path->state = NGX_QUIC_PATH_WAITING; | |
603 path->expires = ngx_current_msec + NGX_QUIC_PATH_MTU_DELAY; | |
604 | |
605 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
606 "quic path seq:%uL schedule mtu:%uz", | |
607 path->seqnum, path->mtud); | |
608 | |
609 ngx_quic_set_path_timer(c); | |
610 } | |
611 | |
612 | |
561 static void | 613 static void |
562 ngx_quic_set_path_timer(ngx_connection_t *c) | 614 ngx_quic_set_path_timer(ngx_connection_t *c) |
563 { | 615 { |
564 ngx_msec_t now; | 616 ngx_msec_t now; |
565 ngx_queue_t *q; | 617 ngx_queue_t *q; |
576 q != ngx_queue_sentinel(&qc->paths); | 628 q != ngx_queue_sentinel(&qc->paths); |
577 q = ngx_queue_next(q)) | 629 q = ngx_queue_next(q)) |
578 { | 630 { |
579 path = ngx_queue_data(q, ngx_quic_path_t, queue); | 631 path = ngx_queue_data(q, ngx_quic_path_t, queue); |
580 | 632 |
581 if (!path->validating) { | 633 if (path->state == NGX_QUIC_PATH_IDLE) { |
582 continue; | 634 continue; |
583 } | 635 } |
584 | 636 |
585 left = path->expires - now; | 637 left = path->expires - now; |
586 left = ngx_max(left, 1); | 638 left = ngx_max(left, 1); |
598 } | 650 } |
599 } | 651 } |
600 | 652 |
601 | 653 |
602 void | 654 void |
603 ngx_quic_path_validation_handler(ngx_event_t *ev) | 655 ngx_quic_path_handler(ngx_event_t *ev) |
604 { | 656 { |
605 ngx_msec_t now; | 657 ngx_msec_t now; |
606 ngx_queue_t *q; | 658 ngx_queue_t *q; |
607 ngx_msec_int_t left, next, pto; | 659 ngx_msec_int_t left; |
608 ngx_quic_path_t *path, *bkp; | 660 ngx_quic_path_t *path; |
609 ngx_connection_t *c; | 661 ngx_connection_t *c; |
610 ngx_quic_send_ctx_t *ctx; | |
611 ngx_quic_connection_t *qc; | 662 ngx_quic_connection_t *qc; |
612 | 663 |
613 c = ev->data; | 664 c = ev->data; |
614 qc = ngx_quic_get_connection(c); | 665 qc = ngx_quic_get_connection(c); |
615 | 666 |
616 ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application); | |
617 | |
618 next = -1; | |
619 now = ngx_current_msec; | 667 now = ngx_current_msec; |
620 | 668 |
621 q = ngx_queue_head(&qc->paths); | 669 q = ngx_queue_head(&qc->paths); |
622 | 670 |
623 while (q != ngx_queue_sentinel(&qc->paths)) { | 671 while (q != ngx_queue_sentinel(&qc->paths)) { |
624 | 672 |
625 path = ngx_queue_data(q, ngx_quic_path_t, queue); | 673 path = ngx_queue_data(q, ngx_quic_path_t, queue); |
626 q = ngx_queue_next(q); | 674 q = ngx_queue_next(q); |
627 | 675 |
628 if (!path->validating) { | 676 if (path->state == NGX_QUIC_PATH_IDLE) { |
629 continue; | 677 continue; |
630 } | 678 } |
631 | 679 |
632 left = path->expires - now; | 680 left = path->expires - now; |
633 | 681 |
634 if (left > 0) { | 682 if (left > 0) { |
635 | 683 continue; |
636 if (next == -1 || left < next) { | 684 } |
637 next = left; | 685 |
686 switch (path->state) { | |
687 case NGX_QUIC_PATH_VALIDATING: | |
688 if (ngx_quic_expire_path_validation(c, path) != NGX_OK) { | |
689 goto failed; | |
638 } | 690 } |
639 | 691 |
692 break; | |
693 | |
694 case NGX_QUIC_PATH_WAITING: | |
695 if (ngx_quic_expire_path_mtu_delay(c, path) != NGX_OK) { | |
696 goto failed; | |
697 } | |
698 | |
699 break; | |
700 | |
701 case NGX_QUIC_PATH_MTUD: | |
702 if (ngx_quic_expire_path_mtu_discovery(c, path) != NGX_OK) { | |
703 goto failed; | |
704 } | |
705 | |
706 break; | |
707 | |
708 default: | |
709 break; | |
710 } | |
711 } | |
712 | |
713 ngx_quic_set_path_timer(c); | |
714 | |
715 return; | |
716 | |
717 failed: | |
718 | |
719 ngx_quic_close_connection(c, NGX_ERROR); | |
720 } | |
721 | |
722 | |
723 static ngx_int_t | |
724 ngx_quic_expire_path_validation(ngx_connection_t *c, ngx_quic_path_t *path) | |
725 { | |
726 ngx_msec_int_t pto; | |
727 ngx_quic_path_t *bkp; | |
728 ngx_quic_send_ctx_t *ctx; | |
729 ngx_quic_connection_t *qc; | |
730 | |
731 qc = ngx_quic_get_connection(c); | |
732 ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application); | |
733 | |
734 if (++path->tries < NGX_QUIC_PATH_RETRIES) { | |
735 pto = ngx_max(ngx_quic_pto(c, ctx), 1000) << path->tries; | |
736 path->expires = ngx_current_msec + pto; | |
737 | |
738 (void) ngx_quic_send_path_challenge(c, path); | |
739 | |
740 return NGX_OK; | |
741 } | |
742 | |
743 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
744 "quic path seq:%uL validation failed", path->seqnum); | |
745 | |
746 /* found expired path */ | |
747 | |
748 path->validated = 0; | |
749 | |
750 | |
751 /* RFC 9000, 9.3.2. On-Path Address Spoofing | |
752 * | |
753 * To protect the connection from failing due to such a spurious | |
754 * migration, an endpoint MUST revert to using the last validated | |
755 * peer address when validation of a new peer address fails. | |
756 */ | |
757 | |
758 if (qc->path == path) { | |
759 /* active path validation failed */ | |
760 | |
761 bkp = ngx_quic_get_path(c, NGX_QUIC_PATH_BACKUP); | |
762 | |
763 if (bkp == NULL) { | |
764 qc->error = NGX_QUIC_ERR_NO_VIABLE_PATH; | |
765 qc->error_reason = "no viable path"; | |
766 return NGX_ERROR; | |
767 } | |
768 | |
769 qc->path = bkp; | |
770 qc->path->tag = NGX_QUIC_PATH_ACTIVE; | |
771 | |
772 ngx_quic_set_connection_path(c, qc->path); | |
773 | |
774 ngx_log_error(NGX_LOG_INFO, c->log, 0, | |
775 "quic path seq:%uL addr:%V is restored from backup", | |
776 qc->path->seqnum, &qc->path->addr_text); | |
777 | |
778 ngx_quic_path_dbg(c, "is active", qc->path); | |
779 } | |
780 | |
781 return ngx_quic_free_path(c, path); | |
782 } | |
783 | |
784 | |
785 static ngx_int_t | |
786 ngx_quic_expire_path_mtu_delay(ngx_connection_t *c, ngx_quic_path_t *path) | |
787 { | |
788 ngx_int_t rc; | |
789 ngx_uint_t i; | |
790 ngx_msec_t pto; | |
791 ngx_quic_send_ctx_t *ctx; | |
792 ngx_quic_connection_t *qc; | |
793 | |
794 qc = ngx_quic_get_connection(c); | |
795 ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application); | |
796 | |
797 path->tries = 0; | |
798 | |
799 for ( ;; ) { | |
800 | |
801 for (i = 0; i < NGX_QUIC_PATH_RETRIES; i++) { | |
802 path->mtu_pnum[i] = NGX_QUIC_UNSET_PN; | |
803 } | |
804 | |
805 rc = ngx_quic_send_path_mtu_probe(c, path); | |
806 | |
807 if (rc == NGX_ERROR) { | |
808 return NGX_ERROR; | |
809 } | |
810 | |
811 if (rc == NGX_OK) { | |
812 pto = ngx_quic_pto(c, ctx); | |
813 path->expires = ngx_current_msec + pto; | |
814 path->state = NGX_QUIC_PATH_MTUD; | |
815 return NGX_OK; | |
816 } | |
817 | |
818 /* rc == NGX_DECLINED */ | |
819 | |
820 path->max_mtu = path->mtud; | |
821 | |
822 if (path->max_mtu - path->mtu <= NGX_QUIC_PATH_MTU_PRECISION) { | |
823 path->state = NGX_QUIC_PATH_IDLE; | |
824 return NGX_OK; | |
825 } | |
826 | |
827 path->mtud = (path->mtu + path->max_mtu) / 2; | |
828 } | |
829 } | |
830 | |
831 | |
832 static ngx_int_t | |
833 ngx_quic_expire_path_mtu_discovery(ngx_connection_t *c, ngx_quic_path_t *path) | |
834 { | |
835 ngx_int_t rc; | |
836 ngx_msec_int_t pto; | |
837 ngx_quic_send_ctx_t *ctx; | |
838 ngx_quic_connection_t *qc; | |
839 | |
840 qc = ngx_quic_get_connection(c); | |
841 ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application); | |
842 | |
843 if (++path->tries < NGX_QUIC_PATH_RETRIES) { | |
844 rc = ngx_quic_send_path_mtu_probe(c, path); | |
845 | |
846 if (rc == NGX_ERROR) { | |
847 return NGX_ERROR; | |
848 } | |
849 | |
850 if (rc == NGX_OK) { | |
851 pto = ngx_quic_pto(c, ctx) << path->tries; | |
852 path->expires = ngx_current_msec + pto; | |
853 return NGX_OK; | |
854 } | |
855 | |
856 /* rc == NGX_DECLINED */ | |
857 } | |
858 | |
859 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
860 "quic path seq:%uL expired mtu:%uz", | |
861 path->seqnum, path->mtud); | |
862 | |
863 path->max_mtu = path->mtud; | |
864 | |
865 ngx_quic_discover_path_mtu(c, path); | |
866 | |
867 return NGX_OK; | |
868 } | |
869 | |
870 | |
871 static ngx_int_t | |
872 ngx_quic_send_path_mtu_probe(ngx_connection_t *c, ngx_quic_path_t *path) | |
873 { | |
874 ngx_int_t rc; | |
875 ngx_uint_t log_error; | |
876 ngx_quic_frame_t frame; | |
877 ngx_quic_send_ctx_t *ctx; | |
878 ngx_quic_connection_t *qc; | |
879 | |
880 ngx_memzero(&frame, sizeof(ngx_quic_frame_t)); | |
881 | |
882 frame.level = ssl_encryption_application; | |
883 frame.type = NGX_QUIC_FT_PING; | |
884 | |
885 qc = ngx_quic_get_connection(c); | |
886 ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application); | |
887 path->mtu_pnum[path->tries] = ctx->pnum; | |
888 | |
889 ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
890 "quic path seq:%uL send probe " | |
891 "mtu:%uz pnum:%uL tries:%ui", | |
892 path->seqnum, path->mtud, ctx->pnum, path->tries); | |
893 | |
894 log_error = c->log_error; | |
895 c->log_error = NGX_ERROR_IGNORE_EMSGSIZE; | |
896 | |
897 rc = ngx_quic_frame_sendto(c, &frame, path->mtud, path); | |
898 c->log_error = log_error; | |
899 | |
900 if (rc == NGX_ERROR) { | |
901 if (c->write->error) { | |
902 c->write->error = 0; | |
903 | |
904 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
905 "quic path seq:%uL rejected mtu:%uz", | |
906 path->seqnum, path->mtud); | |
907 | |
908 return NGX_DECLINED; | |
909 } | |
910 | |
911 return NGX_ERROR; | |
912 } | |
913 | |
914 return NGX_OK; | |
915 } | |
916 | |
917 | |
918 ngx_int_t | |
919 ngx_quic_handle_path_mtu(ngx_connection_t *c, ngx_quic_path_t *path, | |
920 uint64_t min, uint64_t max) | |
921 { | |
922 uint64_t pnum; | |
923 ngx_uint_t i; | |
924 | |
925 if (path->state != NGX_QUIC_PATH_MTUD) { | |
926 return NGX_OK; | |
927 } | |
928 | |
929 for (i = 0; i < NGX_QUIC_PATH_RETRIES; i++) { | |
930 pnum = path->mtu_pnum[i]; | |
931 | |
932 if (pnum == NGX_QUIC_UNSET_PN) { | |
933 break; | |
934 } | |
935 | |
936 if (pnum < min || pnum > max) { | |
640 continue; | 937 continue; |
641 } | 938 } |
642 | 939 |
643 if (++path->tries < NGX_QUIC_PATH_RETRIES) { | 940 path->mtu = path->mtud; |
644 pto = ngx_max(ngx_quic_pto(c, ctx), 1000) << path->tries; | 941 |
645 | 942 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, |
646 path->expires = ngx_current_msec + pto; | 943 "quic path seq:%uL ack mtu:%uz", |
647 | 944 path->seqnum, path->mtu); |
648 if (next == -1 || pto < next) { | 945 |
649 next = pto; | 946 ngx_quic_discover_path_mtu(c, path); |
650 } | 947 |
651 | 948 break; |
652 /* retransmit */ | 949 } |
653 (void) ngx_quic_send_path_challenge(c, path); | 950 |
654 | 951 return NGX_OK; |
655 continue; | 952 } |
656 } | |
657 | |
658 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, | |
659 "quic path seq:%uL validation failed", path->seqnum); | |
660 | |
661 /* found expired path */ | |
662 | |
663 path->validated = 0; | |
664 path->validating = 0; | |
665 | |
666 | |
667 /* RFC 9000, 9.3.2. On-Path Address Spoofing | |
668 * | |
669 * To protect the connection from failing due to such a spurious | |
670 * migration, an endpoint MUST revert to using the last validated | |
671 * peer address when validation of a new peer address fails. | |
672 */ | |
673 | |
674 if (qc->path == path) { | |
675 /* active path validation failed */ | |
676 | |
677 bkp = ngx_quic_get_path(c, NGX_QUIC_PATH_BACKUP); | |
678 | |
679 if (bkp == NULL) { | |
680 qc->error = NGX_QUIC_ERR_NO_VIABLE_PATH; | |
681 qc->error_reason = "no viable path"; | |
682 ngx_quic_close_connection(c, NGX_ERROR); | |
683 return; | |
684 } | |
685 | |
686 qc->path = bkp; | |
687 qc->path->tag = NGX_QUIC_PATH_ACTIVE; | |
688 | |
689 ngx_quic_set_connection_path(c, qc->path); | |
690 | |
691 ngx_log_error(NGX_LOG_INFO, c->log, 0, | |
692 "quic path seq:%uL addr:%V is restored from backup", | |
693 qc->path->seqnum, &qc->path->addr_text); | |
694 | |
695 ngx_quic_path_dbg(c, "is active", qc->path); | |
696 } | |
697 | |
698 if (ngx_quic_free_path(c, path) != NGX_OK) { | |
699 ngx_quic_close_connection(c, NGX_ERROR); | |
700 return; | |
701 } | |
702 } | |
703 | |
704 if (next != -1) { | |
705 ngx_add_timer(&qc->path_validation, next); | |
706 } | |
707 } |