diff --git a/driver/flags_table.c b/driver/flags_table.c index 35fba19701..3208c55ade 100644 --- a/driver/flags_table.c +++ b/driver/flags_table.c @@ -645,3 +645,8 @@ const struct ppm_name_value epoll_create1_flags[] = { {"EPOLL_CLOEXEC", PPM_EPOLL_CLOEXEC}, {0, 0}, }; + +const struct ppm_name_value machine_info_flags[] = { + {"BPF_STATS_ENABLED", PPM_BPF_STATS_ENABLED}, + {0, 0}, +}; diff --git a/driver/ppm_events_public.h b/driver/ppm_events_public.h index 2b888493e1..e81f57a7a6 100644 --- a/driver/ppm_events_public.h +++ b/driver/ppm_events_public.h @@ -2005,4 +2005,9 @@ struct ppm_event_entry { #define RW_MAX_SNAPLEN PPM_MAX_ARG_SIZE #define RW_MAX_FULLCAPTURE_PORT_SNAPLEN 16000 +/* + * machine_info flags. + */ +#define PPM_BPF_STATS_ENABLED (1 << 0) + #endif /* EVENTS_PUBLIC_H_ */ diff --git a/userspace/libscap/CMakeLists.txt b/userspace/libscap/CMakeLists.txt index c53baca77c..d95a10ca23 100644 --- a/userspace/libscap/CMakeLists.txt +++ b/userspace/libscap/CMakeLists.txt @@ -46,6 +46,11 @@ if(NOT DEFINED SCAP_HOST_ROOT_ENV_VAR_NAME) endif() add_definitions(-DSCAP_HOST_ROOT_ENV_VAR_NAME="${SCAP_HOST_ROOT_ENV_VAR_NAME}") +if(NOT DEFINED SCAP_HOSTNAME_ENV_VAR) + set(SCAP_HOSTNAME_ENV_VAR "SCAP_HOSTNAME") +endif() +add_definitions(-DSCAP_HOSTNAME_ENV_VAR="${SCAP_HOSTNAME_ENV_VAR}") + if (DEFINED SCAP_BPF_PROGS_TAIL_CALLED_MAX) add_definitions(-DBPF_PROGS_TAIL_CALLED_MAX=${SCAP_BPF_PROGS_TAIL_CALLED_MAX}) endif() @@ -57,6 +62,7 @@ list(APPEND targetfiles scap_savefile.c scap_procs.c scap_userlist.c + scap_machine_agent.c scap_suppress.c) if(CMAKE_SYSTEM_NAME MATCHES "Linux") diff --git a/userspace/libscap/scap-int.h b/userspace/libscap/scap-int.h index a17c002870..26cdbf0aad 100644 --- a/userspace/libscap/scap-int.h +++ b/userspace/libscap/scap-int.h @@ -53,6 +53,7 @@ struct scap uint64_t m_evtcnt; scap_addrlist* m_addrlist; scap_machine_info m_machine_info; + scap_agent_info m_agent_info; scap_userlist* m_userlist; struct ppm_proclist_info* m_driver_procinfo; uint32_t m_fd_lookup_limit; @@ -122,6 +123,30 @@ int32_t scap_check_suppressed(struct scap_suppress *suppress, scap_evt *pevent, int32_t scap_procfs_get_threadlist(struct scap_engine_handle engine, struct ppm_proclist_info **procinfo_p, char *lasterr); int32_t scap_os_getpid_global(struct scap_engine_handle engine, int64_t *pid, char* error); +// +// Get hostname. +// +// Supports env variable lookup for cloud native integrations +// where the hostname can be equivalent to the Kubernetes pod name. +// Customizable over cmake setup SCAP_HOSTNAME_ENV_VAR. +// +void scap_gethostname(scap_t* handle); + +// +// Retrieve agent info. +// +void scap_retrieve_agent_info(scap_t* handle); + +// +// Retrieve machine info. +// +void scap_retrieve_machine_info(scap_t* handle, uint64_t boot_time); + +// +// Check if kernel.bpf_stats_enabled is set. +// +void scap_get_bpf_stats_enabled(scap_t* handle); + // // Useful stuff // diff --git a/userspace/libscap/scap.c b/userspace/libscap/scap.c index 4ad76dd9ba..72e428f522 100644 --- a/userspace/libscap/scap.c +++ b/userspace/libscap/scap.c @@ -83,15 +83,13 @@ int32_t scap_init_live_int(scap_t* handle, scap_open_args* oargs, const struct s // Extract machine information // - handle->m_machine_info.num_cpus = sysconf(_SC_NPROCESSORS_ONLN); - handle->m_machine_info.memory_size_bytes = (uint64_t)sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE); - gethostname(handle->m_machine_info.hostname, sizeof(handle->m_machine_info.hostname) / sizeof(handle->m_machine_info.hostname[0])); - handle->m_machine_info.boot_ts_epoch = boot_time; - handle->m_machine_info.reserved2 = 0; - handle->m_machine_info.reserved3 = 0; - handle->m_machine_info.reserved4 = 0; - handle->m_driver_procinfo = NULL; - handle->m_fd_lookup_limit = 0; + scap_retrieve_machine_info(handle, boot_time); + + // + // Extract agent information + // + + scap_retrieve_agent_info(handle); // // Create the interface list @@ -200,15 +198,13 @@ int32_t scap_init_udig_int(scap_t* handle, scap_open_args* oargs) // Extract machine information // - handle->m_machine_info.num_cpus = sysconf(_SC_NPROCESSORS_ONLN); - handle->m_machine_info.memory_size_bytes = (uint64_t)sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE); - gethostname(handle->m_machine_info.hostname, sizeof(handle->m_machine_info.hostname) / sizeof(handle->m_machine_info.hostname[0])); - handle->m_machine_info.boot_ts_epoch = boot_time; - handle->m_machine_info.reserved2 = 0; - handle->m_machine_info.reserved3 = 0; - handle->m_machine_info.reserved4 = 0; - handle->m_driver_procinfo = NULL; - handle->m_fd_lookup_limit = 0; + scap_retrieve_machine_info(handle, boot_time); + + // + // Extract agent information + // + + scap_retrieve_agent_info(handle); // // Create the interface list @@ -446,21 +442,19 @@ int32_t scap_init_nodriver_int(scap_t* handle, scap_open_args* oargs) // Extract machine information // - handle->m_machine_info.num_cpus = sysconf(_SC_NPROCESSORS_ONLN); - handle->m_machine_info.memory_size_bytes = (uint64_t)sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE); - gethostname(handle->m_machine_info.hostname, sizeof(handle->m_machine_info.hostname) / sizeof(handle->m_machine_info.hostname[0])); - handle->m_machine_info.boot_ts_epoch = boot_time; - handle->m_machine_info.reserved2 = 0; - handle->m_machine_info.reserved3 = 0; - handle->m_machine_info.reserved4 = 0; - handle->m_driver_procinfo = NULL; - + scap_retrieve_machine_info(handle, boot_time); if(!engine_params || !engine_params->full_proc_scan) { handle->m_minimal_scan = true; handle->m_fd_lookup_limit = SCAP_NODRIVER_MAX_FD_LOOKUP; // fd lookup is limited here because is very expensive } + // + // Extract agent information + // + + scap_retrieve_agent_info(handle); + // // Create the interface list // @@ -527,21 +521,15 @@ int32_t scap_init_plugin_int(scap_t* handle, scap_open_args* oargs) // // Extract machine information // -#ifdef _WIN32 - handle->m_machine_info.num_cpus = 0; - handle->m_machine_info.memory_size_bytes = 0; -#else - handle->m_machine_info.num_cpus = sysconf(_SC_NPROCESSORS_ONLN); - handle->m_machine_info.memory_size_bytes = (uint64_t)sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE); -#endif - gethostname(handle->m_machine_info.hostname, sizeof(handle->m_machine_info.hostname) / sizeof(handle->m_machine_info.hostname[0])); - handle->m_machine_info.boot_ts_epoch = 0; // plugin does not need boot_ts_epoch - handle->m_machine_info.reserved2 = 0; - handle->m_machine_info.reserved3 = 0; - handle->m_machine_info.reserved4 = 0; - handle->m_driver_procinfo = NULL; + scap_retrieve_machine_info(handle, (uint64_t)0); handle->m_fd_lookup_limit = SCAP_NODRIVER_MAX_FD_LOOKUP; // fd lookup is limited here because is very expensive + // + // Extract agent information + // + + scap_retrieve_agent_info(handle); + if((rc = handle->m_vtable->init(handle, oargs)) != SCAP_SUCCESS) { return rc; @@ -972,6 +960,14 @@ const scap_machine_info* scap_get_machine_info(scap_t* handle) } } +// +// Get the agent information +// +const scap_agent_info* scap_get_agent_info(scap_t* handle) +{ + return (const scap_agent_info*)&handle->m_agent_info; +} + int32_t scap_set_snaplen(scap_t* handle, uint32_t snaplen) { if(handle->m_vtable) diff --git a/userspace/libscap/scap.def b/userspace/libscap/scap.def index 5c2c680b8e..eb762d4b08 100644 --- a/userspace/libscap/scap.def +++ b/userspace/libscap/scap.def @@ -31,6 +31,7 @@ EXPORTS scap_proc_free scap_start_capture scap_get_machine_info + scap_get_agent_info scap_stop_dropping_mode scap_start_dropping_mode scap_get_user_list diff --git a/userspace/libscap/scap.h b/userspace/libscap/scap.h index 51a1cec442..5e4c141e76 100644 --- a/userspace/libscap/scap.h +++ b/userspace/libscap/scap.h @@ -324,11 +324,21 @@ typedef struct _scap_machine_info uint64_t max_pid; ///< Highest PID number on this machine char hostname[128]; ///< The machine hostname uint64_t boot_ts_epoch; ///< Host boot ts in nanoseconds (epoch) - uint64_t reserved2; ///< reserved for future use + uint64_t flags; ///< flags uint64_t reserved3; ///< reserved for future use - uint64_t reserved4; ///< reserved for future use + uint64_t reserved4; ///< reserved for future use, note: because of scap file captures needs to remain uint64_t, use flags if possible }scap_machine_info; +/*! + \brief Agent information, not intended for scap file use +*/ +typedef struct _scap_agent_info +{ + uint64_t start_ts_epoch; ///< Agent start timestamp, stat /proc/self/cmdline approach, unit: epoch in nanoseconds + double start_time; ///< /proc/self/stat start_time divided by HZ, unit: seconds + char uname_r[128]; ///< Kernel release `uname -r` +}scap_agent_info; + /*! \brief Interface address type */ @@ -839,6 +849,15 @@ const char* scap_get_ppm_sc_name(ppm_sc_code sc); */ const scap_machine_info* scap_get_machine_info(scap_t* handle); +/*! + \brief Get generic agent information + + \return The pointer to a \ref scap_agent_info structure containing the information. + + \note for live captures only. +*/ +const scap_agent_info* scap_get_agent_info(scap_t* handle); + /*! \brief Set the capture snaplen, i.e. the maximum size an event parameter can reach before the driver starts truncating it. diff --git a/userspace/libscap/scap_machine_agent.c b/userspace/libscap/scap_machine_agent.c new file mode 100644 index 0000000000..6f5a79541c --- /dev/null +++ b/userspace/libscap/scap_machine_agent.c @@ -0,0 +1,135 @@ +/* +Copyright (C) 2023 The Falco Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*/ + +#include +#ifdef _WIN32 +#include +#else +#include +#include +#include +#include +#include +#endif // _WIN32 + +#include "scap.h" +#include "scap-int.h" + +#define SECOND_TO_NS 1000000000 + +void scap_gethostname(scap_t* handle) +{ + char *env_hostname = getenv(SCAP_HOSTNAME_ENV_VAR); + if(env_hostname != NULL) + { + snprintf(handle->m_machine_info.hostname, sizeof(handle->m_machine_info.hostname), "%s", env_hostname); + } + else + { + gethostname(handle->m_machine_info.hostname, sizeof(handle->m_machine_info.hostname) / sizeof(handle->m_machine_info.hostname[0])); + } +} + +void scap_retrieve_machine_info(scap_t* handle, uint64_t boot_time) +{ +#ifdef _WIN32 + handle->m_machine_info.num_cpus = 0; + handle->m_machine_info.memory_size_bytes = 0; +#else + handle->m_machine_info.num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + handle->m_machine_info.memory_size_bytes = (uint64_t)sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE); +#endif + scap_gethostname(handle); + handle->m_machine_info.boot_ts_epoch = boot_time; + scap_get_bpf_stats_enabled(handle); + handle->m_machine_info.reserved3 = 0; + handle->m_machine_info.reserved4 = 0; + handle->m_driver_procinfo = NULL; + handle->m_fd_lookup_limit = 0; +} + +void scap_retrieve_agent_info(scap_t* handle) +{ + handle->m_agent_info.start_ts_epoch = 0; + handle->m_agent_info.start_time = 0; +#ifdef __linux__ + + /* Info 1: + * + * Get epoch timestamp based on procfs stat, only used for (constant) agent start time reporting. + */ + struct stat st = {0}; + char path[256]; + snprintf(path, sizeof(path), "/proc/%d/cmdline", getpid()); + if(stat(path, &st) == 0) + { + handle->m_agent_info.start_ts_epoch = st.st_ctim.tv_sec * (uint64_t) SECOND_TO_NS + st.st_ctim.tv_nsec; + } + + /* Info 2: + * + * Get /proc/self/stat start_time (22nd item) to calculate subsequent snapshots of the elapsed time + * of the agent for CPU usage calculations, e.g. sysinfo uptime - /proc/self/stat start_time. + */ + char proc_stat[256]; + FILE* f; + snprintf(proc_stat, sizeof(proc_stat), "/proc/%d/stat", getpid()); + if((f = fopen(proc_stat, "r"))) + { + unsigned long long stat_start_time = 0; // unit: USER_HZ / jiffies / clock ticks + long hz = 100; +#ifdef _SC_CLK_TCK + if ((hz = sysconf(_SC_CLK_TCK)) < 0) + { + ASSERT(false); + } +#endif + if(fscanf(f, "%*d %*s %*c %*d %*d %*d %*d %*d %*lu %*lu %*lu %*lu %*lu %*llu %*llu %*llu %*llu %*d %*d %*d %*lu %llu", &stat_start_time)) + { + handle->m_agent_info.start_time = (double)stat_start_time / hz; // unit: seconds as type (double) + } + fclose(f); + } + + /* Info 3: + * + * Kernel release `uname -r` of the machine the agent is running on. + */ + + struct utsname uts; + uname(&uts); + snprintf(handle->m_agent_info.uname_r, sizeof(handle->m_agent_info.uname_r), "%s", uts.release); +#endif +} + +void scap_get_bpf_stats_enabled(scap_t* handle) +{ +#ifdef __linux__ + handle->m_machine_info.flags &= ~PPM_BPF_STATS_ENABLED; + FILE* f; + if((f = fopen("/proc/sys/kernel/bpf_stats_enabled", "r"))) + { + uint32_t bpf_stats_enabled = 0; + fscanf(f, "%u", &bpf_stats_enabled); + fclose(f); + if (bpf_stats_enabled != 0) + { + handle->m_machine_info.flags |= PPM_BPF_STATS_ENABLED; + } + } +#endif +} diff --git a/userspace/libsinsp/sinsp.cpp b/userspace/libsinsp/sinsp.cpp index 081ff7538f..e0d2e878ba 100644 --- a/userspace/libsinsp/sinsp.cpp +++ b/userspace/libsinsp/sinsp.cpp @@ -100,6 +100,7 @@ sinsp::sinsp(bool static_container, const std::string &static_id, const std::str m_filter = NULL; m_fds_to_remove = new std::vector; m_machine_info = NULL; + m_agent_info = NULL; #ifdef SIMULATE_DROP_MODE m_isdropping = false; #endif @@ -319,6 +320,15 @@ void sinsp::init() m_num_cpus = 0; } + // + // Retrieve agent information + // + m_agent_info = scap_get_agent_info(m_h); + if (m_agent_info == NULL) + { + ASSERT(false); + } + // // Attach the protocol decoders // @@ -1846,6 +1856,11 @@ const scap_machine_info* sinsp::get_machine_info() return m_machine_info; } +const scap_agent_info* sinsp::get_agent_info() +{ + return m_agent_info; +} + void sinsp::get_filtercheck_fields_info(OUT std::vector& list) { sinsp_utils::get_filtercheck_fields_info(list); diff --git a/userspace/libsinsp/sinsp.h b/userspace/libsinsp/sinsp.h index 4a53ba13d8..db98a3b64e 100644 --- a/userspace/libsinsp/sinsp.h +++ b/userspace/libsinsp/sinsp.h @@ -486,6 +486,13 @@ class SINSP_PUBLIC sinsp : public capture_stats_source */ const scap_machine_info* get_machine_info(); + /*! + \brief Return information about the agent based on start up conditions. + + \note not for use in scap files. + */ + const scap_agent_info* get_agent_info(); + /*! \brief Look up a thread given its tid and return its information, and optionally go dig into proc if the thread is not in the thread table. @@ -1063,6 +1070,7 @@ VISIBILITY_PRIVATE scap_dumper_t* m_dumper; bool m_is_dumping; const scap_machine_info* m_machine_info; + const scap_agent_info* m_agent_info; uint32_t m_num_cpus; sinsp_thread_privatestate_manager m_thread_privatestate_manager; bool m_is_tracers_capture_enabled;