changeset 8675:d3747ba486e7 quic

Core: added interface to linux bpf() system call. It contains wrappers for operations with BPF maps and for loading BPF programs.
author Vladimir Homutov <vl@nginx.com>
date Tue, 15 Dec 2020 15:23:07 +0300
parents 2c7f927f7999
children 7df607cb2d11
files auto/options auto/os/linux src/core/ngx_bpf.c src/core/ngx_bpf.h src/core/ngx_core.h
diffstat 5 files changed, 217 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/auto/options
+++ b/auto/options
@@ -169,6 +169,8 @@ USE_GEOIP=NO
 NGX_GOOGLE_PERFTOOLS=NO
 NGX_CPP_TEST=NO
 
+BPF_FOUND=NO
+
 NGX_LIBATOMIC=NO
 
 NGX_CPU_CACHE_LINE=
--- a/auto/os/linux
+++ b/auto/os/linux
@@ -208,3 +208,29 @@ ngx_include="sys/vfs.h";     . auto/incl
 
 
 CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
+
+
+# (E)BPF
+
+ngx_feature="BPF support"
+ngx_feature_name="NGX_HAVE_BPF"
+ngx_feature_run=no
+ngx_feature_incs="#include <linux/bpf.h>
+                  #include <sys/syscall.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="
+    union bpf_attr attr = { 0 };
+    /* only declare BPF support if all required features found */
+    attr.map_flags = 0;
+    attr.map_type = BPF_MAP_TYPE_SOCKHASH;
+    syscall(__NR_bpf, 0, &attr, 0);"
+
+. auto/feature
+
+if [ $ngx_found = yes ]; then
+    BPF_FOUND=YES
+
+    CORE_SRCS="$CORE_SRCS src/core/ngx_bpf.c"
+    CORE_DEPS="$CORE_DEPS src/core/ngx_bpf.h"
+fi
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_bpf.c
@@ -0,0 +1,143 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+#define NGX_BPF_LOGBUF_SIZE  (16 * 1024)
+
+
+static ngx_inline int
+ngx_bpf(enum bpf_cmd cmd, union bpf_attr *attr, unsigned int size)
+{
+    return syscall(__NR_bpf, cmd, attr, size);
+}
+
+
+void
+ngx_bpf_program_link(ngx_bpf_program_t *program, const char *symbol, int fd)
+{
+    ngx_uint_t        i;
+    ngx_bpf_reloc_t  *rl;
+
+    rl = program->relocs;
+
+    for (i = 0; i < program->nrelocs; i++) {
+        if (ngx_strcmp(rl[i].name, symbol) == 0) {
+            program->ins[rl[i].offset].src_reg = 1;
+            program->ins[rl[i].offset].imm = fd;
+        }
+    }
+}
+
+
+int
+ngx_bpf_load_program(ngx_log_t *log, ngx_bpf_program_t *program)
+{
+    int             fd;
+    union bpf_attr  attr;
+#if (NGX_DEBUG)
+    char            buf[NGX_BPF_LOGBUF_SIZE];
+#endif
+
+    ngx_memzero(&attr, sizeof(union bpf_attr));
+
+    attr.license = (uint64_t) program->license;
+    attr.prog_type = program->type;
+    attr.insns = (uint64_t) program->ins;
+    attr.insn_cnt = program->nins;
+
+#if (NGX_DEBUG)
+    /* for verifier errors */
+    attr.log_buf = (uint64_t) buf;
+    attr.log_size = NGX_BPF_LOGBUF_SIZE;
+    attr.log_level = 1;
+#endif
+
+    fd = ngx_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+    if (fd < 0) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                      "failed to load BPF program");
+
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,
+                       "bpf verifier: %s", buf);
+
+        return -1;
+    }
+
+    return fd;
+}
+
+
+int
+ngx_bpf_map_create(ngx_log_t *log, enum bpf_map_type type, int key_size,
+    int value_size, int max_entries, uint32_t map_flags)
+{
+    int             fd;
+    union bpf_attr  attr;
+
+    ngx_memzero(&attr, sizeof(union bpf_attr));
+
+    attr.map_type = type;
+    attr.key_size = key_size;
+    attr.value_size = value_size;
+    attr.max_entries = max_entries;
+    attr.map_flags = map_flags;
+
+    fd = ngx_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
+    if (fd < 0) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                      "failed to create BPF map");
+        return NGX_ERROR;
+    }
+
+    return fd;
+}
+
+
+int
+ngx_bpf_map_update(int fd, const void *key, const void *value, uint64_t flags)
+{
+    union bpf_attr attr;
+
+    ngx_memzero(&attr, sizeof(union bpf_attr));
+
+    attr.map_fd = fd;
+    attr.key = (uint64_t) key;
+    attr.value = (uint64_t) value;
+    attr.flags = flags;
+
+    return ngx_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
+}
+
+
+int
+ngx_bpf_map_delete(int fd, const void *key)
+{
+    union bpf_attr attr;
+
+    ngx_memzero(&attr, sizeof(union bpf_attr));
+
+    attr.map_fd = fd;
+    attr.key = (uint64_t) key;
+
+    return ngx_bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
+}
+
+
+int
+ngx_bpf_map_lookup(int fd, const void *key, void *value)
+{
+    union bpf_attr attr;
+
+    ngx_memzero(&attr, sizeof(union bpf_attr));
+
+    attr.map_fd = fd;
+    attr.key = (uint64_t) key;
+    attr.value = (uint64_t) value;
+
+    return ngx_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
+}
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_bpf.h
@@ -0,0 +1,43 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_BPF_H_INCLUDED_
+#define _NGX_BPF_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+#include <linux/bpf.h>
+
+
+typedef struct {
+    char                *name;
+    int                  offset;
+} ngx_bpf_reloc_t;
+
+typedef struct {
+    char                *license;
+    enum bpf_prog_type   type;
+    struct bpf_insn     *ins;
+    size_t               nins;
+    ngx_bpf_reloc_t     *relocs;
+    size_t               nrelocs;
+} ngx_bpf_program_t;
+
+
+void ngx_bpf_program_link(ngx_bpf_program_t *program, const char *symbol,
+    int fd);
+int ngx_bpf_load_program(ngx_log_t *log, ngx_bpf_program_t *program);
+
+int ngx_bpf_map_create(ngx_log_t *log, enum bpf_map_type type, int key_size,
+    int value_size, int max_entries, uint32_t map_flags);
+int ngx_bpf_map_update(int fd, const void *key, const void *value,
+    uint64_t flags);
+int ngx_bpf_map_delete(int fd, const void *key);
+int ngx_bpf_map_lookup(int fd, const void *key, void *value);
+
+#endif /* _NGX_BPF_H_INCLUDED_ */
--- a/src/core/ngx_core.h
+++ b/src/core/ngx_core.h
@@ -95,6 +95,9 @@ typedef void (*ngx_connection_handler_pt
 #include <ngx_connection.h>
 #include <ngx_syslog.h>
 #include <ngx_proxy_protocol.h>
+#if (NGX_HAVE_BPF)
+#include <ngx_bpf.h>
+#endif
 
 
 #define LF     (u_char) '\n'