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

Global variables defined

Data types defined

Functions defined

Macros defined

Source code

  1. #include <errno.h>
  2. #include <linux/string.h>
  3. #include <linux/udp.h>
  4. #include <linux/bpf.h>
  5. /*
  6. * the bpf_helpers.h is not included into linux-headers, only available
  7. * with kernel sources in "tools/lib/bpf/bpf_helpers.h" or in libbpf.
  8. */
  9. #include <bpf/bpf_helpers.h>


  10. #if !defined(SEC)
  11. ‌#define SEC(NAME)  __attribute__((section(NAME), used))
  12. #endif


  13. #if defined(LICENSE_GPL)

  14. /*
  15. * To see debug:
  16. *
  17. *  echo 1 > /sys/kernel/debug/tracing/events/bpf_trace/enable
  18. *  cat /sys/kernel/debug/tracing/trace_pipe
  19. *  echo 0 > /sys/kernel/debug/tracing/events/bpf_trace/enable
  20. */

  21. ‌#define debugmsg(fmt, ...)                                                    \
  22. do {                                                                          \
  23.     char __buf[] = fmt;                                                       \
  24.     bpf_trace_printk(__buf, sizeof(__buf), ##__VA_ARGS__);                    \
  25. } while (0)

  26. #else

  27. ‌#define debugmsg(fmt, ...)

  28. #endif

  29. ‌char _license[] SEC("license") = LICENSE;

  30. /*****************************************************************************/

  31. ‌#define NGX_QUIC_PKT_LONG        0x80  /* header form */
  32. ‌#define NGX_QUIC_SERVER_CID_LEN  20


  33. ‌#define ngx_quic_parse_uint64(p)                                              \
  34.     (((__u64)(p)[0] << 56) |                                                  \
  35.      ((__u64)(p)[1] << 48) |                                                  \
  36.      ((__u64)(p)[2] << 40) |                                                  \
  37.      ((__u64)(p)[3] << 32) |                                                  \
  38.      ((__u64)(p)[4] << 24) |                                                  \
  39.      ((__u64)(p)[5] << 16) |                                                  \
  40.      ((__u64)(p)[6] << 8)  |                                                  \
  41.      ((__u64)(p)[7]))

  42. ‌#define ngx_quic_bpf_listen_key(worker)                                       \
  43.     (((__u64) 0xFF << 56) | ((__u64) (worker) & 0xFFFFFFFFFFFFULL))


  44. /*
  45. * actual map object is created by the "bpf" system call,
  46. * all pointers to this variable are replaced by the bpf loader
  47. */
  48. ‌struct {} ngx_quic_sockmap SEC(".maps");
  49. ‌struct {} ngx_quic_worker_counts SEC(".maps");


  50. ‌SEC(PROGNAME)
  51. int ngx_quic_select_socket_by_dcid(struct sk_reuseport_md *ctx)
  52. {
  53.     int             rc, worker_idx;
  54.     __u32           wc_key, *wc0, *wc1, n, m;
  55.     __u64           key;
  56.     size_t          offset;
  57.     unsigned char  *start, *end, *dcid, byte, buf[NGX_QUIC_SERVER_CID_LEN];

  58.     start = (unsigned char *) ctx->data;
  59.     end = (unsigned char *) ctx->data_end;
  60.     offset = sizeof(struct udphdr);

  61.     if (start + offset >= end) {

  62.         if (bpf_skb_load_bytes(ctx, offset, &byte, 1)) {
  63.             goto failed;
  64.         }

  65.     } else {
  66.         byte = start[offset];
  67.     }

  68.     if (byte & NGX_QUIC_PKT_LONG) {

  69.         offset += 5;

  70.         if (start + offset >= end) {

  71.             if (bpf_skb_load_bytes(ctx, offset, &byte, 1)) {
  72.                 goto failed;
  73.             }

  74.         } else {
  75.             byte = start[offset];
  76.         }

  77.         if (byte != NGX_QUIC_SERVER_CID_LEN) {
  78.             goto new;
  79.         }
  80.     }

  81.     offset++;

  82.     if (start + offset + NGX_QUIC_SERVER_CID_LEN > end) {

  83.         if (bpf_skb_load_bytes(ctx, offset, buf, NGX_QUIC_SERVER_CID_LEN)) {
  84.             goto failed;
  85.         }

  86.         dcid = buf;

  87.     } else {
  88.         dcid = start + offset;
  89.     }

  90.     key = ngx_quic_parse_uint64(dcid);

  91.     if ((key >> 56) == 0xFF) {
  92.         goto new;
  93.     }

  94.     rc = bpf_sk_select_reuseport(ctx, &ngx_quic_sockmap, &key, 0);

  95.     if (rc == 0) {
  96.         debugmsg("nginx quic worker socket selected by dcid");
  97.         return SK_PASS;
  98.     }

  99.     if (rc != -ENOENT) {
  100.         debugmsg("nginx quic bpf_sk_select_reuseport() failed: %d", rc);
  101.         return SK_DROP;
  102.     }

  103. new:

  104.     wc_key = 0;
  105.     wc0 = bpf_map_lookup_elem(&ngx_quic_worker_counts, &wc_key);
  106.     if (wc0 == NULL) {
  107.         debugmsg("nginx quic worker count 0 undefined");
  108.         return SK_DROP;
  109.     }

  110.     wc_key = 1;
  111.     wc1 = bpf_map_lookup_elem(&ngx_quic_worker_counts, &wc_key);
  112.     if (wc1 == NULL) {
  113.         debugmsg("nginx quic worker count 1 undefined");
  114.         return SK_DROP;
  115.     }

  116.     n = *wc0 > *wc1 ? *wc0 : *wc1;

  117.     if (n == 0) {
  118.         debugmsg("nginx quic no active workers");
  119.         return SK_DROP;
  120.     }

  121.     worker_idx = ctx->hash % n;
  122.     key = ngx_quic_bpf_listen_key(worker_idx);

  123.     rc = bpf_sk_select_reuseport(ctx, &ngx_quic_sockmap, &key, 0);

  124.     if (rc == 0) {
  125.         debugmsg("nginx quic listener socket selected worker index:%d",
  126.                  worker_idx);
  127.         return SK_PASS;
  128.     }

  129.     if (rc != -ENOENT) {
  130.         debugmsg("nginx quic bpf_sk_select_reuseport() failed: %d", rc);
  131.         return SK_DROP;
  132.     }

  133.     m = *wc0 < *wc1 ? *wc0 : *wc1;

  134.     if (m && m != n) {
  135.         worker_idx = ctx->hash % m;
  136.         key = ngx_quic_bpf_listen_key(worker_idx);

  137.         rc = bpf_sk_select_reuseport(ctx, &ngx_quic_sockmap, &key, 0);

  138.         if (rc == 0) {
  139.             debugmsg("nginx quic listener socket selected "
  140.                      "worker index:%d (fallback)", worker_idx);
  141.             return SK_PASS;
  142.         }

  143.         debugmsg("nginx quic bpf_sk_select_reuseport() fallback "
  144.                  "failed: %d", rc);
  145.     }

  146.     return SK_DROP;

  147. failed:

  148.     debugmsg("nginx quic bad datagram");

  149.     return SK_DROP;
  150. }