src/event/quic/ngx_event_quic_migration.c - nginx source code

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Nginx, Inc.
  3. */


  4. #include <ngx_config.h>
  5. #include <ngx_core.h>
  6. #include <ngx_event.h>
  7. #include <ngx_event_quic_connection.h>


  8. #define NGX_QUIC_PATH_MTU_DELAY       100
  9. #define NGX_QUIC_PATH_MTU_PRECISION   16


  10. static void ngx_quic_set_connection_path(ngx_connection_t *c,
  11.     ngx_quic_path_t *path);
  12. static ngx_int_t ngx_quic_validate_path(ngx_connection_t *c,
  13.     ngx_quic_path_t *path);
  14. static ngx_int_t ngx_quic_send_path_challenge(ngx_connection_t *c,
  15.     ngx_quic_path_t *path);
  16. static void ngx_quic_set_path_timer(ngx_connection_t *c);
  17. static ngx_int_t ngx_quic_expire_path_validation(ngx_connection_t *c,
  18.     ngx_quic_path_t *path);
  19. static ngx_int_t ngx_quic_expire_path_mtu_delay(ngx_connection_t *c,
  20.     ngx_quic_path_t *path);
  21. static ngx_int_t ngx_quic_expire_path_mtu_discovery(ngx_connection_t *c,
  22.     ngx_quic_path_t *path);
  23. static ngx_quic_path_t *ngx_quic_get_path(ngx_connection_t *c, ngx_uint_t tag);
  24. static ngx_int_t ngx_quic_send_path_mtu_probe(ngx_connection_t *c,
  25.     ngx_quic_path_t *path);


  26. ngx_int_t
  27. ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
  28.     ngx_quic_header_t *pkt, ngx_quic_path_challenge_frame_t *f)
  29. {
  30.     size_t                  min;
  31.     ngx_quic_frame_t       *fp;
  32.     ngx_quic_connection_t  *qc;

  33.     if (pkt->level != ssl_encryption_application || pkt->path_challenged) {
  34.         ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
  35.                        "quic ignoring PATH_CHALLENGE");
  36.         return NGX_OK;
  37.     }

  38.     pkt->path_challenged = 1;

  39.     qc = ngx_quic_get_connection(c);

  40.     fp = ngx_quic_alloc_frame(c);
  41.     if (fp == NULL) {
  42.         return NGX_ERROR;
  43.     }

  44.     fp->level = ssl_encryption_application;
  45.     fp->type = NGX_QUIC_FT_PATH_RESPONSE;
  46.     fp->u.path_response = *f;

  47.     /*
  48.      * RFC 9000, 8.2.2.  Path Validation Responses
  49.      *
  50.      * A PATH_RESPONSE frame MUST be sent on the network path where the
  51.      * PATH_CHALLENGE frame was received.
  52.      */

  53.     /*
  54.      * An endpoint MUST expand datagrams that contain a PATH_RESPONSE frame
  55.      * to at least the smallest allowed maximum datagram size of 1200 bytes.
  56.      * ...
  57.      * However, an endpoint MUST NOT expand the datagram containing the
  58.      * PATH_RESPONSE if the resulting data exceeds the anti-amplification limit.
  59.      */

  60.     min = (ngx_quic_path_limit(c, pkt->path, 1200) < 1200) ? 0 : 1200;

  61.     if (ngx_quic_frame_sendto(c, fp, min, pkt->path) == NGX_ERROR) {
  62.         return NGX_ERROR;
  63.     }

  64.     if (pkt->path == qc->path) {
  65.         /*
  66.          * RFC 9000, 9.3.3.  Off-Path Packet Forwarding
  67.          *
  68.          * An endpoint that receives a PATH_CHALLENGE on an active path SHOULD
  69.          * send a non-probing packet in response.
  70.          */

  71.         fp = ngx_quic_alloc_frame(c);
  72.         if (fp == NULL) {
  73.             return NGX_ERROR;
  74.         }

  75.         fp->level = ssl_encryption_application;
  76.         fp->type = NGX_QUIC_FT_PING;

  77.         ngx_quic_queue_frame(qc, fp);
  78.     }

  79.     return NGX_OK;
  80. }


  81. ngx_int_t
  82. ngx_quic_handle_path_response_frame(ngx_connection_t *c,
  83.     ngx_quic_path_challenge_frame_t *f)
  84. {
  85.     ngx_uint_t              rst;
  86.     ngx_queue_t            *q;
  87.     ngx_quic_path_t        *path, *prev;
  88.     ngx_quic_send_ctx_t    *ctx;
  89.     ngx_quic_connection_t  *qc;

  90.     qc = ngx_quic_get_connection(c);

  91.     /*
  92.      * RFC 9000, 8.2.3.  Successful Path Validation
  93.      *
  94.      * A PATH_RESPONSE frame received on any network path validates the path
  95.      * on which the PATH_CHALLENGE was sent.
  96.      */

  97.     for (q = ngx_queue_head(&qc->paths);
  98.          q != ngx_queue_sentinel(&qc->paths);
  99.          q = ngx_queue_next(q))
  100.     {
  101.         path = ngx_queue_data(q, ngx_quic_path_t, queue);

  102.         if (path->state != NGX_QUIC_PATH_VALIDATING) {
  103.             continue;
  104.         }

  105.         if (ngx_memcmp(path->challenge[0], f->data, sizeof(f->data)) == 0
  106.             || ngx_memcmp(path->challenge[1], f->data, sizeof(f->data)) == 0)
  107.         {
  108.             goto valid;
  109.         }
  110.     }

  111.     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
  112.                    "quic stale PATH_RESPONSE ignored");

  113.     return NGX_OK;

  114. valid:

  115.     /*
  116.      * RFC 9000, 9.4.  Loss Detection and Congestion Control
  117.      *
  118.      * On confirming a peer's ownership of its new address,
  119.      * an endpoint MUST immediately reset the congestion controller
  120.      * and round-trip time estimator for the new path to initial values
  121.      * unless the only change in the peer's address is its port number.
  122.      */

  123.     rst = 1;

  124.     prev = ngx_quic_get_path(c, NGX_QUIC_PATH_BACKUP);

  125.     if (prev != NULL) {

  126.         if (ngx_cmp_sockaddr(prev->sockaddr, prev->socklen,
  127.                              path->sockaddr, path->socklen, 0)
  128.             == NGX_OK)
  129.         {
  130.             /* address did not change */
  131.             rst = 0;

  132.             path->mtu = prev->mtu;
  133.             path->max_mtu = prev->max_mtu;
  134.             path->mtu_unvalidated = 0;
  135.         }
  136.     }

  137.     if (rst) {
  138.         /* prevent old path packets contribution to congestion control */

  139.         ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
  140.         qc->rst_pnum = ctx->pnum;

  141.         ngx_memzero(&qc->congestion, sizeof(ngx_quic_congestion_t));

  142.         qc->congestion.window = ngx_min(10 * qc->tp.max_udp_payload_size,
  143.                                    ngx_max(2 * qc->tp.max_udp_payload_size,
  144.                                            14720));
  145.         qc->congestion.ssthresh = (size_t) -1;
  146.         qc->congestion.recovery_start = ngx_current_msec;

  147.         ngx_quic_init_rtt(qc);
  148.     }

  149.     path->validated = 1;

  150.     if (path->mtu_unvalidated) {
  151.         path->mtu_unvalidated = 0;
  152.         return ngx_quic_validate_path(c, path);
  153.     }

  154.     /*
  155.      * RFC 9000, 9.3.  Responding to Connection Migration
  156.      *
  157.      *  After verifying a new client address, the server SHOULD
  158.      *  send new address validation tokens (Section 8) to the client.
  159.      */

  160.     if (ngx_quic_send_new_token(c, path) != NGX_OK) {
  161.         return NGX_ERROR;
  162.     }

  163.     ngx_log_error(NGX_LOG_INFO, c->log, 0,
  164.                   "quic path seq:%uL addr:%V successfully validated",
  165.                   path->seqnum, &path->addr_text);

  166.     ngx_quic_path_dbg(c, "is validated", path);

  167.     ngx_quic_discover_path_mtu(c, path);

  168.     return NGX_OK;
  169. }


  170. ngx_quic_path_t *
  171. ngx_quic_new_path(ngx_connection_t *c,
  172.     struct sockaddr *sockaddr, socklen_t socklen, ngx_quic_client_id_t *cid)
  173. {
  174.     ngx_queue_t            *q;
  175.     ngx_quic_path_t        *path;
  176.     ngx_quic_connection_t  *qc;

  177.     qc = ngx_quic_get_connection(c);

  178.     if (!ngx_queue_empty(&qc->free_paths)) {

  179.         q = ngx_queue_head(&qc->free_paths);
  180.         path = ngx_queue_data(q, ngx_quic_path_t, queue);

  181.         ngx_queue_remove(&path->queue);

  182.         ngx_memzero(path, sizeof(ngx_quic_path_t));

  183.     } else {

  184.         path = ngx_pcalloc(c->pool, sizeof(ngx_quic_path_t));
  185.         if (path == NULL) {
  186.             return NULL;
  187.         }
  188.     }

  189.     ngx_queue_insert_tail(&qc->paths, &path->queue);

  190.     path->cid = cid;
  191.     cid->used = 1;

  192.     path->seqnum = qc->path_seqnum++;

  193.     path->sockaddr = &path->sa.sockaddr;
  194.     path->socklen = socklen;
  195.     ngx_memcpy(path->sockaddr, sockaddr, socklen);

  196.     path->addr_text.data = path->text;
  197.     path->addr_text.len = ngx_sock_ntop(sockaddr, socklen, path->text,
  198.                                         NGX_SOCKADDR_STRLEN, 1);

  199.     path->mtu = NGX_QUIC_MIN_INITIAL_SIZE;

  200.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  201.                    "quic path seq:%uL created addr:%V",
  202.                    path->seqnum, &path->addr_text);
  203.     return path;
  204. }


  205. static ngx_quic_path_t *
  206. ngx_quic_get_path(ngx_connection_t *c, ngx_uint_t tag)
  207. {
  208.     ngx_queue_t            *q;
  209.     ngx_quic_path_t        *path;
  210.     ngx_quic_connection_t  *qc;

  211.     qc = ngx_quic_get_connection(c);

  212.     for (q = ngx_queue_head(&qc->paths);
  213.          q != ngx_queue_sentinel(&qc->paths);
  214.          q = ngx_queue_next(q))
  215.     {
  216.         path = ngx_queue_data(q, ngx_quic_path_t, queue);

  217.         if (path->tag == tag) {
  218.             return path;
  219.         }
  220.     }

  221.     return NULL;
  222. }


  223. ngx_int_t
  224. ngx_quic_set_path(ngx_connection_t *c, ngx_quic_header_t *pkt)
  225. {
  226.     off_t                   len;
  227.     ngx_queue_t            *q;
  228.     ngx_quic_path_t        *path, *probe;
  229.     ngx_quic_socket_t      *qsock;
  230.     ngx_quic_send_ctx_t    *ctx;
  231.     ngx_quic_client_id_t   *cid;
  232.     ngx_quic_connection_t  *qc;

  233.     qc = ngx_quic_get_connection(c);
  234.     qsock = ngx_quic_get_socket(c);

  235.     len = pkt->raw->last - pkt->raw->start;

  236.     if (c->udp->buffer == NULL) {
  237.         /* first ever packet in connection, path already exists  */
  238.         path = qc->path;
  239.         goto update;
  240.     }

  241.     probe = NULL;

  242.     for (q = ngx_queue_head(&qc->paths);
  243.          q != ngx_queue_sentinel(&qc->paths);
  244.          q = ngx_queue_next(q))
  245.     {
  246.         path = ngx_queue_data(q, ngx_quic_path_t, queue);

  247.         if (ngx_cmp_sockaddr(&qsock->sockaddr.sockaddr, qsock->socklen,
  248.                              path->sockaddr, path->socklen, 1)
  249.             == NGX_OK)
  250.         {
  251.             goto update;
  252.         }

  253.         if (path->tag == NGX_QUIC_PATH_PROBE) {
  254.             probe = path;
  255.         }
  256.     }

  257.     /* packet from new path, drop current probe, if any */

  258.     ctx = ngx_quic_get_send_ctx(qc, pkt->level);

  259.     /*
  260.      * only accept highest-numbered packets to prevent connection id
  261.      * exhaustion by excessive probing packets from unknown paths
  262.      */
  263.     if (pkt->pn != ctx->largest_pn) {
  264.         return NGX_DONE;
  265.     }

  266.     if (probe && ngx_quic_free_path(c, probe) != NGX_OK) {
  267.         return NGX_ERROR;
  268.     }

  269.     /* new path requires new client id */
  270.     cid = ngx_quic_next_client_id(c);
  271.     if (cid == NULL) {
  272.         ngx_log_error(NGX_LOG_INFO, c->log, 0,
  273.                       "quic no available client ids for new path");
  274.         /* stop processing of this datagram */
  275.         return NGX_DONE;
  276.     }

  277.     path = ngx_quic_new_path(c, &qsock->sockaddr.sockaddr, qsock->socklen, cid);
  278.     if (path == NULL) {
  279.         return NGX_ERROR;
  280.     }

  281.     path->tag = NGX_QUIC_PATH_PROBE;

  282.     /*
  283.      * client arrived using new path and previously seen DCID,
  284.      * this indicates NAT rebinding (or bad client)
  285.      */
  286.     if (qsock->used) {
  287.         pkt->rebound = 1;
  288.     }

  289. update:

  290.     qsock->used = 1;
  291.     pkt->path = path;

  292.     /* TODO: this may be too late in some cases;
  293.      *       for example, if error happens during decrypt(), we cannot
  294.      *       send CC, if error happens in 1st packet, due to amplification
  295.      *       limit, because path->received = 0
  296.      *
  297.      *       should we account garbage as received or only decrypting packets?
  298.      */
  299.     path->received += len;

  300.     ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
  301.                    "quic packet len:%O via sock seq:%L path seq:%uL",
  302.                    len, (int64_t) qsock->sid.seqnum, path->seqnum);
  303.     ngx_quic_path_dbg(c, "status", path);

  304.     return NGX_OK;
  305. }


  306. ngx_int_t
  307. ngx_quic_free_path(ngx_connection_t *c, ngx_quic_path_t *path)
  308. {
  309.     ngx_quic_connection_t  *qc;

  310.     qc = ngx_quic_get_connection(c);

  311.     ngx_queue_remove(&path->queue);
  312.     ngx_queue_insert_head(&qc->free_paths, &path->queue);

  313.     /*
  314.      * invalidate CID that is no longer usable for any other path;
  315.      * this also requests new CIDs from client
  316.      */
  317.     if (path->cid) {
  318.         if (ngx_quic_free_client_id(c, path->cid) != NGX_OK) {
  319.             return NGX_ERROR;
  320.         }
  321.     }

  322.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  323.                    "quic path seq:%uL addr:%V retired",
  324.                    path->seqnum, &path->addr_text);

  325.     return NGX_OK;
  326. }


  327. static void
  328. ngx_quic_set_connection_path(ngx_connection_t *c, ngx_quic_path_t *path)
  329. {
  330.     ngx_memcpy(c->sockaddr, path->sockaddr, path->socklen);
  331.     c->socklen = path->socklen;

  332.     if (c->addr_text.data) {
  333.         c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->socklen,
  334.                                          c->addr_text.data,
  335.                                          c->listening->addr_text_max_len, 0);
  336.     }

  337.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  338.                    "quic send path set to seq:%uL addr:%V",
  339.                    path->seqnum, &path->addr_text);
  340. }


  341. ngx_int_t
  342. ngx_quic_handle_migration(ngx_connection_t *c, ngx_quic_header_t *pkt)
  343. {
  344.     ngx_quic_path_t        *next, *bkp;
  345.     ngx_quic_send_ctx_t    *ctx;
  346.     ngx_quic_connection_t  *qc;

  347.     /* got non-probing packet via non-active path */

  348.     qc = ngx_quic_get_connection(c);

  349.     ctx = ngx_quic_get_send_ctx(qc, pkt->level);

  350.     /*
  351.      * RFC 9000, 9.3.  Responding to Connection Migration
  352.      *
  353.      * An endpoint only changes the address to which it sends packets in
  354.      * response to the highest-numbered non-probing packet.
  355.      */
  356.     if (pkt->pn != ctx->largest_pn) {
  357.         return NGX_OK;
  358.     }

  359.     next = pkt->path;

  360.     /*
  361.      * RFC 9000, 9.3.3:
  362.      *
  363.      * In response to an apparent migration, endpoints MUST validate the
  364.      * previously active path using a PATH_CHALLENGE frame.
  365.      */
  366.     if (pkt->rebound) {

  367.         /* NAT rebinding: client uses new path with old SID */
  368.         if (ngx_quic_validate_path(c, qc->path) != NGX_OK) {
  369.             return NGX_ERROR;
  370.         }
  371.     }

  372.     if (qc->path->validated) {

  373.         if (next->tag != NGX_QUIC_PATH_BACKUP) {
  374.             /* can delete backup path, if any */
  375.             bkp = ngx_quic_get_path(c, NGX_QUIC_PATH_BACKUP);

  376.             if (bkp && ngx_quic_free_path(c, bkp) != NGX_OK) {
  377.                 return NGX_ERROR;
  378.             }
  379.         }

  380.         qc->path->tag = NGX_QUIC_PATH_BACKUP;
  381.         ngx_quic_path_dbg(c, "is now backup", qc->path);

  382.     } else {
  383.         if (ngx_quic_free_path(c, qc->path) != NGX_OK) {
  384.             return NGX_ERROR;
  385.         }
  386.     }

  387.     /* switch active path to migrated */
  388.     qc->path = next;
  389.     qc->path->tag = NGX_QUIC_PATH_ACTIVE;

  390.     ngx_quic_set_connection_path(c, next);

  391.     if (!next->validated && next->state != NGX_QUIC_PATH_VALIDATING) {
  392.         if (ngx_quic_validate_path(c, next) != NGX_OK) {
  393.             return NGX_ERROR;
  394.         }
  395.     }

  396.     ngx_log_error(NGX_LOG_INFO, c->log, 0,
  397.                   "quic migrated to path seq:%uL addr:%V",
  398.                   qc->path->seqnum, &qc->path->addr_text);

  399.     ngx_quic_path_dbg(c, "is now active", qc->path);

  400.     return NGX_OK;
  401. }


  402. static ngx_int_t
  403. ngx_quic_validate_path(ngx_connection_t *c, ngx_quic_path_t *path)
  404. {
  405.     ngx_msec_t              pto;
  406.     ngx_quic_send_ctx_t    *ctx;
  407.     ngx_quic_connection_t  *qc;

  408.     qc = ngx_quic_get_connection(c);

  409.     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
  410.                    "quic initiated validation of path seq:%uL", path->seqnum);

  411.     path->tries = 0;

  412.     if (RAND_bytes((u_char *) path->challenge, sizeof(path->challenge)) != 1) {
  413.         return NGX_ERROR;
  414.     }

  415.     (void) ngx_quic_send_path_challenge(c, path);

  416.     ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
  417.     pto = ngx_max(ngx_quic_pto(c, ctx), 1000);

  418.     path->expires = ngx_current_msec + pto;
  419.     path->state = NGX_QUIC_PATH_VALIDATING;

  420.     ngx_quic_set_path_timer(c);

  421.     return NGX_OK;
  422. }


  423. static ngx_int_t
  424. ngx_quic_send_path_challenge(ngx_connection_t *c, ngx_quic_path_t *path)
  425. {
  426.     size_t             min;
  427.     ngx_uint_t         n;
  428.     ngx_quic_frame_t  *frame;

  429.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  430.                    "quic path seq:%uL send path_challenge tries:%ui",
  431.                    path->seqnum, path->tries);

  432.     for (n = 0; n < 2; n++) {

  433.         frame = ngx_quic_alloc_frame(c);
  434.         if (frame == NULL) {
  435.             return NGX_ERROR;
  436.         }

  437.         frame->level = ssl_encryption_application;
  438.         frame->type = NGX_QUIC_FT_PATH_CHALLENGE;

  439.         ngx_memcpy(frame->u.path_challenge.data, path->challenge[n], 8);

  440.         /*
  441.          * RFC 9000, 8.2.1.  Initiating Path Validation
  442.          *
  443.          * An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame
  444.          * to at least the smallest allowed maximum datagram size of 1200 bytes,
  445.          * unless the anti-amplification limit for the path does not permit
  446.          * sending a datagram of this size.
  447.          */

  448.         if (path->mtu_unvalidated
  449.             || ngx_quic_path_limit(c, path, 1200) < 1200)
  450.         {
  451.             min = 0;
  452.             path->mtu_unvalidated = 1;

  453.         } else {
  454.             min = 1200;
  455.         }

  456.         if (ngx_quic_frame_sendto(c, frame, min, path) == NGX_ERROR) {
  457.             return NGX_ERROR;
  458.         }
  459.     }

  460.     return NGX_OK;
  461. }


  462. void
  463. ngx_quic_discover_path_mtu(ngx_connection_t *c, ngx_quic_path_t *path)
  464. {
  465.     ngx_quic_connection_t  *qc;

  466.     qc = ngx_quic_get_connection(c);

  467.     if (path->max_mtu) {
  468.         if (path->max_mtu - path->mtu <= NGX_QUIC_PATH_MTU_PRECISION) {
  469.             path->state = NGX_QUIC_PATH_IDLE;
  470.             ngx_quic_set_path_timer(c);
  471.             return;
  472.         }

  473.         path->mtud = (path->mtu + path->max_mtu) / 2;

  474.     } else {
  475.         path->mtud = path->mtu * 2;

  476.         if (path->mtud >= qc->ctp.max_udp_payload_size) {
  477.             path->mtud = qc->ctp.max_udp_payload_size;
  478.             path->max_mtu = qc->ctp.max_udp_payload_size;
  479.         }
  480.     }

  481.     path->state = NGX_QUIC_PATH_WAITING;
  482.     path->expires = ngx_current_msec + NGX_QUIC_PATH_MTU_DELAY;

  483.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  484.                    "quic path seq:%uL schedule mtu:%uz",
  485.                    path->seqnum, path->mtud);

  486.     ngx_quic_set_path_timer(c);
  487. }


  488. static void
  489. ngx_quic_set_path_timer(ngx_connection_t *c)
  490. {
  491.     ngx_msec_t              now;
  492.     ngx_queue_t            *q;
  493.     ngx_msec_int_t          left, next;
  494.     ngx_quic_path_t        *path;
  495.     ngx_quic_connection_t  *qc;

  496.     qc = ngx_quic_get_connection(c);

  497.     now = ngx_current_msec;
  498.     next = -1;

  499.     for (q = ngx_queue_head(&qc->paths);
  500.          q != ngx_queue_sentinel(&qc->paths);
  501.          q = ngx_queue_next(q))
  502.     {
  503.         path = ngx_queue_data(q, ngx_quic_path_t, queue);

  504.         if (path->state == NGX_QUIC_PATH_IDLE) {
  505.             continue;
  506.         }

  507.         left = path->expires - now;
  508.         left = ngx_max(left, 1);

  509.         if (next == -1 || left < next) {
  510.             next = left;
  511.         }
  512.     }

  513.     if (next != -1) {
  514.         ngx_add_timer(&qc->path_validation, next);

  515.     } else if (qc->path_validation.timer_set) {
  516.         ngx_del_timer(&qc->path_validation);
  517.     }
  518. }


  519. void
  520. ngx_quic_path_handler(ngx_event_t *ev)
  521. {
  522.     ngx_msec_t              now;
  523.     ngx_queue_t            *q;
  524.     ngx_msec_int_t          left;
  525.     ngx_quic_path_t        *path;
  526.     ngx_connection_t       *c;
  527.     ngx_quic_connection_t  *qc;

  528.     c = ev->data;
  529.     qc = ngx_quic_get_connection(c);

  530.     now = ngx_current_msec;

  531.     q = ngx_queue_head(&qc->paths);

  532.     while (q != ngx_queue_sentinel(&qc->paths)) {

  533.         path = ngx_queue_data(q, ngx_quic_path_t, queue);
  534.         q = ngx_queue_next(q);

  535.         if (path->state == NGX_QUIC_PATH_IDLE) {
  536.             continue;
  537.         }

  538.         left = path->expires - now;

  539.         if (left > 0) {
  540.             continue;
  541.         }

  542.         switch (path->state) {
  543.         case NGX_QUIC_PATH_VALIDATING:
  544.             if (ngx_quic_expire_path_validation(c, path) != NGX_OK) {
  545.                 goto failed;
  546.             }

  547.             break;

  548.         case NGX_QUIC_PATH_WAITING:
  549.             if (ngx_quic_expire_path_mtu_delay(c, path) != NGX_OK) {
  550.                 goto failed;
  551.             }

  552.             break;

  553.         case NGX_QUIC_PATH_MTUD:
  554.             if (ngx_quic_expire_path_mtu_discovery(c, path) != NGX_OK) {
  555.                 goto failed;
  556.             }

  557.             break;

  558.         default:
  559.             break;
  560.         }
  561.     }

  562.     ngx_quic_set_path_timer(c);

  563.     return;

  564. failed:

  565.     ngx_quic_close_connection(c, NGX_ERROR);
  566. }


  567. static ngx_int_t
  568. ngx_quic_expire_path_validation(ngx_connection_t *c, ngx_quic_path_t *path)
  569. {
  570.     ngx_msec_int_t          pto;
  571.     ngx_quic_path_t        *bkp;
  572.     ngx_quic_send_ctx_t    *ctx;
  573.     ngx_quic_connection_t  *qc;

  574.     qc = ngx_quic_get_connection(c);
  575.     ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);

  576.     if (++path->tries < NGX_QUIC_PATH_RETRIES) {
  577.         pto = ngx_max(ngx_quic_pto(c, ctx), 1000) << path->tries;
  578.         path->expires = ngx_current_msec + pto;

  579.         (void) ngx_quic_send_path_challenge(c, path);

  580.         return NGX_OK;
  581.     }

  582.     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
  583.                    "quic path seq:%uL validation failed", path->seqnum);

  584.     /* found expired path */

  585.     path->validated = 0;


  586.     /* RFC 9000, 9.3.2.  On-Path Address Spoofing
  587.      *
  588.      * To protect the connection from failing due to such a spurious
  589.      * migration, an endpoint MUST revert to using the last validated
  590.      * peer address when validation of a new peer address fails.
  591.      */

  592.     if (qc->path == path) {
  593.         /* active path validation failed */

  594.         bkp = ngx_quic_get_path(c, NGX_QUIC_PATH_BACKUP);

  595.         if (bkp == NULL) {
  596.             qc->error = NGX_QUIC_ERR_NO_VIABLE_PATH;
  597.             qc->error_reason = "no viable path";
  598.             return NGX_ERROR;
  599.         }

  600.         qc->path = bkp;
  601.         qc->path->tag = NGX_QUIC_PATH_ACTIVE;

  602.         ngx_quic_set_connection_path(c, qc->path);

  603.         ngx_log_error(NGX_LOG_INFO, c->log, 0,
  604.                       "quic path seq:%uL addr:%V is restored from backup",
  605.                       qc->path->seqnum, &qc->path->addr_text);

  606.         ngx_quic_path_dbg(c, "is active", qc->path);
  607.     }

  608.     return ngx_quic_free_path(c, path);
  609. }


  610. static ngx_int_t
  611. ngx_quic_expire_path_mtu_delay(ngx_connection_t *c, ngx_quic_path_t *path)
  612. {
  613.     ngx_int_t               rc;
  614.     ngx_uint_t              i;
  615.     ngx_msec_t              pto;
  616.     ngx_quic_send_ctx_t    *ctx;
  617.     ngx_quic_connection_t  *qc;

  618.     qc = ngx_quic_get_connection(c);
  619.     ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);

  620.     path->tries = 0;

  621.     for ( ;; ) {

  622.         for (i = 0; i < NGX_QUIC_PATH_RETRIES; i++) {
  623.             path->mtu_pnum[i] = NGX_QUIC_UNSET_PN;
  624.         }

  625.         rc = ngx_quic_send_path_mtu_probe(c, path);

  626.         if (rc == NGX_ERROR) {
  627.             return NGX_ERROR;
  628.         }

  629.         if (rc == NGX_OK) {
  630.             pto = ngx_quic_pto(c, ctx);
  631.             path->expires = ngx_current_msec + pto;
  632.             path->state = NGX_QUIC_PATH_MTUD;
  633.             return NGX_OK;
  634.         }

  635.         /* rc == NGX_DECLINED */

  636.         path->max_mtu = path->mtud;

  637.         if (path->max_mtu - path->mtu <= NGX_QUIC_PATH_MTU_PRECISION) {
  638.             path->state = NGX_QUIC_PATH_IDLE;
  639.             return NGX_OK;
  640.         }

  641.         path->mtud = (path->mtu + path->max_mtu) / 2;
  642.     }
  643. }


  644. static ngx_int_t
  645. ngx_quic_expire_path_mtu_discovery(ngx_connection_t *c, ngx_quic_path_t *path)
  646. {
  647.     ngx_int_t               rc;
  648.     ngx_msec_int_t          pto;
  649.     ngx_quic_send_ctx_t    *ctx;
  650.     ngx_quic_connection_t  *qc;

  651.     qc = ngx_quic_get_connection(c);
  652.     ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);

  653.     if (++path->tries < NGX_QUIC_PATH_RETRIES) {
  654.         rc = ngx_quic_send_path_mtu_probe(c, path);

  655.         if (rc == NGX_ERROR) {
  656.             return NGX_ERROR;
  657.         }

  658.         if (rc == NGX_OK) {
  659.             pto = ngx_quic_pto(c, ctx) << path->tries;
  660.             path->expires = ngx_current_msec + pto;
  661.             return NGX_OK;
  662.         }

  663.         /* rc == NGX_DECLINED */
  664.     }

  665.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  666.                    "quic path seq:%uL expired mtu:%uz",
  667.                    path->seqnum, path->mtud);

  668.     path->max_mtu = path->mtud;

  669.     ngx_quic_discover_path_mtu(c, path);

  670.     return NGX_OK;
  671. }


  672. static ngx_int_t
  673. ngx_quic_send_path_mtu_probe(ngx_connection_t *c, ngx_quic_path_t *path)
  674. {
  675.     size_t                  mtu;
  676.     uint64_t                pnum;
  677.     ngx_int_t               rc;
  678.     ngx_uint_t              log_error;
  679.     ngx_quic_frame_t       *frame;
  680.     ngx_quic_send_ctx_t    *ctx;
  681.     ngx_quic_connection_t  *qc;

  682.     frame = ngx_quic_alloc_frame(c);
  683.     if (frame == NULL) {
  684.         return NGX_ERROR;
  685.     }

  686.     frame->level = ssl_encryption_application;
  687.     frame->type = NGX_QUIC_FT_PING;

  688.     qc = ngx_quic_get_connection(c);
  689.     ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
  690.     pnum = ctx->pnum;

  691.     ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
  692.                    "quic path seq:%uL send probe "
  693.                    "mtu:%uz pnum:%uL tries:%ui",
  694.                    path->seqnum, path->mtud, ctx->pnum, path->tries);

  695.     log_error = c->log_error;
  696.     c->log_error = NGX_ERROR_IGNORE_EMSGSIZE;

  697.     mtu = path->mtu;
  698.     path->mtu = path->mtud;

  699.     rc = ngx_quic_frame_sendto(c, frame, path->mtud, path);

  700.     path->mtu = mtu;
  701.     c->log_error = log_error;

  702.     if (rc == NGX_OK) {
  703.         path->mtu_pnum[path->tries] = pnum;
  704.         return NGX_OK;
  705.     }

  706.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  707.                    "quic path seq:%uL rejected mtu:%uz",
  708.                    path->seqnum, path->mtud);

  709.     if (rc == NGX_ERROR) {
  710.         if (c->write->error) {
  711.             c->write->error = 0;
  712.             return NGX_DECLINED;
  713.         }

  714.         return NGX_ERROR;
  715.     }

  716.     return NGX_OK;
  717. }


  718. ngx_int_t
  719. ngx_quic_handle_path_mtu(ngx_connection_t *c, ngx_quic_path_t *path,
  720.     uint64_t min, uint64_t max)
  721. {
  722.     uint64_t    pnum;
  723.     ngx_uint_t  i;

  724.     if (path->state != NGX_QUIC_PATH_MTUD) {
  725.         return NGX_OK;
  726.     }

  727.     for (i = 0; i < NGX_QUIC_PATH_RETRIES; i++) {
  728.         pnum = path->mtu_pnum[i];

  729.         if (pnum == NGX_QUIC_UNSET_PN) {
  730.             continue;
  731.         }

  732.         if (pnum < min || pnum > max) {
  733.             continue;
  734.         }

  735.         path->mtu = path->mtud;

  736.         ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  737.                        "quic path seq:%uL ack mtu:%uz",
  738.                        path->seqnum, path->mtu);

  739.         ngx_quic_discover_path_mtu(c, path);

  740.         break;
  741.     }

  742.     return NGX_OK;
  743. }