src/event/quic/bpf/ngx_quic_reuseport_helper.c - nginx source code

Global variables 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 advance_data(nbytes)                                                  \
  34.     offset += nbytes;                                                         \
  35.     if (start + offset > end) {                                               \
  36.         debugmsg("cannot read %ld bytes at offset %ld", nbytes, offset);      \
  37.         goto failed;                                                          \
  38.     }                                                                         \
  39.     data = start + offset - 1;


  40. #define ngx_quic_parse_uint64(p)                                              \
  41.     (((__u64)(p)[0] << 56) |                                                  \
  42.      ((__u64)(p)[1] << 48) |                                                  \
  43.      ((__u64)(p)[2] << 40) |                                                  \
  44.      ((__u64)(p)[3] << 32) |                                                  \
  45.      ((__u64)(p)[4] << 24) |                                                  \
  46.      ((__u64)(p)[5] << 16) |                                                  \
  47.      ((__u64)(p)[6] << 8)  |                                                  \
  48.      ((__u64)(p)[7]))

  49. /*
  50. * actual map object is created by the "bpf" system call,
  51. * all pointers to this variable are replaced by the bpf loader
  52. */
  53. struct bpf_map_def SEC("maps") ngx_quic_sockmap;


  54. SEC(PROGNAME)
  55. int ngx_quic_select_socket_by_dcid(struct sk_reuseport_md *ctx)
  56. {
  57.     int             rc;
  58.     __u64           key;
  59.     size_t          len, offset;
  60.     unsigned char  *start, *end, *data, *dcid;

  61.     start = ctx->data;
  62.     end = (unsigned char *) ctx->data_end;
  63.     offset = 0;

  64.     advance_data(sizeof(struct udphdr)); /* data at UDP header */
  65.     advance_data(1); /* data at QUIC flags */

  66.     if (data[0] & NGX_QUIC_PKT_LONG) {

  67.         advance_data(4); /* data at QUIC version */
  68.         advance_data(1); /* data at DCID len */

  69.         len = data[0];   /* read DCID length */

  70.         if (len < 8) {
  71.             /* it's useless to search for key in such short DCID */
  72.             return SK_PASS;
  73.         }

  74.     } else {
  75.         len = NGX_QUIC_SERVER_CID_LEN;
  76.     }

  77.     dcid = &data[1];
  78.     advance_data(len); /* we expect the packet to have full DCID */

  79.     /* make verifier happy */
  80.     if (dcid + sizeof(__u64) > end) {
  81.         goto failed;
  82.     }

  83.     key = ngx_quic_parse_uint64(dcid);

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

  85.     switch (rc) {
  86.     case 0:
  87.         debugmsg("nginx quic socket selected by key 0x%llx", key);
  88.         return SK_PASS;

  89.     /* kernel returns positive error numbers, errno.h defines positive */
  90.     case -ENOENT:
  91.         debugmsg("nginx quic default route for key 0x%llx", key);
  92.         /* let the default reuseport logic decide which socket to choose */
  93.         return SK_PASS;

  94.     default:
  95.         debugmsg("nginx quic bpf_sk_select_reuseport err: %d key 0x%llx",
  96.                  rc, key);
  97.         goto failed;
  98.     }

  99. failed:
  100.     /*
  101.      * SK_DROP will generate ICMP, but we may want to process "invalid" packet
  102.      * in userspace quic to investigate further and finally react properly
  103.      * (maybe ignore, maybe send something in response or close connection)
  104.      */
  105.     return SK_PASS;
  106. }