This is an automated email from the ASF dual-hosted git repository.

bmahler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git


The following commit(s) were added to refs/heads/master by this push:
     new d5f4fda30 [ebpf] Introduced API for loading eBPF programs into the 
kernel.
d5f4fda30 is described below

commit d5f4fda307265e9b8904bca0be2a302835e27f34
Author: Devin Leamy <[email protected]>
AuthorDate: Wed Mar 6 20:33:33 2024 +0000

    [ebpf] Introduced API for loading eBPF programs into the kernel.
    
    Introduces the `ebpf::ProgramBuilder` and `ebpf::Program` classes,
    and `ebpf::load(program)` function, allowing generic eBPF programs
    to be loaded into the kernel.
---
 src/linux/ebpf.cpp |  84 +++++++++++++++++++++++++++++++++++++-
 src/linux/ebpf.hpp | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 199 insertions(+), 1 deletion(-)

diff --git a/src/linux/ebpf.cpp b/src/linux/ebpf.cpp
index 22eab1e5c..7bffcb68c 100644
--- a/src/linux/ebpf.cpp
+++ b/src/linux/ebpf.cpp
@@ -14,4 +14,86 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "linux/ebpf.hpp"
\ No newline at end of file
+#include "linux/ebpf.hpp"
+
+#include <linux/bpf.h>
+#include <sys/syscall.h>
+
+#include <string>
+#include <vector>
+
+#include "stout/check.hpp"
+#include "stout/error.hpp"
+#include "stout/try.hpp"
+
+using std::string;
+using std::vector;
+
+namespace ebpf {
+
+Try<int, ErrnoError> bpf(int cmd, bpf_attr* attr, size_t size)
+{
+  // We retry the system call `attempts` times on EAGAIN. The default is 5,
+  // as per what's done by libbpf:
+  // https://github.com/libbpf/libbpf/blob/master/src/bpf.h#L71-L75
+  int result, attempts = 5;
+  do {
+    // glibc does not expose the bpf() function, requiring us to make the
+    // syscall directly: https://lwn.net/Articles/655028/
+    result = (int)syscall(__NR_bpf, cmd, attr, size);
+  } while (result == -1 && errno == EAGAIN && --attempts > 0);
+
+  if (result == -1) {
+    return ErrnoError();
+  }
+  return result;
+}
+
+
+Program::Program(bpf_prog_type _type) : type(_type) {}
+
+
+void Program::append(vector<bpf_insn>&& instructions)
+{
+  program.insert(
+      program.end(),
+      std::make_move_iterator(instructions.begin()),
+      std::make_move_iterator(instructions.end()));
+}
+
+
+Try<int> load(const Program& program)
+{
+  bpf_attr attribute;
+  std::memset(&attribute, 0, sizeof(attribute));
+  attribute.prog_type = program.type;
+  attribute.insn_cnt = program.program.size();
+  attribute.insns = reinterpret_cast<uint64_t>(program.program.data());
+  attribute.license = reinterpret_cast<uint64_t>("Apache 2.0");
+
+  Try<int, ErrnoError> fd = bpf(BPF_PROG_LOAD, &attribute, sizeof(attribute));
+
+  if (fd.isError() && fd.error().code == EACCES) {
+    // If bpf() fails with EACCES (a verifier error) the system call is called
+    // again with an additional buffer to capture the verifier error logs.
+    string verifier_logs(8196, '\0');
+    attribute.log_level = 1;
+    attribute.log_buf = reinterpret_cast<uint64_t>(&verifier_logs.front());
+    attribute.log_size = verifier_logs.size();
+
+    fd = bpf(BPF_PROG_LOAD, &attribute, sizeof(attribute));
+
+    CHECK_ERROR(fd);
+    CHECK_EQ(EACCES, fd.error().code)
+      << "Expected BPF syscall to fail again with EACCES";
+    return Error(string("BPF verifier failed: ") + verifier_logs.c_str());
+  }
+
+  if (fd.isError()) {
+    return Error("Unexpected error from BPF syscall: " + fd.error().message);
+  }
+
+  return *fd;
+}
+
+} // namespace ebpf {
diff --git a/src/linux/ebpf.hpp b/src/linux/ebpf.hpp
index 959d31471..b55e3b962 100644
--- a/src/linux/ebpf.hpp
+++ b/src/linux/ebpf.hpp
@@ -14,7 +14,123 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// TODO(dleamy): Look into using libbpf: https://github.com/libbpf/libbpf
+//               to simplify and or replace the low-level BPF operations.
+
 #ifndef __EBPF_HPP__
 #define __EBPF_HPP__
 
+#include <linux/bpf.h>
+
+#include <vector>
+
+#include "stout/try.hpp"
+
+namespace ebpf {
+
+// eBPF program.
+class Program
+{
+public:
+  explicit Program(bpf_prog_type type);
+
+  // Append instructions to the end of the eBPF program.
+  void append(std::vector<bpf_insn>&& instructions);
+
+  // Type of eBPF program.
+  const bpf_prog_type type;
+
+  // Instructions of the eBPF program.
+  std::vector<bpf_insn> program;
+};
+
+
+// Loads the provided eBPF program into the kernel and returns the file
+// descriptor of loaded program.
+Try<int> load(const Program& program);
+
+
+// Utility macros for constructing eBPF instructions.
+#define BPF_ALU32_IMM(OP, DST, IMM)       \
+  ((bpf_insn){                            \
+    .code = BPF_ALU | BPF_OP(OP) | BPF_K, \
+    .dst_reg = DST,                       \
+    .src_reg = 0,                         \
+    .off = 0,                             \
+    .imm = IMM})
+
+
+#define BPF_LDX_MEM(SIZE, DST, SRC, OFF)        \
+  ((bpf_insn){                                  \
+    .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \
+    .dst_reg = DST,                             \
+    .src_reg = SRC,                             \
+    .off = OFF,                                 \
+    .imm = 0})
+
+
+#define BPF_MOV64_REG(DST, SRC)          \
+  ((bpf_insn){                           \
+    .code = BPF_ALU64 | BPF_MOV | BPF_X, \
+    .dst_reg = DST,                      \
+    .src_reg = SRC,                      \
+    .off = 0,                            \
+    .imm = 0})
+
+
+#define BPF_JMP_A(OFF)        \
+  ((bpf_insn){                \
+    .code = BPF_JMP | BPF_JA, \
+    .dst_reg = 0,             \
+    .src_reg = 0,             \
+    .off = OFF,               \
+    .imm = 0})
+
+
+#define BPF_JMP_IMM(OP, DST, IMM, OFF)    \
+  ((bpf_insn){                            \
+    .code = BPF_JMP | BPF_OP(OP) | BPF_K, \
+    .dst_reg = DST,                       \
+    .src_reg = 0,                         \
+    .off = OFF,                           \
+    .imm = IMM})
+
+
+#define BPF_JMP_REG(OP, DST, SRC, OFF)    \
+  ((bpf_insn){                            \
+    .code = BPF_JMP | BPF_OP(OP) | BPF_X, \
+    .dst_reg = DST,                       \
+    .src_reg = SRC,                       \
+    .off = OFF,                           \
+    .imm = 0})
+
+
+#define BPF_MOV64_IMM(DST, IMM)          \
+  ((bpf_insn){                           \
+    .code = BPF_ALU64 | BPF_MOV | BPF_K, \
+    .dst_reg = DST,                      \
+    .src_reg = 0,                        \
+    .off = 0,                            \
+    .imm = IMM})
+
+
+#define BPF_MOV32_REG(DST, SRC)        \
+  ((bpf_insn){                         \
+    .code = BPF_ALU | BPF_MOV | BPF_X, \
+    .dst_reg = DST,                    \
+    .src_reg = SRC,                    \
+    .off = 0,                          \
+    .imm = 0})
+
+
+#define BPF_EXIT_INSN()         \
+  ((bpf_insn){                  \
+    .code = BPF_JMP | BPF_EXIT, \
+    .dst_reg = 0,               \
+    .src_reg = 0,               \
+    .off = 0,                   \
+    .imm = 0})
+
+} // namespace ebpf {
+
 #endif // __EBPF_HPP__
\ No newline at end of file

Reply via email to