From 990a11ba9d801780f493c1c352ce1124f1744112 Mon Sep 17 00:00:00 2001 From: fruffy Date: Tue, 24 Dec 2024 11:37:36 +0100 Subject: [PATCH] Revert. Signed-off-by: fruffy --- backends/ebpf/ebpfProgram.cpp | 2 +- backends/ebpf/runtime/ebpf_kernel.h | 50 ++++++++----- backends/ebpf/targets/kernel_target.py | 97 +++++++++++++++++--------- tools/ci-build.sh | 7 +- 4 files changed, 98 insertions(+), 58 deletions(-) diff --git a/backends/ebpf/ebpfProgram.cpp b/backends/ebpf/ebpfProgram.cpp index e57a6c55a14..c8381e3d2d9 100644 --- a/backends/ebpf/ebpfProgram.cpp +++ b/backends/ebpf/ebpfProgram.cpp @@ -102,7 +102,7 @@ void EBPFProgram::emitC(CodeBuilder *builder, const std::filesystem::path &heade if (model.arch == ModelArchitecture::XdpSwitch) builder->target->emitCodeSection(builder, "xdp"_cs); else - builder->target->emitCodeSection(builder, "prog"_cs); + builder->target->emitCodeSection(builder, "tc"_cs); builder->emitIndent(); builder->target->emitMain(builder, functionName, model.CPacketName.toString()); builder->blockStart(); diff --git a/backends/ebpf/runtime/ebpf_kernel.h b/backends/ebpf/runtime/ebpf_kernel.h index a021cffdd16..d735125c50e 100644 --- a/backends/ebpf/runtime/ebpf_kernel.h +++ b/backends/ebpf/runtime/ebpf_kernel.h @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + /// This file contains all functions and definitions necessary for the kernel target C /// code to compile. It must be included with any file generated by the p4c-ebpf kernel /// compiler. @@ -21,6 +22,7 @@ limitations under the License. #define BACKENDS_EBPF_RUNTIME_EBPF_KERNEL_H_ #include "ebpf_common.h" + #include "install/libbpf/include/bpf/bpf_endian.h" // definitions for bpf_ntohs etc... #undef htonl @@ -33,83 +35,93 @@ limitations under the License. #define bpf_htonll(x) htonll(x) #endif -#define load_byte(data, b) (*(((u8 *)(data)) + (b))) -#define load_half(data, b) bpf_ntohs(*(u16 *)((u8 *)(data) + (b))) -#define load_word(data, b) bpf_ntohl(*(u32 *)((u8 *)(data) + (b))) -#define load_dword(data, b) bpf_be64_to_cpu(*(u64 *)((u8 *)(data) + (b))) +#define load_byte(data, b) (*(((u8*)(data)) + (b))) +#define load_half(data, b) bpf_ntohs(*(u16 *)((u8*)(data) + (b))) +#define load_word(data, b) bpf_ntohl(*(u32 *)((u8*)(data) + (b))) +#define load_dword(data, b) bpf_be64_to_cpu(*(u64 *)((u8*)(data) + (b))) /// If we operate in user space we only need to include bpf.h and /// define the userspace API macros. /// For kernel programs we need to specify a list of kernel helpers. These are -/// taken from here: https://github.com/libbpf/libbpf/blob/master/src/bpf_helpers.h +/// taken from here: https://github.com/torvalds/linux/blob/master/tools/lib/bpf/bpf_helpers.h -#ifdef CONTROL_PLANE // BEGIN EBPF USER SPACE DEFINITIONS +#ifdef CONTROL_PLANE // BEGIN EBPF USER SPACE DEFINITIONS #include "install/libbpf/include/bpf/bpf.h" // bpf_obj_get/pin, bpf_map_update_elem -#define BPF_USER_MAP_UPDATE_ELEM(index, key, value, flags) \ +#define BPF_USER_MAP_UPDATE_ELEM(index, key, value, flags)\ bpf_map_update_elem(index, key, value, flags) #define BPF_OBJ_PIN(table, name) bpf_obj_pin(table, name) #define BPF_OBJ_GET(name) bpf_obj_get(name) -#else // BEGIN EBPF KERNEL DEFINITIONS +#else // BEGIN EBPF KERNEL DEFINITIONS // These files are provided by the system, not libbpf. -#include // BPF_ANY, +#include // BPF_ANY, #include // TC_ACT_OK, TC_ACT_SHOT // This file contains the definitions of all the kernel bpf essentials #include "install/libbpf/include/bpf/bpf_helpers.h" +#include "install/libbpf/include/bpf/btf.h" /// Simple descriptor which replaces the kernel sk_buff structure. #define SK_BUFF struct __sk_buff + #define REGISTER_START() #define REGISTER_TABLE(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES) \ struct { \ __uint(type, TYPE); \ KEY_TYPE *key; \ VALUE_TYPE *value; \ + __uint(pinning, LIBBPF_PIN_BY_NAME); \ __uint(max_entries, MAX_ENTRIES); \ - } NAME SEC(".maps"); + } NAME SEC(MAPS_ELF_SEC); #define REGISTER_TABLE_FLAGS(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES, FLAGS) \ struct { \ __uint(type, TYPE); \ KEY_TYPE *key; \ VALUE_TYPE *value; \ __uint(max_entries, MAX_ENTRIES); \ + __uint(pinning, LIBBPF_PIN_BY_NAME); \ __uint(map_flags, FLAGS); \ - } NAME SEC(".maps"); + } NAME SEC(MAPS_ELF_SEC); #define REGISTER_TABLE_INNER(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES, ID, INNER_IDX) \ struct NAME { \ __uint(type, TYPE); \ KEY_TYPE *key; \ VALUE_TYPE *value; \ __uint(max_entries, MAX_ENTRIES); \ - } NAME SEC(".maps"); + } NAME SEC(MAPS_ELF_SEC); #define REGISTER_TABLE_OUTER(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES, INNER_ID, INNER_NAME) \ struct { \ __uint(type, TYPE); \ KEY_TYPE *key; \ VALUE_TYPE *value; \ __uint(max_entries, MAX_ENTRIES); \ + __uint(pinning, LIBBPF_PIN_BY_NAME); \ __array(values, struct INNER_NAME); \ - } NAME SEC(".maps") = {.values = {&INNER_NAME}}; + } NAME SEC(MAPS_ELF_SEC) = {.values = {&INNER_NAME}}; #define REGISTER_TABLE_NO_KEY_TYPE(NAME, TYPE, KEY_SIZE, VALUE_TYPE, MAX_ENTRIES) \ struct { \ __uint(type, TYPE); \ __uint(key_size, KEY_SIZE); \ VALUE_TYPE *value; \ + __uint(pinning, LIBBPF_PIN_BY_NAME); \ __uint(max_entries, MAX_ENTRIES); \ - } NAME SEC(".maps"); + } NAME SEC(MAPS_ELF_SEC); #define REGISTER_END() -#define BPF_MAP_LOOKUP_ELEM(table, key) bpf_map_lookup_elem(&table, key) -#define BPF_MAP_UPDATE_ELEM(table, key, value, flags) bpf_map_update_elem(&table, key, value, flags) -#define BPF_MAP_DELETE_ELEM(table, key) bpf_map_delete_elem(&table, key) -#define BPF_USER_MAP_UPDATE_ELEM(index, key, value, flags) bpf_update_elem(index, key, value, flags) +#define BPF_MAP_LOOKUP_ELEM(table, key) \ + bpf_map_lookup_elem(&table, key) +#define BPF_MAP_UPDATE_ELEM(table, key, value, flags) \ + bpf_map_update_elem(&table, key, value, flags) +#define BPF_MAP_DELETE_ELEM(table, key) \ + bpf_map_delete_elem(&table, key) +#define BPF_USER_MAP_UPDATE_ELEM(index, key, value, flags)\ + bpf_update_elem(index, key, value, flags) #define BPF_OBJ_PIN(table, name) bpf_obj_pin(table, name) #define BPF_OBJ_GET(name) bpf_obj_get(name) -#endif // END EBPF KERNEL DEFINITIONS +#endif // END EBPF KERNEL DEFINITIONS #endif // BACKENDS_EBPF_RUNTIME_EBPF_KERNEL_H_ diff --git a/backends/ebpf/targets/kernel_target.py b/backends/ebpf/targets/kernel_target.py index b377b2f2548..c52eda198f2 100644 --- a/backends/ebpf/targets/kernel_target.py +++ b/backends/ebpf/targets/kernel_target.py @@ -122,43 +122,76 @@ def _kill_processes(self, procs) -> None: # kill process, 15 is SIGTERM os.kill(proc.pid, 15) - def _attach_filters(self, bridge: Bridge, proc: subprocess.Popen) -> int: - # Is this a XDP or TC (ebpf_filter) program? - p_result = testutils.exec_process(f"objdump -hj xdp {self.template}.o").returncode - is_xdp = p_result == testutils.SUCCESS + + def _load_filter(self, bridge, proc, port_name): # Load the specified eBPF object to "port_name" egress # As a side-effect, this may create maps in /sys/fs/bpf/ - # Get the command to load eBPF code to all the attached ports - result = bridge.ns_proc_write(proc, f"mount -t bpf none {self.EBPF_PATH}") - if result != testutils.SUCCESS: - return result - result = bridge.ns_proc_append(proc, f"mkdir -p {self.ebpf_map_path}") - if result != testutils.SUCCESS: - return result - load_type = "xdp" if is_xdp else "tc" - cmd = f"{self.bpftool} prog load {self.template}.o {self.EBPF_PATH}/ebpf_filter pinmaps {self.ebpf_map_path} type {load_type}" - result = bridge.ns_proc_append(proc, cmd) - if result != testutils.SUCCESS: - return result - attach_type = "xdp" if is_xdp else "tcx_egress" - ports = bridge.br_ports if is_xdp else bridge.edge_ports - if len(ports) > 0: - for port_name in ports: - result = bridge.ns_proc_append( - proc, - f"{self.bpftool} net attach {attach_type} pinned {self.EBPF_PATH}/ebpf_filter dev {port_name}", - ) - if result != testutils.SUCCESS: - return result + # Is this a XDP or TC (ebpf_filter) program? + result = testutils.exec_process(f"objdump -hj xdp {self.template}.o") + if result.returncode == testutils.SUCCESS: + # NB: XDP programs attach to the Rx end (but TC below attaches to Tx). + cmd = f"ip link set br_{port_name} xdpgeneric obj {self.template}.o sec xdp" else: - # No ports attached (no pcap files), load to bridge instead - result = bridge.ns_proc_append( - proc, - f"{self.bpftool} net attach {attach_type} pinned {self.EBPF_PATH}/ebpf_filter dev {bridge.br_name}", + # Add the qdisc. MUST be clsact layer. + bridge.ns_exec(f"tc qdisc add dev {port_name} clsact") + cmd = ( + f"tc filter add dev {port_name} egress" + f" bpf da obj {self.template}.o section tc verbose " ) + return bridge.ns_proc_write(proc, cmd) - return result + def _attach_filters(self, bridge, proc): + # Get the command to load eBPF code to all the attached ports + if len(bridge.edge_ports) > 0: + for port in bridge.edge_ports: + result = self._load_filter(bridge, proc, port) + bridge.ns_proc_append(proc, "") + else: + # No ports attached (no pcap files), load to bridge instead + result = self._load_filter(bridge, proc, bridge.br_name) + bridge.ns_proc_append(proc, "") + if result != testutils.SUCCESS: + return result + return testutils.SUCCESS + + # def _attach_filters(self, bridge: Bridge, proc: subprocess.Popen) -> int: + # # Is this a XDP or TC (ebpf_filter) program? + # p_result = testutils.exec_process(f"objdump -hj xdp {self.template}.o").returncode + # is_xdp = p_result == testutils.SUCCESS + # # Load the specified eBPF object to "port_name" egress + # # As a side-effect, this may create maps in /sys/fs/bpf/ + # # Get the command to load eBPF code to all the attached ports + # result = bridge.ns_proc_write(proc, f"mount -t bpf none {self.EBPF_PATH}") + # if result != testutils.SUCCESS: + # return result + # result = bridge.ns_proc_append(proc, f"mkdir -p {self.ebpf_map_path}") + # if result != testutils.SUCCESS: + # return result + # load_type = "xdp" if is_xdp else "tc" + # cmd = f"{self.bpftool} prog load {self.template}.o {self.EBPF_PATH}/ebpf_filter pinmaps {self.ebpf_map_path} type {load_type}" + # result = bridge.ns_proc_append(proc, cmd) + # if result != testutils.SUCCESS: + # return result + + # attach_type = "xdp" if is_xdp else "tcx_egress" + # ports = bridge.br_ports if is_xdp else bridge.edge_ports + # if len(ports) > 0: + # for port_name in ports: + # result = bridge.ns_proc_append( + # proc, + # f"{self.bpftool} net attach {attach_type} pinned {self.EBPF_PATH}/ebpf_filter dev {port_name}", + # ) + # if result != testutils.SUCCESS: + # return result + # else: + # # No ports attached (no pcap files), load to bridge instead + # result = bridge.ns_proc_append( + # proc, + # f"{self.bpftool} net attach {attach_type} pinned {self.EBPF_PATH}/ebpf_filter dev {bridge.br_name}", + # ) + + # return result def _run_tcpdump(self, bridge, filename, port): cmd = f"{bridge.get_ns_prefix()} tcpdump -w {filename} -i {port}" @@ -184,7 +217,7 @@ def _run_in_namespace(self, bridge): if result != testutils.SUCCESS: return result # Finally, append the actual runtime command to the process - result = bridge.ns_proc_append(proc, self._get_run_cmd()) + result = bridge.ns_proc_write(proc, self._get_run_cmd()) if result != testutils.SUCCESS: return result # Execute the command queue and close the process, retrieve result diff --git a/tools/ci-build.sh b/tools/ci-build.sh index 5e1fb6dcba4..6b0817472f8 100755 --- a/tools/ci-build.sh +++ b/tools/ci-build.sh @@ -173,12 +173,7 @@ function build_ebpf() { iproute2 \ iptables \ net-tools" - - if [[ "${DISTRIB_RELEASE}" == "18.04" || "${DISTRIB_RELEASE}" == "20.04" ]] ; then - P4C_EBPF_DEPS+=" libllvm-12-ocaml-dev libllvm12 llvm-12 llvm-12-dev llvm-12-doc llvm-12-examples llvm-12-runtime clang-12 clang-tools-12 clang-12-doc libclang-common-12-dev libclang-12-dev clang-format-12 clang-tidy-12 " - else - P4C_EBPF_DEPS+=" llvm clang " - fi + P4C_EBPF_DEPS+=" llvm clang " sudo apt-get install -y --no-install-recommends ${P4C_EBPF_DEPS} }