diff --git a/configure.ac b/configure.ac index 73a59037f..0666b118f 100644 --- a/configure.ac +++ b/configure.ac @@ -1258,6 +1258,7 @@ RB_CHK_SYSHEADER(linux/hw_breakpoint.h, [LINUX_HW_BREAKPOINT_H]) RB_CHK_SYSHEADER(linux/io_uring.h, [LINUX_IO_URING_H]) RB_CHK_SYSHEADER(linux/icmp.h, [LINUX_ICMP_H]) RB_CHK_SYSHEADER(linux/input-event-codes.h, [LINUX_INPUT_EVENT_CODES_H]) +RB_CHK_SYSHEADER(linux/bpf.h, [LINUX_BPF_H]) dnl windows platform RB_CHK_SYSHEADER(windows.h, [WINDOWS_H]) diff --git a/include/ircd/net/bpf.h b/include/ircd/net/bpf.h new file mode 100644 index 000000000..dfc3d34b2 --- /dev/null +++ b/include/ircd/net/bpf.h @@ -0,0 +1,51 @@ +// Matrix Construct +// +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2022 Jason Volk +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice is present in all copies. The +// full license for this software is available in the LICENSE file. + +#pragma once +#define HAVE_IRCD_NET_BPF_H + +namespace ircd::net::bpf +{ + struct map; + struct prog; + + extern log::log log; +} + +struct ircd::net::bpf::map +{ + fs::fd fd; + + public: + explicit operator bool() const + { + return bool(fd); + } + + map(); + ~map() noexcept; +}; + +struct ircd::net::bpf::prog +{ + const_buffer insns; + mutable_buffer log_buf; + fs::fd fd; + + public: + explicit operator bool() const + { + return bool(fd); + } + + prog(const const_buffer &, const mutable_buffer &log_buf); + prog(const const_buffer &); + ~prog() noexcept; +}; diff --git a/include/ircd/net/net.h b/include/ircd/net/net.h index 9f6e9a4d0..f40ed8740 100644 --- a/include/ircd/net/net.h +++ b/include/ircd/net/net.h @@ -37,6 +37,7 @@ namespace ircd::net #include "hostport.h" #include "ipaddr.h" #include "ipport.h" +#include "bpf.h" #include "dns.h" #include "dns_cache.h" #include "listener.h" diff --git a/ircd/Makefile.am b/ircd/Makefile.am index d41310a6e..d120e7d4b 100644 --- a/ircd/Makefile.am +++ b/ircd/Makefile.am @@ -247,6 +247,9 @@ libircd_la_SOURCES += net_dns_cache.cc libircd_la_SOURCES += net_dns_resolver.cc libircd_la_SOURCES += net_listener.cc libircd_la_SOURCES += net_listener_udp.cc +if LINUX +libircd_la_SOURCES += net_bpf.cc +endif libircd_la_SOURCES += server.cc libircd_la_SOURCES += client.cc libircd_la_SOURCES += resource.cc diff --git a/ircd/net_bpf.cc b/ircd/net_bpf.cc new file mode 100644 index 000000000..780d89749 --- /dev/null +++ b/ircd/net_bpf.cc @@ -0,0 +1,218 @@ +// The Construct +// +// Copyright (C) The Construct Developers, Authors & Contributors +// Copyright (C) 2016-2022 Jason Volk +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice is present in all copies. The +// full license for this software is available in the LICENSE file. + +#include +#include + +namespace ircd::net::bpf +{ + struct [[clang::internal_linkage]] call; + + static constexpr uint + log_bufs{8}, + log_buf_sz{4_KiB}; + + static thread_local uint log_bufn; + static thread_local char log_buf[log_bufs][log_buf_sz]; +} + +class [[gnu::visibility("internal")]] +ircd::net::bpf::call +{ + int ret; + + public: + operator const int &() const; + + call(const int &, union bpf_attr *const &); + call(const int &, const union bpf_attr &); +}; + +decltype(ircd::net::bpf::log) +ircd::net::bpf::log +{ + "net.bpf" +}; + +// +// bpf::prog +// + +ircd::net::bpf::prog::prog(const const_buffer &insns) +:prog +{ + insns, mutable_buffer + { + bpf::log_buf[log_bufn++ % log_bufs], log_buf_sz + } +} +{ + assert(log_bufn <= log_bufs); //TODO: XXX +} + +ircd::net::bpf::prog::prog(const const_buffer &insns, + const mutable_buffer &log_buf) +try +:insns +{ + insns +} +,log_buf +{ + log_buf +} +,fd +{ + #if defined(__clang__) || RB_CXX_EPOCH >= 11 + empty(insns)? -1: call + { + BPF_PROG_LOAD, bpf_attr + { + .prog_type = BPF_PROG_TYPE_SOCKET_FILTER, + .insn_cnt = u32(size(insns) / sizeof(bpf_insn)), + .insns = uintptr_t(data(insns)), + #ifdef _GNU_SOURCE + .license = uintptr_t("GPL"), + #endif + .log_level = !empty(log_buf)? 1U: 0, + .log_size = u32(size(log_buf)), + .log_buf = uintptr_t(data(log_buf)), + } + } + #endif +} +{ + if(!fd) + return; + + log::debug + { + log, "Loaded prog:%p fd:%d bin:%p bytes:%zu", + this, + int(fd), + ircd::data(insns), + ircd::size(insns), + }; +} +catch(const std::exception &e) +{ + const string_view log_str + { + data(log_buf), strnlen(data(log_buf), size(log_buf)) + }; + + uint i(0); + ircd::tokens(log_str, '\n', [this, &i] + (const string_view &line) + { + if(likely(line)) + log::error + { + log, "Log prog:%p %2u :%s", + this, + i++, + line, + }; + }); + + log::critical + { + log, "Failed to load prog:%p bin:%p bytes:%zu :%s", + this, + data(insns), + size(insns), + e.what(), + }; + + throw; +} + +ircd::net::bpf::prog::~prog() +noexcept +{ + if(!fd) + return; + + log::debug + { + log, "Unloading prog:%p fd:%d ...", + this, + int(fd), + }; +} + +// +// bpf::map +// + +ircd::net::bpf::map::map() +try +:fd +{ + #if defined(__clang__) || RB_CXX_EPOCH >= 11 + call + { + BPF_MAP_CREATE, bpf_attr + { + .map_type = BPF_MAP_TYPE_UNSPEC, + .key_size = 8, + .value_size = 8, + .max_entries = 8, + }, + }, + #endif +} +{ +} +catch(const std::exception &e) +{ + char pbuf[48]; + log::error + { + log, "Mapping failed :%s", + e.what(), + }; + + throw; +} + +ircd::net::bpf::map::~map() +noexcept +{ +} + +// +// internal +// + +ircd::net::bpf::call::call(const int &cmd, + const union bpf_attr &attr) +:call +{ + cmd, mutable_cast(&attr) +} +{ +} + +ircd::net::bpf::call::call(const int &cmd, + union bpf_attr *const &attr) +:ret +{ + int(sys::call(cmd, attr, sizeof(union bpf_attr))) +} +{ +} + +ircd::net::bpf::call::operator +const int &() +const +{ + return ret; +}