src/event/quic/ngx_event_quic_bpf.c - nginx-1.31.9 nginx/ @ 2a3a19c6f

Global variables defined

Data types defined

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_BPF_VARNAME  "NGINX_BPF_MAPS"
  9. ‌#define NGX_QUIC_BPF_VARSEP    ';'
  10. ‌#define NGX_QUIC_BPF_ADDRSEP   '#'


  11. ‌#define ngx_quic_bpf_get_conf(cycle)                                          \
  12.     (ngx_quic_bpf_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_quic_bpf_module)

  13. ‌#define ngx_quic_bpf_get_old_conf(cycle)                                      \
  14.     cycle->old_cycle->conf_ctx ? ngx_quic_bpf_get_conf(cycle->old_cycle)      \
  15.                                : NULL

  16. ‌#define ngx_core_get_conf(cycle)                                              \
  17.     (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module)

  18. ‌#define ngx_quic_bpf_listen_key(worker)                                       \
  19.     (((uint64_t) 0xFF << 56)                                                  \
  20.      | ((uint64_t) (worker) & 0xFFFFFFFFFFFFULL))


  21. ‌typedef struct {
  22.     ngx_queue_t           queue;

  23.     int                   sock_map;
  24.     int                   worker_counts_map;

  25.     struct sockaddr      *sockaddr;
  26.     socklen_t             socklen;

  27.     ngx_array_t           listening;
  28. ‌} ngx_quic_sock_group_t;


  29. ‌typedef struct {
  30.     ngx_socket_t          fd;
  31.     uint64_t              key;
  32.     ngx_listening_t      *listening;
  33.     ngx_connection_t     *connection;
  34. ‌} ngx_quic_bpf_listening_t;


  35. ‌typedef struct {
  36.     ngx_flag_t            enabled;
  37.     ngx_uint_t            map_size;
  38.     ngx_uint_t            master_index;
  39.     u_char               *env;
  40.     ngx_queue_t           groups;     /* of ngx_quic_sock_group_t */
  41. ‌} ngx_quic_bpf_conf_t;


  42. static void *ngx_quic_bpf_create_conf(ngx_cycle_t *cycle);
  43. static char *ngx_quic_bpf_init_conf(ngx_cycle_t *cycle, void *conf);
  44. static ngx_int_t ngx_quic_bpf_module_init(ngx_cycle_t *cycle);
  45. static void ngx_quic_bpf_exit_master(ngx_cycle_t *cycle);

  46. static void ngx_quic_bpf_cleanup(void *data);
  47. static ngx_inline void ngx_quic_bpf_close(ngx_log_t *log, int fd,
  48.     const char *name);
  49. static ngx_inline ngx_int_t ngx_quic_bpf_map_update(ngx_log_t *log, int fd,
  50.     const void *key, const void *value, const char *name);

  51. static ngx_quic_sock_group_t *ngx_quic_bpf_find_group(ngx_quic_bpf_conf_t *bcf,
  52.     ngx_listening_t *ls);
  53. static ngx_quic_sock_group_t *ngx_quic_bpf_alloc_group(ngx_cycle_t *cycle,
  54.     ngx_listening_t *ls);
  55. static ngx_quic_sock_group_t *ngx_quic_bpf_create_group(ngx_cycle_t *cycle,
  56.     ngx_listening_t *ls);
  57. static ngx_int_t ngx_quic_bpf_inherit_fd(ngx_cycle_t *cycle, int fd);
  58. static ngx_quic_sock_group_t *ngx_quic_bpf_get_group(ngx_cycle_t *cycle,
  59.     ngx_listening_t *ls);
  60. static ngx_int_t ngx_quic_bpf_group_add_socket(ngx_cycle_t *cycle,
  61.     ngx_listening_t *ls);
  62. static ngx_int_t ngx_quic_bpf_add_worker_socket(ngx_cycle_t *cycle,
  63.     ngx_quic_sock_group_t *grp, ngx_listening_t *ls);
  64. static ngx_int_t ngx_quic_bpf_publish_workers(ngx_cycle_t *cycle,
  65.     ngx_quic_sock_group_t *grp, ngx_uint_t worker_count);

  66. static ngx_int_t ngx_quic_bpf_export_maps(ngx_cycle_t *cycle);
  67. static ngx_int_t ngx_quic_bpf_import_maps(ngx_cycle_t *cycle);

  68. extern ngx_bpf_program_t  ngx_quic_reuseport_helper;


  69. ‌static ngx_command_t  ngx_quic_bpf_commands[] = {

  70.     { ngx_string("quic_bpf"),
  71.       NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
  72.       ngx_conf_set_flag_slot,
  73.       0,
  74.       offsetof(ngx_quic_bpf_conf_t, enabled),
  75.       NULL },

  76.       ngx_null_command
  77. };


  78. ‌static ngx_core_module_t  ngx_quic_bpf_module_ctx = {
  79.     ngx_string("quic_bpf"),
  80.     ngx_quic_bpf_create_conf,
  81.     ngx_quic_bpf_init_conf
  82. };


  83. ‌ngx_module_t  ngx_quic_bpf_module = {
  84.     NGX_MODULE_V1,
  85.     &ngx_quic_bpf_module_ctx,              /* module context */
  86.     ngx_quic_bpf_commands,                 /* module directives */
  87.     NGX_CORE_MODULE,                       /* module type */
  88.     NULL,                                  /* init master */
  89.     ngx_quic_bpf_module_init,              /* init module */
  90.     NULL,                                  /* init process */
  91.     NULL,                                  /* init thread */
  92.     NULL,                                  /* exit thread */
  93.     NULL,                                  /* exit process */
  94.     ngx_quic_bpf_exit_master,              /* exit master */
  95.     NGX_MODULE_V1_PADDING
  96. };


  97. static void *
  98. ‌ngx_quic_bpf_create_conf(ngx_cycle_t *cycle)
  99. {
  100.     size_t                len;
  101.     u_char               *env;
  102.     ngx_quic_bpf_conf_t  *bcf;

  103.     bcf = ngx_pcalloc(cycle->pool, sizeof(ngx_quic_bpf_conf_t));
  104.     if (bcf == NULL) {
  105.         return NULL;
  106.     }

  107.     bcf->enabled = NGX_CONF_UNSET;
  108.     bcf->map_size = NGX_CONF_UNSET_UINT;

  109.     /*
  110.      * preserve environment variable value before it may be reset by
  111.      * some module, i.e. perl
  112.      */
  113.     env = (u_char *) getenv(NGX_QUIC_BPF_VARNAME);
  114.     if (env != NULL && *env != '\0') {
  115.         len = ngx_strlen(env);
  116.         bcf->env = ngx_pnalloc(cycle->pool, len + 1);
  117.         if (bcf->env == NULL) {
  118.             return NULL;
  119.         }

  120.         ngx_memcpy(bcf->env, env, len + 1);
  121.     }

  122.     ngx_queue_init(&bcf->groups);

  123.     return bcf;
  124. }


  125. static char *
  126. ‌ngx_quic_bpf_init_conf(ngx_cycle_t *cycle, void *conf)
  127. {
  128.     ngx_quic_bpf_conf_t  *bcf = conf;
  129.     ngx_quic_bpf_conf_t  *obcf;

  130.     ngx_conf_init_value(bcf->enabled, 0);

  131.     if (cycle->old_cycle->conf_ctx == NULL) {
  132.         return NGX_CONF_OK;
  133.     }

  134.     obcf = ngx_quic_bpf_get_conf(cycle->old_cycle);
  135.     if (obcf == NULL) {
  136.         return NGX_CONF_OK;
  137.     }

  138.     if (obcf->enabled != bcf->enabled) {
  139.         ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
  140.                       "cannot change \"quic_bpf\" after reload, ignoring");
  141.         bcf->enabled = obcf->enabled;
  142.     }

  143.     bcf->master_index = obcf->master_index;

  144.     return NGX_CONF_OK;
  145. }


  146. static ngx_int_t
  147. ‌ngx_quic_bpf_module_init(ngx_cycle_t *cycle)
  148. {
  149.     ngx_uint_t              i;
  150.     ngx_queue_t            *q;
  151.     ngx_listening_t        *ls;
  152.     ngx_core_conf_t        *ccf;
  153.     ngx_pool_cleanup_t     *cln;
  154.     ngx_quic_bpf_conf_t    *bcf;
  155.     ngx_quic_sock_group_t  *grp;

  156.     if (ngx_test_config) {
  157.         /*
  158.          * during config test, SO_REUSEPORT socket option is
  159.          * not set, thus making further processing meaningless
  160.          */
  161.         return NGX_OK;
  162.     }

  163.     ccf = ngx_core_get_conf(cycle);
  164.     bcf = ngx_quic_bpf_get_conf(cycle);

  165.     if (!bcf->enabled) {

  166.         if (ngx_inherited && ngx_is_init_cycle(cycle->old_cycle)
  167.             && bcf->env != NULL)
  168.         {
  169.             ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
  170.                           "\"quic_bpf\" cannot be disabled "
  171.                           "during binary upgrade");
  172.             return NGX_ERROR;
  173.         }

  174.         return NGX_OK;
  175.     }

  176.     bcf->map_size = ccf->worker_processes * 32;

  177.     cln = ngx_pool_cleanup_add(cycle->pool, 0);
  178.     if (cln == NULL) {
  179.         goto failed;
  180.     }

  181.     cln->data = bcf;
  182.     cln->handler = ngx_quic_bpf_cleanup;

  183.     if (ngx_inherited && ngx_is_init_cycle(cycle->old_cycle)) {
  184.         if (ngx_quic_bpf_import_maps(cycle) != NGX_OK) {
  185.             goto failed;
  186.         }
  187.     }

  188.     ls = cycle->listening.elts;

  189.     for (i = 0; i < cycle->listening.nelts; i++) {
  190.         if (ls[i].quic && ls[i].reuseport) {
  191.             if (ngx_quic_bpf_group_add_socket(cycle, &ls[i]) != NGX_OK) {
  192.                 goto failed;
  193.             }
  194.         }
  195.     }

  196.     q = ngx_queue_head(&bcf->groups);

  197.     while (q != ngx_queue_sentinel(&bcf->groups)) {

  198.         grp = ngx_queue_data(q, ngx_quic_sock_group_t, queue);
  199.         q = ngx_queue_next(q);

  200.         if (grp->listening.nelts == 0) {

  201.             ngx_quic_bpf_close(cycle->log, grp->sock_map, "sockmap");
  202.             ngx_quic_bpf_close(cycle->log, grp->worker_counts_map,
  203.                                "worker_counts");
  204.             ngx_queue_remove(&grp->queue);
  205.             continue;
  206.         }

  207.         if (ngx_quic_bpf_publish_workers(cycle, grp, ccf->worker_processes)
  208.             != NGX_OK)
  209.         {
  210.             goto failed;
  211.         }
  212.     }

  213.     if (ngx_quic_bpf_export_maps(cycle) != NGX_OK) {
  214.         goto failed;
  215.     }

  216.     return NGX_OK;

  217. failed:

  218.     if (ngx_is_init_cycle(cycle->old_cycle)) {
  219.         ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
  220.                       "ngx_quic_bpf_module failed to initialize, check limits");

  221.         /* refuse to start */
  222.         return NGX_ERROR;
  223.     }

  224.     /*
  225.      * returning error now will lead to master process exiting immediately
  226.      * leaving worker processes orphaned, what is really unexpected.
  227.      * Instead, just issue a not about failed initialization and try
  228.      * to cleanup a bit. Still program can be already loaded to kernel
  229.      * for some reuseport groups, and there is no way to revert, so
  230.      * behaviour may be inconsistent.
  231.      */

  232.     ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
  233.                   "ngx_quic_bpf_module failed to initialize properly, ignored."
  234.                   "please check limits and note that nginx state now "
  235.                   "can be inconsistent and restart may be required");

  236.     return NGX_OK;
  237. }


  238. ngx_uint_t
  239. ‌ngx_quic_bpf_enabled(ngx_cycle_t *cycle)
  240. {
  241.     ngx_quic_bpf_conf_t  *bcf;

  242.     bcf = ngx_quic_bpf_get_conf(cycle);

  243.     if (bcf == NULL) {
  244.         return 0;
  245.     }

  246.     return bcf->enabled;
  247. }


  248. static void
  249. ‌ngx_quic_bpf_cleanup(void *data)
  250. {
  251.     ngx_quic_bpf_conf_t  *bcf = (ngx_quic_bpf_conf_t *) data;

  252.     ngx_uint_t                 i;
  253.     ngx_log_t                 *log;
  254.     ngx_queue_t               *q;
  255.     ngx_quic_sock_group_t     *grp;
  256.     ngx_quic_bpf_listening_t  *bls;

  257.     log = ngx_cycle->log;

  258.     for (q = ngx_queue_head(&bcf->groups);
  259.          q != ngx_queue_sentinel(&bcf->groups);
  260.          q = ngx_queue_next(q))
  261.     {
  262.         grp = ngx_queue_data(q, ngx_quic_sock_group_t, queue);

  263.         bls = grp->listening.elts;

  264.         for (i = 0; i < grp->listening.nelts; i++) {
  265.             if (bls[i].fd != (ngx_socket_t) -1) {
  266.                 if (ngx_close_socket(bls[i].fd) == -1) {
  267.                     ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,
  268.                                   ngx_close_socket_n " failed");
  269.                 }
  270.             }
  271.         }

  272.         ngx_quic_bpf_close(log, grp->sock_map, "sockmap");
  273.         ngx_quic_bpf_close(log, grp->worker_counts_map, "worker_counts");
  274.     }
  275. }


  276. static void
  277. ‌ngx_quic_bpf_exit_master(ngx_cycle_t *cycle)
  278. {
  279.     uint32_t                key, value;
  280.     ngx_queue_t            *q;
  281.     ngx_quic_bpf_conf_t    *bcf;
  282.     ngx_quic_sock_group_t  *grp;

  283.     bcf = ngx_quic_bpf_get_conf(cycle);
  284.     if (!bcf->enabled) {
  285.         return;
  286.     }

  287.     key = bcf->master_index;
  288.     value = 0;

  289.     for (q = ngx_queue_head(&bcf->groups);
  290.          q != ngx_queue_sentinel(&bcf->groups);
  291.          q = ngx_queue_next(q))
  292.     {
  293.         grp = ngx_queue_data(q, ngx_quic_sock_group_t, queue);

  294.         if (ngx_quic_bpf_map_update(cycle->log, grp->worker_counts_map,
  295.                                     &key, &value, "worker_counts")
  296.             != NGX_OK)
  297.         {
  298.             continue;
  299.         }
  300.     }
  301. }


  302. static ngx_inline void
  303. ‌ngx_quic_bpf_close(ngx_log_t *log, int fd, const char *name)
  304. {
  305.     if (fd == -1) {
  306.         return;
  307.     }

  308.     if (close(fd) != -1) {
  309.         return;
  310.     }

  311.     ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
  312.                   "quic bpf close %s fd:%d failed", name, fd);
  313. }


  314. static ngx_inline ngx_int_t
  315. ‌ngx_quic_bpf_map_update(ngx_log_t *log, int fd, const void *key,
  316.     const void *value, const char *name)
  317. {
  318.     if (ngx_bpf_map_update(fd, key, value, BPF_ANY) != -1) {
  319.         return NGX_OK;
  320.     }

  321.     ngx_log_error(NGX_LOG_ERR, log, ngx_errno,
  322.                   "quic bpf failed to update %s map fd:%d", name, fd);

  323.     return NGX_ERROR;
  324. }


  325. static ngx_quic_sock_group_t *
  326. ‌ngx_quic_bpf_find_group(ngx_quic_bpf_conf_t *bcf, ngx_listening_t *ls)
  327. {
  328.     ngx_queue_t            *q;
  329.     ngx_quic_sock_group_t  *grp;

  330.     for (q = ngx_queue_head(&bcf->groups);
  331.          q != ngx_queue_sentinel(&bcf->groups);
  332.          q = ngx_queue_next(q))
  333.     {
  334.         grp = ngx_queue_data(q, ngx_quic_sock_group_t, queue);

  335.         if (ngx_cmp_sockaddr(ls->sockaddr, ls->socklen,
  336.                              grp->sockaddr, grp->socklen, 1)
  337.             == NGX_OK)
  338.         {
  339.             return grp;
  340.         }
  341.     }

  342.     return NULL;
  343. }


  344. static ngx_quic_sock_group_t *
  345. ‌ngx_quic_bpf_alloc_group(ngx_cycle_t *cycle, ngx_listening_t *ls)
  346. {
  347.     ngx_quic_bpf_conf_t    *bcf;
  348.     ngx_quic_sock_group_t  *grp;

  349.     bcf = ngx_quic_bpf_get_conf(cycle);

  350.     grp = ngx_pcalloc(cycle->pool, sizeof(ngx_quic_sock_group_t));
  351.     if (grp == NULL) {
  352.         return NULL;
  353.     }

  354.     grp->sock_map = -1;
  355.     grp->worker_counts_map = -1;

  356.     grp->sockaddr = ls->sockaddr;
  357.     grp->socklen = ls->socklen;

  358.     if (ngx_array_init(&grp->listening, cycle->pool, 1,
  359.                        sizeof(ngx_quic_bpf_listening_t))
  360.         != NGX_OK)
  361.     {
  362.         return NULL;
  363.     }

  364.     ngx_queue_insert_tail(&bcf->groups, &grp->queue);

  365.     return grp;
  366. }


  367. static ngx_quic_sock_group_t *
  368. ‌ngx_quic_bpf_create_group(ngx_cycle_t *cycle, ngx_listening_t *ls)
  369. {
  370.     int                     progfd, failed;
  371.     ngx_quic_bpf_conf_t    *bcf;
  372.     ngx_quic_sock_group_t  *grp;

  373.     bcf = ngx_quic_bpf_get_conf(cycle);

  374.     grp = ngx_quic_bpf_alloc_group(cycle, ls);
  375.     if (grp == NULL) {
  376.         return NULL;
  377.     }

  378.     grp->sock_map = ngx_bpf_map_create(cycle->log,
  379.                                        BPF_MAP_TYPE_SOCKHASH,
  380.                                        sizeof(uint64_t),
  381.                                        sizeof(uint64_t),
  382.                                        bcf->map_size, 0);
  383.     if (grp->sock_map == -1) {
  384.         goto failed;
  385.     }

  386.     if (ngx_quic_bpf_inherit_fd(cycle, grp->sock_map) != NGX_OK) {
  387.         goto failed;
  388.     }

  389.     ngx_bpf_program_link(&ngx_quic_reuseport_helper,
  390.                          "ngx_quic_sockmap", grp->sock_map);

  391.     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
  392.                    "quic bpf sockmap created fd:%d",
  393.                    grp->sock_map);

  394.     grp->worker_counts_map = ngx_bpf_map_create(cycle->log,
  395.                                                 BPF_MAP_TYPE_ARRAY,
  396.                                                 sizeof(uint32_t),
  397.                                                 sizeof(uint32_t), 2, 0);
  398.     if (grp->worker_counts_map == -1) {
  399.         goto failed;
  400.     }

  401.     if (ngx_quic_bpf_inherit_fd(cycle, grp->worker_counts_map) != NGX_OK) {
  402.         goto failed;
  403.     }

  404.     ngx_bpf_program_link(&ngx_quic_reuseport_helper,
  405.                          "ngx_quic_worker_counts", grp->worker_counts_map);

  406.     progfd = ngx_bpf_load_program(cycle->log, &ngx_quic_reuseport_helper);
  407.     if (progfd < 0) {
  408.         goto failed;
  409.     }

  410.     failed = 0;

  411.     if (setsockopt(ls->fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF,
  412.                    &progfd, sizeof(int))
  413.         == -1)
  414.     {
  415.         ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
  416.                       "quic bpf setsockopt(SO_ATTACH_REUSEPORT_EBPF) failed");
  417.         failed = 1;
  418.     }

  419.     ngx_quic_bpf_close(cycle->log, progfd, "program");

  420.     if (failed) {
  421.         goto failed;
  422.     }

  423.     return grp;

  424. failed:

  425.     ngx_quic_bpf_close(cycle->log, grp->sock_map, "sockmap");
  426.     ngx_quic_bpf_close(cycle->log, grp->worker_counts_map, "worker_counts");

  427.     ngx_queue_remove(&grp->queue);

  428.     return NULL;
  429. }


  430. static ngx_int_t
  431. ‌ngx_quic_bpf_inherit_fd(ngx_cycle_t *cycle, int fd)
  432. {
  433.     int  flags;

  434.     flags = fcntl(fd, F_GETFD);
  435.     if (flags == -1) {
  436.         ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
  437.                       "quic bpf fcntl(F_GETFD) failed");
  438.         return NGX_ERROR;
  439.     }

  440.     flags &= ~FD_CLOEXEC;

  441.     if (fcntl(fd, F_SETFD, flags) == -1) {
  442.         ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
  443.                       "quic bpf fcntl(F_SETFD) failed");
  444.         return NGX_ERROR;
  445.     }

  446.     return NGX_OK;
  447. }


  448. static ngx_quic_sock_group_t *
  449. ‌ngx_quic_bpf_get_group(ngx_cycle_t *cycle, ngx_listening_t *ls)
  450. {
  451.     ngx_quic_bpf_conf_t    *bcf, *old_bcf;
  452.     ngx_quic_sock_group_t  *grp, *ogrp;

  453.     bcf = ngx_quic_bpf_get_conf(cycle);

  454.     grp = ngx_quic_bpf_find_group(bcf, ls);
  455.     if (grp) {
  456.         return grp;
  457.     }

  458.     old_bcf = ngx_quic_bpf_get_old_conf(cycle);

  459.     if (old_bcf == NULL) {
  460.         return ngx_quic_bpf_create_group(cycle, ls);
  461.     }

  462.     ogrp = ngx_quic_bpf_find_group(old_bcf, ls);
  463.     if (ogrp == NULL) {
  464.         return ngx_quic_bpf_create_group(cycle, ls);
  465.     }

  466.     grp = ngx_quic_bpf_alloc_group(cycle, ls);
  467.     if (grp == NULL) {
  468.         return NULL;
  469.     }

  470.     grp->sock_map = dup(ogrp->sock_map);
  471.     if (grp->sock_map == -1) {
  472.         ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
  473.                       "quic bpf failed to duplicate sockmap");
  474.         goto failed;
  475.     }

  476.     grp->worker_counts_map = dup(ogrp->worker_counts_map);
  477.     if (grp->worker_counts_map == -1) {
  478.         ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
  479.                       "quic bpf failed to duplicate worker counts map");
  480.         goto failed;
  481.     }

  482.     return grp;

  483. failed:

  484.     ngx_quic_bpf_close(cycle->log, grp->sock_map, "sockmap");
  485.     ngx_quic_bpf_close(cycle->log, grp->worker_counts_map, "worker_counts");

  486.     ngx_queue_remove(&grp->queue);

  487.     return NULL;
  488. }


  489. static ngx_int_t
  490. ‌ngx_quic_bpf_group_add_socket(ngx_cycle_t *cycle, ngx_listening_t *ls)
  491. {
  492.     uint64_t                key, value;
  493.     ngx_quic_sock_group_t  *grp;

  494.     grp = ngx_quic_bpf_get_group(cycle, ls);
  495.     if (grp == NULL) {
  496.         return NGX_ERROR;
  497.     }

  498.     if (ngx_quic_bpf_add_worker_socket(cycle, grp, ls) != NGX_OK) {
  499.         return NGX_ERROR;
  500.     }

  501.     key = ngx_quic_bpf_listen_key(ls->worker);
  502.     value = ls->fd;

  503.     if (ngx_quic_bpf_map_update(cycle->log, grp->sock_map,
  504.                                 &key, &value, "listen")
  505.         != NGX_OK)
  506.     {
  507.         return NGX_ERROR;
  508.     }

  509.     return NGX_OK;
  510. }


  511. static ngx_int_t
  512. ‌ngx_quic_bpf_publish_workers(ngx_cycle_t *cycle, ngx_quic_sock_group_t *grp,
  513.     ngx_uint_t worker_count)
  514. {
  515.     uint32_t              key;
  516.     uint32_t              value;
  517.     ngx_quic_bpf_conf_t  *bcf;

  518.     bcf = ngx_quic_bpf_get_conf(cycle);

  519.     key = bcf->master_index;
  520.     value = worker_count;

  521.     if (ngx_quic_bpf_map_update(cycle->log, grp->worker_counts_map,
  522.                                 &key, &value, "worker_counts")
  523.         != NGX_OK)
  524.     {
  525.         return NGX_ERROR;
  526.     }

  527.     return NGX_OK;
  528. }


  529. static ngx_int_t
  530. ‌ngx_quic_bpf_add_worker_socket(ngx_cycle_t *cycle, ngx_quic_sock_group_t *grp,
  531.     ngx_listening_t *ls)
  532. {
  533.     int                        value;
  534.     uint64_t                   key, map_value;
  535.     ngx_uint_t                 i, n, map_updated;
  536.     ngx_socket_t               s;
  537.     ngx_quic_bpf_listening_t  *bls;

  538.     s = ngx_socket(ls->sockaddr->sa_family, SOCK_DGRAM, 0);
  539.     if (s == (ngx_socket_t) -1) {
  540.         ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_socket_errno,
  541.                       "quic bpf " ngx_socket_n " failed");
  542.         return NGX_ERROR;
  543.     }

  544.     map_updated = 0;

  545.     if (ngx_nonblocking(s) == -1) {
  546.         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
  547.                       "quic bpf " ngx_nonblocking_n " failed");
  548.         goto failed;
  549.     }

  550.     if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) {
  551.         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
  552.                       "quic bpf fcntl(FD_CLOEXEC) failed");
  553.         goto failed;
  554.     }

  555.     value = 1;

  556.     if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
  557.                    (const void *) &value, sizeof(int))
  558.         == -1)
  559.     {
  560.         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
  561.                       "quic bpf setsockopt(SO_REUSEADDR) failed");
  562.         goto failed;
  563.     }

  564.     if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT,
  565.                    (const void *) &value, sizeof(int))
  566.         == -1)
  567.     {
  568.         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
  569.                       "quic bpf setsockopt(SO_REUSEPORT) failed");
  570.         goto failed;
  571.     }

  572.     if (ls->rcvbuf != -1) {
  573.         if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
  574.                        (const void *) &ls->rcvbuf, sizeof(int))
  575.             == -1)
  576.         {
  577.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
  578.                           "quic bpf setsockopt(SO_RCVBUF, %d) failed",
  579.                           ls->rcvbuf);
  580.             goto failed;
  581.         }
  582.     }

  583.     if (ls->sndbuf != -1) {
  584.         if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
  585.                        (const void *) &ls->sndbuf, sizeof(int))
  586.             == -1)
  587.         {
  588.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
  589.                           "quic bpf setsockopt(SO_SNDBUF, %d) failed",
  590.                           ls->sndbuf);
  591.             goto failed;
  592.         }
  593.     }

  594. #if (NGX_HAVE_IP_PKTINFO)
  595.     if (ls->wildcard && ls->sockaddr->sa_family == AF_INET) {
  596.         if (setsockopt(s, IPPROTO_IP, IP_PKTINFO,
  597.                        (const void *) &value, sizeof(int))
  598.             == -1)
  599.         {
  600.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
  601.                           "quic bpf setsockopt(IP_PKTINFO) failed");
  602.             goto failed;
  603.         }
  604.     }
  605. #endif

  606. #if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)
  607.     if (ls->wildcard && ls->sockaddr->sa_family == AF_INET6) {
  608.         if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
  609.                        (const void *) &value, sizeof(int))
  610.             == -1)
  611.         {
  612.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
  613.                           "quic bpf setsockopt(IPV6_RECVPKTINFO) failed");
  614.             goto failed;
  615.         }
  616.     }
  617. #endif

  618. #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)

  619.     if (ls->sockaddr->sa_family == AF_INET6) {
  620.         int  ipv6only;

  621.         ipv6only = ls->ipv6only;

  622.         if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
  623.                        (const void *) &ipv6only, sizeof(int))
  624.             == -1)
  625.         {
  626.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
  627.                           "quic bpf setsockopt(IPV6_V6ONLY) %V failed",
  628.                           &ls->addr_text);
  629.             goto failed;
  630.         }
  631.     }

  632. #endif

  633. #if (NGX_HAVE_IP_MTU_DISCOVER)

  634.     if (ls->sockaddr->sa_family == AF_INET) {
  635.         value = IP_PMTUDISC_DO;

  636.         if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER,
  637.                        (const void *) &value, sizeof(int))
  638.             == -1)
  639.         {
  640.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
  641.                           "quic bpf setsockopt(IP_MTU_DISCOVER) "
  642.                           "failed, ignored");
  643.         }
  644.     }

  645. #elif (NGX_HAVE_IP_DONTFRAG)

  646.     if (ls->sockaddr->sa_family == AF_INET) {
  647.         value = 1;

  648.         if (setsockopt(s, IPPROTO_IP, IP_DONTFRAG,
  649.                        (const void *) &value, sizeof(int))
  650.             == -1)
  651.         {
  652.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
  653.                           "quic bpf setsockopt(IP_DONTFRAG) "
  654.                           "failed, ignored");
  655.         }
  656.     }

  657. #endif

  658. #if (NGX_HAVE_INET6)

  659. #if (NGX_HAVE_IPV6_MTU_DISCOVER)

  660.     if (ls->sockaddr->sa_family == AF_INET6) {
  661.         value = IPV6_PMTUDISC_DO;

  662.         if (setsockopt(s, IPPROTO_IPV6, IPV6_MTU_DISCOVER,
  663.                        (const void *) &value, sizeof(int))
  664.             == -1)
  665.         {
  666.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
  667.                           "quic bpf setsockopt(IPV6_MTU_DISCOVER) "
  668.                           "failed, ignored");
  669.         }
  670.     }

  671. #elif (NGX_HAVE_IPV6_DONTFRAG)

  672.     if (ls->sockaddr->sa_family == AF_INET6) {
  673.         value = 1;

  674.         if (setsockopt(s, IPPROTO_IPV6, IPV6_DONTFRAG,
  675.                        (const void *) &value, sizeof(int))
  676.             == -1)
  677.         {
  678.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
  679.                           "quic bpf setsockopt(IPV6_DONTFRAG) "
  680.                           "failed, ignored");
  681.         }
  682.     }

  683. #endif

  684. #endif

  685.     if (bind(s, ls->sockaddr, ls->socklen) == -1) {
  686.         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
  687.                       "quic bpf bind() failed");
  688.         goto failed;
  689.     }

  690.     if (RAND_bytes((u_char *) &key, sizeof(uint64_t)) != 1) {
  691.         ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
  692.                       "quic bpf RAND_bytes() failed");
  693.         goto failed;
  694.     }

  695.     if ((key >> 56) == 0xFF) {
  696.         key &= ~((uint64_t) 0xFF << 56);
  697.     }

  698.     map_value = s;
  699.     if (ngx_quic_bpf_map_update(cycle->log, grp->sock_map,
  700.                                 &key, &map_value, "sockmap")
  701.         != NGX_OK)
  702.     {
  703.         goto failed;
  704.     }

  705.     map_updated = 1;

  706.     if (ls->worker >= grp->listening.nelts) {
  707.         n = ls->worker + 1 - grp->listening.nelts;

  708.         bls = ngx_array_push_n(&grp->listening, n);
  709.         if (bls == NULL) {
  710.             goto failed;
  711.         }

  712.         ngx_memzero(bls, n * sizeof(ngx_quic_bpf_listening_t));

  713.         for (i = 0; i < n; i++) {
  714.             bls[i].fd = (ngx_socket_t) -1;
  715.         }
  716.     }

  717.     bls = grp->listening.elts;
  718.     bls[ls->worker].fd = s;
  719.     bls[ls->worker].key = key;
  720.     bls[ls->worker].listening = ls;

  721.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
  722.                    "quic bpf socket fd:%d key:%uL", s, key);

  723.     return NGX_OK;

  724. failed:

  725.     if (map_updated) {
  726.         if (ngx_bpf_map_delete(grp->sock_map, &key) == -1) {
  727.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
  728.                           "quic bpf failed to delete map entry");
  729.         }
  730.     }

  731.     if (ngx_close_socket(s) == -1) {
  732.         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
  733.                       "quic bpf " ngx_close_socket_n " failed");
  734.     }

  735.     return NGX_ERROR;
  736. }


  737. static ngx_int_t
  738. ‌ngx_quic_bpf_export_maps(ngx_cycle_t *cycle)
  739. {
  740.     u_char                 *p, *buf;
  741.     size_t                  len;
  742.     ngx_str_t              *var;
  743.     ngx_queue_t            *q;
  744.     ngx_core_conf_t        *ccf;
  745.     ngx_quic_bpf_conf_t    *bcf;
  746.     ngx_quic_sock_group_t  *grp;

  747.     ccf = ngx_core_get_conf(cycle);
  748.     bcf = ngx_quic_bpf_get_conf(cycle);
  749.     if (!bcf->enabled) {
  750.         return NGX_OK;
  751.     }

  752.     if (ngx_queue_empty(&bcf->groups)) {
  753.         return NGX_OK;
  754.     }

  755.     len = sizeof(NGX_QUIC_BPF_VARNAME) + 1;

  756.     for (q = ngx_queue_head(&bcf->groups);
  757.          q != ngx_queue_sentinel(&bcf->groups);
  758.          q = ngx_queue_next(q))
  759.     {
  760.         len += (NGX_INT32_LEN + 1) * 2 + NGX_SOCKADDR_STRLEN + 1;
  761.     }

  762.     len++;

  763.     buf = ngx_palloc(cycle->pool, len);
  764.     if (buf == NULL) {
  765.         return NGX_ERROR;
  766.     }

  767.     p = ngx_cpymem(buf, NGX_QUIC_BPF_VARNAME "=",
  768.                    sizeof(NGX_QUIC_BPF_VARNAME));

  769.     *p++ = bcf->master_index ? '-' : '+';

  770.     for (q = ngx_queue_head(&bcf->groups);
  771.          q != ngx_queue_sentinel(&bcf->groups);
  772.          q = ngx_queue_next(q))
  773.     {
  774.         grp = ngx_queue_data(q, ngx_quic_sock_group_t, queue);

  775.         p = ngx_sprintf(p, "%ud", grp->sock_map);
  776.         *p++ = NGX_QUIC_BPF_ADDRSEP;

  777.         p = ngx_sprintf(p, "%ud", grp->worker_counts_map);
  778.         *p++ = NGX_QUIC_BPF_ADDRSEP;

  779.         p += ngx_sock_ntop(grp->sockaddr, grp->socklen, p,
  780.                            NGX_SOCKADDR_STRLEN, 1);

  781.         *p++ = NGX_QUIC_BPF_VARSEP;
  782.     }

  783.     *p = '\0';

  784.     var = ngx_array_push(&ccf->env);
  785.     if (var == NULL) {
  786.         return NGX_ERROR;
  787.     }

  788.     var->data = buf;
  789.     var->len = sizeof(NGX_QUIC_BPF_VARNAME) - 1;

  790.     return NGX_OK;
  791. }


  792. static ngx_int_t
  793. ‌ngx_quic_bpf_import_maps(ngx_cycle_t *cycle)
  794. {
  795.     int                     fds[2];
  796.     u_char                 *inherited, *p, *v;
  797.     ngx_int_t               fd;
  798.     ngx_uint_t              i, nfd;
  799.     ngx_addr_t              tmp;
  800.     ngx_quic_bpf_conf_t    *bcf;
  801.     ngx_quic_sock_group_t  *grp;

  802.     bcf = ngx_quic_bpf_get_conf(cycle);
  803.     inherited = bcf->env;

  804.     if (inherited == NULL) {
  805.         return NGX_OK;
  806.     }

  807.     nfd = 0;

  808.     ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
  809.                   "quic bpf using inherited QUIC BPF maps from \"%s\"",
  810.                   inherited);

  811.     switch (*inherited) {

  812.     case '+':
  813.         bcf->master_index = 1;
  814.         break;

  815.     case '-':
  816.         bcf->master_index = 0;
  817.         break;

  818.     default:
  819.         goto failed;
  820.     }

  821.     for (p = inherited + 1, v = p; *p; p++) {

  822.         switch (*p) {

  823.         case NGX_QUIC_BPF_ADDRSEP:

  824.             if (nfd > 1) {
  825.                 goto failed;
  826.             }

  827.             fd = ngx_atoi(v, p - v);
  828.             if (fd == NGX_ERROR) {
  829.                 goto failed;
  830.             }

  831.             fds[nfd++] = fd;
  832.             v = p + 1;
  833.             break;

  834.         case NGX_QUIC_BPF_VARSEP:

  835.             if (nfd != 2) {
  836.                 goto failed;
  837.             }

  838.             grp = ngx_pcalloc(cycle->pool, sizeof(ngx_quic_sock_group_t));
  839.             if (grp == NULL) {
  840.                 goto failed;
  841.             }

  842.             if (ngx_array_init(&grp->listening, cycle->pool, 1,
  843.                                sizeof(ngx_quic_bpf_listening_t))
  844.                 != NGX_OK)
  845.             {
  846.                 goto failed;
  847.             }

  848.             grp->sock_map = fds[0];
  849.             grp->worker_counts_map = fds[1];

  850.             if (ngx_parse_addr_port(cycle->pool, &tmp, v, p - v) != NGX_OK) {
  851.                 goto failed;
  852.             }

  853.             grp->sockaddr = ngx_pcalloc(cycle->pool, tmp.socklen);
  854.             if (grp->sockaddr == NULL) {
  855.                 goto failed;
  856.             }

  857.             ngx_memcpy(grp->sockaddr, tmp.sockaddr, tmp.socklen);
  858.             grp->socklen = tmp.socklen;

  859.             ngx_queue_insert_tail(&bcf->groups, &grp->queue);

  860.             ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
  861.                            "quic bpf sockmap inherited with "
  862.                            "fds:%d/%d address:%*s",
  863.                            fds[0], fds[1], p - v, v);

  864.             nfd = 0;
  865.             v = p + 1;
  866.             break;

  867.         default:
  868.             break;
  869.         }
  870.     }

  871.     return NGX_OK;

  872. failed:

  873.     for (i = 0; i < nfd; i++) {
  874.         ngx_quic_bpf_close(cycle->log, fds[i], "inherited");
  875.     }

  876.     ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
  877.                   "quic bpf failed to parse inherited QUIC BPF variable");

  878.     return NGX_ERROR;
  879. }


  880. ngx_int_t
  881. ‌ngx_quic_bpf_get_worker_fd(ngx_connection_t *lc, ngx_socket_t *fd)
  882. {
  883.     ngx_event_t               *rev;
  884.     ngx_connection_t          *c;
  885.     ngx_quic_bpf_conf_t       *bcf;
  886.     ngx_quic_sock_group_t     *grp;
  887.     ngx_quic_bpf_listening_t  *bpf_listening, *bls;

  888.     bcf = ngx_quic_bpf_get_conf((ngx_cycle_t *) ngx_cycle);

  889.     if (!bcf->enabled || !lc->listening->quic || !lc->listening->reuseport) {
  890.         *fd = lc->fd;
  891.         return NGX_OK;
  892.     }

  893.     grp = ngx_quic_bpf_find_group(bcf, lc->listening);

  894.     if (grp == NULL) {
  895.         *fd = lc->fd;
  896.         return NGX_OK;
  897.     }

  898.     if (ngx_worker >= grp->listening.nelts) {
  899.         return NGX_ERROR;
  900.     }

  901.     bpf_listening = grp->listening.elts;
  902.     bls = &bpf_listening[ngx_worker];

  903.     if (bls->fd == (ngx_socket_t) -1) {
  904.         return NGX_ERROR;
  905.     }

  906.     if (bls->connection == NULL) {
  907.         c = ngx_get_connection(bls->fd, lc->log);
  908.         if (c == NULL) {
  909.             return NGX_ERROR;
  910.         }

  911.         c->type = SOCK_DGRAM;
  912.         c->log = lc->log;
  913.         c->listening = bls->listening;

  914.         rev = c->read;
  915.         rev->quic = 1;
  916.         rev->log = c->log;
  917.         rev->handler = ngx_quic_recvmsg;

  918.         if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
  919.             ngx_free_connection(c);
  920.             return NGX_ERROR;
  921.         }

  922.         bls->connection = c;

  923.         ngx_log_debug1(NGX_LOG_DEBUG_EVENT, lc->log, 0,
  924.                        "quic bpf socket connection fd:%d", bls->fd);
  925.     }

  926.     *fd = bls->fd;

  927.     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, lc->log, 0,
  928.                    "quic bpf socket fd:%d", bls->fd);

  929.     return NGX_OK;
  930. }


  931. ngx_int_t
  932. ‌ngx_quic_bpf_get_worker_key(ngx_connection_t *c, uint64_t *key)
  933. {
  934.     ngx_quic_bpf_conf_t       *bcf;
  935.     ngx_quic_sock_group_t     *grp;
  936.     ngx_quic_bpf_listening_t  *bls;

  937.     bcf = ngx_quic_bpf_get_conf((ngx_cycle_t *) ngx_cycle);

  938.     if (!bcf->enabled || !c->listening->quic || !c->listening->reuseport) {
  939.         return NGX_DECLINED;
  940.     }

  941.     grp = ngx_quic_bpf_find_group(bcf, c->listening);

  942.     if (grp == NULL) {
  943.         return NGX_DECLINED;
  944.     }

  945.     if (ngx_worker >= grp->listening.nelts) {
  946.         return NGX_ERROR;
  947.     }

  948.     bls = grp->listening.elts;

  949.     if (bls[ngx_worker].fd == (ngx_socket_t) -1) {
  950.         return NGX_ERROR;
  951.     }

  952.     *key = bls[ngx_worker].key;

  953.     return NGX_OK;
  954. }