Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

manager: use built-in xdp prog #728

Merged
merged 9 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions manager/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ ENV PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/usr/lib64/pkgconfig

# Install build dependencies
RUN apt-get update -y && \
apt-get install -y git gcc pkg-config make m4 clang llvm zlib1g-dev libelf-dev libpcap-dev libcap-ng-dev meson
apt-get install -y git gcc pkg-config make m4 clang llvm zlib1g-dev libelf-dev libpcap-dev libcap-ng-dev meson gcc-multilib

# Clone and build the xdp-tools project
RUN git clone --recurse-submodules https://github.com/xdp-project/xdp-tools.git && \
Expand All @@ -22,7 +22,9 @@ RUN git clone --recurse-submodules https://github.com/xdp-project/xdp-tools.git
COPY . manager

# Build the MTL Manager
RUN cd manager && meson setup build && meson compile -C build
RUN cd manager && \
meson setup build && \
DESTDIR=/install meson install -C build

# Runtime stage, ubuntu 22.04
FROM ubuntu@sha256:149d67e29f765f4db62aa52161009e99e389544e25a8f43c8c89d4a445a7ca37
Expand All @@ -35,8 +37,7 @@ RUN apt-get update -y && \

# Copy the necessary binaries and libraries from the builder
COPY --from=builder /install /
COPY --from=builder /manager/build /app

WORKDIR /app
RUN ldconfig

ENTRYPOINT ["./MtlManager"]
ENTRYPOINT ["MtlManager"]
29 changes: 20 additions & 9 deletions manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,43 @@ MTL Manager is a daemon server designed to operate with root privileges. Its pri

## Build

To compile the MTL Manager, use the following commands:
To compile and install the MTL Manager, use the following commands:

```bash
meson setup build
meson compile -C build
sudo meson install -C build
```

Besides MTL Manager, it will also install a built-in XDP program for udp port filtering.

## Run

To run the MTL Manager, execute:

```bash
sudo ./build/MtlManager
sudo MtlManager
```

This command will start the MTL Manager with root privileges, which are necessary for the advanced eBPF and network configurations and management tasks it performs.

## Run with our XDP program

We have a modified version of the original AF_XDP eBPF program which allows user to add or remove udp dest port in the eBPF program to act as a packet filter, please see [ebpf tool](../tools/ebpf) for how to build it.

To run the MTL Manager with our XDP program, execute:
The XDP program for udp port filtering will be loaded along with the libxdp's built-in xsk program when the AF_XDP socket is created. It utilizes the xdp-dispatcher program provided by libxdp which allows running of multiple XDP programs in chain on the same interface. You can check the loaded programs with xdp-loader:

```bash
sudo MTL_XDP_PROG_PATH=/path/to/Media-Transport-Library/tools/ebpf/xsk.xdp.o ./build/MtlManager
$ sudo xdp-loader status
Interface Prio Program name Mode ID Tag Chain actions
--------------------------------------------------------------------------------------
lo <No XDP program loaded!>
ens787f0 <No XDP program loaded!>
ens787f1 <No XDP program loaded!>
ens785f0 xdp_dispatcher native 23661 90f686eb86991928
=> 19 mtl_dp_filter 23670 02aea45cd16e8656 XDP_DROP
=> 20 xsk_def_prog 23622 8f9c40757cb0a6a2 XDP_PASS
ens785f1 xdp_dispatcher native 23675 90f686eb86991928
=> 19 mtl_dp_filter 23678 02aea45cd16e8656 XDP_DROP
=> 20 xsk_def_prog 23639 8f9c40757cb0a6a2 XDP_PASS
virbr0 <No XDP program loaded!>
docker0 <No XDP program loaded!>
```

## Run in a Docker container
Expand All @@ -57,7 +69,6 @@ docker run -d \
--privileged --net=host \
-v /var/run/imtl:/var/run/imtl \
-v /sys/fs/bpf:/sys/fs/bpf \
-v "$(pwd)"/../tools/ebpf/xsk.xdp.o:/tmp/imtl/xdp_prog.o \
mtl-manager:latest
```

Expand Down
20 changes: 19 additions & 1 deletion manager/meson.build
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright 2023 Intel Corporation

project('mtl_manager', 'cpp', default_options: ['buildtype=release', 'cpp_std=c++17'])
project('mtl_manager', 'cpp', default_options: ['buildtype=release', 'cpp_std=c++17'], version: '0.2.0')

exec_env = host_machine.system()
set_variable('is_windows', exec_env == 'windows')
Expand Down Expand Up @@ -37,12 +37,30 @@ libxdp_dep = dependency('libxdp', required: false)
libbpf_dep = dependency('libbpf', required: false)
if libxdp_dep.found() and libbpf_dep.found()
add_global_arguments('-DMTL_HAS_XDP_BACKEND', language : 'cpp')
clang = find_program('clang')
llvm_strip = find_program('llvm-strip')
# Build XDP prog obj
xdp_src = files('mtl.xdp.c')
xdp_temp_obj = custom_target('mtl.xdp.temp.o',
input : xdp_src,
output : 'mtl.xdp.temp.o',
command : [clang, '-g', '-O2', '-target', 'bpf', '-c', '@INPUT@', '-o', '@OUTPUT@']
)
xdp_obj = custom_target('mtl.xdp.o',
depends : xdp_temp_obj,
input : xdp_temp_obj,
output : 'mtl.xdp.o',
install: true,
install_dir: get_option('prefix') + '/lib/bpf',
command : [llvm_strip, '-g', '@INPUT@', '-o', '@OUTPUT@']
)
else
message('libxdp and libbpf not found, no af_xdp backend')
endif

executable('MtlManager', sources,
cpp_args: cpp_args,
link_args: link_cpp_args,
install: true,
dependencies: [asan_dep, libxdp_dep, libbpf_dep]
)
64 changes: 64 additions & 0 deletions manager/mtl.xdp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
* Copyright(c) 2024 Intel Corporation
*/

//clang-format off
#include <linux/bpf.h>
//clang-format off

#include <bpf/bpf_helpers.h>
#include <xdp/parsing_helpers.h>
#include <xdp/xdp_helpers.h>

char LICENSE[] SEC("license") = "Dual BSD/GPL";

struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 65536);
__type(key, int);
__type(value, int);
} udp4_dp_filter SEC(".maps");

struct {
__uint(priority, 19);
__uint(XDP_PASS, 0);
__uint(XDP_DROP, 1);
} XDP_RUN_CONFIG(mtl_dp_filter);

static int __always_inline lookup_udp4_dp(int dp) {
int* value;

value = bpf_map_lookup_elem(&udp4_dp_filter, &dp);
if (value && *value != 0) return 1;
return 0;
}

SEC("xdp")
int mtl_dp_filter(struct xdp_md* ctx) {
void* data_end = (void*)(long)ctx->data_end;
void* data = (void*)(long)ctx->data;

struct hdr_cursor nh;
struct ethhdr* eth;
int eth_type;

nh.pos = data;
eth_type = parse_ethhdr(&nh, data_end, &eth);
if (eth_type != bpf_htons(ETH_P_IP)) return XDP_PASS;

struct iphdr* iphdr;
int ip_type;
ip_type = parse_iphdr(&nh, data_end, &iphdr);
if (ip_type != IPPROTO_UDP) return XDP_PASS;

struct udphdr* udphdr;
int ret;
ret = parse_udphdr(&nh, data_end, &udphdr);
if (ret < 0) return XDP_PASS;

int dst_port = bpf_ntohs(udphdr->dest);
if (lookup_udp4_dp(dst_port) == 0) return XDP_PASS;

/* go to next program: xsk_def_prog */
return XDP_DROP;
}
108 changes: 48 additions & 60 deletions manager/mtl_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@
class mtl_interface {
private:
const int ifindex;
#ifdef MTL_HAS_XDP_BACKEND
struct xdp_program* xdp_prog;
int xsks_map_fd;
int udp4_dp_filter_fd;
enum xdp_attach_mode xdp_mode;
#endif

private:
void log(const log_level& level, const std::string& message) const {
Expand All @@ -40,13 +44,22 @@ class mtl_interface {
mtl_interface(int ifindex);
~mtl_interface();

int get_xsks_map_fd() { return xsks_map_fd; }
int get_xsks_map_fd() {
#ifdef MTL_HAS_XDP_BACKEND
return xsks_map_fd;
#else
return -1;
#endif
}
int update_udp_dp_filter(uint16_t dst_port, bool add);
};

mtl_interface::mtl_interface(int ifindex)
: ifindex(ifindex), xdp_prog(nullptr), xsks_map_fd(-1) {
mtl_interface::mtl_interface(int ifindex) : ifindex(ifindex) {
#ifdef MTL_HAS_XDP_BACKEND
xdp_prog = nullptr;
xsks_map_fd = -1;
udp4_dp_filter_fd = -1;
xdp_mode = XDP_MODE_UNSPEC;
if (load_xdp() < 0) throw std::runtime_error("Failed to load XDP program.");
#endif

Expand All @@ -69,15 +82,13 @@ int mtl_interface::update_udp_dp_filter(uint16_t dst_port, bool add) {
return -1;
}

int map_fd = bpf_map__fd(
bpf_object__find_map_by_name(xdp_program__bpf_obj(xdp_prog), "udp4_dp_filter"));
if (map_fd < 0) {
log(log_level::WARNING, "Failed to get udp4_dp_filter map fd");
if (udp4_dp_filter_fd < 0) {
log(log_level::WARNING, "No valid udp4_dp_filter map fd");
return -1;
}

int value = add ? 1 : 0;
if (bpf_map_update_elem(map_fd, &dst_port, &value, BPF_ANY) < 0) {
if (bpf_map_update_elem(udp4_dp_filter_fd, &dst_port, &value, BPF_ANY) < 0) {
log(log_level::WARNING, "Failed to update udp4_dp_filter map");
return -1;
}
Expand Down Expand Up @@ -135,74 +146,51 @@ int mtl_interface::clear_flow_rules() {
}

#ifdef MTL_HAS_XDP_BACKEND

int mtl_interface::load_xdp() {
/* get xdp prog path from env or use manager default path */
std::string xdp_prog_path;
char* xdp_prog_path_env = getenv("MTL_XDP_PROG_PATH");
if (xdp_prog_path_env != nullptr) {
xdp_prog_path = xdp_prog_path_env;
} else {
xdp_prog_path = "/tmp/imtl/xdp_prog.o";
/* load built-in xdp prog */
xdp_prog = xdp_program__find_file("mtl.xdp.o", NULL, NULL);
if (libxdp_get_error(xdp_prog)) {
log(log_level::ERROR, "Failed to load built-in xdp program.");
return -1;
}

if (!std::filesystem::is_regular_file(xdp_prog_path)) {
if (xdp_program__attach(xdp_prog, ifindex, XDP_MODE_NATIVE, 0) < 0) {
log(log_level::WARNING,
"Fallback to use libxdp default prog for " + xdp_prog_path + " is not valid.");
} else {
xdp_prog = xdp_program__open_file(xdp_prog_path.c_str(), NULL, NULL);
if (libxdp_get_error(xdp_prog)) {
log(log_level::WARNING,
"Fallback to use libxdp default prog for " + xdp_prog_path + " cannot load.");
xdp_prog = nullptr;
} else {
if (xdp_program__attach(xdp_prog, ifindex, XDP_MODE_NATIVE, 0) < 0) {
log(log_level::WARNING,
"Failed to attach XDP program with native mode, try skb mode.");
if (xdp_program__attach(xdp_prog, ifindex, XDP_MODE_SKB, 0) < 0) {
log(log_level::ERROR, "Failed to attach XDP program " + xdp_prog_path);
xdp_program__close(xdp_prog);
return -1;
}
}
"Failed to attach XDP program with native mode, try skb mode.");
if (xdp_program__attach(xdp_prog, ifindex, XDP_MODE_SKB, 0) < 0) {
log(log_level::ERROR, "Failed to attach XDP program.");
xdp_program__close(xdp_prog);
return -1;
}
xdp_mode = XDP_MODE_SKB;
}
xdp_mode = XDP_MODE_NATIVE;

if (xsk_setup_xdp_prog(ifindex, &xsks_map_fd) < 0 || xsks_map_fd < 0) {
if (xdp_prog != nullptr) {
xdp_program__detach(xdp_prog, ifindex, XDP_MODE_NATIVE, 0);
xdp_program__close(xdp_prog);
}
/* unload all xdp programs for the interface */
struct xdp_multiprog* multiprog = xdp_multiprog__get_from_ifindex(ifindex);
if (multiprog != nullptr) {
xdp_multiprog__detach(multiprog);
xdp_multiprog__close(multiprog);
}
/* save the filter map fd */
udp4_dp_filter_fd = bpf_map__fd(
bpf_object__find_map_by_name(xdp_program__bpf_obj(xdp_prog), "udp4_dp_filter"));
if (udp4_dp_filter_fd < 0) {
log(log_level::ERROR, "Failed to get udp4_dp_filter map fd.");
unload_xdp();
return -1;
}

if (xsk_setup_xdp_prog(ifindex, &xsks_map_fd) < 0 || xsks_map_fd < 0) {
log(log_level::ERROR, "Failed to setup AF_XDP socket.");
unload_xdp();
return -1;
}

if (xdp_prog == nullptr) xdp_prog_path = "<libxdp_default>";
log(log_level::INFO, "Loaded xdp prog " + xdp_prog_path);
log(log_level::INFO, "Loaded xdp prog.");
return 0;
}

void mtl_interface::unload_xdp() {
if (xdp_prog != nullptr) {
xdp_program__detach(xdp_prog, ifindex, XDP_MODE_NATIVE, 0);
xdp_program__close(xdp_prog);
xdp_prog = nullptr;
} else {
log(log_level::WARNING, "Using libxdp default prog. Try to unload all.");
}
/* unload all xdp programs for the interface */
struct xdp_multiprog* multiprog = xdp_multiprog__get_from_ifindex(ifindex);
if (multiprog != nullptr) {
xdp_multiprog__detach(multiprog);
xdp_multiprog__close(multiprog);
}
log(log_level::INFO, "Unloaded xdp prog");
xdp_program__detach(xdp_prog, ifindex, xdp_mode, 0);
xdp_program__close(xdp_prog);

log(log_level::INFO, "Unloaded xdp prog.");
}
#endif

Expand Down
Loading