aboutsummaryrefslogtreecommitdiff
path: root/src/event/quic/bpf/ngx_quic_reuseport_helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/event/quic/bpf/ngx_quic_reuseport_helper.c')
-rw-r--r--src/event/quic/bpf/ngx_quic_reuseport_helper.c140
1 files changed, 140 insertions, 0 deletions
diff --git a/src/event/quic/bpf/ngx_quic_reuseport_helper.c b/src/event/quic/bpf/ngx_quic_reuseport_helper.c
new file mode 100644
index 000000000..999e7607c
--- /dev/null
+++ b/src/event/quic/bpf/ngx_quic_reuseport_helper.c
@@ -0,0 +1,140 @@
+#include <errno.h>
+#include <linux/string.h>
+#include <linux/udp.h>
+#include <linux/bpf.h>
+/*
+ * the bpf_helpers.h is not included into linux-headers, only available
+ * with kernel sources in "tools/lib/bpf/bpf_helpers.h" or in libbpf.
+ */
+#include <bpf/bpf_helpers.h>
+
+
+#if !defined(SEC)
+#define SEC(NAME) __attribute__((section(NAME), used))
+#endif
+
+
+#if defined(LICENSE_GPL)
+
+/*
+ * To see debug:
+ *
+ * echo 1 > /sys/kernel/debug/tracing/events/bpf_trace/enable
+ * cat /sys/kernel/debug/tracing/trace_pipe
+ * echo 0 > /sys/kernel/debug/tracing/events/bpf_trace/enable
+ */
+
+#define debugmsg(fmt, ...) \
+do { \
+ char __buf[] = fmt; \
+ bpf_trace_printk(__buf, sizeof(__buf), ##__VA_ARGS__); \
+} while (0)
+
+#else
+
+#define debugmsg(fmt, ...)
+
+#endif
+
+char _license[] SEC("license") = LICENSE;
+
+/*****************************************************************************/
+
+#define NGX_QUIC_PKT_LONG 0x80 /* header form */
+#define NGX_QUIC_SERVER_CID_LEN 20
+
+
+#define advance_data(nbytes) \
+ offset += nbytes; \
+ if (start + offset > end) { \
+ debugmsg("cannot read %ld bytes at offset %ld", nbytes, offset); \
+ goto failed; \
+ } \
+ data = start + offset - 1;
+
+
+#define ngx_quic_parse_uint64(p) \
+ (((__u64)(p)[0] << 56) | \
+ ((__u64)(p)[1] << 48) | \
+ ((__u64)(p)[2] << 40) | \
+ ((__u64)(p)[3] << 32) | \
+ ((__u64)(p)[4] << 24) | \
+ ((__u64)(p)[5] << 16) | \
+ ((__u64)(p)[6] << 8) | \
+ ((__u64)(p)[7]))
+
+/*
+ * actual map object is created by the "bpf" system call,
+ * all pointers to this variable are replaced by the bpf loader
+ */
+struct bpf_map_def SEC("maps") ngx_quic_sockmap;
+
+
+SEC(PROGNAME)
+int ngx_quic_select_socket_by_dcid(struct sk_reuseport_md *ctx)
+{
+ int rc;
+ __u64 key;
+ size_t len, offset;
+ unsigned char *start, *end, *data, *dcid;
+
+ start = ctx->data;
+ end = (unsigned char *) ctx->data_end;
+ offset = 0;
+
+ advance_data(sizeof(struct udphdr)); /* data at UDP header */
+ advance_data(1); /* data at QUIC flags */
+
+ if (data[0] & NGX_QUIC_PKT_LONG) {
+
+ advance_data(4); /* data at QUIC version */
+ advance_data(1); /* data at DCID len */
+
+ len = data[0]; /* read DCID length */
+
+ if (len < 8) {
+ /* it's useless to search for key in such short DCID */
+ return SK_PASS;
+ }
+
+ } else {
+ len = NGX_QUIC_SERVER_CID_LEN;
+ }
+
+ dcid = &data[1];
+ advance_data(len); /* we expect the packet to have full DCID */
+
+ /* make verifier happy */
+ if (dcid + sizeof(__u64) > end) {
+ goto failed;
+ }
+
+ key = ngx_quic_parse_uint64(dcid);
+
+ rc = bpf_sk_select_reuseport(ctx, &ngx_quic_sockmap, &key, 0);
+
+ switch (rc) {
+ case 0:
+ debugmsg("nginx quic socket selected by key 0x%llx", key);
+ return SK_PASS;
+
+ /* kernel returns positive error numbers, errno.h defines positive */
+ case -ENOENT:
+ debugmsg("nginx quic default route for key 0x%llx", key);
+ /* let the default reuseport logic decide which socket to choose */
+ return SK_PASS;
+
+ default:
+ debugmsg("nginx quic bpf_sk_select_reuseport err: %d key 0x%llx",
+ rc, key);
+ goto failed;
+ }
+
+failed:
+ /*
+ * SK_DROP will generate ICMP, but we may want to process "invalid" packet
+ * in userspace quic to investigate further and finally react properly
+ * (maybe ignore, maybe send something in response or close connection)
+ */
+ return SK_PASS;
+}