Skip to content

Commit

Permalink
Revert.
Browse files Browse the repository at this point in the history
Signed-off-by: fruffy <[email protected]>
  • Loading branch information
fruffy committed Dec 24, 2024
1 parent fbc2432 commit 43a55e3
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 59 deletions.
2 changes: 1 addition & 1 deletion backends/ebpf/ebpfProgram.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
118 changes: 98 additions & 20 deletions backends/ebpf/runtime/ebpf_kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ 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.
#ifndef BACKENDS_EBPF_RUNTIME_EBPF_KERNEL_H_
#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
Expand All @@ -33,83 +35,159 @@ 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 <linux/bpf.h> // BPF_ANY,
#include <linux/pkt_cls.h> // TC_ACT_OK, TC_ACT_SHOT
#include "contrib/bpftool/include/uapi/linux/bpf.h" // BPF_ANY,
#include "contrib/bpftool/include/uapi/linux/pkt_cls.h" // 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

/// From iproute2, annotate table with BTF which allows to read types at runtime.
/// TODO: Do we need this with Ubuntu 24.04?
#define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val) \
struct ____btf_map_##name { \
type_key key; \
type_val value; \
}; \
struct ____btf_map_##name \
__attribute__ ((section(".maps." #name), used)) \
____btf_map_##name = {};

#define REGISTER_START()
#if (defined(__clang__) && (__clang_major__ < 12))
/// A helper structure used by an eBPF C program
/// to describe map attributes for the elf_bpf loader
/// FIXME: We only need this because we are loading with iproute2
struct bpf_elf_map {
__u32 type;
__u32 size_key;
__u32 size_value;
__u32 max_elem;
__u32 flags;
__u32 id;
__u32 pinning;
__u32 inner_id;
__u32 inner_idx;
};
/// Note: pinning exports the table name globally, do not remove.
#define REGISTER_TABLE(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES) \
struct bpf_elf_map SEC("maps") NAME = { \
.type = TYPE, \
.size_key = sizeof(KEY_TYPE), \
.size_value = sizeof(VALUE_TYPE), \
.max_elem = MAX_ENTRIES, \
.pinning = 2, \
.flags = 0, \
};
#define REGISTER_TABLE_INNER(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES, ID, INNER_IDX) \
struct bpf_elf_map SEC("maps") NAME = { \
.type = TYPE, \
.size_key = sizeof(KEY_TYPE), \
.size_value = sizeof(VALUE_TYPE), \
.max_elem = MAX_ENTRIES, \
.pinning = 2, \
.flags = 0, \
.id = ID, \
.inner_idx = INNER_IDX, \
};
#define REGISTER_TABLE_OUTER(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES, INNER_ID, INNER_NAME) \
struct bpf_elf_map SEC("maps") NAME = { \
.type = TYPE, \
.size_key = sizeof(KEY_TYPE), \
.size_value = sizeof(VALUE_TYPE), \
.max_elem = MAX_ENTRIES, \
.pinning = 2, \
.flags = 0, \
.inner_id = INNER_ID, \
};
#define REGISTER_TABLE_FLAGS(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES, FLAGS) \
struct bpf_elf_map SEC("maps") NAME = { \
.type = TYPE, \
.size_key = sizeof(KEY_TYPE), \
.size_value = sizeof(VALUE_TYPE), \
.max_elem = MAX_ENTRIES, \
.pinning = 2, \
.flags = FLAGS, \
};
#else
#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); \
__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);
#endif
#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_
9 changes: 9 additions & 0 deletions backends/ebpf/target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ void KernelSamplesTarget::emitTableDecl(Util::SourceCodeBuilder *builder, cstrin
flags);
}
builder->newline();
annotateTableWithBTF(builder, tblName, keyType, valueType);
}

void KernelSamplesTarget::emitTableDeclSpinlock(Util::SourceCodeBuilder *builder, cstring tblName,
Expand Down Expand Up @@ -130,12 +131,14 @@ void KernelSamplesTarget::emitMapInMapDecl(Util::SourceCodeBuilder *builder, cst
builder->appendFormat(registerInnerTable, innerName, kind, innerKeyType, innerValueType,
innerSize, innerMapIndex, innerMapIndex);
builder->newline();
annotateTableWithBTF(builder, innerName, innerKeyType, innerValueType);

kind = getBPFMapType(outerTableKind);
cstring keyType = outerTableKind == TableArray ? "__u32"_cs : outerKeyType;
builder->appendFormat(registerOuterTable, outerName, kind, keyType, "__u32", outerSize,
innerMapIndex, innerName);
builder->newline();
annotateTableWithBTF(builder, outerName, keyType, "__u32"_cs);
}

void KernelSamplesTarget::emitLicense(Util::SourceCodeBuilder *builder, cstring license) const {
Expand Down Expand Up @@ -199,6 +202,12 @@ void KernelSamplesTarget::emitTraceMessage(Util::SourceCodeBuilder *builder, con
builder->newline();
}

void KernelSamplesTarget::annotateTableWithBTF(Util::SourceCodeBuilder *builder, cstring name,
cstring keyType, cstring valueType) const {
builder->appendFormat("BPF_ANNOTATE_KV_PAIR(%v, %v, %v)", name, keyType, valueType);
builder->newline();
}

//////////////////////////////////////////////////////////////
void XdpTarget::emitResizeBuffer(Util::SourceCodeBuilder *builder, cstring buffer,
cstring offsetVar) const {
Expand Down
3 changes: 3 additions & 0 deletions backends/ebpf/target.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ class KernelSamplesTarget : public Target {
cstring sysMapPath() const override { return "/sys/fs/bpf/tc/globals"_cs; }

cstring packetDescriptorType() const override { return "struct __sk_buff"_cs; }

void annotateTableWithBTF(Util::SourceCodeBuilder *builder, cstring name, cstring keyType,
cstring valueType) const;
};

class P4TCTarget : public KernelSamplesTarget {
Expand Down
96 changes: 64 additions & 32 deletions backends/ebpf/targets/kernel_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,43 +122,75 @@ 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}"
Expand All @@ -184,7 +216,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
Expand Down
7 changes: 1 addition & 6 deletions tools/ci-build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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}
}

Expand Down

0 comments on commit 43a55e3

Please sign in to comment.