src/event/quic/ngx_event_quic_connid.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_MAX_SERVER_IDS   8


  9. #if (NGX_QUIC_BPF)
  10. static ngx_int_t ngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id);
  11. #endif
  12. static ngx_int_t ngx_quic_retire_client_id(ngx_connection_t *c,
  13.     ngx_quic_client_id_t *cid);
  14. static ngx_quic_client_id_t *ngx_quic_alloc_client_id(ngx_connection_t *c,
  15.     ngx_quic_connection_t *qc);
  16. static ngx_int_t ngx_quic_send_server_id(ngx_connection_t *c,
  17.     ngx_quic_server_id_t *sid);


  18. ngx_int_t
  19. ngx_quic_create_server_id(ngx_connection_t *c, u_char *id)
  20. {
  21.     if (RAND_bytes(id, NGX_QUIC_SERVER_CID_LEN) != 1) {
  22.         return NGX_ERROR;
  23.     }

  24. #if (NGX_QUIC_BPF)
  25.     if (ngx_quic_bpf_attach_id(c, id) != NGX_OK) {
  26.         ngx_log_error(NGX_LOG_ERR, c->log, 0,
  27.                       "quic bpf failed to generate socket key");
  28.         /* ignore error, things still may work */
  29.     }
  30. #endif

  31.     return NGX_OK;
  32. }


  33. #if (NGX_QUIC_BPF)

  34. static ngx_int_t
  35. ngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id)
  36. {
  37.     int        fd;
  38.     uint64_t   cookie;
  39.     socklen_t  optlen;

  40.     fd = c->listening->fd;

  41.     optlen = sizeof(cookie);

  42.     if (getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &optlen) == -1) {
  43.         ngx_log_error(NGX_LOG_ERR, c->log, ngx_socket_errno,
  44.                       "quic getsockopt(SO_COOKIE) failed");

  45.         return NGX_ERROR;
  46.     }

  47.     ngx_quic_dcid_encode_key(id, cookie);

  48.     return NGX_OK;
  49. }

  50. #endif


  51. ngx_int_t
  52. ngx_quic_handle_new_connection_id_frame(ngx_connection_t *c,
  53.     ngx_quic_new_conn_id_frame_t *f)
  54. {
  55.     ngx_str_t               id;
  56.     ngx_queue_t            *q;
  57.     ngx_quic_frame_t       *frame;
  58.     ngx_quic_client_id_t   *cid, *item;
  59.     ngx_quic_connection_t  *qc;

  60.     qc = ngx_quic_get_connection(c);

  61.     if (f->seqnum < qc->max_retired_seqnum) {
  62.         /*
  63.          * RFC 9000, 19.15.  NEW_CONNECTION_ID Frame
  64.          *
  65.          *  An endpoint that receives a NEW_CONNECTION_ID frame with
  66.          *  a sequence number smaller than the Retire Prior To field
  67.          *  of a previously received NEW_CONNECTION_ID frame MUST send
  68.          *  a corresponding RETIRE_CONNECTION_ID frame that retires
  69.          *  the newly received connection ID, unless it has already
  70.          *  done so for that sequence number.
  71.          */

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

  76.         frame->level = ssl_encryption_application;
  77.         frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID;
  78.         frame->u.retire_cid.sequence_number = f->seqnum;

  79.         ngx_quic_queue_frame(qc, frame);

  80.         goto retire;
  81.     }

  82.     cid = NULL;

  83.     for (q = ngx_queue_head(&qc->client_ids);
  84.          q != ngx_queue_sentinel(&qc->client_ids);
  85.          q = ngx_queue_next(q))
  86.     {
  87.         item = ngx_queue_data(q, ngx_quic_client_id_t, queue);

  88.         if (item->seqnum == f->seqnum) {
  89.             cid = item;
  90.             break;
  91.         }
  92.     }

  93.     if (cid) {
  94.         /*
  95.          * Transmission errors, timeouts, and retransmissions might cause the
  96.          * same NEW_CONNECTION_ID frame to be received multiple times.
  97.          */

  98.         if (cid->len != f->len
  99.             || ngx_strncmp(cid->id, f->cid, f->len) != 0
  100.             || ngx_strncmp(cid->sr_token, f->srt, NGX_QUIC_SR_TOKEN_LEN) != 0)
  101.         {
  102.             /*
  103.              * ..if a sequence number is used for different connection IDs,
  104.              * the endpoint MAY treat that receipt as a connection error
  105.              * of type PROTOCOL_VIOLATION.
  106.              */
  107.             qc->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION;
  108.             qc->error_reason = "seqnum refers to different connection id/token";
  109.             return NGX_ERROR;
  110.         }

  111.     } else {

  112.         id.data = f->cid;
  113.         id.len = f->len;

  114.         if (ngx_quic_create_client_id(c, &id, f->seqnum, f->srt) == NULL) {
  115.             return NGX_ERROR;
  116.         }
  117.     }

  118. retire:

  119.     if (qc->max_retired_seqnum && f->retire <= qc->max_retired_seqnum) {
  120.         /*
  121.          * Once a sender indicates a Retire Prior To value, smaller values sent
  122.          * in subsequent NEW_CONNECTION_ID frames have no effect.  A receiver
  123.          * MUST ignore any Retire Prior To fields that do not increase the
  124.          * largest received Retire Prior To value.
  125.          */
  126.         goto done;
  127.     }

  128.     qc->max_retired_seqnum = f->retire;

  129.     q = ngx_queue_head(&qc->client_ids);

  130.     while (q != ngx_queue_sentinel(&qc->client_ids)) {

  131.         cid = ngx_queue_data(q, ngx_quic_client_id_t, queue);
  132.         q = ngx_queue_next(q);

  133.         if (cid->seqnum >= f->retire) {
  134.             continue;
  135.         }

  136.         if (ngx_quic_retire_client_id(c, cid) != NGX_OK) {
  137.             return NGX_ERROR;
  138.         }
  139.     }

  140. done:

  141.     if (qc->nclient_ids > qc->tp.active_connection_id_limit) {
  142.         /*
  143.          * RFC 9000, 5.1.1.  Issuing Connection IDs
  144.          *
  145.          * After processing a NEW_CONNECTION_ID frame and
  146.          * adding and retiring active connection IDs, if the number of active
  147.          * connection IDs exceeds the value advertised in its
  148.          * active_connection_id_limit transport parameter, an endpoint MUST
  149.          * close the connection with an error of type CONNECTION_ID_LIMIT_ERROR.
  150.          */
  151.         qc->error = NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR;
  152.         qc->error_reason = "too many connection ids received";
  153.         return NGX_ERROR;
  154.     }

  155.     return NGX_OK;
  156. }


  157. static ngx_int_t
  158. ngx_quic_retire_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid)
  159. {
  160.     ngx_queue_t            *q;
  161.     ngx_quic_path_t        *path;
  162.     ngx_quic_client_id_t   *new_cid;
  163.     ngx_quic_connection_t  *qc;

  164.     qc = ngx_quic_get_connection(c);

  165.     if (!cid->used) {
  166.         return ngx_quic_free_client_id(c, cid);
  167.     }

  168.     /* we are going to retire client id which is in use */

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

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

  171.         path = ngx_queue_data(q, ngx_quic_path_t, queue);
  172.         q = ngx_queue_next(q);

  173.         if (path->cid != cid) {
  174.             continue;
  175.         }

  176.         if (path == qc->path) {
  177.             /* this is the active path: update it with new CID */
  178.             new_cid = ngx_quic_next_client_id(c);
  179.             if (new_cid == NULL) {
  180.                 return NGX_ERROR;
  181.             }

  182.             qc->path->cid = new_cid;
  183.             new_cid->used = 1;

  184.             return ngx_quic_free_client_id(c, cid);
  185.         }

  186.         return ngx_quic_free_path(c, path);
  187.     }

  188.     return NGX_OK;
  189. }


  190. static ngx_quic_client_id_t *
  191. ngx_quic_alloc_client_id(ngx_connection_t *c, ngx_quic_connection_t *qc)
  192. {
  193.     ngx_queue_t           *q;
  194.     ngx_quic_client_id_t  *cid;

  195.     if (!ngx_queue_empty(&qc->free_client_ids)) {

  196.         q = ngx_queue_head(&qc->free_client_ids);
  197.         cid = ngx_queue_data(q, ngx_quic_client_id_t, queue);

  198.         ngx_queue_remove(&cid->queue);

  199.         ngx_memzero(cid, sizeof(ngx_quic_client_id_t));

  200.     } else {

  201.         cid = ngx_pcalloc(c->pool, sizeof(ngx_quic_client_id_t));
  202.         if (cid == NULL) {
  203.             return NULL;
  204.         }
  205.     }

  206.     return cid;
  207. }


  208. ngx_quic_client_id_t *
  209. ngx_quic_create_client_id(ngx_connection_t *c, ngx_str_t *id,
  210.     uint64_t seqnum, u_char *token)
  211. {
  212.     ngx_quic_client_id_t   *cid;
  213.     ngx_quic_connection_t  *qc;

  214.     qc = ngx_quic_get_connection(c);

  215.     cid = ngx_quic_alloc_client_id(c, qc);
  216.     if (cid == NULL) {
  217.         return NULL;
  218.     }

  219.     cid->seqnum = seqnum;

  220.     cid->len = id->len;
  221.     ngx_memcpy(cid->id, id->data, id->len);

  222.     if (token) {
  223.         ngx_memcpy(cid->sr_token, token, NGX_QUIC_SR_TOKEN_LEN);
  224.     }

  225.     ngx_queue_insert_tail(&qc->client_ids, &cid->queue);
  226.     qc->nclient_ids++;

  227.     if (seqnum > qc->client_seqnum) {
  228.         qc->client_seqnum = seqnum;
  229.     }

  230.     ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
  231.                    "quic cid seq:%uL received id:%uz:%xV:%*xs",
  232.                     cid->seqnum, id->len, id,
  233.                     (size_t) NGX_QUIC_SR_TOKEN_LEN, cid->sr_token);

  234.     return cid;
  235. }


  236. ngx_quic_client_id_t *
  237. ngx_quic_next_client_id(ngx_connection_t *c)
  238. {
  239.     ngx_queue_t            *q;
  240.     ngx_quic_client_id_t   *cid;
  241.     ngx_quic_connection_t  *qc;

  242.     qc = ngx_quic_get_connection(c);

  243.     for (q = ngx_queue_head(&qc->client_ids);
  244.          q != ngx_queue_sentinel(&qc->client_ids);
  245.          q = ngx_queue_next(q))
  246.     {
  247.         cid = ngx_queue_data(q, ngx_quic_client_id_t, queue);

  248.         if (!cid->used) {
  249.             return cid;
  250.         }
  251.     }

  252.     return NULL;
  253. }


  254. ngx_int_t
  255. ngx_quic_handle_retire_connection_id_frame(ngx_connection_t *c,
  256.     ngx_quic_retire_cid_frame_t *f)
  257. {
  258.     ngx_quic_socket_t      *qsock;
  259.     ngx_quic_connection_t  *qc;

  260.     qc = ngx_quic_get_connection(c);

  261.     if (f->sequence_number >= qc->server_seqnum) {
  262.         /*
  263.          * RFC 9000, 19.16.
  264.          *
  265.          *  Receipt of a RETIRE_CONNECTION_ID frame containing a sequence
  266.          *  number greater than any previously sent to the peer MUST be
  267.          *  treated as a connection error of type PROTOCOL_VIOLATION.
  268.          */
  269.         qc->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION;
  270.         qc->error_reason = "sequence number of id to retire was never issued";

  271.         return NGX_ERROR;
  272.     }

  273.     qsock = ngx_quic_get_socket(c);

  274.     if (qsock->sid.seqnum == f->sequence_number) {

  275.         /*
  276.          * RFC 9000, 19.16.
  277.          *
  278.          * The sequence number specified in a RETIRE_CONNECTION_ID frame MUST
  279.          * NOT refer to the Destination Connection ID field of the packet in
  280.          * which the frame is contained.  The peer MAY treat this as a
  281.          * connection error of type PROTOCOL_VIOLATION.
  282.          */

  283.         qc->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION;
  284.         qc->error_reason = "sequence number of id to retire refers DCID";

  285.         return NGX_ERROR;
  286.     }

  287.     qsock = ngx_quic_find_socket(c, f->sequence_number);
  288.     if (qsock == NULL) {
  289.         return NGX_OK;
  290.     }

  291.     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
  292.                    "quic socket seq:%uL is retired", qsock->sid.seqnum);

  293.     ngx_quic_close_socket(c, qsock);

  294.     /* restore socket count up to a limit after deletion */
  295.     if (ngx_quic_create_sockets(c) != NGX_OK) {
  296.         return NGX_ERROR;
  297.     }

  298.     return NGX_OK;
  299. }


  300. ngx_int_t
  301. ngx_quic_create_sockets(ngx_connection_t *c)
  302. {
  303.     ngx_uint_t              n;
  304.     ngx_quic_socket_t      *qsock;
  305.     ngx_quic_connection_t  *qc;

  306.     qc = ngx_quic_get_connection(c);

  307.     n = ngx_min(NGX_QUIC_MAX_SERVER_IDS, qc->ctp.active_connection_id_limit);

  308.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  309.                    "quic create sockets has:%ui max:%ui", qc->nsockets, n);

  310.     while (qc->nsockets < n) {

  311.         qsock = ngx_quic_create_socket(c, qc);
  312.         if (qsock == NULL) {
  313.             return NGX_ERROR;
  314.         }

  315.         if (ngx_quic_listen(c, qc, qsock) != NGX_OK) {
  316.             return NGX_ERROR;
  317.         }

  318.         if (ngx_quic_send_server_id(c, &qsock->sid) != NGX_OK) {
  319.             return NGX_ERROR;
  320.         }
  321.     }

  322.     return NGX_OK;
  323. }


  324. static ngx_int_t
  325. ngx_quic_send_server_id(ngx_connection_t *c, ngx_quic_server_id_t *sid)
  326. {
  327.     ngx_str_t               dcid;
  328.     ngx_quic_frame_t       *frame;
  329.     ngx_quic_connection_t  *qc;

  330.     qc = ngx_quic_get_connection(c);

  331.     dcid.len = sid->len;
  332.     dcid.data = sid->id;

  333.     frame = ngx_quic_alloc_frame(c);
  334.     if (frame == NULL) {
  335.         return NGX_ERROR;
  336.     }

  337.     frame->level = ssl_encryption_application;
  338.     frame->type = NGX_QUIC_FT_NEW_CONNECTION_ID;
  339.     frame->u.ncid.seqnum = sid->seqnum;
  340.     frame->u.ncid.retire = 0;
  341.     frame->u.ncid.len = NGX_QUIC_SERVER_CID_LEN;
  342.     ngx_memcpy(frame->u.ncid.cid, sid->id, NGX_QUIC_SERVER_CID_LEN);

  343.     if (ngx_quic_new_sr_token(c, &dcid, qc->conf->sr_token_key,
  344.                               frame->u.ncid.srt)
  345.         != NGX_OK)
  346.     {
  347.         return NGX_ERROR;
  348.     }

  349.     ngx_quic_queue_frame(qc, frame);

  350.     return NGX_OK;
  351. }


  352. ngx_int_t
  353. ngx_quic_free_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid)
  354. {
  355.     ngx_quic_frame_t       *frame;
  356.     ngx_quic_connection_t  *qc;

  357.     qc = ngx_quic_get_connection(c);

  358.     frame = ngx_quic_alloc_frame(c);
  359.     if (frame == NULL) {
  360.         return NGX_ERROR;
  361.     }

  362.     frame->level = ssl_encryption_application;
  363.     frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID;
  364.     frame->u.retire_cid.sequence_number = cid->seqnum;

  365.     ngx_quic_queue_frame(qc, frame);

  366.     /* we are no longer going to use this client id */

  367.     ngx_queue_remove(&cid->queue);
  368.     ngx_queue_insert_head(&qc->free_client_ids, &cid->queue);

  369.     qc->nclient_ids--;

  370.     return NGX_OK;
  371. }