From fe81d720649602a4717ade07a61c0d5d10d6bce3 Mon Sep 17 00:00:00 2001 From: Melissa Kilby Date: Thu, 25 Jul 2024 00:54:39 +0000 Subject: [PATCH] update(anomalydetection): add lastevent_fd to tinfo + start fd / fs related filterchecks 2/n --- plugins/anomalydetection/src/plugin.cpp | 295 +++++++++++++++++- plugins/anomalydetection/src/plugin.h | 58 +++- plugins/anomalydetection/src/plugin_utils.cpp | 67 ++-- plugins/anomalydetection/src/plugin_utils.h | 6 + .../test/include/test_helpers.h | 23 +- .../anomalydetection/test/src/num/cms.ut.cpp | 76 ++++- 6 files changed, 466 insertions(+), 59 deletions(-) diff --git a/plugins/anomalydetection/src/plugin.cpp b/plugins/anomalydetection/src/plugin.cpp index 311d1f31..b26808a5 100644 --- a/plugins/anomalydetection/src/plugin.cpp +++ b/plugins/anomalydetection/src/plugin.cpp @@ -162,6 +162,16 @@ void anomalydetection::parse_init_config(nlohmann::json& config_json) if (config_json.contains(behavior_profiles_pointer) && config_json[behavior_profiles_pointer].is_array()) { const auto& behavior_profiles = config_json[behavior_profiles_pointer]; + const std::vector supported_codes_fd_profile = { + PPME_SYSCALL_OPEN_X, + PPME_SOCKET_ACCEPT_5_X, + PPME_SOCKET_ACCEPT4_6_X, + PPME_SYSCALL_CREAT_X, + PPME_SOCKET_CONNECT_X, + PPME_SYSCALL_OPENAT_2_X, + PPME_SYSCALL_OPENAT2_X, + PPME_SYSCALL_OPEN_BY_HANDLE_AT_X + }; for (const auto& profile : behavior_profiles) { std::vector filter_check_fields; @@ -173,6 +183,19 @@ void anomalydetection::parse_init_config(nlohmann::json& config_json) { codes.insert((ppm_event_code)code.get()); } + /* Some rudimentary initial checks to ensure profiles with %fd fields are applied on fd related events only */ + if (profile["fields"].get().find("%fd") != std::string::npos) + { + for (const auto& code : codes) + { + if (std::find(supported_codes_fd_profile .begin(), supported_codes_fd_profile .end(), code) == supported_codes_fd_profile .end()) + { + log_error("Current behavior profile: " + profile["fields"].get()); + log_error("The above behavior profile contains '%fd' related fields for non fd related event codes, read the docs for help, exiting..."); + exit(1); + } + } + } } m_behavior_profiles_fields.emplace_back(filter_check_fields); m_behavior_profiles_event_codes.emplace_back(std::move(codes)); @@ -226,6 +249,13 @@ bool anomalydetection::init(falcosecurity::init_input& in) // Accessor to falcosecurity/libs' thread table (process cache / core state engine) m_thread_table = t.get_table(THREAD_TABLE_NAME, st::SS_PLUGIN_ST_INT64); // Define accessors to falcosecurity/libs' thread table fields + + /* Subtables */ + m_args = m_thread_table.get_field(t.fields(), "args", st::SS_PLUGIN_ST_TABLE); + m_env = m_thread_table.get_field(t.fields(), "env", st::SS_PLUGIN_ST_TABLE); + m_fds = m_thread_table.get_field(t.fields(), "file_descriptors", st::SS_PLUGIN_ST_TABLE); + + /* proc related */ m_tid = m_thread_table.get_field(t.fields(), "tid", st::SS_PLUGIN_ST_INT64); m_pid = m_thread_table.get_field(t.fields(), "pid", st::SS_PLUGIN_ST_INT64); m_ptid = m_thread_table.get_field(t.fields(), "ptid", st::SS_PLUGIN_ST_INT64); @@ -236,18 +266,41 @@ bool anomalydetection::init(falcosecurity::init_input& in) m_exe_writable = m_thread_table.get_field(t.fields(), "exe_writable", st::SS_PLUGIN_ST_BOOL); m_exe_upper_layer = m_thread_table.get_field(t.fields(), "exe_upper_layer", st::SS_PLUGIN_ST_BOOL); m_exe_from_memfd = m_thread_table.get_field(t.fields(), "exe_from_memfd", st::SS_PLUGIN_ST_BOOL); - m_args = m_thread_table.get_field(t.fields(), "args", st::SS_PLUGIN_ST_TABLE); m_args_value = t.get_subtable_field(m_thread_table, m_args, "value", st::SS_PLUGIN_ST_STRING); - // m_env = m_thread_table.get_field(t.fields(), "env", TBD); - m_container_id = m_thread_table.get_field(t.fields(), "container_id", st::SS_PLUGIN_ST_STRING); - // m_user = m_thread_table.get_field(t.fields(), "user", TBD); - // m_loginuser = m_thread_table.get_field(t.fields(), "loginuser", TBD); - // m_group = m_thread_table.get_field(t.fields(), "group", TBD); + m_env_value = t.get_subtable_field(m_thread_table, m_env, "value", st::SS_PLUGIN_ST_STRING); m_vtid = m_thread_table.get_field(t.fields(), "vtid", st::SS_PLUGIN_ST_INT64); m_vpid = m_thread_table.get_field(t.fields(), "vpid", st::SS_PLUGIN_ST_INT64); m_vpgid = m_thread_table.get_field(t.fields(), "vpgid", st::SS_PLUGIN_ST_INT64); m_tty = m_thread_table.get_field(t.fields(), "tty", st::SS_PLUGIN_ST_UINT32); m_cwd = m_thread_table.get_field(t.fields(), "cwd", st::SS_PLUGIN_ST_STRING); + + /* user related */ + // Not available until the next libs plugins API expansion + + // m_user = m_thread_table.get_field(t.fields(), "user", TBD); + // m_loginuser = m_thread_table.get_field(t.fields(), "loginuser", TBD); + // m_group = m_thread_table.get_field(t.fields(), "group", TBD); + + /* fd or fs related */ + // m_fd_type_value = t.get_subtable_field(m_thread_table, m_fds, "type", st::SS_PLUGIN_ST_UINT32); // todo fix, likely type issue given its of type scap_fd_type + m_fd_openflags_value = t.get_subtable_field(m_thread_table, m_fds, "open_flags", st::SS_PLUGIN_ST_UINT32); + // m_fd_sockinfo_value = t.get_subtable_field(m_thread_table, m_fds, "sock_info", st::SS_PLUGIN_ST_UINT32); // todo fix, likely type issue given its of type sinsp_sockinfo + m_fd_name_value = t.get_subtable_field(m_thread_table, m_fds, "name", st::SS_PLUGIN_ST_STRING); + m_fd_nameraw_value = t.get_subtable_field(m_thread_table, m_fds, "name_raw", st::SS_PLUGIN_ST_STRING); + m_fd_oldname_value = t.get_subtable_field(m_thread_table, m_fds, "old_name", st::SS_PLUGIN_ST_STRING); + m_fd_flags_value = t.get_subtable_field(m_thread_table, m_fds, "flags", st::SS_PLUGIN_ST_UINT32); + m_fd_dev_value = t.get_subtable_field(m_thread_table, m_fds, "dev", st::SS_PLUGIN_ST_UINT32); + m_fd_mount_id_value = t.get_subtable_field(m_thread_table, m_fds, "mount_id", st::SS_PLUGIN_ST_UINT32); + m_fd_ino_value = t.get_subtable_field(m_thread_table, m_fds, "ino", st::SS_PLUGIN_ST_UINT64); + m_fd_pid_value = t.get_subtable_field(m_thread_table, m_fds, "pid", st::SS_PLUGIN_ST_INT64); + m_fd_fd_value = t.get_subtable_field(m_thread_table, m_fds, "fd", st::SS_PLUGIN_ST_INT64); + + /* container related */ + m_container_id = m_thread_table.get_field(t.fields(), "container_id", st::SS_PLUGIN_ST_STRING); + + /* Custom fields */ + m_lastevent_fd_field = m_thread_table.add_field( + t.fields(), "lastevent_fd", st::SS_PLUGIN_ST_INT64); } catch(falcosecurity::plugin_exception e) { @@ -318,7 +371,8 @@ bool anomalydetection::extract(const falcosecurity::extract_fields_input& in) { auto& req = in.get_extract_request(); auto& tr = in.get_table_reader(); - int64_t thread_id = in.get_event_reader().get_tid(); + auto& evt = in.get_event_reader(); + int64_t thread_id = evt.get_tid(); uint64_t count_min_sketch_estimate = 0; std::string behavior_profile_concat_str; auto index = req.get_arg_index(); @@ -330,14 +384,14 @@ bool anomalydetection::extract(const falcosecurity::extract_fields_input& in) switch(req.get_field_id()) { case ANOMALYDETECTION_COUNT_MIN_SKETCH_COUNT: - if(extract_filterchecks_concat_profile(thread_id, tr, m_behavior_profiles_fields[index], behavior_profile_concat_str)) + if(extract_filterchecks_concat_profile(evt, tr, m_behavior_profiles_fields[index], behavior_profile_concat_str)) { count_min_sketch_estimate = m_count_min_sketches[index].get()->estimate(behavior_profile_concat_str); req.set_value(count_min_sketch_estimate, true); } return true; case ANOMALYDETECTION_COUNT_MIN_SKETCH_BEHAVIOR_PROFILE_CONCAT_STR: - if(extract_filterchecks_concat_profile(thread_id, tr, m_behavior_profiles_fields[index], behavior_profile_concat_str)) + if(extract_filterchecks_concat_profile(evt, tr, m_behavior_profiles_fields[index], behavior_profile_concat_str)) { req.set_value(behavior_profile_concat_str, true); } @@ -354,17 +408,19 @@ bool anomalydetection::extract(const falcosecurity::extract_fields_input& in) // Parse capability ////////////////////////// -bool anomalydetection::extract_filterchecks_concat_profile(int64_t thread_id, const falcosecurity::table_reader &tr, const std::vector& fields, std::string& behavior_profile_concat_str) +bool anomalydetection::extract_filterchecks_concat_profile(const falcosecurity::event_reader &evt, const falcosecurity::table_reader &tr, const std::vector& fields, std::string& behavior_profile_concat_str) { using st = falcosecurity::state_value_type; + int64_t thread_id = evt.get_tid(); auto thread_entry = m_thread_table.get_entry(tr, thread_id); // Create a concatenated string formed out of each field per behavior profile // No concept of null fields (instead its always an empty string) compared to libsinsp for (const auto& field : fields) { - std::string tstr; + std::string tstr = ""; + uint64_t tuint64 = UINT64_MAX; uint32_t tuint32 = UINT32_MAX; int64_t tint64 = -1; int64_t ptid = -1; @@ -684,6 +740,130 @@ bool anomalydetection::extract_filterchecks_concat_profile(int64_t thread_id, co m_comm.read_value(tr, *leader, tstr); break; } + + // + // fd or fs related + // + + // todo implement fallbacks from null fd table entry aka extract from evt args + + case plugin_sinsp_filterchecks::TYPE_FDNUM: + { + auto fd_table = m_thread_table.get_subtable( + tr, m_fds, thread_entry, + st::SS_PLUGIN_ST_INT64); + m_lastevent_fd_field.read_value(tr, thread_entry, tint64); + tstr = std::to_string(tint64); + break; + } + case plugin_sinsp_filterchecks::TYPE_FDNAME: + { + auto fd_table = m_thread_table.get_subtable( + tr, m_fds, thread_entry, + st::SS_PLUGIN_ST_INT64); + switch(evt.get_type()) + { + case PPME_SYSCALL_OPEN_X: + case PPME_SOCKET_ACCEPT_5_X: + case PPME_SOCKET_ACCEPT4_6_X: + case PPME_SYSCALL_CREAT_X: + case PPME_SOCKET_CONNECT_X: + case PPME_SYSCALL_OPENAT_2_X: + case PPME_SYSCALL_OPENAT2_X: + case PPME_SYSCALL_OPEN_BY_HANDLE_AT_X: + { + m_lastevent_fd_field.read_value(tr, thread_entry, tint64); + auto fd_entry = fd_table.get_entry(tr, tint64); + m_fd_name_value.read_value(tr, fd_entry, tstr); + break; + } + } + break; + } + case plugin_sinsp_filterchecks::TYPE_DIRECTORY: + { + auto fd_table = m_thread_table.get_subtable( + tr, m_fds, thread_entry, + st::SS_PLUGIN_ST_INT64); + switch(evt.get_type()) + { + case PPME_SYSCALL_OPEN_X: + case PPME_SYSCALL_CREAT_X: + case PPME_SYSCALL_OPENAT_2_X: + case PPME_SYSCALL_OPENAT2_X: + case PPME_SYSCALL_OPEN_BY_HANDLE_AT_X: + { + m_lastevent_fd_field.read_value(tr, thread_entry, tint64); + auto fd_entry = fd_table.get_entry(tr, tint64); + m_fd_name_value.read_value(tr, fd_entry, tstr); + size_t pos = tstr.find_last_of('/'); + if (pos != std::string::npos) + { + tstr = tstr.substr(0, pos); + } + break; + } + } + break; + } + case plugin_sinsp_filterchecks::TYPE_FILENAME: + { + auto fd_table = m_thread_table.get_subtable( + tr, m_fds, thread_entry, + st::SS_PLUGIN_ST_INT64); + switch(evt.get_type()) + { + case PPME_SYSCALL_OPEN_X: + case PPME_SYSCALL_CREAT_X: + case PPME_SYSCALL_OPENAT_2_X: + case PPME_SYSCALL_OPENAT2_X: + case PPME_SYSCALL_OPEN_BY_HANDLE_AT_X: + { + m_lastevent_fd_field.read_value(tr, thread_entry, tint64); + auto fd_entry = fd_table.get_entry(tr, tint64); + m_fd_name_value.read_value(tr, fd_entry, tstr); + size_t pos = tstr.find_last_of('/'); + if (pos != std::string::npos) + { + tstr = tstr.substr(pos + 1); + } + break; + } + } + break; + } + case plugin_sinsp_filterchecks::TYPE_INO: + { + auto fd_table = m_thread_table.get_subtable( + tr, m_fds, thread_entry, + st::SS_PLUGIN_ST_INT64); + m_lastevent_fd_field.read_value(tr, thread_entry, tint64); + auto fd_entry = fd_table.get_entry(tr, tint64); + m_fd_ino_value.read_value(tr, fd_entry, tint64); + tstr = std::to_string(tint64); + break; + } + case plugin_sinsp_filterchecks::TYPE_DEV: + { + auto fd_table = m_thread_table.get_subtable( + tr, m_fds, thread_entry, + st::SS_PLUGIN_ST_INT64); + m_lastevent_fd_field.read_value(tr, thread_entry, tint64); + auto fd_entry = fd_table.get_entry(tr, tint64); + m_fd_dev_value.read_value(tr, fd_entry, tuint32); + tstr = std::to_string(tuint32); + break; + } + case plugin_sinsp_filterchecks::TYPE_FDNAMERAW: + { + auto fd_table = m_thread_table.get_subtable( + tr, m_fds, thread_entry, + st::SS_PLUGIN_ST_INT64); + m_lastevent_fd_field.read_value(tr, thread_entry, tint64); + auto fd_entry = fd_table.get_entry(tr, tint64); + m_fd_nameraw_value.read_value(tr, fd_entry, tstr); + break; + } default: break; } @@ -692,21 +872,110 @@ bool anomalydetection::extract_filterchecks_concat_profile(int64_t thread_id, co return true; } +// Adopted from the k8smeta plugin, obtain a param from a sinsp event +static inline sinsp_param get_syscall_evt_param(void* evt, uint32_t num_param) +{ + uint32_t dataoffset = 0; + // pointer to the lengths array inside the event. + auto len = (uint16_t*)((uint8_t*)evt + + sizeof(falcosecurity::_internal::ss_plugin_event)); + for(uint32_t j = 0; j < num_param; j++) + { + // sum lengths of the previous params. + dataoffset += len[j]; + } + return {.param_len = len[num_param], + .param_pointer = + ((uint8_t*)&len + [((falcosecurity::_internal::ss_plugin_event*)evt) + ->nparams]) + + dataoffset}; +} + bool anomalydetection::parse_event(const falcosecurity::parse_event_input& in) { auto& evt = in.get_event_reader(); auto& tr = in.get_table_reader(); + auto& tw = in.get_table_writer(); + int64_t thread_id = evt.get_tid(); // note: Plugin event parsing guaranteed to happen after libs' `sinsp_parser::process_event` has finished. // Needs to stay in sync w/ libs updates. // Ultimately gated by `base_syscalls` restrictions if Falco is used w/ `base_syscalls`. + + + // The plugin currently cannot access for examle m_lastevent_fd from libs + // Write this info to tinfo within the plugin + switch(evt.get_type()) + { + case PPME_SYSCALL_OPEN_X: // fd param 0 + case PPME_SOCKET_ACCEPT_5_X: + case PPME_SOCKET_ACCEPT4_6_X: + case PPME_SYSCALL_CREAT_X: + { + auto res_param = get_syscall_evt_param(in.get_event_reader().get_buf(), + 0); + if (res_param.param_pointer == nullptr) + { + return false; + } + + int64_t fd = *(int64_t*)(res_param.param_pointer); + auto thread_entry = m_thread_table.get_entry(tr, thread_id); + m_lastevent_fd_field.write_value(tw, thread_entry, fd); + break; + } + case PPME_SOCKET_CONNECT_X: // fd param 2 + { + auto res_param = get_syscall_evt_param(in.get_event_reader().get_buf(), + 2); + if (res_param.param_pointer == nullptr) + { + return false; + } + int64_t fd = *(int64_t*)(res_param.param_pointer); + auto thread_entry = m_thread_table.get_entry(tr, thread_id); + m_lastevent_fd_field.write_value(tw, thread_entry, fd); + break; + } + case PPME_SYSCALL_OPENAT_2_X: // fd param 0 + case PPME_SYSCALL_OPENAT2_X: + { + auto res_param = get_syscall_evt_param(in.get_event_reader().get_buf(), + 0); + if (res_param.param_pointer == nullptr) + { + return false; + } + int64_t fd = *(int64_t*)(res_param.param_pointer); + auto thread_entry = m_thread_table.get_entry(tr, thread_id); + m_lastevent_fd_field.write_value(tw, thread_entry, fd); + break; + } + case PPME_SYSCALL_OPEN_BY_HANDLE_AT_X: + { + auto res_param = get_syscall_evt_param(in.get_event_reader().get_buf(), + 0); + if (res_param.param_pointer == nullptr) + { + return false; + } + int64_t fd = *(int64_t*)(res_param.param_pointer); + auto thread_entry = m_thread_table.get_entry(tr, thread_id); + m_lastevent_fd_field.write_value(tw, thread_entry, fd); + break; + } + default: + break; + } + + // Loop over behavior profiles, extract profile fields and update the count_min_sketch counts. int i = 0; std::string behavior_profile_concat_str; for(const auto& set : m_behavior_profiles_event_codes) { if(set.find((ppm_event_code)evt.get_type()) != set.end()) { - int64_t thread_id = in.get_event_reader().get_tid(); if(thread_id <= 0) { return false; @@ -714,7 +983,7 @@ bool anomalydetection::parse_event(const falcosecurity::parse_event_input& in) try { behavior_profile_concat_str.clear(); - if (i < m_n_sketches && extract_filterchecks_concat_profile(thread_id, tr, m_behavior_profiles_fields[i], behavior_profile_concat_str) && !behavior_profile_concat_str.empty()) + if (i < m_n_sketches && extract_filterchecks_concat_profile(evt, tr, m_behavior_profiles_fields[i], behavior_profile_concat_str) && !behavior_profile_concat_str.empty()) { m_count_min_sketches[i].get()->update(behavior_profile_concat_str, (uint64_t)1); } diff --git a/plugins/anomalydetection/src/plugin.h b/plugins/anomalydetection/src/plugin.h index 705b55b0..1e52b623 100644 --- a/plugins/anomalydetection/src/plugin.h +++ b/plugins/anomalydetection/src/plugin.h @@ -31,7 +31,13 @@ limitations under the License. #include #include -# define UINT32_MAX (4294967295U) +#define UINT32_MAX (4294967295U) + +struct sinsp_param +{ + uint16_t param_len; + uint8_t* param_pointer; +}; class anomalydetection { @@ -74,7 +80,7 @@ class anomalydetection std::string get_last_error() { return m_lasterr; } - void log_error(std::string err_mess); + static void log_error(std::string err_mess); ////////////////////////// // Extract capability @@ -118,7 +124,7 @@ class anomalydetection bool parse_event(const falcosecurity::parse_event_input& in); // Custom helper function within event parsing - bool extract_filterchecks_concat_profile(int64_t thread_id, const falcosecurity::table_reader &tr, const std::vector& fields, std::string& behavior_profile_concat_str); + bool extract_filterchecks_concat_profile(const falcosecurity::event_reader &evt, const falcosecurity::table_reader &tr, const std::vector& fields, std::string& behavior_profile_concat_str); private: @@ -136,8 +142,13 @@ class anomalydetection std::string m_lasterr; // required; standard plugin API; accessor to falcosecurity/libs' thread table falcosecurity::table m_thread_table; - // Accessors to the fixed fields of falcosecurity/libs' thread table -> non comprehensive re-definition of sinsp_threadinfo - // Reference in falcosecurity/libs: userspace/libsinsp/threadinfo.h + + /* Subtables */ + falcosecurity::table_field m_args; ///< args subtable + falcosecurity::table_field m_env; ///< env variables subtable + falcosecurity::table_field m_fds; ///< fd subtable + + /* proc related */ falcosecurity::table_field m_tid; ///< The id of this thread falcosecurity::table_field m_pid; ///< The id of the process containing this thread. In single thread threads, this is equal to tid. falcosecurity::table_field m_ptid; ///< The id of the process that started this thread. @@ -148,20 +159,41 @@ class anomalydetection falcosecurity::table_field m_exe_writable; falcosecurity::table_field m_exe_upper_layer; ///< True if the executable file belongs to upper layer in overlayfs falcosecurity::table_field m_exe_from_memfd; ///< True if the executable is stored in fileless memory referenced by memfd - falcosecurity::table_field m_args; ///< Command line arguments (e.g. "-d1") - falcosecurity::table_field m_args_value; ///< String value entry from the args array - falcosecurity::table_field m_env; ///< Environment variables - falcosecurity::table_field m_container_id; ///< heuristic-based container id - falcosecurity::table_field m_uid; ///< user uid - falcosecurity::table_field m_user; ///< user infos - falcosecurity::table_field m_loginuid; ///< auid - falcosecurity::table_field m_loginuser; ///< loginuser infos (auid) + falcosecurity::table_field m_args_value; ///< Value entry to command line arguments (e.g. "-d1") from the args array + falcosecurity::table_field m_env_value; ///< Value entry falcosecurity::table_field m_group; ///< group infos falcosecurity::table_field m_vtid; ///< The virtual id of this thread. falcosecurity::table_field m_vpid; ///< The virtual id of the process containing this thread. In single thread threads, this is equal to vtid. falcosecurity::table_field m_vpgid; // The virtual process group id, as seen from its pid namespace falcosecurity::table_field m_tty; ///< Number of controlling terminal falcosecurity::table_field m_cwd; ///< current working directory + + /* user related */ + // Not available until the next libs plugins API expansion + falcosecurity::table_field m_uid; ///< user uid + falcosecurity::table_field m_user; ///< user infos + falcosecurity::table_field m_loginuid; ///< auid + falcosecurity::table_field m_loginuser; ///< loginuser infos (auid) + + /* fd or fs related */ + falcosecurity::table_field m_fd_type_value; // todo fix + falcosecurity::table_field m_fd_openflags_value; + falcosecurity::table_field m_fd_sockinfo_value; // todo fix + falcosecurity::table_field m_fd_name_value; + falcosecurity::table_field m_fd_nameraw_value; + falcosecurity::table_field m_fd_oldname_value; + falcosecurity::table_field m_fd_flags_value; + falcosecurity::table_field m_fd_dev_value; + falcosecurity::table_field m_fd_mount_id_value; + falcosecurity::table_field m_fd_ino_value; + falcosecurity::table_field m_fd_pid_value; + falcosecurity::table_field m_fd_fd_value; + + /* container related */ + falcosecurity::table_field m_container_id; ///< heuristic-based container id + + /* Custom write/read fields*/ + falcosecurity::table_field m_lastevent_fd_field; // todo expose via plugin API }; // required; standard plugin API diff --git a/plugins/anomalydetection/src/plugin_utils.cpp b/plugins/anomalydetection/src/plugin_utils.cpp index 2b132b86..b1495e20 100644 --- a/plugins/anomalydetection/src/plugin_utils.cpp +++ b/plugins/anomalydetection/src/plugin_utils.cpp @@ -125,44 +125,44 @@ static const filtercheck_field_info sinsp_filter_check_fields[] = {PT_CHARBUF, EPF_NONE, PF_NA, "container.ip", "Container ip address", "The container's / pod's primary ip address as retrieved from the container engine. Only ipv4 addresses are tracked. Consider container.cni.json (CRI use case) for logging ip addresses for each network interface. In instances of userspace container engine lookup delays, this field may not be available yet."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.cni.json", "Container's / pod's CNI result json", "The container's / pod's CNI result field from the respective pod status info. It contains ip addresses for each network interface exposed as unparsed escaped JSON string. Supported for CRI container engine (containerd, cri-o runtimes), optimized for containerd (some non-critical JSON keys removed). Useful for tracking ips (ipv4 and ipv6, dual-stack support) for each network interface (multi-interface support). In instances of userspace container engine lookup delays, this field may not be available yet."}, {PT_INT64, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_ID, "fd.num", "FD Number", "the unique number identifying the file descriptor."}, - {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_DEC, "fd.type", "FD Type", "type of FD. Can be 'file', 'directory', 'ipv4', 'ipv6', 'unix', 'pipe', 'event', 'signalfd', 'eventpoll', 'inotify' 'signalfd' or 'memfd'."}, - {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_DEC, "fd.typechar", "FD Type Char", "type of FD as a single character. Can be 'f' for file, 4 for IPv4 socket, 6 for IPv6 socket, 'u' for unix socket, p for pipe, 'e' for eventfd, 's' for signalfd, 'l' for eventpoll, 'i' for inotify, 'b' for bpf, 'u' for userfaultd, 'r' for io_uring, 'm' for memfd ,'o' for unknown."}, + {PT_CHARBUF, EPF_NONE, PF_DEC, "fd.type", "FD Type", "type of FD. Can be 'file', 'directory', 'ipv4', 'ipv6', 'unix', 'pipe', 'event', 'signalfd', 'eventpoll', 'inotify' 'signalfd' or 'memfd'."}, + {PT_CHARBUF, EPF_NONE, PF_DEC, "fd.typechar", "FD Type Char", "type of FD as a single character. Can be 'f' for file, 4 for IPv4 socket, 6 for IPv6 socket, 'u' for unix socket, p for pipe, 'e' for eventfd, 's' for signalfd, 'l' for eventpoll, 'i' for inotify, 'b' for bpf, 'u' for userfaultd, 'r' for io_uring, 'm' for memfd ,'o' for unknown."}, {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fd.name", "FD Name", "FD full name. If the fd is a file, this field contains the full path. If the FD is a socket, this field contain the connection tuple."}, {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fd.directory", "FD Directory", "If the fd is a file, the directory that contains it."}, {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fd.filename", "FD Filename", "If the fd is a file, the filename without the path."}, - {PT_IPADDR, EPF_ANOMALY_PLUGIN | EPF_FILTER_ONLY | EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "fd.ip", "FD IP Address", "matches the ip address (client or server) of the fd."}, - {PT_IPADDR, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fd.cip", "FD Client Address", "client IP address."}, - {PT_IPADDR, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fd.sip", "FD Server Address", "server IP address."}, - {PT_IPADDR, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fd.lip", "FD Local Address", "local IP address."}, - {PT_IPADDR, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fd.rip", "FD Remote Address", "remote IP address."}, - {PT_PORT, EPF_ANOMALY_PLUGIN | EPF_FILTER_ONLY | EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_DEC, "fd.port", "FD Port", "matches the port (either client or server) of the fd."}, - {PT_PORT, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_DEC, "fd.cport", "FD Client Port", "for TCP/UDP FDs, the client port."}, - {PT_PORT, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_DEC, "fd.sport", "FD Server Port", "for TCP/UDP FDs, server port."}, - {PT_PORT, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_DEC, "fd.lport", "FD Local Port", "for TCP/UDP FDs, the local port."}, - {PT_PORT, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_DEC, "fd.rport", "FD Remote Port", "for TCP/UDP FDs, the remote port."}, - {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fd.l4proto", "FD IP Protocol", "the IP protocol of a socket. Can be 'tcp', 'udp', 'icmp' or 'raw'."}, - {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fd.sockfamily", "FD Socket Family", "the socket family for socket events. Can be 'ip' or 'unix'."}, + {PT_IPADDR, EPF_FILTER_ONLY | EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "fd.ip", "FD IP Address", "matches the ip address (client or server) of the fd."}, + {PT_IPADDR, EPF_NONE, PF_NA, "fd.cip", "FD Client Address", "client IP address."}, + {PT_IPADDR, EPF_NONE, PF_NA, "fd.sip", "FD Server Address", "server IP address."}, + {PT_IPADDR, EPF_NONE, PF_NA, "fd.lip", "FD Local Address", "local IP address."}, + {PT_IPADDR, EPF_NONE, PF_NA, "fd.rip", "FD Remote Address", "remote IP address."}, + {PT_PORT, EPF_FILTER_ONLY | EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_DEC, "fd.port", "FD Port", "matches the port (either client or server) of the fd."}, + {PT_PORT, EPF_NONE, PF_DEC, "fd.cport", "FD Client Port", "for TCP/UDP FDs, the client port."}, + {PT_PORT, EPF_NONE, PF_DEC, "fd.sport", "FD Server Port", "for TCP/UDP FDs, server port."}, + {PT_PORT, EPF_NONE, PF_DEC, "fd.lport", "FD Local Port", "for TCP/UDP FDs, the local port."}, + {PT_PORT, EPF_NONE, PF_DEC, "fd.rport", "FD Remote Port", "for TCP/UDP FDs, the remote port."}, + {PT_CHARBUF, EPF_NONE, PF_NA, "fd.l4proto", "FD IP Protocol", "the IP protocol of a socket. Can be 'tcp', 'udp', 'icmp' or 'raw'."}, + {PT_CHARBUF, EPF_NONE, PF_NA, "fd.sockfamily", "FD Socket Family", "the socket family for socket events. Can be 'ip' or 'unix'."}, {PT_BOOL, EPF_NONE, PF_NA, "fd.is_server", "FD Server", "'true' if the process owning this FD is the server endpoint in the connection."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.uid", "FD ID", "a unique identifier for the FD, created by chaining the FD number and the thread ID."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.containername", "FD Container Name", "chaining of the container ID and the FD name. Useful when trying to identify which container an FD belongs to."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.containerdirectory", "FD Container Directory", "chaining of the container ID and the directory name. Useful when trying to identify which container a directory belongs to."}, {PT_PORT, EPF_FILTER_ONLY | EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "fd.proto", "FD Protocol", "matches the protocol (either client or server) of the fd."}, - {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fd.cproto", "FD Client Protocol", "for TCP/UDP FDs, the client protocol."}, - {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fd.sproto", "FD Server Protocol", "for TCP/UDP FDs, server protocol."}, - {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fd.lproto", "FD Local Protocol", "for TCP/UDP FDs, the local protocol."}, - {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fd.rproto", "FD Remote Protocol", "for TCP/UDP FDs, the remote protocol."}, - {PT_IPNET, EPF_ANOMALY_PLUGIN | EPF_FILTER_ONLY | EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "fd.net", "FD IP Network", "matches the IP network (client or server) of the fd."}, - {PT_IPNET, EPF_ANOMALY_PLUGIN | EPF_FILTER_ONLY | EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "fd.cnet", "FD Client Network", "matches the client IP network of the fd."}, - {PT_IPNET, EPF_ANOMALY_PLUGIN | EPF_FILTER_ONLY | EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "fd.snet", "FD Server Network", "matches the server IP network of the fd."}, - {PT_IPNET, EPF_ANOMALY_PLUGIN | EPF_FILTER_ONLY | EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "fd.lnet", "FD Local Network", "matches the local IP network of the fd."}, - {PT_IPNET, EPF_ANOMALY_PLUGIN | EPF_FILTER_ONLY | EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "fd.rnet", "FD Remote Network", "matches the remote IP network of the fd."}, + {PT_CHARBUF, EPF_NONE, PF_NA, "fd.cproto", "FD Client Protocol", "for TCP/UDP FDs, the client protocol."}, + {PT_CHARBUF, EPF_NONE, PF_NA, "fd.sproto", "FD Server Protocol", "for TCP/UDP FDs, server protocol."}, + {PT_CHARBUF, EPF_NONE, PF_NA, "fd.lproto", "FD Local Protocol", "for TCP/UDP FDs, the local protocol."}, + {PT_CHARBUF, EPF_NONE, PF_NA, "fd.rproto", "FD Remote Protocol", "for TCP/UDP FDs, the remote protocol."}, + {PT_IPNET, EPF_FILTER_ONLY | EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "fd.net", "FD IP Network", "matches the IP network (client or server) of the fd."}, + {PT_IPNET, EPF_FILTER_ONLY | EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "fd.cnet", "FD Client Network", "matches the client IP network of the fd."}, + {PT_IPNET, EPF_FILTER_ONLY | EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "fd.snet", "FD Server Network", "matches the server IP network of the fd."}, + {PT_IPNET, EPF_FILTER_ONLY | EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "fd.lnet", "FD Local Network", "matches the local IP network of the fd."}, + {PT_IPNET, EPF_FILTER_ONLY | EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "fd.rnet", "FD Remote Network", "matches the remote IP network of the fd."}, {PT_BOOL, EPF_NONE, PF_NA, "fd.connected", "FD Connected", "for TCP/UDP FDs, 'true' if the socket is connected."}, {PT_BOOL, EPF_NONE, PF_NA, "fd.name_changed", "FD Name Changed", "True when an event changes the name of an fd used by this event. This can occur in some cases such as udp connections where the connection tuple changes."}, {PT_CHARBUF, EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "fd.cip.name", "FD Client Domain Name", "Domain name associated with the client IP address."}, {PT_CHARBUF, EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "fd.sip.name", "FD Server Domain Name", "Domain name associated with the server IP address."}, {PT_CHARBUF, EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "fd.lip.name", "FD Local Domain Name", "Domain name associated with the local IP address."}, {PT_CHARBUF, EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "fd.rip.name", "FD Remote Domain Name", "Domain name associated with the remote IP address."}, - {PT_INT32, EPF_NONE, PF_HEX, "fd.dev", "FD Device", "device number (major/minor) containing the referenced file"}, + {PT_INT32, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_HEX, "fd.dev", "FD Device", "device number (major/minor) containing the referenced file"}, {PT_INT32, EPF_NONE, PF_DEC, "fd.dev.major", "FD Major Device", "major device number containing the referenced file"}, {PT_INT32, EPF_NONE, PF_DEC, "fd.dev.minor", "FD Minor Device", "minor device number containing the referenced file"}, {PT_INT64, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_DEC, "fd.ino", "FD Inode Number", "inode number of the referenced file"}, @@ -216,13 +216,20 @@ const std::vector get_profile_fields(const std: argid = std::stoi(rawfield.substr(start_pos + 1, end_pos - start_pos - 1)); } } - if ((sinsp_filter_check_fields[i].m_flags & EPF_ANOMALY_PLUGIN) && std::string(sinsp_filter_check_fields[i].m_name) == fieldname) + if (std::string(sinsp_filter_check_fields[i].m_name) == fieldname) { - fields.emplace_back(plugin_sinsp_filterchecks_field{ - id, - argid, - argname - }); + if ((sinsp_filter_check_fields[i].m_flags & EPF_ANOMALY_PLUGIN)) + { + fields.emplace_back(plugin_sinsp_filterchecks_field{ + id, + argid, + argname + }); + } else + { + plugin_anomalydetection::utils::log_error("Remove the following unsupported behavior profile field: '" + fieldname + "' exiting..."); + exit(1); + } } argid = 0; argname.clear(); diff --git a/plugins/anomalydetection/src/plugin_utils.h b/plugins/anomalydetection/src/plugin_utils.h index e37f6325..c149736a 100644 --- a/plugins/anomalydetection/src/plugin_utils.h +++ b/plugins/anomalydetection/src/plugin_utils.h @@ -21,6 +21,7 @@ limitations under the License. #include #include #include "plugin_sinsp_filterchecks.h" +#include "plugin_consts.h" typedef struct plugin_sinsp_filterchecks_field { @@ -37,4 +38,9 @@ namespace plugin_anomalydetection::utils // No need for performance optimization atm as the typical use case is to have less than 3-8 sketches const std::vector get_profile_fields(const std::string& behavior_profile); + inline void log_error(std::string err_mess) + { + printf("%s %s\n", PLUGIN_LOG_PREFIX, err_mess.c_str()); + } + } // plugin_anomalydetection::utils \ No newline at end of file diff --git a/plugins/anomalydetection/test/include/test_helpers.h b/plugins/anomalydetection/test/include/test_helpers.h index 6aeb37e1..ba187448 100644 --- a/plugins/anomalydetection/test/include/test_helpers.h +++ b/plugins/anomalydetection/test/include/test_helpers.h @@ -15,7 +15,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -#define INIT_CONFIG "{\"count_min_sketch\":{\"enabled\":true,\"n_sketches\":3,\"gamma_eps\":[[0.001,0.0001],[0.001,0.0001],[0.001,0.0001]],\"behavior_profiles\":[{\"fields\":\"%container.id %proc.name %proc.pname %proc.exepath %proc.pexepath %proc.tty %proc.vpid %proc.pvid]\",\"event_codes\":[293,331]},{\"fields\":\"%container.id %proc.name %proc.aname[1] %proc.aname[2] %proc.aname[3] %proc.exepath %proc.tty %proc.vpgid.name %proc.sname %fd.name\",\"event_codes\":[3,307,327]},{\"fields\":\"%container.id %proc.cmdline %proc.name %proc.aname[0] %proc.aname[1] %proc.aname[2] %proc.aname[3] %proc.aname[4] %proc.aname[5] %proc.aname[6] %proc.aname[7] %proc.pid %proc.apid[0] %proc.apid[1] %proc.apid[2] %proc.apid[3] %proc.apid[4] %proc.apid[5] %proc.apid[6] %proc.apid[7] %proc.exepath %proc.aexepath[0] %proc.aexepath[1] %proc.aexepath[2] %proc.aexepath[3] %proc.aexepath[4] %proc.aexepath[5] %proc.aexepath[6] %proc.aexepath[7] %proc.vpgid %proc.vpgid.name %proc.sid %proc.sname\",\"event_codes\":[293,331]}]}}" +#define INIT_CONFIG "{\"count_min_sketch\":{\"enabled\":true,\"n_sketches\":3,\"gamma_eps\":[[0.001,0.0001],[0.001,0.0001],[0.001,0.0001]],\"behavior_profiles\":[\ +{\"fields\":\"%container.id %proc.name %proc.pname %proc.exepath %proc.pexepath %proc.tty %proc.vpid %proc.pvid]\",\ +\"event_codes\":[293,331]},\ +{\"fields\":\"%proc.pid %fd.num %fd.name %fd.directory %fd.filename %fd.dev %fd.ino %fd.nameraw %fs.path.name %fs.path.nameraw\",\ +\"event_codes\":[3,307,327,23]},\ +{\"fields\":\"%container.id %proc.cmdline %proc.name %proc.aname[0] %proc.aname[1] %proc.aname[2] %proc.aname[3] %proc.aname[4] %proc.aname[5] %proc.aname[6] %proc.aname[7] %proc.pid %proc.apid[0] %proc.apid[1] %proc.apid[2] %proc.apid[3] %proc.apid[4] %proc.apid[5] %proc.apid[6] %proc.apid[7] %proc.exepath %proc.aexepath[0] %proc.aexepath[1] %proc.aexepath[2] %proc.aexepath[3] %proc.aexepath[4] %proc.aexepath[5] %proc.aexepath[6] %proc.aexepath[7] %proc.vpgid %proc.vpgid.name %proc.sid %proc.sname\",\ +\"event_codes\":[293,331]}]}}" #define ASSERT_PLUGIN_INITIALIZATION(p_o, p_l) \ { \ @@ -26,3 +32,18 @@ limitations under the License. p_l.add_filter_check(m_inspector.new_generic_filtercheck()); \ p_l.add_filter_check(sinsp_plugin::new_filtercheck(p_o)); \ } + +#define DEFAULT_IPV4_CLIENT_STRING "172.40.111.222" +#define DEFAULT_IPV6_CLIENT_STRING "::1" +#define DEFAULT_CLIENT_PORT_STRING "54321" +#define DEFAULT_CLIENT_PORT 54321 + +#define DEFAULT_IPV4_SERVER_STRING "142.251.111.147" +#define DEFAULT_IPV6_SERVER_STRING "2001:4860:4860::8888" +#define DEFAULT_SERVER_PORT_STRING "443" +#define DEFAULT_SERVER_PORT 443 + +#define DEFAULT_IPV4_FDNAME "172.40.111.222:54321->142.251.111.147:443" +#define DEFAULT_IPV6_FDNAME "::1:54321->2001:4860:4860::8888:443" + +#define DEFAULT_IP_STRING_SIZE 100 diff --git a/plugins/anomalydetection/test/src/num/cms.ut.cpp b/plugins/anomalydetection/test/src/num/cms.ut.cpp index 3b329404..59ecab95 100644 --- a/plugins/anomalydetection/test/src/num/cms.ut.cpp +++ b/plugins/anomalydetection/test/src/num/cms.ut.cpp @@ -105,7 +105,6 @@ TEST_F(sinsp_with_test_input, plugin_anomalydetection_filterchecks_fields) ASSERT_TRUE(field_exists(evt, "anomaly.count_min_sketch.profile", pl_flist)); ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch.profile", pl_flist), "test-exeinit/bin/test-exe/sbin/init3481820"); ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch.profile[0]", pl_flist), "test-exeinit/bin/test-exe/sbin/init3481820"); - ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch.profile[1]", pl_flist), "test-exeinit/bin/test-exe34818initinit"); ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch.profile[2]", pl_flist), "test-exe -c 'echo aGVsbG8K | base64 -d'test-exetest-exeinit20201/bin/test-exe/bin/test-exe/sbin/init1init0init"); } @@ -115,7 +114,6 @@ TEST_F(sinsp_with_test_input, plugin_anomalydetection_filterchecks_fields_proc_l filter_check_list pl_flist; ASSERT_PLUGIN_INITIALIZATION(plugin_owner, pl_flist) uint64_t not_relevant_64 = 0; - uint32_t not_relevant_32 = 0; uint64_t pgid = 9999; uint32_t loginuid = UINT32_MAX - 1, euid = 2000U; scap_const_sized_buffer empty_bytebuf = {.buf = nullptr, .size = 0}; @@ -135,3 +133,77 @@ TEST_F(sinsp_with_test_input, plugin_anomalydetection_filterchecks_fields_proc_l ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch.profile[2]", pl_flist), "p6_t1_comm -c 'echo aGVsbG8K | base64 -d'p6_t1_commp6_t1_commp5_t1_commp4_t1_commp3_t1_commp2_t1_comminit8787827672251/usr/bin/p6_t1_exepath/usr/bin/p6_t1_exepath/usr/bin/p5_t1_exepath/usr/bin/p4_t1_exepath/usr/bin/p3_t1_exepath/usr/bin/p2_t1_exepath/sbin/init9999p5_t1_comm0init"); } + +TEST_F(sinsp_with_test_input, plugin_anomalydetection_filterchecks_fields_fd_fs_files) +{ + std::shared_ptr plugin_owner; + filter_check_list pl_flist; + ASSERT_PLUGIN_INITIALIZATION(plugin_owner, pl_flist) + add_default_init_thread(); + + sinsp_evt *evt; + open_inspector(); + + int64_t fd = 4; + add_event(increasing_ts(), 3, PPME_SYSCALL_OPEN_E, 3, "/tmp/subdir1/subdir2/subdir3/subdir4/../../the_file", 0, 0); + add_event_advance_ts(increasing_ts(), 3, PPME_SYSCALL_OPEN_X, 6, fd, "/tmp/some_other_file", 0, 0, 0, (uint64_t) 0); + fd = 5; + add_event(increasing_ts(), 3, PPME_SYSCALL_OPEN_E, 3, "/tmp/subdir1/subdir2/subdir3/subdir4/../../the_file2", 0, 0); + evt = add_event_advance_ts(increasing_ts(), 3, PPME_SYSCALL_OPEN_X, 6, fd, "/tmp/some_other_file2", 0, 0, 0, (uint64_t) 0); + ASSERT_EQ(get_field_as_string(evt, "fd.num"), "5"); + ASSERT_EQ(get_field_as_string(evt, "fd.name"), "/tmp/subdir1/subdir2/the_file2"); + ASSERT_EQ(get_field_as_string(evt, "fd.directory"), "/tmp/subdir1/subdir2"); + ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch.profile[1]", pl_flist), "-15/tmp/subdir1/subdir2/the_file2/tmp/subdir1/subdir2the_file200/tmp/subdir1/subdir2/subdir3/subdir4/../../the_file2"); +} + +TEST_F(sinsp_with_test_input, plugin_anomalydetection_filterchecks_fields_fd_network) +{ + std::shared_ptr plugin_owner; + filter_check_list pl_flist; + ASSERT_PLUGIN_INITIALIZATION(plugin_owner, pl_flist) + add_default_init_thread(); + + open_inspector(); + sinsp_evt* evt = NULL; + sinsp_fdinfo* fdinfo = NULL; + int64_t client_fd = 8; + int64_t return_value = 0; + + add_event_advance_ts(increasing_ts(), 1, PPME_SOCKET_SOCKET_E, 3, (uint32_t) PPM_AF_INET, (uint32_t) SOCK_STREAM, (uint32_t) 0); + add_event_advance_ts(increasing_ts(), 1, PPME_SOCKET_SOCKET_X, 1, client_fd); + + sockaddr_in client = test_utils::fill_sockaddr_in(DEFAULT_CLIENT_PORT, DEFAULT_IPV4_CLIENT_STRING); + sockaddr_in server = test_utils::fill_sockaddr_in(DEFAULT_SERVER_PORT, DEFAULT_IPV4_SERVER_STRING); + + std::vector server_sockaddr = test_utils::pack_sockaddr(reinterpret_cast(&server)); + evt = add_event_advance_ts(increasing_ts(), 1, PPME_SOCKET_CONNECT_E, 2, client_fd, scap_const_sized_buffer{server_sockaddr.data(), server_sockaddr.size()}); + std::vector socktuple = test_utils::pack_socktuple(reinterpret_cast(&client), reinterpret_cast(&server)); + evt = add_event_advance_ts(increasing_ts(), 1, PPME_SOCKET_CONNECT_X, 3, return_value, scap_const_sized_buffer{socktuple.data(), socktuple.size()}, client_fd); + + /* We are able to recover the fdinfo in the connect exit event even when interleaved */ + fdinfo = evt->get_fd_info(); + ASSERT_NE(fdinfo, nullptr); + + ASSERT_EQ(get_field_as_string(evt, "fd.connected"), "true"); + ASSERT_EQ(get_field_as_string(evt, "fd.name"), "172.40.111.222:54321->142.251.111.147:443"); + ASSERT_EQ(get_field_as_string(evt, "fd.rip"), "172.40.111.222"); + ASSERT_EQ(get_field_as_string(evt, "fd.lip"), "142.251.111.147"); + ASSERT_EQ(get_field_as_string(evt, "fd.cip"), "172.40.111.222"); + ASSERT_EQ(get_field_as_string(evt, "fd.sip"), "142.251.111.147"); + ASSERT_EQ(get_field_as_string(evt, "fd.num"), "8"); + ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch.profile[1]", pl_flist), "18172.40.111.222:54321->142.251.111.147:44300"); + + client = test_utils::fill_sockaddr_in(DEFAULT_CLIENT_PORT, DEFAULT_IPV4_CLIENT_STRING); + std::vector st = test_utils::pack_socktuple(reinterpret_cast(&client), reinterpret_cast(&server)); + + int64_t new_connected_fd = 6; + add_event_advance_ts(increasing_ts(), 1, PPME_SOCKET_ACCEPT_5_E, 0); + add_event_advance_ts(increasing_ts(), 1, PPME_SOCKET_ACCEPT_5_X, 5, new_connected_fd, scap_const_sized_buffer{st.data(), st.size()}, (uint8_t) 0, (uint32_t) 0, (uint32_t) 5); + ASSERT_EQ(get_field_as_string(evt, "fd.name"), "172.40.111.222:54321->142.251.111.147:443"); + ASSERT_EQ(get_field_as_string(evt, "fd.num"), "6"); + ASSERT_EQ(get_field_as_string(evt, "fd.rip"), "172.40.111.222"); + ASSERT_EQ(get_field_as_string(evt, "fd.lip"), "142.251.111.147"); + ASSERT_EQ(get_field_as_string(evt, "fd.cip"), "172.40.111.222"); + ASSERT_EQ(get_field_as_string(evt, "fd.sip"), "142.251.111.147"); + ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch.profile[1]", pl_flist), "16172.40.111.222:54321->142.251.111.147:44300"); +}