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

new(driver/modern_bpf,userspace/libpman): support multiple programs for each event #2255

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
2 changes: 1 addition & 1 deletion .github/workflows/driverkit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
kernelrelease: 6.4.1-1.el9.elrepo.aarch64
target: centos
kernelurls: https://download.falco.org/fixtures/libs/kernel-ml-devel-6.4.1-1.el9.elrepo.aarch64.rpm
runs-on: ubuntu-latest
runs-on: ubuntu-24.04-arm
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improvement: run driverkit arm64 CI on arm64 gh runners.

container:
image: falcosecurity/driverkit:latest
steps:
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/drivers_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,9 @@ jobs:
cd src && make install
cd ../../
git clone https://github.com/libbpf/libbpf.git --branch v1.3.0 --single-branch
cd libbpf/src && BUILD_STATIC_ONLY=y DESTDIR=/ make install
cd libbpf/src && BUILD_STATIC_ONLY=y DESTDIR=/ make install install_uapi_headers
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix: s390x was using system libbpf headers, not the one built by us in the CI.

ln -s /usr/lib64/libbpf.a /usr/lib/s390x-linux-gnu/
ln -s /usr/include/bpf /usr/include/s390x-linux-gnu/
# Please note: we cannot inject the BPF probe inside QEMU, so right now, we only build it
run: |
git config --global --add safe.directory $GITHUB_WORKSPACE
Expand Down
3 changes: 0 additions & 3 deletions driver/modern_bpf/helpers/base/shared_size.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@
/* Maximum number of `iovec` structures that we can analyze. */
#define MAX_IOVCNT 32

/* Maximum number of supported sendmmsg/recvmmsg messages */
#define MAX_SENDMMSG_RECVMMSG_SIZE 8

/* Maximum number of `pollfd` structures that we can analyze. */
#define MAX_POLLFD 16

Expand Down
56 changes: 34 additions & 22 deletions driver/modern_bpf/helpers/store/auxmap_store_params.h
Original file line number Diff line number Diff line change
Expand Up @@ -1506,7 +1506,9 @@ static __always_inline void auxmap__store_fdlist_param(struct auxiliary_map *aux
typedef struct {
bool only_port_range;
ppm_event_code evt_type;
long mmsg_index;
long mmsg_index; // Only used by sendmmsg/recvmmsg to pass the current message index
unsigned long *mm_args; // Only used by sendmmsg/recvmmsg to reduce stack size to avoid
// verifier issues
} dynamic_snaplen_args;

static __always_inline void apply_dynamic_snaplen(struct pt_regs *regs,
Expand Down Expand Up @@ -1556,6 +1558,12 @@ static __always_inline void apply_dynamic_snaplen(struct pt_regs *regs,
*/
unsigned long args[5] = {0};
struct sockaddr *sockaddr = NULL;
union {
struct compat_msghdr compat_mh;
struct user_msghdr mh;
struct compat_mmsghdr compat_mmh;
struct mmsghdr mmh;
} msg_mh = {};

switch(input_args->evt_type) {
case PPME_SOCKET_SENDTO_X:
Expand All @@ -1568,43 +1576,43 @@ static __always_inline void apply_dynamic_snaplen(struct pt_regs *regs,
case PPME_SOCKET_SENDMSG_X: {
extract__network_args(args, 3, regs);
if(bpf_in_ia32_syscall()) {
struct compat_msghdr compat_mh = {};
if(likely(bpf_probe_read_user(&compat_mh,
if(likely(bpf_probe_read_user(&msg_mh.compat_mh,
bpf_core_type_size(struct compat_msghdr),
(void *)args[1]) == 0)) {
sockaddr = (struct sockaddr *)(unsigned long)(compat_mh.msg_name);
sockaddr = (struct sockaddr *)(unsigned long)(msg_mh.compat_mh.msg_name);
}
// in any case we break the switch.
break;
}

struct user_msghdr mh = {};
if(bpf_probe_read_user(&mh, bpf_core_type_size(struct user_msghdr), (void *)args[1]) == 0) {
sockaddr = (struct sockaddr *)mh.msg_name;
if(bpf_probe_read_user(&msg_mh.mh,
bpf_core_type_size(struct user_msghdr),
(void *)args[1]) == 0) {
sockaddr = (struct sockaddr *)msg_mh.mh.msg_name;
}
} break;

case PPME_SOCKET_RECVMMSG_X:
case PPME_SOCKET_SENDMMSG_X: {
extract__network_args(args, 3, regs);
// To avoid verifier stack size issues, sendmmsg and recvmmsg directly pass args
// in dynamic_snaplen_args.
// This also gives a small perf boost while using `bpf_loop` because we don't need
// to re-fetch first 3 syscall args at every iteration.
__builtin_memcpy(args, input_args->mm_args, 3 * sizeof(unsigned long));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sendmmsg/recvmmsg improvement: since we are going to call apply_dynamic_snaplen for each bpf_loop iteration, just call extract__network_args once (in the sendmmsg/recvmmsg main program) and then reference it.
This also fixes a verifier issue on clang-14 about stack size too large.

if(bpf_in_ia32_syscall()) {
struct compat_mmsghdr compat_mmh = {};
struct compat_mmsghdr *mmh_ptr = (struct compat_mmsghdr *)args[1];
if(likely(bpf_probe_read_user(&compat_mmh,
if(likely(bpf_probe_read_user(&msg_mh.compat_mmh,
bpf_core_type_size(struct compat_mmsghdr),
(void *)(mmh_ptr + input_args->mmsg_index)) == 0)) {
sockaddr = (struct sockaddr *)(unsigned long)(compat_mmh.msg_hdr.msg_name);
sockaddr = (struct sockaddr *)(unsigned long)(msg_mh.compat_mmh.msg_hdr.msg_name);
}
// in any case we break the switch.
break;
}

struct mmsghdr mmh = {};
struct mmsghdr *mmh_ptr = (struct mmsghdr *)args[1];
if(bpf_probe_read_user(&mmh,
if(bpf_probe_read_user(&msg_mh.mmh,
bpf_core_type_size(struct mmsghdr),
(void *)(mmh_ptr + input_args->mmsg_index)) == 0) {
sockaddr = (struct sockaddr *)mmh.msg_hdr.msg_name;
sockaddr = (struct sockaddr *)msg_mh.mmh.msg_hdr.msg_name;
}
} break;

Expand Down Expand Up @@ -1642,16 +1650,20 @@ static __always_inline void apply_dynamic_snaplen(struct pt_regs *regs,
port_remote = ntohs(port_remote);

if(port_remote == 0 && sockaddr != NULL) {
union {
struct sockaddr_in sockaddr_in;
struct sockaddr_in6 sockaddr_in6;
} saddr_in = {};
if(socket_family == AF_INET) {
struct sockaddr_in sockaddr_in = {};
bpf_probe_read_user(&sockaddr_in, bpf_core_type_size(struct sockaddr_in), sockaddr);
port_remote = ntohs(sockaddr_in.sin_port);
bpf_probe_read_user(&saddr_in.sockaddr_in,
bpf_core_type_size(struct sockaddr_in),
sockaddr);
port_remote = ntohs(saddr_in.sockaddr_in.sin_port);
} else {
struct sockaddr_in6 sockaddr_in6 = {};
bpf_probe_read_user(&sockaddr_in6,
bpf_probe_read_user(&saddr_in.sockaddr_in6,
bpf_core_type_size(struct sockaddr_in6),
sockaddr);
port_remote = ntohs(sockaddr_in6.sin6_port);
port_remote = ntohs(saddr_in.sockaddr_in6.sin6_port);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ int BPF_PROG(recvmmsg_e, struct pt_regs *regs, long id) {
typedef struct {
uint32_t fd;
struct mmsghdr *mmh;
struct pt_regs *regs;
void *ctx;
unsigned long *args;
} recvmmsg_data_t;

static __always_inline long handle_exit(uint32_t index, void *ctx) {
Expand Down Expand Up @@ -76,9 +75,10 @@ static __always_inline long handle_exit(uint32_t index, void *ctx) {
.only_port_range = true,
.evt_type = PPME_SOCKET_RECVMMSG_X,
.mmsg_index = index,
.mm_args = data->args,
};
uint16_t snaplen = maps__get_snaplen();
apply_dynamic_snaplen(data->regs, &snaplen, &snaplen_args);
apply_dynamic_snaplen(NULL, &snaplen, &snaplen_args);
if(snaplen > mmh.msg_len) {
snaplen = mmh.msg_len;
}
Expand Down Expand Up @@ -150,26 +150,67 @@ int BPF_PROG(recvmmsg_x, struct pt_regs *regs, long ret) {
}

/* Collect parameters at the beginning to manage socketcalls */
unsigned long args[2];
extract__network_args(args, 2, regs);
unsigned long args[3];
extract__network_args(args, 3, regs);
recvmmsg_data_t data = {
.fd = args[0],
.mmh = (struct mmsghdr *)args[1],
.regs = regs,
.ctx = ctx,
.args = args,
};

// We can't use bpf_loop() helper since the below check triggers a verifier failure:
// see
// https://lore.kernel.org/bpf/CAGQdkDt9zyQwr5JyftXqL=OLKscNcqUtEteY4hvOkx2S4GdEkQ@mail.gmail.com/T/#u
/*if(bpf_core_enum_value_exists(enum bpf_func_id, BPF_FUNC_loop)) {
uint32_t nr_loops = ret < 1024 ? ret : 1024;
bpf_loop(nr_loops, handle_exit, &data, 0);
} else {*/
for(int i = 0; i < ret && i < MAX_SENDMMSG_RECVMMSG_SIZE; i++) {
handle_exit(i, &data);
uint32_t nr_loops = ret < 1024 ? ret : 1024;
bpf_loop(nr_loops, handle_exit, &data, 0);

return 0;
}

SEC("tp_btf/sys_exit")
int BPF_PROG(recvmmsg_old_x, struct pt_regs *regs, long ret) {
if(ret <= 0) {
unsigned long fd = 0;
struct auxiliary_map *auxmap = auxmap__get();
if(!auxmap) {
return 0;
}

auxmap__preload_event_header(auxmap, PPME_SOCKET_RECVMMSG_X);

/* Parameter 1: res (type: PT_ERRNO) */
auxmap__store_s64_param(auxmap, ret);

/* Parameter 2: fd (type: PT_FD) */
extract__network_args(&fd, 1, regs);
auxmap__store_s64_param(auxmap, (int64_t)(int32_t)fd);

/* Parameter 3: size (type: PT_UINT32) */
auxmap__store_u32_param(auxmap, 0);

/* Parameter 4: data (type: PT_BYTEBUF) */
auxmap__store_empty_param(auxmap);

/* Parameter 5: tuple (type: PT_SOCKTUPLE) */
auxmap__store_empty_param(auxmap);

/* Parameter 6: msg_control (type: PT_BYTEBUF) */
auxmap__store_empty_param(auxmap);

auxmap__finalize_event_header(auxmap);

auxmap__submit_event(auxmap);
return 0;
}
//}

/* Collect parameters at the beginning to manage socketcalls */
unsigned long args[3];
extract__network_args(args, 3, regs);
recvmmsg_data_t data = {
.fd = args[0],
.mmh = (struct mmsghdr *)args[1],
.args = args,
};

// Send only first message
handle_exit(0, &data);

return 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ int BPF_PROG(sendmmsg_e, struct pt_regs *regs, long id) {
typedef struct {
uint32_t fd;
struct mmsghdr *mmh;
struct pt_regs *regs;
void *ctx;
unsigned long *args;
} sendmmsg_exit_t;

static __always_inline long handle_exit(uint32_t index, void *ctx) {
Expand Down Expand Up @@ -81,26 +80,23 @@ static __always_inline long handle_exit(uint32_t index, void *ctx) {
.only_port_range = true,
.evt_type = PPME_SOCKET_SENDMMSG_X,
.mmsg_index = index,
.mm_args = data->args,
};
uint16_t snaplen = maps__get_snaplen();
apply_dynamic_snaplen(data->regs, &snaplen, &snaplen_args);
apply_dynamic_snaplen(NULL, &snaplen, &snaplen_args);
if(mmh.msg_len > 0 && snaplen > mmh.msg_len) {
snaplen = mmh.msg_len;
}

/* Parameter 4: data (type: PT_BYTEBUF) */
unsigned long msghdr_pointer = (unsigned long)&mmh.msg_hdr;
auxmap__store_iovec_data_param(auxmap,
(unsigned long)mmh.msg_hdr.msg_iov,
mmh.msg_hdr.msg_iovlen,
snaplen);

/* Parameter 5: tuple (type: PT_SOCKTUPLE)*/
if(data->fd >= 0) {
auxmap__store_socktuple_param(auxmap, data->fd, OUTBOUND, mmh.msg_hdr.msg_name);
} else {
auxmap__store_empty_param(auxmap);
}
auxmap__store_socktuple_param(auxmap, data->fd, OUTBOUND, mmh.msg_hdr.msg_name);

/*=============================== COLLECT PARAMETERS ===========================*/

auxmap__finalize_event_header(auxmap);
Expand Down Expand Up @@ -143,26 +139,64 @@ int BPF_PROG(sendmmsg_x, struct pt_regs *regs, long ret) {
}

/* Collect parameters at the beginning to manage socketcalls */
unsigned long args[2];
extract__network_args(args, 2, regs);
unsigned long args[3];
extract__network_args(args, 3, regs);
sendmmsg_exit_t data = {
.fd = args[0],
.mmh = (struct mmsghdr *)args[1],
.regs = regs,
.ctx = ctx,
.args = args,
};

// We can't use bpf_loop() helper since the below check triggers a verifier failure:
// see
// https://lore.kernel.org/bpf/CAGQdkDt9zyQwr5JyftXqL=OLKscNcqUtEteY4hvOkx2S4GdEkQ@mail.gmail.com/T/#u
/*if(bpf_core_enum_value_exists(enum bpf_func_id, BPF_FUNC_loop)) {
uint32_t nr_loops = ret < 1024 ? ret : 1024;
bpf_loop(nr_loops, handle_exit, &data, 0);
} else {*/
for(int i = 0; i < ret && i < MAX_SENDMMSG_RECVMMSG_SIZE; i++) {
handle_exit(i, &data);
uint32_t nr_loops = ret < 1024 ? ret : 1024;
bpf_loop(nr_loops, handle_exit, &data, 0);

return 0;
}

SEC("tp_btf/sys_exit")
int BPF_PROG(sendmmsg_old_x, struct pt_regs *regs, long ret) {
if(ret <= 0) {
unsigned long fd = 0;
struct auxiliary_map *auxmap = auxmap__get();
if(!auxmap) {
return 0;
}

auxmap__preload_event_header(auxmap, PPME_SOCKET_SENDMMSG_X);

/* Parameter 1: res (type: PT_ERRNO) */
auxmap__store_s64_param(auxmap, ret);

/* Parameter 2: fd (type: PT_FD) */
extract__network_args(&fd, 1, regs);
auxmap__store_s64_param(auxmap, (int64_t)(int32_t)fd);

/* Parameter 3: size (type: PT_UINT32) */
auxmap__store_u32_param(auxmap, 0);

/* Parameter 4: data (type: PT_BYTEBUF) */
auxmap__store_empty_param(auxmap);

/* Parameter 5: tuple (type: PT_SOCKTUPLE) */
auxmap__store_empty_param(auxmap);

auxmap__finalize_event_header(auxmap);

auxmap__submit_event(auxmap);
return 0;
}
//}

/* Collect parameters at the beginning to manage socketcalls */
unsigned long args[3];
extract__network_args(args, 3, regs);
sendmmsg_exit_t data = {
.fd = args[0],
.mmh = (struct mmsghdr *)args[1],
.args = args,
};

// Only first message
handle_exit(0, &data);

return 0;
}
Expand Down
1 change: 1 addition & 0 deletions userspace/libpman/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ add_library(
src/configuration.c
src/state.c
src/sc_set.c
src/events_prog_table.c
)

target_include_directories(
Expand Down
8 changes: 8 additions & 0 deletions userspace/libpman/include/libpman.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ bool pman_check_support();
*/
int pman_open_probe(void);

/**
* @brief Prepares the bpf skeleton object checking if
* it satisfies each events_prog_name feature requirements for each prog.
*
* @return `0` on success, `errno` in case of error.
*/
int pman_prepare_progs_before_loading(void);

/**
* @brief Load into the kernel all the programs and maps
* contained into the skeleton.
Expand Down
Loading
Loading