diff --git a/driver/API_VERSION b/driver/API_VERSION index fcdb2e109f..1454f6ed4b 100644 --- a/driver/API_VERSION +++ b/driver/API_VERSION @@ -1 +1 @@ -4.0.0 +4.0.1 diff --git a/driver/CMakeLists.txt b/driver/CMakeLists.txt index f2a3a7ad94..131bdbf9f4 100644 --- a/driver/CMakeLists.txt +++ b/driver/CMakeLists.txt @@ -118,6 +118,8 @@ set(DRIVER_SOURCES ppm_tp.c ppm_consumer.h capture_macro.h + socketcall_to_syscall.h + socketcall_to_syscall.c ) foreach(FILENAME IN LISTS DRIVER_SOURCES) diff --git a/driver/Makefile.in b/driver/Makefile.in index 68ed22b979..1c5781b161 100644 --- a/driver/Makefile.in +++ b/driver/Makefile.in @@ -5,7 +5,7 @@ # MIT.txt or GPL.txt for full copies of the license. # -@DRIVER_NAME@-y += main.o dynamic_params_table.o fillers_table.o flags_table.o ppm_events.o ppm_fillers.o event_table.o syscall_table32.o syscall_table64.o ppm_cputime.o ppm_tp.o +@DRIVER_NAME@-y += main.o dynamic_params_table.o fillers_table.o flags_table.o ppm_events.o ppm_fillers.o event_table.o syscall_table32.o syscall_table64.o ppm_cputime.o ppm_tp.o socketcall_to_syscall.o obj-m += @DRIVER_NAME@.o ccflags-y := @KBUILD_FLAGS@ diff --git a/driver/SCHEMA_VERSION b/driver/SCHEMA_VERSION index 197c4d5c2d..005119baaa 100644 --- a/driver/SCHEMA_VERSION +++ b/driver/SCHEMA_VERSION @@ -1 +1 @@ -2.4.0 +2.4.1 diff --git a/driver/bpf/filler_helpers.h b/driver/bpf/filler_helpers.h index 96c8ecd302..25e8a0baa8 100644 --- a/driver/bpf/filler_helpers.h +++ b/driver/bpf/filler_helpers.h @@ -660,14 +660,26 @@ static __always_inline long bpf_fd_to_socktuple(struct filler_data *data, struct sockaddr_in *usrsockaddr_in = (struct sockaddr_in *)usrsockaddr; if (is_inbound) { - /* To take inbound info we cannot use the `src_addr` obtained from the syscall - * it could be empty! - * From kernel 3.13 we can take both ipv4 and ipv6 info from here - * https://elixir.bootlin.com/linux/v3.13/source/include/net/sock.h#L164 + /* To take peer address info we try to use the kernel where possible. + * TCP allows us to obtain the right information, while the kernel doesn't fill + * `sk->__sk_common.skc_daddr` for UDP connection. + * Instead of having a custom logic for each protocol we try to read from + * kernel structs and if we don't find valid data we fallback to userspace + * structs. */ - bpf_probe_read_kernel(&sip, sizeof(sip), &sk->__sk_common.skc_daddr); bpf_probe_read_kernel(&sport, sizeof(sport), &sk->__sk_common.skc_dport); - sport = ntohs(sport); + if(sport != 0) + { + /* We can read from the kernel */ + bpf_probe_read_kernel(&sip, sizeof(sip), &sk->__sk_common.skc_daddr); + sport = ntohs(sport); + } + else + { + /* Fallback to userspace struct */ + sip = usrsockaddr_in->sin_addr.s_addr; + sport = ntohs(usrsockaddr_in->sin_port); + } dip = ((struct sockaddr_in *)sock_address)->sin_addr.s_addr; dport = ntohs(((struct sockaddr_in *)sock_address)->sin_port); } else { @@ -722,10 +734,20 @@ static __always_inline long bpf_fd_to_socktuple(struct filler_data *data, struct sockaddr_in6 *usrsockaddr_in6 = (struct sockaddr_in6 *)usrsockaddr; if (is_inbound) { - bpf_probe_read_kernel(&in6, sizeof(in6), &sk->__sk_common.skc_v6_daddr); - sip6 = in6.in6_u.u6_addr8; bpf_probe_read_kernel(&sport, sizeof(sport), &sk->__sk_common.skc_dport); - sport = ntohs(sport); + if(sport != 0) + { + /* We can read from the kernel */ + bpf_probe_read_kernel(&in6, sizeof(in6), &sk->__sk_common.skc_v6_daddr); + sip6 = in6.in6_u.u6_addr8; + sport = ntohs(sport); + } + else + { + /* Fallback to userspace struct */ + sip6 = usrsockaddr_in6->sin6_addr.s6_addr; + sport = ntohs(usrsockaddr_in6->sin6_port); + } dip6 = ((struct sockaddr_in6 *)sock_address)->sin6_addr.s6_addr; dport = ntohs(((struct sockaddr_in6 *)sock_address)->sin6_port); } else { diff --git a/driver/main.c b/driver/main.c index 380f85c27c..410c6487b9 100644 --- a/driver/main.c +++ b/driver/main.c @@ -57,6 +57,10 @@ or GPL2.txt for full copies of the license. #include "ppm.h" #include "ppm_tp.h" +#ifdef _HAS_SOCKETCALL +#include "socketcall_to_syscall.h" +#endif + #define __NR_ia32_socketcall 102 MODULE_LICENSE("GPL"); @@ -1361,119 +1365,21 @@ static const unsigned char compat_nas[21] = { static long convert_network_syscalls(struct pt_regs *regs, long socketcall_syscall) { unsigned long __user args[6] = {}; + int new_syscall_id = 0; ppm_syscall_get_arguments(current, regs, args); /* args[0] is the specific syscall code */ - switch(args[0]) - { -#ifdef __NR_socket - case SYS_SOCKET: - return __NR_socket; -#endif - -#ifdef __NR_socketpair - case SYS_SOCKETPAIR: - return __NR_socketpair; -#endif - - case SYS_ACCEPT: -#if defined(CONFIG_S390) && defined(__NR_accept4) - return __NR_accept4; -#elif defined(__NR_accept) - return __NR_accept; -#endif - break; - -#ifdef __NR_accept4 - case SYS_ACCEPT4: - return __NR_accept4; -#endif - -#ifdef __NR_bind - case SYS_BIND: - return __NR_bind; -#endif - -#ifdef __NR_listen - case SYS_LISTEN: - return __NR_listen; -#endif - -#ifdef __NR_connect - case SYS_CONNECT: - return __NR_connect; -#endif - -#ifdef __NR_getsockname - case SYS_GETSOCKNAME: - return __NR_getsockname; -#endif - -#ifdef __NR_getpeername - case SYS_GETPEERNAME: - return __NR_getpeername; -#endif - -#ifdef __NR_getsockopt - case SYS_GETSOCKOPT: - return __NR_getsockopt; -#endif - -#ifdef __NR_setsockopt - case SYS_SETSOCKOPT: - return __NR_setsockopt; -#endif - -#ifdef __NR_recv - case SYS_RECV: - return __NR_recv; -#endif - -#ifdef __NR_recvfrom - case SYS_RECVFROM: - return __NR_recvfrom; -#endif - -#ifdef __NR_recvmsg - case SYS_RECVMSG: - return __NR_recvmsg; -#endif - -#ifdef __NR_recvmmsg - case SYS_RECVMMSG: - return __NR_recvmmsg; -#endif - -#ifdef __NR_send - case SYS_SEND: - return __NR_send; -#endif - -#ifdef __NR_sendto - case SYS_SENDTO: - return __NR_sendto; -#endif + new_syscall_id = socketcall_code_to_syscall_code(args[0]); -#ifdef __NR_sendmsg - case SYS_SENDMSG: - return __NR_sendmsg; -#endif - -#ifdef __NR_sendmmsg - case SYS_SENDMMSG: - return __NR_sendmmsg; -#endif - -#ifdef __NR_shutdown - case SYS_SHUTDOWN: - return __NR_shutdown; -#endif - default: - break; + /* If we are not able to convert the socketcall code to + * a valid syscall code we return the original socketcall id + * and we send a generic event + */ + if(new_syscall_id == -1) + { + new_syscall_id = socketcall_syscall; } - - // reset NR_socketcall and send a generic event - return socketcall_syscall; + return new_syscall_id; } static int load_socketcall_params(struct event_filler_arguments *filler_args) @@ -2912,7 +2818,11 @@ int scap_init(void) goto init_module_err; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0) g_ppm_class = class_create(THIS_MODULE, DRIVER_DEVICE_NAME); +#else + g_ppm_class = class_create(DRIVER_DEVICE_NAME); +#endif if (IS_ERR(g_ppm_class)) { pr_err("can't allocate device class\n"); ret = -EFAULT; diff --git a/driver/ppm_events.c b/driver/ppm_events.c index 2b1c448723..a572dff2d6 100644 --- a/driver/ppm_events.c +++ b/driver/ppm_events.c @@ -1258,20 +1258,28 @@ u16 fd_to_socktuple(int fd, */ usrsockaddr_in = (struct sockaddr_in *)usrsockaddr; - if (is_inbound) { - /* To take inbound info we cannot use the `src_addr` obtained from the syscall - * it could be empty! - * From kernel 3.13 we can take both ipv4 and ipv6 info from here - * https://elixir.bootlin.com/linux/v3.13/source/include/net/sock.h#L164 + if (is_inbound) + { + /* To take peer address info we try to use the kernel where possible. + * TCP allows us to obtain the right information, while the kernel doesn't fill + * `sk->__sk_common.skc_daddr` for UDP connection. + * Instead of having a custom logic for each protocol we try to read from + * kernel structs and if we don't find valid data we fallback to userspace + * structs. */ - #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) + sport = ntohs(sock->sk->__sk_common.skc_dport); + if(sport != 0) + { + /* We can read from the kernel */ sip = sock->sk->__sk_common.skc_daddr; - sport = ntohs(sock->sk->__sk_common.skc_dport); - #else - /* this is probably wrong, we need to find an alternative in old kernels */ - sip = ((struct sockaddr_in *) &sock_address)->sin_addr.s_addr; + } + else +#endif + { + sip = usrsockaddr_in->sin_addr.s_addr; sport = ntohs(usrsockaddr_in->sin_port); - #endif + } dip = ((struct sockaddr_in *) &sock_address)->sin_addr.s_addr; dport = ntohs(((struct sockaddr_in *) &sock_address)->sin_port); } else { @@ -1321,15 +1329,22 @@ u16 fd_to_socktuple(int fd, */ usrsockaddr_in6 = (struct sockaddr_in6 *)usrsockaddr; - if (is_inbound) { - #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) + if (is_inbound) + { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) + sport = ntohs(sock->sk->__sk_common.skc_dport); + if(sport != 0) + { + /* We can read from the kernel */ sip6 = sock->sk->__sk_common.skc_v6_daddr.in6_u.u6_addr8; - sport = ntohs(sock->sk->__sk_common.skc_dport); - #else - /* this is probably wrong, we need to find an alternative in old kernels */ + } + else +#endif + { + /* Fallback to userspace struct */ sip6 = usrsockaddr_in6->sin6_addr.s6_addr; sport = ntohs(usrsockaddr_in6->sin6_port); - #endif + } dip6 = ((struct sockaddr_in6 *) &sock_address)->sin6_addr.s6_addr; dport = ntohs(((struct sockaddr_in6 *) &sock_address)->sin6_port); } else { diff --git a/driver/ppm_version.h b/driver/ppm_version.h index 00e5197feb..bdecd09d33 100644 --- a/driver/ppm_version.h +++ b/driver/ppm_version.h @@ -1,3 +1,6 @@ +#ifndef PPM_VERSION_H_ +#define PPM_VERSION_H_ + #ifndef UDIG #include #endif @@ -18,3 +21,5 @@ #define PPM_RHEL_RELEASE_CODE 0 #define PPM_RHEL_RELEASE_VERSION(x,y) 0 #endif + +#endif /* PPM_VERSION_H_ */ diff --git a/driver/socketcall_to_syscall.c b/driver/socketcall_to_syscall.c new file mode 100644 index 0000000000..820bba3e69 --- /dev/null +++ b/driver/socketcall_to_syscall.c @@ -0,0 +1,162 @@ +/* + +Copyright (C) 2023 The Falco Authors. + +This file is dual licensed under either the MIT or GPL 2. See MIT.txt +or GPL2.txt for full copies of the license. + +*/ + +#include + +/* Right now we don't support architectures that have + * socket-calls both on 64 and 32-bit + */ +#if defined(__KERNEL__) && defined(CONFIG_IA32_EMULATION) +#include +#else +#include +#endif + +/* We need to import them explicitly otherwise if we import `linux/net` + * we will have a redefinition of syscall codes. + */ +#define SYS_SOCKET 1 /* sys_socket(2) */ +#define SYS_BIND 2 /* sys_bind(2) */ +#define SYS_CONNECT 3 /* sys_connect(2) */ +#define SYS_LISTEN 4 /* sys_listen(2) */ +#define SYS_ACCEPT 5 /* sys_accept(2) */ +#define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */ +#define SYS_GETPEERNAME 7 /* sys_getpeername(2) */ +#define SYS_SOCKETPAIR 8 /* sys_socketpair(2) */ +#define SYS_SEND 9 /* sys_send(2) */ +#define SYS_RECV 10 /* sys_recv(2) */ +#define SYS_SENDTO 11 /* sys_sendto(2) */ +#define SYS_RECVFROM 12 /* sys_recvfrom(2) */ +#define SYS_SHUTDOWN 13 /* sys_shutdown(2) */ +#define SYS_SETSOCKOPT 14 /* sys_setsockopt(2) */ +#define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */ +#define SYS_SENDMSG 16 /* sys_sendmsg(2) */ +#define SYS_RECVMSG 17 /* sys_recvmsg(2) */ +#define SYS_ACCEPT4 18 /* sys_accept4(2) */ +#define SYS_RECVMMSG 19 /* sys_recvmmsg(2) */ +#define SYS_SENDMMSG 20 /* sys_sendmmsg(2) */ + +/* Please note that here `socketcall_syscall_id` could be the code + * on 64-bit or 32-bit. + */ +int socketcall_code_to_syscall_code(int socketcall_code) +{ + switch(socketcall_code) + { +#ifdef __NR_socket + case SYS_SOCKET: + return __NR_socket; +#endif + +#ifdef __NR_socketpair + case SYS_SOCKETPAIR: + return __NR_socketpair; +#endif + + case SYS_ACCEPT: +#if defined(CONFIG_S390) && defined(__NR_accept4) + return __NR_accept4; +#elif defined(__NR_accept) + return __NR_accept; +#endif + break; + +#ifdef __NR_accept4 + case SYS_ACCEPT4: + return __NR_accept4; +#endif + +#ifdef __NR_bind + case SYS_BIND: + return __NR_bind; +#endif + +#ifdef __NR_listen + case SYS_LISTEN: + return __NR_listen; +#endif + +#ifdef __NR_connect + case SYS_CONNECT: + return __NR_connect; +#endif + +#ifdef __NR_getsockname + case SYS_GETSOCKNAME: + return __NR_getsockname; +#endif + +#ifdef __NR_getpeername + case SYS_GETPEERNAME: + return __NR_getpeername; +#endif + +#ifdef __NR_getsockopt + case SYS_GETSOCKOPT: + return __NR_getsockopt; +#endif + +#ifdef __NR_setsockopt + case SYS_SETSOCKOPT: + return __NR_setsockopt; +#endif + +#ifdef __NR_recv + case SYS_RECV: + return __NR_recv; +#endif + +#ifdef __NR_recvfrom + case SYS_RECVFROM: + return __NR_recvfrom; +#endif + +#ifdef __NR_recvmsg + case SYS_RECVMSG: + return __NR_recvmsg; +#endif + +#ifdef __NR_recvmmsg + case SYS_RECVMMSG: + return __NR_recvmmsg; +#endif + +#ifdef __NR_send + case SYS_SEND: + return __NR_send; +#endif + +#ifdef __NR_sendto + case SYS_SENDTO: + return __NR_sendto; +#endif + +#ifdef __NR_sendmsg + case SYS_SENDMSG: + return __NR_sendmsg; +#endif + +#ifdef __NR_sendmmsg + case SYS_SENDMMSG: + return __NR_sendmmsg; +#endif + +#ifdef __NR_shutdown + case SYS_SHUTDOWN: + return __NR_shutdown; +#endif + default: + break; + } + + /* if we are not able to convert the + * socketcall code we return -1 + */ + return -1; +} diff --git a/driver/socketcall_to_syscall.h b/driver/socketcall_to_syscall.h new file mode 100644 index 0000000000..32d255576a --- /dev/null +++ b/driver/socketcall_to_syscall.h @@ -0,0 +1,15 @@ +/* + +Copyright (C) 2023 The Falco Authors. + +This file is dual licensed under either the MIT or GPL 2. See MIT.txt +or GPL2.txt for full copies of the license. + +*/ + +#ifndef SOCKETCALL_TO_SYSCALL_H +#define SOCKETCALL_TO_SYSCALL_H + +int socketcall_code_to_syscall_code(int socketcall_code); + +#endif /* SOCKETCALL_TO_SYSCALL_H */ diff --git a/test/drivers/event_class/event_class.cpp b/test/drivers/event_class/event_class.cpp index 1baab29e0c..1ea5fa707d 100644 --- a/test/drivers/event_class/event_class.cpp +++ b/test/drivers/event_class/event_class.cpp @@ -390,6 +390,32 @@ void event_test::connect_ipv4_client_to_server(int32_t* client_socket, struct so assert_syscall_state(SYSCALL_SUCCESS, "connect (client)", syscall(__NR_connect, *client_socket, (struct sockaddr*)server_sockaddr, sizeof(*server_sockaddr)), NOT_EQUAL, -1); } +void event_test::connect_ipv4_udp_client_to_server(int32_t* client_socket, struct sockaddr_in* client_sockaddr, int32_t* server_socket, struct sockaddr_in* server_sockaddr, int32_t port_client, int32_t port_server) +{ + /* Create the server socket. */ + *server_socket = syscall(__NR_socket, AF_INET, SOCK_DGRAM, 0); + assert_syscall_state(SYSCALL_SUCCESS, "socket (server)", *server_socket, NOT_EQUAL, -1); + server_reuse_address_port(*server_socket); + + memset(server_sockaddr, 0, sizeof(*server_sockaddr)); + server_fill_sockaddr_in(server_sockaddr, port_server); + + /* Now we bind the server socket with the server address. */ + assert_syscall_state(SYSCALL_SUCCESS, "bind (server)", syscall(__NR_bind, *server_socket, (struct sockaddr*)server_sockaddr, sizeof(*server_sockaddr)), NOT_EQUAL, -1); + + /* The server now is ready, we need to create at least one connection from the client. */ + + *client_socket = syscall(__NR_socket, AF_INET, SOCK_DGRAM, 0); + assert_syscall_state(SYSCALL_SUCCESS, "socket (client)", *client_socket, NOT_EQUAL, -1); + client_reuse_address_port(*client_socket); + + memset(client_sockaddr, 0, sizeof(*client_sockaddr)); + client_fill_sockaddr_in(client_sockaddr, port_client); + + /* We need to bind the client socket with an address otherwise we cannot assert against it. */ + assert_syscall_state(SYSCALL_SUCCESS, "bind (client)", syscall(__NR_bind, *client_socket, (struct sockaddr*)client_sockaddr, sizeof(*client_sockaddr)), NOT_EQUAL, -1); +} + void event_test::connect_ipv6_client_to_server(int32_t* client_socket, struct sockaddr_in6* client_sockaddr, int32_t* server_socket, struct sockaddr_in6* server_sockaddr) { /* Create the server socket. */ diff --git a/test/drivers/event_class/event_class.h b/test/drivers/event_class/event_class.h index 9cc8382611..eb8c6a260b 100644 --- a/test/drivers/event_class/event_class.h +++ b/test/drivers/event_class/event_class.h @@ -181,14 +181,14 @@ class event_test /** * @brief Set stasd port - * + * * @param port new statsd port */ void set_statsd_port(uint16_t port); /** * @brief Set a range of network ports as interesting - * + * * @param start first port of the range * @param end last port of the range */ @@ -306,6 +306,8 @@ class event_test void connect_ipv6_client_to_server(int32_t* client_socket, struct sockaddr_in6* client_sockaddr, int32_t* server_socket, struct sockaddr_in6* server_sockaddr); void connect_unix_client_to_server(int32_t* client_socket, struct sockaddr_un* client_sockaddr, int32_t* server_socket, struct sockaddr_un* server_sockaddr); + void connect_ipv4_udp_client_to_server(int32_t* client_socket, struct sockaddr_in* client_sockaddr, int32_t* server_socket, struct sockaddr_in* server_sockaddr, int32_t client_port = IPV4_PORT_CLIENT, int32_t server_port = IPV4_PORT_SERVER); + ///////////////////////////////// // GENERIC EVENT ASSERTIONS ///////////////////////////////// diff --git a/test/drivers/event_class/network_utils.h b/test/drivers/event_class/network_utils.h index 9d6c0ad580..f370ba6b76 100644 --- a/test/drivers/event_class/network_utils.h +++ b/test/drivers/event_class/network_utils.h @@ -18,13 +18,18 @@ /*=============================== IPV4 ===========================*/ +/* Empty endpoint */ +#define IPV4_EMPTY "0.0.0.0" +#define IPV4_PORT_EMPTY 0 +#define IPV4_PORT_EMPTY_STRING "0" + /* IPv4 Client */ #define IPV4_CLIENT "127.0.21.34" #define IPV4_PORT_CLIENT 51789 #define IPV4_PORT_CLIENT_STRING "51789" /* IPv4 Server */ -#define IPV4_SERVER "127.0.21.35" +#define IPV4_SERVER "127.59.21.35" #define IPV4_PORT_SERVER 52889 #define IPV4_PORT_SERVER_STRING "52889" diff --git a/test/drivers/test_suites/syscall_exit_suite/connect_x.cpp b/test/drivers/test_suites/syscall_exit_suite/connect_x.cpp index 877e7e70bb..4b439be53e 100644 --- a/test/drivers/test_suites/syscall_exit_suite/connect_x.cpp +++ b/test/drivers/test_suites/syscall_exit_suite/connect_x.cpp @@ -237,15 +237,15 @@ TEST(SyscallExit, connectX_failure) evt_test->assert_num_params_pushed(3); } -TEST(SyscallExit, connectX_failure_connection_refused) +TEST(SyscallExit, connectX_failure_ECONNREFUSED) { auto evt_test = get_syscall_event_test(__NR_connect, EXIT_EVENT); evt_test->enable_capture(); /*=============================== TRIGGER SYSCALL ===========================*/ - - int32_t client_socket_fd = syscall(__NR_socket, AF_INET, SOCK_DGRAM, 0); + /* The socket here is blocking so the errno will be ECONNREFUSED */ + int32_t client_socket_fd = syscall(__NR_socket, AF_INET, SOCK_STREAM, 0); assert_syscall_state(SYSCALL_SUCCESS, "socket (client)", client_socket_fd, NOT_EQUAL, -1); evt_test->client_reuse_address_port(client_socket_fd); @@ -255,13 +255,11 @@ TEST(SyscallExit, connectX_failure_connection_refused) /* We need to bind the client socket with an address otherwise we cannot assert against it. */ assert_syscall_state(SYSCALL_SUCCESS, "bind (client)", syscall(__NR_bind, client_socket_fd, (struct sockaddr *)&client_addr, sizeof(client_addr)), NOT_EQUAL, -1); - /* Now we associate the client socket with the server address. - * Here the server `254.254.254.3:1` should be not reachable - */ + /* We try to reach this server that doesn't exist */ struct sockaddr_in server_addr; - evt_test->server_fill_sockaddr_in(&server_addr, 1, "254.254.254.3"); + evt_test->server_fill_sockaddr_in(&server_addr); + assert_syscall_state(SYSCALL_FAILURE, "connect (client)", syscall(__NR_connect, client_socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr))); - int64_t errno_value = -errno; /*=============================== TRIGGER SYSCALL ===========================*/ @@ -281,7 +279,7 @@ TEST(SyscallExit, connectX_failure_connection_refused) /*=============================== ASSERT PARAMETERS ===========================*/ /* Parameter 1: res (type: PT_ERRNO) */ - evt_test->assert_numeric_param(1, (int64_t)errno_value); + evt_test->assert_numeric_param(1, (int64_t)-ECONNREFUSED); /* Parameter 2: tuple (type: PT_SOCKTUPLE) */ /* Modern BPF doesn't return the tuple in case of failure */ @@ -291,7 +289,7 @@ TEST(SyscallExit, connectX_failure_connection_refused) } else { - evt_test->assert_tuple_inet_param(2, PPM_AF_INET, IPV4_CLIENT, "254.254.254.3", IPV4_PORT_CLIENT_STRING, "1"); + evt_test->assert_tuple_inet_param(2, PPM_AF_INET, IPV4_CLIENT, IPV4_SERVER, IPV4_PORT_CLIENT_STRING, IPV4_PORT_SERVER_STRING); } /* Parameter 3: fd (type: PT_FD) */ @@ -301,4 +299,72 @@ TEST(SyscallExit, connectX_failure_connection_refused) evt_test->assert_num_params_pushed(3); } + +TEST(SyscallExit, connectX_failure_EINPROGRESS) +{ + auto evt_test = get_syscall_event_test(__NR_connect, EXIT_EVENT); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + /* The socket here is not blocking so the errno will be EINPROGRESS */ + int32_t client_socket_fd = syscall(__NR_socket, AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + assert_syscall_state(SYSCALL_SUCCESS, "socket (client)", client_socket_fd, NOT_EQUAL, -1); + evt_test->client_reuse_address_port(client_socket_fd); + + struct sockaddr_in client_addr; + evt_test->client_fill_sockaddr_in(&client_addr); + + assert_syscall_state(SYSCALL_SUCCESS, "bind (client)", syscall(__NR_bind, client_socket_fd, (struct sockaddr *)&client_addr, sizeof(client_addr)), NOT_EQUAL, -1); + + int32_t server_socket_fd = syscall(__NR_socket, AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + assert_syscall_state(SYSCALL_SUCCESS, "socket (server)", server_socket_fd, NOT_EQUAL, -1); + evt_test->server_reuse_address_port(server_socket_fd); + + struct sockaddr_in server_addr; + evt_test->server_fill_sockaddr_in(&server_addr); + + /* Now we bind the server socket with the server address. */ + assert_syscall_state(SYSCALL_SUCCESS, "bind (server)", syscall(__NR_bind, server_socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)), NOT_EQUAL, -1); + + /* Here we don't call listen so the connection from the client should be + * in progress. + */ + + assert_syscall_state(SYSCALL_FAILURE, "connect (client)", syscall(__NR_connect, client_socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr))); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + evt_test->disable_capture(); + + evt_test->assert_event_presence(); + + if(HasFatalFailure()) + { + return; + } + + evt_test->parse_event(); + + evt_test->assert_header(); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + /* Parameter 1: res (type: PT_ERRNO) */ + evt_test->assert_numeric_param(1, (int64_t)-EINPROGRESS); + + /* Parameter 2: tuple (type: PT_SOCKTUPLE) */ + /* `EINPROGRESS` is the unique failure case that the modern bpf probe + * can catch. + */ + evt_test->assert_tuple_inet_param(2, PPM_AF_INET, IPV4_CLIENT, IPV4_SERVER, IPV4_PORT_CLIENT_STRING, IPV4_PORT_SERVER_STRING); + + /* Parameter 3: fd (type: PT_FD) */ + evt_test->assert_numeric_param(3, (int64_t)client_socket_fd); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(3); +} #endif diff --git a/test/drivers/test_suites/syscall_exit_suite/recvfrom_x.cpp b/test/drivers/test_suites/syscall_exit_suite/recvfrom_x.cpp index a788329047..185e6d9b61 100644 --- a/test/drivers/test_suites/syscall_exit_suite/recvfrom_x.cpp +++ b/test/drivers/test_suites/syscall_exit_suite/recvfrom_x.cpp @@ -4,7 +4,7 @@ #if defined(__NR_accept4) && defined(__NR_connect) && defined(__NR_socket) && defined(__NR_bind) && defined(__NR_listen) && defined(__NR_close) && defined(__NR_setsockopt) && defined(__NR_shutdown) && defined(__NR_sendto) -TEST(SyscallExit, recvfromX_no_snaplen) +TEST(SyscallExit, recvfromX_tcp_connection_no_snaplen) { auto evt_test = get_syscall_event_test(__NR_recvfrom, EXIT_EVENT); @@ -31,8 +31,6 @@ TEST(SyscallExit, recvfromX_no_snaplen) char received_data[MAX_RECV_BUF_SIZE]; socklen_t received_data_len = MAX_RECV_BUF_SIZE; uint32_t recvfrom_flags = 0; - /// TODO: if we use `struct sockaddr_in* src_addr = NULL` kernel module and old bpf are not able to get correct data. - /// Fixing them means changing how we retrieve network data, so it would be quite a big change. struct sockaddr_in src_addr = {0}; socklen_t addrlen = sizeof(src_addr); @@ -79,7 +77,7 @@ TEST(SyscallExit, recvfromX_no_snaplen) evt_test->assert_num_params_pushed(3); } -TEST(SyscallExit, recvfromX_snaplen) +TEST(SyscallExit, recvfromX_tcp_connection_snaplen) { auto evt_test = get_syscall_event_test(__NR_recvfrom, EXIT_EVENT); @@ -151,6 +149,247 @@ TEST(SyscallExit, recvfromX_snaplen) evt_test->assert_num_params_pushed(3); } + +TEST(SyscallExit, recvfromX_tcp_connection_NULL_sockaddr) +{ + auto evt_test = get_syscall_event_test(__NR_recvfrom, EXIT_EVENT); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + int32_t client_socket_fd = 0; + int32_t server_socket_fd = 0; + struct sockaddr_in client_addr = {0}; + struct sockaddr_in server_addr = {0}; + evt_test->connect_ipv4_client_to_server(&client_socket_fd, &client_addr, &server_socket_fd, &server_addr); + + /* Send a message to the server */ + char sent_data[FULL_MESSAGE_LEN] = FULL_MESSAGE; + uint32_t sendto_flags = 0; + int64_t sent_bytes = syscall(__NR_sendto, client_socket_fd, sent_data, sizeof(sent_data), sendto_flags, (struct sockaddr *)&server_addr, sizeof(server_addr)); + assert_syscall_state(SYSCALL_SUCCESS, "sendto (client)", sent_bytes, NOT_EQUAL, -1); + + /* The server accepts the connection and receives the message */ + int connected_socket_fd = syscall(__NR_accept4, server_socket_fd, NULL, NULL, 0); + assert_syscall_state(SYSCALL_SUCCESS, "accept4 (server)", connected_socket_fd, NOT_EQUAL, -1); + + char received_data[MAX_RECV_BUF_SIZE]; + socklen_t received_data_len = MAX_RECV_BUF_SIZE; + uint32_t recvfrom_flags = 0; + + /// TODO: if we use `struct sockaddr_in* src_addr = NULL` kernel module and old bpf are not able to get correct data. + /// There is a check on the sockaddr pointer if it is NULL we return an empty tuple. We need to fix it. + int64_t received_bytes = syscall(__NR_recvfrom, connected_socket_fd, received_data, received_data_len, recvfrom_flags, NULL, 0); + assert_syscall_state(SYSCALL_SUCCESS, "recvfrom (server)", received_bytes, NOT_EQUAL, -1); + + /* Cleaning phase */ + syscall(__NR_shutdown, connected_socket_fd, 2); + syscall(__NR_shutdown, server_socket_fd, 2); + syscall(__NR_shutdown, client_socket_fd, 2); + syscall(__NR_close, connected_socket_fd); + syscall(__NR_close, server_socket_fd); + syscall(__NR_close, client_socket_fd); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + evt_test->disable_capture(); + + evt_test->assert_event_presence(); + + if(HasFatalFailure()) + { + return; + } + + evt_test->parse_event(); + + evt_test->assert_header(); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + /* Parameter 1: res (type: PT_ERRNO) */ + evt_test->assert_numeric_param(1, (int64_t)MAX_RECV_BUF_SIZE); + + /* Parameter 2: data (type: PT_BYTEBUF) */ + evt_test->assert_bytebuf_param(2, FULL_MESSAGE, DEFAULT_SNAPLEN); + + /* Parameter 3: tuple (type: PT_SOCKTUPLE) */ + if(evt_test->is_modern_bpf_engine()) + { + /* The server performs a 'recvmsg` so the server is the final destination of the packet while the client is the src. */ + evt_test->assert_tuple_inet_param(3, PPM_AF_INET, IPV4_CLIENT, IPV4_SERVER, IPV4_PORT_CLIENT_STRING, IPV4_PORT_SERVER_STRING); + } + else + { + evt_test->assert_empty_param(3); + evt_test->assert_num_params_pushed(3); + GTEST_SKIP() << "[RECVFROM_X]: we send an empty tuple, but we can fix this case" << std::endl; + } + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(3); +} + +TEST(SyscallExit, recvfromX_udp_connection_snaplen) +{ + auto evt_test = get_syscall_event_test(__NR_recvfrom, EXIT_EVENT); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + int32_t client_socket_fd = 0; + int32_t server_socket_fd = 0; + struct sockaddr_in client_addr = {0}; + struct sockaddr_in server_addr = {0}; + evt_test->connect_ipv4_udp_client_to_server(&client_socket_fd, &client_addr, &server_socket_fd, &server_addr); + + /* Send a message to the server */ + char sent_data[FULL_MESSAGE_LEN] = FULL_MESSAGE; + uint32_t sendto_flags = 0; + int64_t sent_bytes = syscall(__NR_sendto, client_socket_fd, sent_data, sizeof(sent_data), sendto_flags, + (struct sockaddr *)&server_addr, sizeof(server_addr)); + assert_syscall_state(SYSCALL_SUCCESS, "sendto (client)", sent_bytes, NOT_EQUAL, -1); + + char received_data[MAX_RECV_BUF_SIZE]; + socklen_t received_data_len = MAX_RECV_BUF_SIZE; + uint32_t recvfrom_flags = 0; + struct sockaddr_in src_addr = {0}; + socklen_t addrlen = sizeof(src_addr); + + int64_t received_bytes = syscall(__NR_recvfrom, server_socket_fd, received_data, received_data_len, + recvfrom_flags, (struct sockaddr *)&src_addr, &addrlen); + assert_syscall_state(SYSCALL_SUCCESS, "recvfrom (server)", received_bytes, NOT_EQUAL, -1); + + /* Cleaning phase */ + syscall(__NR_shutdown, server_socket_fd, 2); + syscall(__NR_shutdown, client_socket_fd, 2); + syscall(__NR_close, server_socket_fd); + syscall(__NR_close, client_socket_fd); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + evt_test->disable_capture(); + + evt_test->assert_event_presence(); + + if(HasFatalFailure()) + { + return; + } + + evt_test->parse_event(); + + evt_test->assert_header(); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + /* Parameter 1: res (type: PT_ERRNO) */ + evt_test->assert_numeric_param(1, (int64_t)MAX_RECV_BUF_SIZE); + + /* Parameter 2: data (type: PT_BYTEBUF) */ + evt_test->assert_bytebuf_param(2, FULL_MESSAGE, DEFAULT_SNAPLEN); + + /* Parameter 3: tuple (type: PT_SOCKTUPLE) */ + if(!evt_test->is_modern_bpf_engine()) + { + /* The server performs a 'recvmsg` so the server is the final destination of the packet while the client is the src. */ + evt_test->assert_tuple_inet_param(3, PPM_AF_INET, IPV4_CLIENT, IPV4_SERVER, IPV4_PORT_CLIENT_STRING, IPV4_PORT_SERVER_STRING); + } + else + { + /* In UDP connections we cannot extract the tuple from kernel structs we always need to use the userspace struct + * Right now the modern probe doesn't support this behavior we need to fix it + */ + evt_test->assert_tuple_inet_param(3, PPM_AF_INET, IPV4_EMPTY, IPV4_SERVER, IPV4_PORT_EMPTY_STRING, IPV4_PORT_SERVER_STRING); + evt_test->assert_num_params_pushed(3); + GTEST_SKIP() << "[RECVFROM_X]: we send a tuple without the source, but we can fix this case" << std::endl; + } + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(3); +} + +TEST(SyscallExit, recvfromX_udp_connection_NULL_sockaddr) +{ + auto evt_test = get_syscall_event_test(__NR_recvfrom, EXIT_EVENT); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + int32_t client_socket_fd = 0; + int32_t server_socket_fd = 0; + struct sockaddr_in client_addr = {0}; + struct sockaddr_in server_addr = {0}; + evt_test->connect_ipv4_udp_client_to_server(&client_socket_fd, &client_addr, &server_socket_fd, &server_addr); + + /* Send a message to the server */ + char sent_data[FULL_MESSAGE_LEN] = FULL_MESSAGE; + uint32_t sendto_flags = 0; + int64_t sent_bytes = syscall(__NR_sendto, client_socket_fd, sent_data, sizeof(sent_data), sendto_flags, + (struct sockaddr *)&server_addr, sizeof(server_addr)); + assert_syscall_state(SYSCALL_SUCCESS, "sendto (client)", sent_bytes, NOT_EQUAL, -1); + + char received_data[MAX_RECV_BUF_SIZE]; + socklen_t received_data_len = MAX_RECV_BUF_SIZE; + uint32_t recvfrom_flags = 0; + + int64_t received_bytes = syscall(__NR_recvfrom, server_socket_fd, received_data, received_data_len, + recvfrom_flags, NULL, 0); + assert_syscall_state(SYSCALL_SUCCESS, "recvfrom (server)", received_bytes, NOT_EQUAL, -1); + + /* Cleaning phase */ + syscall(__NR_shutdown, server_socket_fd, 2); + syscall(__NR_shutdown, client_socket_fd, 2); + syscall(__NR_close, server_socket_fd); + syscall(__NR_close, client_socket_fd); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + evt_test->disable_capture(); + + evt_test->assert_event_presence(); + + if(HasFatalFailure()) + { + return; + } + + evt_test->parse_event(); + + evt_test->assert_header(); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + /* Parameter 1: res (type: PT_ERRNO) */ + evt_test->assert_numeric_param(1, (int64_t)MAX_RECV_BUF_SIZE); + + /* Parameter 2: data (type: PT_BYTEBUF) */ + evt_test->assert_bytebuf_param(2, FULL_MESSAGE, DEFAULT_SNAPLEN); + + /* Parameter 3: tuple (type: PT_SOCKTUPLE) */ + if(!evt_test->is_modern_bpf_engine()) + { + evt_test->assert_empty_param(3); + GTEST_SKIP() << "[RECVFROM_X]: we send an empty tuple, but we can at least send the dest ip and source" << std::endl; + } + else + { + /* This is the correct behavior because if the userspace struct is empty + * we cannot extract the source ip and port, unless we directly read the packet + * headers! + */ + evt_test->assert_tuple_inet_param(3, PPM_AF_INET, IPV4_EMPTY, IPV4_SERVER, IPV4_PORT_EMPTY_STRING, IPV4_PORT_SERVER_STRING); + } + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(3); +} #endif TEST(SyscallExit, recvfromX_fail) diff --git a/test/drivers/test_suites/syscall_exit_suite/recvmsg_x.cpp b/test/drivers/test_suites/syscall_exit_suite/recvmsg_x.cpp index 0170f507f6..4137eb372f 100644 --- a/test/drivers/test_suites/syscall_exit_suite/recvmsg_x.cpp +++ b/test/drivers/test_suites/syscall_exit_suite/recvmsg_x.cpp @@ -4,7 +4,7 @@ #if defined(__NR_accept4) && defined(__NR_connect) && defined(__NR_socket) && defined(__NR_bind) && defined(__NR_listen) && defined(__NR_close) && defined(__NR_setsockopt) && defined(__NR_shutdown) && defined(__NR_sendto) -TEST(SyscallExit, recvmsgX_no_snaplen) +TEST(SyscallExit, recvmsgX_tcp_connection_no_snaplen) { auto evt_test = get_syscall_event_test(__NR_recvmsg, EXIT_EVENT); @@ -90,12 +90,11 @@ TEST(SyscallExit, recvmsgX_no_snaplen) } else { - /// TODO: same as `recvfrom` the kernel code tries to get information from userspace structs - /// but these could be empty so this is not the correct way to retrieve information we have to - /// change it. + /// TODO: If the socket is connected, the msg_name and msg_namelen members shall be ignored, but + /// right now we use them to send data also in TCP connections so we need to change this behavior! evt_test->assert_empty_param(4); evt_test->assert_num_params_pushed(4); - GTEST_SKIP() << "[RECVMSG_X]: what we receive is correct but we need to reimplement it, see the code" << std::endl; + GTEST_SKIP() << "[RECVMSG_X]: we receive an empty tuple but we have all the data in the kernel to obtain the correct tuple" << std::endl; } /*=============================== ASSERT PARAMETERS ===========================*/ @@ -103,7 +102,7 @@ TEST(SyscallExit, recvmsgX_no_snaplen) evt_test->assert_num_params_pushed(4); } -TEST(SyscallExit, recvmsgX_snaplen) +TEST(SyscallExit, recvmsgX_tcp_connection_snaplen) { auto evt_test = get_syscall_event_test(__NR_recvmsg, EXIT_EVENT); @@ -188,12 +187,286 @@ TEST(SyscallExit, recvmsgX_snaplen) } else { - /// TODO: same as `recvfrom` the kernel code tries to get information from userspace structs - /// but these could be empty so this is not the correct way to retrieve information we have to - /// change it. evt_test->assert_empty_param(4); evt_test->assert_num_params_pushed(4); - GTEST_SKIP() << "[RECVMSG_X]: what we receive is correct but we need to reimplement it, see the code" << std::endl; + GTEST_SKIP() << "[RECVMSG_X]: we receive an empty tuple but we have all the data in the kernel to obtain the correct tuple" << std::endl; + } + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(4); +} + +TEST(SyscallExit, recvmsgX_tcp_connection_NULL_sockaddr) +{ + auto evt_test = get_syscall_event_test(__NR_recvmsg, EXIT_EVENT); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + int32_t client_socket_fd = 0; + int32_t server_socket_fd = 0; + struct sockaddr_in client_addr = {0}; + struct sockaddr_in server_addr = {0}; + evt_test->connect_ipv4_client_to_server(&client_socket_fd, &client_addr, &server_socket_fd, &server_addr); + + /* Send a message to the server */ + char sent_data[FULL_MESSAGE_LEN] = FULL_MESSAGE; + uint32_t sendto_flags = 0; + int64_t sent_bytes = syscall(__NR_sendto, client_socket_fd, sent_data, sizeof(sent_data), sendto_flags, (struct sockaddr *)&server_addr, sizeof(server_addr)); + assert_syscall_state(SYSCALL_SUCCESS, "sendto (client)", sent_bytes, NOT_EQUAL, -1); + + /* The server accepts the connection and receives the message */ + int connected_socket_fd = syscall(__NR_accept4, server_socket_fd, NULL, NULL, 0); + assert_syscall_state(SYSCALL_SUCCESS, "accept4 (server)", connected_socket_fd, NOT_EQUAL, -1); + + struct msghdr recv_msg; + struct iovec iov[2]; + memset(&recv_msg, 0, sizeof(recv_msg)); + memset(iov, 0, sizeof(iov)); + recv_msg.msg_name = NULL; + recv_msg.msg_namelen = 0; + char data_1[MAX_RECV_BUF_SIZE]; + char data_2[MAX_RECV_BUF_SIZE]; + iov[0].iov_base = data_1; + iov[0].iov_len = MAX_RECV_BUF_SIZE; + iov[1].iov_base = data_2; + iov[1].iov_len = MAX_RECV_BUF_SIZE; + recv_msg.msg_iov = iov; + recv_msg.msg_iovlen = 2; + uint32_t recvmsg_flags = 0; + + int64_t received_bytes = syscall(__NR_recvmsg, connected_socket_fd, &recv_msg, recvmsg_flags); + assert_syscall_state(SYSCALL_SUCCESS, "recvmsg (server)", received_bytes, NOT_EQUAL, -1); + + /* Cleaning phase */ + syscall(__NR_shutdown, connected_socket_fd, 2); + syscall(__NR_shutdown, server_socket_fd, 2); + syscall(__NR_shutdown, client_socket_fd, 2); + syscall(__NR_close, connected_socket_fd); + syscall(__NR_close, server_socket_fd); + syscall(__NR_close, client_socket_fd); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + evt_test->disable_capture(); + + evt_test->assert_event_presence(); + + if(HasFatalFailure()) + { + return; + } + + evt_test->parse_event(); + + evt_test->assert_header(); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + /* Parameter 1: res (type: PT_ERRNO) */ + evt_test->assert_numeric_param(1, (int64_t)FULL_MESSAGE_LEN); + + /* Parameter 2: size (type: PT_UINT32) */ + evt_test->assert_numeric_param(2, (uint32_t)FULL_MESSAGE_LEN); + + /* Parameter 3: data (type: PT_BYTEBUF) */ + evt_test->assert_bytebuf_param(3, FULL_MESSAGE, DEFAULT_SNAPLEN); + + if(evt_test->is_modern_bpf_engine()) + { + /* Parameter 4: tuple (type: PT_SOCKTUPLE) */ + /* The server performs a 'recvmsg` so the server is the final destination of the packet while the client is the src. */ + evt_test->assert_tuple_inet_param(4, PPM_AF_INET, IPV4_CLIENT, IPV4_SERVER, IPV4_PORT_CLIENT_STRING, IPV4_PORT_SERVER_STRING); + } + else + { + evt_test->assert_empty_param(4); + evt_test->assert_num_params_pushed(4); + GTEST_SKIP() << "[RECVMSG_X]: we receive an empty tuple because the pointer to sockaddr is NULL, but we should rely on kernel structs" << std::endl; + } + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(4); +} + +TEST(SyscallExit, recvmsgX_udp_connection_snaplen) +{ + auto evt_test = get_syscall_event_test(__NR_recvmsg, EXIT_EVENT); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + int32_t client_socket_fd = 0; + int32_t server_socket_fd = 0; + struct sockaddr_in client_addr = {0}; + struct sockaddr_in server_addr = {0}; + evt_test->connect_ipv4_udp_client_to_server(&client_socket_fd, &client_addr, &server_socket_fd, &server_addr); + + /* Send a message to the server */ + char sent_data[FULL_MESSAGE_LEN] = FULL_MESSAGE; + uint32_t sendto_flags = 0; + int64_t sent_bytes = syscall(__NR_sendto, client_socket_fd, sent_data, sizeof(sent_data), sendto_flags, (struct sockaddr *)&server_addr, sizeof(server_addr)); + assert_syscall_state(SYSCALL_SUCCESS, "sendto (client)", sent_bytes, NOT_EQUAL, -1); + + struct msghdr recv_msg; + struct iovec iov[2]; + memset(&recv_msg, 0, sizeof(recv_msg)); + memset(iov, 0, sizeof(iov)); + recv_msg.msg_name = (struct sockaddr *)&client_addr; + recv_msg.msg_namelen = sizeof(client_addr); + char data_1[MAX_RECV_BUF_SIZE]; + char data_2[MAX_RECV_BUF_SIZE]; + iov[0].iov_base = data_1; + iov[0].iov_len = MAX_RECV_BUF_SIZE; + iov[1].iov_base = data_2; + iov[1].iov_len = MAX_RECV_BUF_SIZE; + recv_msg.msg_iov = iov; + recv_msg.msg_iovlen = 2; + uint32_t recvmsg_flags = 0; + + int64_t received_bytes = syscall(__NR_recvmsg, server_socket_fd, &recv_msg, recvmsg_flags); + assert_syscall_state(SYSCALL_SUCCESS, "recvmsg (server)", received_bytes, NOT_EQUAL, -1); + + /* Cleaning phase */ + syscall(__NR_shutdown, server_socket_fd, 2); + syscall(__NR_shutdown, client_socket_fd, 2); + syscall(__NR_close, server_socket_fd); + syscall(__NR_close, client_socket_fd); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + evt_test->disable_capture(); + + evt_test->assert_event_presence(); + + if(HasFatalFailure()) + { + return; + } + + evt_test->parse_event(); + + evt_test->assert_header(); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + /* Parameter 1: res (type: PT_ERRNO) */ + evt_test->assert_numeric_param(1, (int64_t)FULL_MESSAGE_LEN); + + /* Parameter 2: size (type: PT_UINT32) */ + evt_test->assert_numeric_param(2, (uint32_t)FULL_MESSAGE_LEN); + + /* Parameter 3: data (type: PT_BYTEBUF) */ + evt_test->assert_bytebuf_param(3, FULL_MESSAGE, DEFAULT_SNAPLEN); + + /* Parameter 4: tuple (type: PT_SOCKTUPLE) */ + if(evt_test->is_modern_bpf_engine()) + { + /* we are not able to get the sorce ip + port because right now + * we don't use the userspace struct. + */ + evt_test->assert_tuple_inet_param(4, PPM_AF_INET, IPV4_EMPTY, IPV4_SERVER, IPV4_PORT_EMPTY_STRING, IPV4_PORT_SERVER_STRING); + evt_test->assert_num_params_pushed(4); + GTEST_SKIP() << "[RECVMSG_X]: we are not able to get the sorce ip + port because right now we don't use the userspace struct." << std::endl; + } + else + { + evt_test->assert_tuple_inet_param(4, PPM_AF_INET, IPV4_CLIENT, IPV4_SERVER, IPV4_PORT_CLIENT_STRING, IPV4_PORT_SERVER_STRING); + } + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(4); +} + +TEST(SyscallExit, recvmsgX_udp_connection_NULL_sockaddr) +{ + auto evt_test = get_syscall_event_test(__NR_recvmsg, EXIT_EVENT); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + int32_t client_socket_fd = 0; + int32_t server_socket_fd = 0; + struct sockaddr_in client_addr = {0}; + struct sockaddr_in server_addr = {0}; + evt_test->connect_ipv4_udp_client_to_server(&client_socket_fd, &client_addr, &server_socket_fd, &server_addr); + + /* Send a message to the server */ + char sent_data[FULL_MESSAGE_LEN] = FULL_MESSAGE; + uint32_t sendto_flags = 0; + int64_t sent_bytes = syscall(__NR_sendto, client_socket_fd, sent_data, sizeof(sent_data), sendto_flags, (struct sockaddr *)&server_addr, sizeof(server_addr)); + assert_syscall_state(SYSCALL_SUCCESS, "sendto (client)", sent_bytes, NOT_EQUAL, -1); + + struct msghdr recv_msg; + struct iovec iov[2]; + memset(&recv_msg, 0, sizeof(recv_msg)); + memset(iov, 0, sizeof(iov)); + recv_msg.msg_name = NULL; + recv_msg.msg_namelen = 0; + char data_1[MAX_RECV_BUF_SIZE]; + char data_2[MAX_RECV_BUF_SIZE]; + iov[0].iov_base = data_1; + iov[0].iov_len = MAX_RECV_BUF_SIZE; + iov[1].iov_base = data_2; + iov[1].iov_len = MAX_RECV_BUF_SIZE; + recv_msg.msg_iov = iov; + recv_msg.msg_iovlen = 2; + uint32_t recvmsg_flags = 0; + + int64_t received_bytes = syscall(__NR_recvmsg, server_socket_fd, &recv_msg, recvmsg_flags); + assert_syscall_state(SYSCALL_SUCCESS, "recvmsg (server)", received_bytes, NOT_EQUAL, -1); + + /* Cleaning phase */ + syscall(__NR_shutdown, server_socket_fd, 2); + syscall(__NR_shutdown, client_socket_fd, 2); + syscall(__NR_close, server_socket_fd); + syscall(__NR_close, client_socket_fd); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + evt_test->disable_capture(); + + evt_test->assert_event_presence(); + + if(HasFatalFailure()) + { + return; + } + + evt_test->parse_event(); + + evt_test->assert_header(); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + /* Parameter 1: res (type: PT_ERRNO) */ + evt_test->assert_numeric_param(1, (int64_t)FULL_MESSAGE_LEN); + + /* Parameter 2: size (type: PT_UINT32) */ + evt_test->assert_numeric_param(2, (uint32_t)FULL_MESSAGE_LEN); + + /* Parameter 3: data (type: PT_BYTEBUF) */ + evt_test->assert_bytebuf_param(3, FULL_MESSAGE, DEFAULT_SNAPLEN); + + /* Parameter 4: tuple (type: PT_SOCKTUPLE) */ + if(evt_test->is_modern_bpf_engine()) + { + /* This is the correct behavior because if the userspace struct is empty + * we cannot extract the source ip and port, unless we directly read the packet + * headers! + */ + evt_test->assert_tuple_inet_param(4, PPM_AF_INET, IPV4_EMPTY, IPV4_SERVER, IPV4_PORT_EMPTY_STRING, IPV4_PORT_SERVER_STRING); + } + else + { + evt_test->assert_empty_param(4); + GTEST_SKIP() << "[RECVMSG_X]: we send an empty tuple, but we can at least send the dest ip and source" << std::endl; } /*=============================== ASSERT PARAMETERS ===========================*/ diff --git a/test/libscap/test_suites/engines/bpf/bpf.cpp b/test/libscap/test_suites/engines/bpf/bpf.cpp index bf132e33cb..534b211f89 100644 --- a/test/libscap/test_suites/engines/bpf/bpf.cpp +++ b/test/libscap/test_suites/engines/bpf/bpf.cpp @@ -108,6 +108,28 @@ TEST(bpf, scap_stats_check) scap_close(h); } +TEST(bpf, double_scap_stats_call) +{ + char error_buffer[FILENAME_MAX] = {0}; + int ret = 0; + scap_t* h = open_bpf_engine(error_buffer, &ret, 4 * 4096, LIBSCAP_TEST_BPF_PROBE_PATH); + ASSERT_FALSE(!h || ret != SCAP_SUCCESS) << "unable to open bpf engine: " << error_buffer << std::endl; + + scap_stats stats; + + ASSERT_EQ(scap_start_capture(h), SCAP_SUCCESS); + + ASSERT_EQ(scap_get_stats(h, &stats), SCAP_SUCCESS); + ASSERT_GT(stats.n_evts, 0); + + /* Double call */ + ASSERT_EQ(scap_get_stats(h, &stats), SCAP_SUCCESS); + ASSERT_GT(stats.n_evts, 0); + + ASSERT_EQ(scap_stop_capture(h), SCAP_SUCCESS); + scap_close(h); +} + TEST(bpf, scap_stats_v2_check_results) { char error_buffer[SCAP_LASTERR_SIZE] = {0}; @@ -144,6 +166,29 @@ TEST(bpf, scap_stats_v2_check_results) scap_close(h); } +TEST(bpf, double_scap_stats_v2_call) +{ + char error_buffer[SCAP_LASTERR_SIZE] = {0}; + int ret = 0; + scap_t* h = open_bpf_engine(error_buffer, &ret, 4 * 4096, LIBSCAP_TEST_BPF_PROBE_PATH); + ASSERT_FALSE(!h || ret != SCAP_SUCCESS) << "unable to open bpf engine: " << error_buffer << std::endl; + + uint32_t flags = PPM_SCAP_STATS_KERNEL_COUNTERS | PPM_SCAP_STATS_LIBBPF_STATS; + uint32_t nstats; + int32_t rc; + + scap_get_stats_v2(h, flags, &nstats, &rc); + ASSERT_EQ(rc, SCAP_SUCCESS); + ASSERT_GT(nstats, 0); + + /* Double call */ + scap_get_stats_v2(h, flags, &nstats, &rc); + ASSERT_EQ(rc, SCAP_SUCCESS); + ASSERT_GT(nstats, 0); + + scap_close(h); +} + TEST(bpf, scap_stats_v2_check_empty) { char error_buffer[SCAP_LASTERR_SIZE] = {0}; diff --git a/test/libscap/test_suites/engines/kmod/kmod.cpp b/test/libscap/test_suites/engines/kmod/kmod.cpp index dcd793ea39..b50f1b5fe5 100644 --- a/test/libscap/test_suites/engines/kmod/kmod.cpp +++ b/test/libscap/test_suites/engines/kmod/kmod.cpp @@ -164,6 +164,28 @@ TEST(kmod, scap_stats_check) scap_close(h); } +TEST(kmod, double_scap_stats_call) +{ + char error_buffer[FILENAME_MAX] = {0}; + int ret = 0; + scap_t* h = open_kmod_engine(error_buffer, &ret, 4 * 4096, LIBSCAP_TEST_KERNEL_MODULE_PATH); + ASSERT_FALSE(!h || ret != SCAP_SUCCESS) << "unable to open kmod engine: " << error_buffer << std::endl; + + scap_stats stats; + + ASSERT_EQ(scap_start_capture(h), SCAP_SUCCESS); + + ASSERT_EQ(scap_get_stats(h, &stats), SCAP_SUCCESS); + ASSERT_GT(stats.n_evts, 0); + + /* Double call */ + ASSERT_EQ(scap_get_stats(h, &stats), SCAP_SUCCESS); + ASSERT_GT(stats.n_evts, 0); + + ASSERT_EQ(scap_stop_capture(h), SCAP_SUCCESS); + scap_close(h); +} + TEST(kmod, scap_stats_v2_check_results) { char error_buffer[SCAP_LASTERR_SIZE] = {0}; @@ -200,6 +222,29 @@ TEST(kmod, scap_stats_v2_check_results) scap_close(h); } +TEST(kmod, double_scap_stats_v2_call) +{ + char error_buffer[SCAP_LASTERR_SIZE] = {0}; + int ret = 0; + scap_t* h = open_kmod_engine(error_buffer, &ret, 4 * 4096, LIBSCAP_TEST_KERNEL_MODULE_PATH); + ASSERT_FALSE(!h || ret != SCAP_SUCCESS) << "unable to open kmod engine: " << error_buffer << std::endl; + + uint32_t flags = PPM_SCAP_STATS_KERNEL_COUNTERS; + uint32_t nstats; + int32_t rc; + + scap_get_stats_v2(h, flags, &nstats, &rc); + ASSERT_EQ(rc, SCAP_SUCCESS); + ASSERT_GT(nstats, 0); + + /* Double call */ + scap_get_stats_v2(h, flags, &nstats, &rc); + ASSERT_EQ(rc, SCAP_SUCCESS); + ASSERT_GT(nstats, 0); + + scap_close(h); +} + TEST(kmod, scap_stats_v2_check_empty) { char error_buffer[SCAP_LASTERR_SIZE] = {0}; diff --git a/test/libscap/test_suites/engines/modern_bpf/modern_bpf.cpp b/test/libscap/test_suites/engines/modern_bpf/modern_bpf.cpp index 024b655d12..01cbb74a77 100644 --- a/test/libscap/test_suites/engines/modern_bpf/modern_bpf.cpp +++ b/test/libscap/test_suites/engines/modern_bpf/modern_bpf.cpp @@ -209,6 +209,28 @@ TEST(modern_bpf, scap_stats_check) scap_close(h); } +TEST(modern_bpf, double_scap_stats_call) +{ + char error_buffer[FILENAME_MAX] = {0}; + int ret = 0; + /* We use buffers of 1 MB to be sure that we don't have drops */ + scap_t* h = open_modern_bpf_engine(error_buffer, &ret, 1 * 1024 * 1024, 0, false); + ASSERT_EQ(!h || ret != SCAP_SUCCESS, false) << "unable to open modern bpf engine with one single shared ring buffer: " << error_buffer << std::endl; + + scap_stats stats; + ASSERT_EQ(scap_start_capture(h), SCAP_SUCCESS); + + ASSERT_EQ(scap_get_stats(h, &stats), SCAP_SUCCESS); + ASSERT_GT(stats.n_evts, 0); + + /* Double call */ + ASSERT_EQ(scap_get_stats(h, &stats), SCAP_SUCCESS); + ASSERT_GT(stats.n_evts, 0); + + ASSERT_EQ(scap_stop_capture(h), SCAP_SUCCESS); + scap_close(h); +} + TEST(modern_bpf, scap_stats_v2_check_results) { char error_buffer[FILENAME_MAX] = {0}; @@ -262,3 +284,27 @@ TEST(modern_bpf, scap_stats_v2_check_empty) ASSERT_EQ(rc, SCAP_SUCCESS); scap_close(h); } + +TEST(modern_bpf, double_scap_stats_v2_call) +{ + char error_buffer[FILENAME_MAX] = {0}; + int ret = 0; + /* We use buffers of 1 MB to be sure that we don't have drops */ + scap_t* h = open_modern_bpf_engine(error_buffer, &ret, 1 * 1024 * 1024, 0, false); + ASSERT_EQ(!h || ret != SCAP_SUCCESS, false) << "unable to open modern bpf engine with one single shared ring buffer: " << error_buffer << std::endl; + + uint32_t flags = PPM_SCAP_STATS_KERNEL_COUNTERS | PPM_SCAP_STATS_LIBBPF_STATS; + uint32_t nstats; + int32_t rc; + + scap_get_stats_v2(h, flags, &nstats, &rc); + ASSERT_EQ(rc, SCAP_SUCCESS); + ASSERT_GT(nstats, 0); + + /* Double call */ + scap_get_stats_v2(h, flags, &nstats, &rc); + ASSERT_EQ(rc, SCAP_SUCCESS); + ASSERT_GT(nstats, 0); + + scap_close(h); +} diff --git a/userspace/libpman/src/stats.c b/userspace/libpman/src/stats.c index 412ab759c8..04a672dc19 100644 --- a/userspace/libpman/src/stats.c +++ b/userspace/libpman/src/stats.c @@ -201,7 +201,6 @@ struct scap_stats_v2 *pman_get_scap_stats_v2(uint32_t flags, uint32_t *nstats, i g_state.stats[MODERN_BPF_N_DROPS_SCRATCH_MAP].value.u64 += cnt_map.n_drops_max_event_size; g_state.stats[MODERN_BPF_N_DROPS].value.u64 += (cnt_map.n_drops_buffer + cnt_map.n_drops_max_event_size); } - close(counter_maps_fd); offset = MODERN_BPF_MAX_KERNEL_COUNTERS_STATS; } diff --git a/userspace/libsinsp/eventformatter.h b/userspace/libsinsp/eventformatter.h index c3aeb797b6..f99db7eb62 100644 --- a/userspace/libsinsp/eventformatter.h +++ b/userspace/libsinsp/eventformatter.h @@ -71,7 +71,7 @@ class SINSP_PUBLIC sinsp_evt_formatter : public gen_event_formatter // interface. It just calls resolve_tokens(). bool get_field_values(gen_event *evt, std::map &fields) override; - void get_field_names(std::vector &fields); + void get_field_names(std::vector &fields) override; gen_event_formatter::output_format get_output_format() override; diff --git a/userspace/libsinsp/gen_filter.h b/userspace/libsinsp/gen_filter.h index 3fe6e94adf..05eef13467 100644 --- a/userspace/libsinsp/gen_filter.h +++ b/userspace/libsinsp/gen_filter.h @@ -291,6 +291,8 @@ class gen_event_formatter // (e.g. "proc.name"), to field value (e.g. "nginx") virtual bool get_field_values(gen_event *evt, std::map &fields) = 0; + virtual void get_field_names(std::vector &fields) = 0; + virtual output_format get_output_format() = 0; };