summaryrefslogtreecommitdiffstats
path: root/plugins/epan/falco_bridge
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/epan/falco_bridge')
-rw-r--r--plugins/epan/falco_bridge/CMakeLists.txt7
-rw-r--r--plugins/epan/falco_bridge/README.md4
-rw-r--r--plugins/epan/falco_bridge/packet-falco-bridge.c1218
-rw-r--r--plugins/epan/falco_bridge/sinsp-span.cpp839
-rw-r--r--plugins/epan/falco_bridge/sinsp-span.h95
5 files changed, 1918 insertions, 245 deletions
diff --git a/plugins/epan/falco_bridge/CMakeLists.txt b/plugins/epan/falco_bridge/CMakeLists.txt
index 384d81d2..dea7f9cc 100644
--- a/plugins/epan/falco_bridge/CMakeLists.txt
+++ b/plugins/epan/falco_bridge/CMakeLists.txt
@@ -18,7 +18,6 @@ set(DISSECTOR_SRC
)
set(DISSECTOR_HEADERS
- conversation-macros.h
sinsp-span.h
)
@@ -50,6 +49,12 @@ target_include_directories(falco-bridge SYSTEM PRIVATE
${SINSP_INCLUDE_DIRS}
)
+if(WIN32)
+ # libsinsp/dumper.h includes libscap/scap_savefile_api.h, which includes
+ # libscap/scap_zlib.h.
+ target_include_directories(falco-bridge SYSTEM PRIVATE ${ZLIB_INCLUDE_DIR})
+endif()
+
target_link_libraries(falco-bridge
epan
${SINSP_LINK_LIBRARIES}
diff --git a/plugins/epan/falco_bridge/README.md b/plugins/epan/falco_bridge/README.md
index d88276c7..88406d08 100644
--- a/plugins/epan/falco_bridge/README.md
+++ b/plugins/epan/falco_bridge/README.md
@@ -20,7 +20,7 @@ cmake \
## Quick Start
-1. Create a directory named "falco" at the same level as the "epan" plugin folder.
+1. Create a directory named "falco" one level above the "epan" plugin folder.
You can find the global and per-user plugin folder locations on your system in About → Folders or in the [User's Guide](https://www.wireshark.org/docs/wsug_html_chunked/ChPluginFolders.html).
1. Build your desired [Falco plugin](https://github.com/falcosecurity/plugins/) and place it in the "falco" plugin directory.
@@ -49,7 +49,7 @@ They depend on the following libraries:
- zlib: zlib
Wireshark is released under the GPL version 2 (GPL-2.0-or-later). It and the Apache-2.0 license are compatible via the "any later version" provision in the GPL version 2.
-As discussed at https://www.wireshark.org/lists/wireshark-dev/202203/msg00020.html, combining Wireshark and libsinsp+libscap should be OK, but that in effect invokes the GPLv2's "any later version" provision, making the Wireshark portion of the combined work GPLv3+.
+As discussed at https://lists.wireshark.org/archives/wireshark-dev/202203/msg00020.html, combining Wireshark and libsinsp+libscap should be OK, but that in effect invokes the GPLv2's "any later version" provision, making the Wireshark portion of the combined work GPLv3+.
Debian would appear to concur: https://lists.debian.org/debian-legal/2014/08/msg00102.html.
diff --git a/plugins/epan/falco_bridge/packet-falco-bridge.c b/plugins/epan/falco_bridge/packet-falco-bridge.c
index 81dbc6c1..2475c44b 100644
--- a/plugins/epan/falco_bridge/packet-falco-bridge.c
+++ b/plugins/epan/falco_bridge/packet-falco-bridge.c
@@ -17,8 +17,14 @@
// - Add a configuration preference for configure_plugin?
// - Add a configuration preference for individual conversation filters vs ANDing them?
// We would need to add deregister_(|log_)conversation_filter before we implement this.
+// - Add syscall IP address conversation support
+// - Add prefs for
+// - set_snaplen
+// - set_dopfailed
+// - set_import_users
#include "config.h"
+#define WS_LOG_DOMAIN "falco-bridge"
#include <stddef.h>
#include <stdint.h>
@@ -29,22 +35,35 @@
#include <dlfcn.h>
#endif
+#include <wiretap/wtap.h>
+
+#include <epan/conversation.h>
+#include <epan/conversation_filter.h>
+#include <epan/dfilter/dfilter-translator.h>
+#include <epan/dfilter/sttype-field.h>
+#include <epan/dfilter/sttype-op.h>
#include <epan/exceptions.h>
+#include <epan/follow.h>
#include <epan/packet.h>
+#include <epan/prefs.h>
#include <epan/proto.h>
#include <epan/proto_data.h>
-#include <epan/conversation.h>
-#include <epan/conversation_filter.h>
-#include <epan/tap.h>
+#include <epan/stats_tree.h>
#include <epan/stat_tap_ui.h>
+#include <epan/tap.h>
+
+#include <epan/dissectors/packet-sysdig-event.h>
#include <wsutil/file_util.h>
#include <wsutil/filesystem.h>
#include <wsutil/inet_addr.h>
#include <wsutil/report_message.h>
+#include <wsutil/strtoi.h>
#include "sinsp-span.h"
+#define FALCO_PPME_PLUGINEVENT_E 322
+
typedef enum bridge_field_flags_e {
BFF_NONE = 0,
BFF_HIDDEN = 1 << 1, // Unused
@@ -69,40 +88,79 @@ typedef struct bridge_info {
hf_register_info* hf_v6;
int *hf_v6_ids;
int* hf_id_to_addr_id; // Maps an hf offset to an hf_v[46] offset
- uint32_t visible_fields;
+ unsigned visible_fields;
+ unsigned addr_fields;
uint32_t* field_flags;
int* field_ids;
uint32_t num_conversation_filters;
conv_filter_info *conversation_filters;
} bridge_info;
-static int proto_falco_bridge = -1;
-static gint ett_falco_bridge = -1;
-static gint ett_sinsp_span = -1;
-static gint ett_address = -1;
+typedef struct falco_conv_filter_fields {
+ const char* container_id;
+ int64_t pid;
+ int64_t tid;
+ int64_t fd;
+ const char* fd_containername;
+} falco_conv_filter_fields;
+
+typedef struct fd_follow_tap_info {
+ const char* data;
+ int32_t datalen;
+ bool is_write;
+} fd_follow_tap_info;
+
+typedef struct container_io_tap_info {
+ const char* container_id;
+ const char* proc_name;
+ const char* fd_name;
+ int32_t io_bytes;
+ bool is_write;
+} container_io_tap_info;
+
+static int proto_falco_bridge;
+static int proto_syscalls[NUM_SINSP_SYSCALL_CATEGORIES];
+
+static int ett_falco_bridge;
+static int ett_syscalls[NUM_SINSP_SYSCALL_CATEGORIES];
+static int ett_lineage[N_PROC_LINEAGE_ENTRIES];
+
+static int ett_sinsp_enriched;
+static int ett_sinsp_span;
+static int ett_address;
+static int ett_json;
+
+static int container_io_tap;
+
+static bool pref_show_internal;
+
static dissector_table_t ptype_dissector_table;
+static dissector_handle_t json_handle;
-static int dissect_falco_bridge(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
-static int dissect_sinsp_span(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_);
+static int fd_follow_tap;
+
+static int dissect_sinsp_enriched(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *bi_ptr, sysdig_event_param_data *event_param_data);
+static int dissect_sinsp_plugin(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *bi_ptr);
+static bridge_info* get_bridge_info(uint32_t source_id);
+const char* get_str_value(sinsp_field_extract_t *sinsp_fields, uint32_t sf_idx);
/*
* Array of plugin bridges
*/
-bridge_info* bridges = NULL;
-guint nbridges = 0;
-guint n_conv_fields = 0;
+bridge_info* bridges;
+size_t nbridges;
/*
* sinsp extractor span
*/
-sinsp_span_t *sinsp_span = NULL;
+sinsp_span_t *sinsp_span;
/*
* Fields
*/
-static int hf_sdp_source_id_size = -1;
-static int hf_sdp_lengths = -1;
-static int hf_sdp_source_id = -1;
+static int hf_sdp_source_id_size;
+static int hf_sdp_lengths;
+static int hf_sdp_source_id;
static hf_register_info hf[] = {
{ &hf_sdp_source_id_size,
@@ -125,9 +183,18 @@ static hf_register_info hf[] = {
},
};
+static void
+falco_bridge_cleanup(void) {
+ close_sinsp_capture(sinsp_span);
+}
+
// Returns true if the field might contain an IPv4 or IPv6 address.
// XXX This should probably be a preference.
-static bool is_addr_field(const char *abbrev) {
+static bool
+is_string_address_field(enum ftenum ftype, const char *abbrev) {
+ if (ftype != FT_STRINGZ) {
+ return false;
+ }
if (strstr(abbrev, ".srcip")) { // ct.srcip
return true;
} else if (strstr(abbrev, ".client.ip")) { // okta.client.ip
@@ -136,13 +203,13 @@ static bool is_addr_field(const char *abbrev) {
return false;
}
-static gboolean
+static bool
is_filter_valid(packet_info *pinfo, void *cfi_ptr)
{
conv_filter_info *cfi = (conv_filter_info *)cfi_ptr;
if (!cfi->is_present) {
- return FALSE;
+ return false;
}
int proto_id = proto_registrar_get_parent(cfi->field_info->hfinfo.id);
@@ -154,20 +221,152 @@ is_filter_valid(packet_info *pinfo, void *cfi_ptr)
return proto_is_frame_protocol(pinfo->layers, proto_registrar_get_nth(proto_id)->abbrev);
}
-static gchar*
-build_filter(packet_info *pinfo _U_, void *cfi_ptr)
+static char*
+build_conversation_filter(packet_info *pinfo _U_, void *cfi_ptr)
{
conv_filter_info *cfi = (conv_filter_info *)cfi_ptr;
if (!cfi->is_present) {
- return FALSE;
+ return NULL;
}
return ws_strdup_printf("%s eq %s", cfi->field_info->hfinfo.abbrev, cfi->strbuf->str);
}
-void
-configure_plugin(bridge_info* bi, char* config _U_)
+// Falco rule translation
+
+const char *
+stnode_op_to_string(stnode_op_t op) {
+ switch (op) {
+ case STNODE_OP_NOT: return "!";
+ case STNODE_OP_AND: return "and";
+ case STNODE_OP_OR: return "or";
+ case STNODE_OP_ANY_EQ: return "=";
+ case STNODE_OP_ALL_NE: return "!=";
+ case STNODE_OP_GT: return ">";
+ case STNODE_OP_GE: return ">=";
+ case STNODE_OP_LT: return "<";
+ case STNODE_OP_LE: return "<=";
+ case STNODE_OP_CONTAINS: return "icontains";
+ case STNODE_OP_UNARY_MINUS: return "-";
+ case STNODE_OP_IN:
+ case STNODE_OP_NOT_IN:
+ default:
+ break;
+ }
+ return NULL;
+}
+
+char *hfinfo_to_filtercheck(header_field_info *hfinfo) {
+ if (!hfinfo) {
+ return NULL;
+ }
+
+ const char *filtercheck = NULL;
+ for (size_t br_idx = 0; br_idx < nbridges && !filtercheck; br_idx++) {
+ bridge_info *bridge = &bridges[br_idx];
+ unsigned hf_idx;
+ for (hf_idx = 0; hf_idx < bridge->visible_fields; hf_idx++) {
+ if (hfinfo->id == bridge->hf_ids[hf_idx]) {
+ ptrdiff_t pfx_off = 0;
+ if (g_str_has_prefix(hfinfo->abbrev, FALCO_FIELD_NAME_PREFIX)) {
+ pfx_off = strlen(FALCO_FIELD_NAME_PREFIX);
+ }
+ return g_strdup(hfinfo->abbrev + pfx_off);
+ }
+ }
+ for (hf_idx = 0; hf_idx < bridge->addr_fields; hf_idx++) {
+ if (hfinfo->id == bridge->hf_v4_ids[hf_idx] || hfinfo->id == bridge->hf_v6_ids[hf_idx]) {
+ size_t fc_len = strlen(hfinfo->abbrev) - strlen(".v?");
+ return g_strndup(hfinfo->abbrev, fc_len);
+ }
+ }
+ }
+ return NULL;
+}
+
+// Falco rule syntax is specified at
+// https://github.com/falcosecurity/libs/blob/master/userspace/libsinsp/filter/parser.h
+
+// NOLINTNEXTLINE(misc-no-recursion)
+bool visit_dfilter_node(stnode_t *node, stnode_op_t parent_bool_op, GString *falco_rule)
+{
+ stnode_t *left, *right;
+
+ if (stnode_type_id(node) == STTYPE_TEST) {
+ stnode_op_t op = STNODE_OP_UNINITIALIZED;
+ sttype_oper_get(node, &op, &left, &right);
+
+ const char *op_str = stnode_op_to_string(op);
+ if (!op_str) {
+ return false;
+ }
+
+ if (left && right) {
+ if ((op == STNODE_OP_ANY_EQ || op == STNODE_OP_ALL_NE) && stnode_type_id(right) != STTYPE_FVALUE) {
+ // XXX Not yet supported; need to add a version check.
+ return false;
+ }
+ bool add_parens = (op == STNODE_OP_AND || op == STNODE_OP_OR) && op != parent_bool_op && parent_bool_op != STNODE_OP_UNINITIALIZED;
+ if (add_parens) {
+ g_string_append_c(falco_rule, '(');
+ }
+ if (!visit_dfilter_node(left, op, falco_rule)) {
+ return false;
+ }
+ g_string_append_printf(falco_rule, " %s ", op_str);
+ if (!visit_dfilter_node(right, op, falco_rule)) {
+ return false;
+ }
+ if (add_parens) {
+ g_string_append_c(falco_rule, ')');
+ }
+ }
+ else if (left) {
+ op = op == STNODE_OP_NOT ? op : parent_bool_op;
+ if (falco_rule->len > 0) {
+ g_string_append_c(falco_rule, ' ');
+ }
+ g_string_append_printf(falco_rule, "%s ", op_str);
+ if (!visit_dfilter_node(left, op, falco_rule)) {
+ return false;
+ }
+ }
+ else if (right) {
+ ws_assert_not_reached();
+ }
+ }
+ else if (stnode_type_id(node) == STTYPE_SET) {
+ return false;
+ }
+ else if (stnode_type_id(node) == STTYPE_FUNCTION) {
+ return false;
+ }
+ else if (stnode_type_id(node) == STTYPE_FIELD) {
+ header_field_info *hfinfo = sttype_field_hfinfo(node);
+ char *filtercheck = hfinfo_to_filtercheck(hfinfo);
+ if (!filtercheck) {
+ return false;
+ }
+ g_string_append_printf(falco_rule, "%s", filtercheck);
+ g_free(filtercheck);
+ }
+ else if (stnode_type_id(node) == STTYPE_FVALUE) {
+ g_string_append_printf(falco_rule, "%s", stnode_tostr(node, true));
+ }
+ else {
+ g_string_append_printf(falco_rule, "%s", stnode_type_name(node));
+ }
+
+ return true;
+}
+
+bool dfilter_to_falco_rule(stnode_t *root_node, GString *falco_rule) {
+ return visit_dfilter_node(root_node, STNODE_OP_UNINITIALIZED, falco_rule);
+}
+
+static void
+create_source_hfids(bridge_info* bi)
{
/*
* Initialize the plugin
@@ -176,7 +375,7 @@ configure_plugin(bridge_info* bi, char* config _U_)
size_t tot_fields = get_sinsp_source_nfields(bi->ssi);
bi->visible_fields = 0;
- uint32_t addr_fields = 0;
+ bi->addr_fields = 0;
sinsp_field_info_t sfi;
bi->num_conversation_filters = 0;
@@ -189,8 +388,8 @@ configure_plugin(bridge_info* bi, char* config _U_)
*/
continue;
}
- if (sfi.type == SFT_STRINGZ && is_addr_field(sfi.abbrev)) {
- addr_fields++;
+ if (sfi.is_numeric_address || is_string_address_field(sfi.type, sfi.abbrev)) {
+ bi->addr_fields++;
}
bi->visible_fields++;
@@ -201,16 +400,16 @@ configure_plugin(bridge_info* bi, char* config _U_)
if (bi->visible_fields) {
bi->hf = (hf_register_info*)wmem_alloc(wmem_epan_scope(), bi->visible_fields * sizeof(hf_register_info));
- bi->hf_ids = (int*)wmem_alloc(wmem_epan_scope(), bi->visible_fields * sizeof(int));
+ bi->hf_ids = (int*)wmem_alloc0(wmem_epan_scope(), bi->visible_fields * sizeof(int));
bi->field_ids = (int*)wmem_alloc(wmem_epan_scope(), bi->visible_fields * sizeof(int));
- bi->field_flags = (guint32*)wmem_alloc(wmem_epan_scope(), bi->visible_fields * sizeof(guint32));
+ bi->field_flags = (uint32_t*)wmem_alloc(wmem_epan_scope(), bi->visible_fields * sizeof(uint32_t));
- if (addr_fields) {
+ if (bi->addr_fields) {
bi->hf_id_to_addr_id = (int *)wmem_alloc(wmem_epan_scope(), bi->visible_fields * sizeof(int));
- bi->hf_v4 = (hf_register_info*)wmem_alloc(wmem_epan_scope(), addr_fields * sizeof(hf_register_info));
- bi->hf_v4_ids = (int*)wmem_alloc(wmem_epan_scope(), addr_fields * sizeof(int));
- bi->hf_v6 = (hf_register_info*)wmem_alloc(wmem_epan_scope(), addr_fields * sizeof(hf_register_info));
- bi->hf_v6_ids = (int*)wmem_alloc(wmem_epan_scope(), addr_fields * sizeof(int));
+ bi->hf_v4 = (hf_register_info*)wmem_alloc(wmem_epan_scope(), bi->addr_fields * sizeof(hf_register_info));
+ bi->hf_v4_ids = (int*)wmem_alloc0(wmem_epan_scope(), bi->addr_fields * sizeof(int));
+ bi->hf_v6 = (hf_register_info*)wmem_alloc(wmem_epan_scope(), bi->addr_fields * sizeof(hf_register_info));
+ bi->hf_v6_ids = (int*)wmem_alloc0(wmem_epan_scope(), bi->addr_fields * sizeof(int));
}
if (bi->num_conversation_filters) {
@@ -223,11 +422,6 @@ configure_plugin(bridge_info* bi, char* config _U_)
for (size_t j = 0; j < tot_fields; j++)
{
- bi->hf_ids[fld_cnt] = -1;
- bi->field_ids[fld_cnt] = (int) j;
- bi->field_flags[fld_cnt] = BFF_NONE;
- hf_register_info* ri = bi->hf + fld_cnt;
-
get_sinsp_source_field_info(bi->ssi, j, &sfi);
if (sfi.is_hidden) {
@@ -237,14 +431,33 @@ configure_plugin(bridge_info* bi, char* config _U_)
continue;
}
- enum ftenum ftype;
+ ws_assert(fld_cnt < bi->visible_fields);
+ bi->field_ids[fld_cnt] = (int) j;
+ bi->field_flags[fld_cnt] = BFF_NONE;
+
+ enum ftenum ftype = sfi.type;
int fdisplay = BASE_NONE;
switch (sfi.type) {
- case SFT_STRINGZ:
- ftype = FT_STRINGZ;
+ case FT_STRINGZ:
+ case FT_BOOLEAN:
+ case FT_BYTES:
break;
- case SFT_UINT64:
- ftype = FT_UINT64;
+ case FT_RELATIVE_TIME:
+ case FT_ABSOLUTE_TIME:
+ fdisplay = BASE_DEC;
+ break;
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT32:
+ case FT_INT64:
+ case FT_DOUBLE:
+ // This differs from libsinsp
+ fdisplay = BASE_DEC;
+ break;
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT32:
+ case FT_UINT64:
switch (sfi.display_format) {
case SFDF_DECIMAL:
fdisplay = BASE_DEC;
@@ -256,15 +469,20 @@ configure_plugin(bridge_info* bi, char* config _U_)
fdisplay = BASE_OCT;
break;
default:
- THROW_FORMATTED(DissectorError, "error in plugin %s: display format %s is not supported",
- get_sinsp_source_name(bi->ssi),
- sfi.abbrev);
+ THROW_FORMATTED(DissectorError, "error in Falco bridge plugin %s: format %d for field %s is not supported",
+ get_sinsp_source_name(bi->ssi), sfi.display_format, sfi.abbrev);
}
break;
default:
- THROW_FORMATTED(DissectorError, "error in plugin %s: type of field %s is not supported",
+ ftype = FT_NONE;
+ ws_warning("plugin %s: type of field %s (%d) is not supported",
get_sinsp_source_name(bi->ssi),
- sfi.abbrev);
+ sfi.abbrev, sfi.type);
+ }
+
+ if(strlen(sfi.display) == 0) {
+ THROW_FORMATTED(DissectorError, "error in Falco bridge plugin %s: field %s is missing display name",
+ get_sinsp_source_name(bi->ssi), sfi.abbrev);
}
hf_register_info finfo = {
@@ -276,16 +494,17 @@ configure_plugin(bridge_info* bi, char* config _U_)
wmem_strdup(wmem_epan_scope(), sfi.description), HFILL
}
};
- *ri = finfo;
+ bi->hf[fld_cnt] = finfo;
if (sfi.is_conversation) {
+ ws_assert(conv_fld_cnt < bi->num_conversation_filters);
bi->field_flags[fld_cnt] |= BFF_CONVERSATION;
- bi->conversation_filters[conv_fld_cnt].field_info = ri;
+ bi->conversation_filters[conv_fld_cnt].field_info = &bi->hf[fld_cnt];
bi->conversation_filters[conv_fld_cnt].strbuf = wmem_strbuf_new(wmem_epan_scope(), "");
const char *source_name = get_sinsp_source_name(bi->ssi);
- const char *conv_filter_name = wmem_strdup_printf(wmem_epan_scope(), "%s %s", source_name, ri->hfinfo.name);
- register_log_conversation_filter(source_name, conv_filter_name, is_filter_valid, build_filter, &bi->conversation_filters[conv_fld_cnt]);
+ const char *conv_filter_name = wmem_strdup_printf(wmem_epan_scope(), "%s %s", source_name, bi->hf[fld_cnt].hfinfo.name);
+ register_log_conversation_filter(source_name, conv_filter_name, is_filter_valid, build_conversation_filter, &bi->conversation_filters[conv_fld_cnt]);
if (conv_fld_cnt == 0) {
add_conversation_filter_protocol(source_name);
}
@@ -296,11 +515,10 @@ configure_plugin(bridge_info* bi, char* config _U_)
bi->field_flags[fld_cnt] |= BFF_INFO;
}
- if (sfi.type == SFT_STRINGZ && is_addr_field(sfi.abbrev)) {
+ if (sfi.is_numeric_address || is_string_address_field(sfi.type, sfi.abbrev)) {
+ ws_assert(addr_fld_cnt < bi->addr_fields);
bi->hf_id_to_addr_id[fld_cnt] = addr_fld_cnt;
- bi->hf_v4_ids[addr_fld_cnt] = -1;
- hf_register_info* ri_v4 = bi->hf_v4 + addr_fld_cnt;
hf_register_info finfo_v4 = {
bi->hf_v4_ids + addr_fld_cnt,
{
@@ -311,27 +529,26 @@ configure_plugin(bridge_info* bi, char* config _U_)
wmem_strdup_printf(wmem_epan_scope(), "%s (IPv4)", sfi.description), HFILL
}
};
- *ri_v4 = finfo_v4;
+ bi->hf_v4[addr_fld_cnt] = finfo_v4;
- bi->hf_v6_ids[addr_fld_cnt] = -1;
- hf_register_info* ri_v6 = bi->hf_v6 + addr_fld_cnt;
hf_register_info finfo_v6 = {
bi->hf_v6_ids + addr_fld_cnt,
{
wmem_strdup_printf(wmem_epan_scope(), "%s (IPv6)", sfi.display),
wmem_strdup_printf(wmem_epan_scope(), "%s.v6", sfi.abbrev),
- FT_IPv4, BASE_NONE,
+ FT_IPv6, BASE_NONE,
NULL, 0x0,
wmem_strdup_printf(wmem_epan_scope(), "%s (IPv6)", sfi.description), HFILL
}
};
- *ri_v6 = finfo_v6;
+ bi->hf_v6[addr_fld_cnt] = finfo_v6;
addr_fld_cnt++;
} else if (bi->hf_id_to_addr_id) {
bi->hf_id_to_addr_id[fld_cnt] = -1;
}
fld_cnt++;
}
+
proto_register_field_array(proto_falco_bridge, bi->hf, fld_cnt);
if (addr_fld_cnt) {
proto_register_field_array(proto_falco_bridge, bi->hf_v4, addr_fld_cnt);
@@ -346,7 +563,7 @@ import_plugin(char* fname)
nbridges++;
bridge_info* bi = &bridges[nbridges - 1];
- char *err_str = create_sinsp_source(sinsp_span, fname, &(bi->ssi));
+ char *err_str = create_sinsp_plugin_source(sinsp_span, fname, &(bi->ssi));
if (err_str) {
nbridges--;
report_failure("Unable to load sinsp plugin %s: %s.", fname, err_str);
@@ -354,18 +571,14 @@ import_plugin(char* fname)
return;
}
- configure_plugin(bi, "");
+ create_source_hfids(bi);
const char *source_name = get_sinsp_source_name(bi->ssi);
- const char *plugin_name = g_strdup_printf("%s Plugin", source_name);
- bi->proto = proto_register_protocol (
- plugin_name, /* full name */
- source_name, /* short name */
- source_name /* filter_name */
- );
+ const char *plugin_name = g_strdup_printf("%s Falco Bridge Plugin", source_name);
+ bi->proto = proto_register_protocol(plugin_name, source_name, source_name);
static dissector_handle_t ct_handle;
- ct_handle = create_dissector_handle(dissect_sinsp_span, bi->proto);
+ ct_handle = create_dissector_handle(dissect_sinsp_plugin, bi->proto);
dissector_add_uint("falcobridge.id", bi->source_id, ct_handle);
}
@@ -377,97 +590,230 @@ on_wireshark_exit(void)
sinsp_span = NULL;
}
-void
-proto_register_falcoplugin(void)
-{
- proto_falco_bridge = proto_register_protocol (
- "Falco Bridge", /* name */
- "Falco Bridge", /* short name */
- "falcobridge" /* abbrev */
- );
- register_dissector("falcobridge", dissect_falco_bridge, proto_falco_bridge);
+static bool
+extract_syscall_conversation_fields (packet_info *pinfo, falco_conv_filter_fields* args) {
+ args->container_id = NULL;
+ args->pid = -1;
+ args->tid = -1;
+ args->fd = -1;
+ args->fd_containername = NULL;
- /*
- * Create the dissector table that we will use to route the dissection to
- * the appropriate Falco plugin.
- */
- ptype_dissector_table = register_dissector_table("falcobridge.id",
- "Falco Bridge Plugin ID", proto_falco_bridge, FT_UINT32, BASE_DEC);
+ // Syscalls are always the bridge with source_id 0.
+ bridge_info* bi = get_bridge_info(0);
- /*
- * Load the plugins
- */
- WS_DIR *dir;
- WS_DIRENT *file;
- char *filename;
- char *spdname = g_build_filename(get_plugins_dir_with_version(), "falco", NULL);
- char *ppdname = g_build_filename(get_plugins_pers_dir_with_version(), "falco", NULL);
+ sinsp_field_extract_t *sinsp_fields = NULL;
+ uint32_t sinsp_fields_count = 0;
+ void* sinp_evt_info;
+ bool rc = get_extracted_syscall_source_fields(sinsp_span, pinfo->fd->num, &sinsp_fields, &sinsp_fields_count, &sinp_evt_info);
- /*
- * We scan the plugins directory twice. The first time we count how many
- * plugins we have, which we need to know in order to allocate the right
- * amount of memory. The second time we actually load and configure
- * each plugin.
- */
- if ((dir = ws_dir_open(spdname, 0, NULL)) != NULL) {
- while ((ws_dir_read_name(dir)) != NULL) {
- nbridges++;
- }
- ws_dir_close(dir);
+ if (!rc) {
+ REPORT_DISSECTOR_BUG("cannot extract falco conversation fields for event %" PRIu32, pinfo->fd->num);
}
- if ((dir = ws_dir_open(ppdname, 0, NULL)) != NULL) {
- while ((ws_dir_read_name(dir)) != NULL) {
- nbridges++;
+ for (uint32_t hf_idx = 0, sf_idx = 0; hf_idx < bi->visible_fields && sf_idx < sinsp_fields_count; hf_idx++) {
+ if (sinsp_fields[sf_idx].field_idx != hf_idx) {
+ continue;
}
- ws_dir_close(dir);
- }
- sinsp_span = create_sinsp_span();
+ header_field_info* hfinfo = &(bi->hf[hf_idx].hfinfo);
- bridges = g_new0(bridge_info, nbridges);
- nbridges = 0;
+ if (strcmp(hfinfo->abbrev, "container.id") == 0) {
+ args->container_id = get_str_value(sinsp_fields, sf_idx);
+ // if (args->container_id == NULL) {
+ // REPORT_DISSECTOR_BUG("cannot extract the container ID for event %" PRIu32, pinfo->fd->num);
+ // }
+ }
- if ((dir = ws_dir_open(spdname, 0, NULL)) != NULL) {
- while ((file = ws_dir_read_name(dir)) != NULL) {
- filename = g_build_filename(spdname, ws_dir_get_name(file), NULL);
- import_plugin(filename);
- g_free(filename);
+ if (strcmp(hfinfo->abbrev, "proc.pid") == 0) {
+ args->pid = sinsp_fields[sf_idx].res.u64;
}
- ws_dir_close(dir);
- }
- if ((dir = ws_dir_open(ppdname, 0, NULL)) != NULL) {
- while ((file = ws_dir_read_name(dir)) != NULL) {
- filename = g_build_filename(ppdname, ws_dir_get_name(file), NULL);
- import_plugin(filename);
- g_free(filename);
+ if (strcmp(hfinfo->abbrev, "thread.tid") == 0) {
+ args->tid = sinsp_fields[sf_idx].res.u64;
}
- ws_dir_close(dir);
+
+ if (strcmp(hfinfo->abbrev, "fd.num") == 0) {
+ args->fd = sinsp_fields[sf_idx].res.u64;
+ }
+
+ if (strcmp(hfinfo->abbrev, "fd.containername") == 0) {
+ args->fd_containername = get_str_value(sinsp_fields, sf_idx);
+ }
+
+ sf_idx++;
}
- g_free(spdname);
- g_free(ppdname);
+ // args->fd=-1 means that either there's no FD (e.g. a clone syscall), or that the FD is not a valid one (e.g., failed open).
+ if (args->fd == -1) {
+ return false;
+ }
- /*
- * Setup protocol subtree array
- */
- static gint *ett[] = {
- &ett_falco_bridge,
- &ett_sinsp_span,
- &ett_address,
- };
+ return true;
+}
- proto_register_field_array(proto_falco_bridge, hf, array_length(hf));
- proto_register_subtree_array(ett, array_length(ett));
+static bool sysdig_syscall_filter_valid(packet_info *pinfo, void *user_data _U_) {
+ if (!proto_is_frame_protocol(pinfo->layers, "sysdig")) {
+ return false;
+ }
- register_shutdown_routine(on_wireshark_exit);
+ // This only supports the syscall source.
+ if (pinfo->rec->rec_header.syscall_header.event_type == FALCO_PPME_PLUGINEVENT_E) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool sysdig_syscall_container_filter_valid(packet_info *pinfo, void *user_data) {
+ if (!sysdig_syscall_filter_valid(pinfo, user_data)) {
+ return false;
+ }
+
+ falco_conv_filter_fields cff;
+ if (!extract_syscall_conversation_fields(pinfo, &cff)) {
+ return false;
+ }
+
+ return cff.container_id != NULL;
+}
+
+static bool sysdig_syscall_fd_filter_valid(packet_info *pinfo, void *user_data) {
+ if (!sysdig_syscall_filter_valid(pinfo, user_data)) {
+ return false;
+ }
+
+ falco_conv_filter_fields cff;
+ return extract_syscall_conversation_fields(pinfo, &cff);
+}
+
+static char* sysdig_container_build_filter(packet_info *pinfo, void *user_data _U_) {
+ falco_conv_filter_fields cff;
+ extract_syscall_conversation_fields(pinfo, &cff);
+ return ws_strdup_printf("container.id==\"%s\"", cff.container_id);
+}
+
+static char* sysdig_proc_build_filter(packet_info *pinfo, void *user_data _U_) {
+ falco_conv_filter_fields cff;
+ extract_syscall_conversation_fields(pinfo, &cff);
+ if (cff.container_id) {
+ return ws_strdup_printf("container.id==\"%s\" && proc.pid==%" PRId64, cff.container_id, cff.pid);
+ } else {
+ return ws_strdup_printf("proc.pid==%" PRId64, cff.pid);
+ }
+}
+
+static char* sysdig_procdescendants_build_filter(packet_info *pinfo, void *user_data _U_) {
+ falco_conv_filter_fields cff;
+ extract_syscall_conversation_fields(pinfo, &cff);
+
+ if (cff.container_id) {
+ return ws_strdup_printf("container.id==\"%s\" && (proc.pid==%" PRId64 " || proc.apid.1==%" PRId64 " || proc.apid.2==%" PRId64 " || proc.apid.3==%" PRId64 " || proc.apid.4==%" PRId64 ")",
+ cff.container_id,
+ cff.pid,
+ cff.pid,
+ cff.pid,
+ cff.pid,
+ cff.pid);
+ } else {
+ return ws_strdup_printf("proc.pid==%" PRId64 " || proc.apid.1==%" PRId64 " || proc.apid.2==%" PRId64 " || proc.apid.3==%" PRId64 " || proc.apid.4==%" PRId64,
+ cff.pid,
+ cff.pid,
+ cff.pid,
+ cff.pid,
+ cff.pid);
+ }
+}
+
+static char* sysdig_thread_build_filter(packet_info *pinfo, void *user_data _U_) {
+ falco_conv_filter_fields cff;
+ extract_syscall_conversation_fields(pinfo, &cff);
+ if (cff.container_id) {
+ return ws_strdup_printf("container.id==\"%s\" && thread.tid==%" PRIu64, cff.container_id, cff.tid);
+ } else {
+ return ws_strdup_printf("thread.tid==%" PRId64, cff.tid);
+ }
+}
+
+static char* sysdig_fd_build_filter(packet_info *pinfo, void *user_data _U_) {
+ falco_conv_filter_fields cff;
+ extract_syscall_conversation_fields(pinfo, &cff);
+ if (cff.container_id) {
+ return ws_strdup_printf("container.id==\"%s\" && thread.tid==%" PRId64 " && fd.containername==\"%s\"",
+ cff.container_id,
+ cff.tid,
+ cff.fd_containername);
+ } else {
+ return ws_strdup_printf("thread.tid==%" PRId64, cff.tid);
+ }
+}
+
+static char *fd_follow_conv_filter(epan_dissect_t *edt _U_, packet_info *pinfo _U_, unsigned *stream _U_, unsigned *sub_stream _U_)
+{
+ // This only supports the syscall source.
+ if (pinfo->rec->rec_header.syscall_header.event_type == FALCO_PPME_PLUGINEVENT_E) {
+ return NULL;
+ }
+
+ return sysdig_fd_build_filter(pinfo, NULL);
+}
+
+static char *fd_follow_index_filter(unsigned stream _U_, unsigned sub_stream _U_)
+{
+ return NULL;
+}
+
+static char *fd_follow_address_filter(address *src_addr _U_, address *dst_addr _U_, int src_port _U_, int dst_port _U_)
+{
+ return NULL;
+}
+
+char *
+fd_port_to_display(wmem_allocator_t *allocator _U_, unsigned port _U_)
+{
+ return NULL;
+}
+
+tap_packet_status
+fd_tap_listener(void *tapdata, packet_info *pinfo,
+ epan_dissect_t *edt _U_, const void *data, tap_flags_t flags _U_)
+{
+ follow_record_t *follow_record;
+ follow_info_t *follow_info = (follow_info_t *)tapdata;
+ fd_follow_tap_info *tap_info = (fd_follow_tap_info *)data;
+ bool is_server;
+
+ is_server = tap_info->is_write;
+
+ follow_record = g_new0(follow_record_t, 1);
+ follow_record->is_server = is_server;
+ follow_record->packet_num = pinfo->fd->num;
+ follow_record->abs_ts = pinfo->fd->abs_ts;
+ follow_record->data = g_byte_array_append(g_byte_array_new(),
+ tap_info->data,
+ tap_info->datalen);
+
+ follow_info->bytes_written[is_server] += follow_record->data->len;
+ follow_info->payload = g_list_prepend(follow_info->payload, follow_record);
+
+ return TAP_PACKET_DONT_REDRAW;
}
+uint32_t get_fd_stream_count(void)
+{
+ // This effectively disables the "streams" dropdown, which is we don't really care about for the moment in logray.
+ return 1;
+}
+
+
+
static bridge_info*
-get_bridge_info(guint32 source_id)
+get_bridge_info(uint32_t source_id)
{
- for(guint j = 0; j < nbridges; j++)
+ if (source_id == 0) {
+ return &bridges[0];
+ }
+
+ for(size_t j = 0; j < nbridges; j++)
{
if(bridges[j].source_id == source_id)
{
@@ -479,70 +825,346 @@ get_bridge_info(guint32 source_id)
}
static int
-dissect_falco_bridge(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
+dissect_falco_bridge(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *epd_p)
{
+ int encoding = pinfo->rec->rec_header.syscall_header.byte_order == G_BIG_ENDIAN ? ENC_BIG_ENDIAN : ENC_LITTLE_ENDIAN;
+
col_set_str(pinfo->cinfo, COL_PROTOCOL, "Falco Bridge");
- /* Clear out stuff in the info column */
- col_clear(pinfo->cinfo,COL_INFO);
// https://github.com/falcosecurity/libs/blob/9c942f27/userspace/libscap/scap.c#L1900
- proto_item *ti = proto_tree_add_item(tree, proto_falco_bridge, tvb, 0, 12, ENC_NA);
- proto_tree *fb_tree = proto_item_add_subtree(ti, ett_falco_bridge);
- proto_tree_add_item(fb_tree, hf_sdp_source_id_size, tvb, 0, 4, ENC_LITTLE_ENDIAN);
- proto_tree_add_item(fb_tree, hf_sdp_lengths, tvb, 4, 4, ENC_LITTLE_ENDIAN);
- proto_item *idti = proto_tree_add_item(fb_tree, hf_sdp_source_id, tvb, 8, 4, ENC_LITTLE_ENDIAN);
- guint32 source_id = tvb_get_guint32(tvb, 8, ENC_LITTLE_ENDIAN);
+ uint32_t source_id = 0;
+ if (pinfo->rec->rec_header.syscall_header.event_type == FALCO_PPME_PLUGINEVENT_E) {
+ source_id = tvb_get_uint32(tvb, 8, encoding);
+ }
+
bridge_info* bi = get_bridge_info(source_id);
- col_add_fstr(pinfo->cinfo, COL_INFO, "Plugin ID: %u", source_id);
- if (bi == NULL) {
- proto_item_append_text(idti, " (NOT SUPPORTED)");
- col_append_str(pinfo->cinfo, COL_INFO, " (NOT SUPPORTED)");
+ if (bi && bi->source_id == 0) {
+ sysdig_event_param_data *event_param_data = (sysdig_event_param_data *) epd_p;
+ dissect_sinsp_enriched(tvb, pinfo, tree, bi, event_param_data);
+ } else {
+ proto_item *ti = proto_tree_add_item(tree, proto_falco_bridge, tvb, 0, 12, ENC_NA);
+ proto_tree *fb_tree = proto_item_add_subtree(ti, ett_falco_bridge);
+
+ proto_tree_add_item(fb_tree, hf_sdp_source_id_size, tvb, 0, 4, encoding);
+ proto_tree_add_item(fb_tree, hf_sdp_lengths, tvb, 4, 4, encoding);
+ /* Clear out stuff in the info column */
+ col_clear(pinfo->cinfo,COL_INFO);
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Plugin ID: %u", source_id);
+
+ proto_item *idti = proto_tree_add_item(fb_tree, hf_sdp_source_id, tvb, 8, 4, encoding);
+ if (bi == NULL) {
+ proto_item_append_text(idti, " (NOT SUPPORTED)");
+ col_append_str(pinfo->cinfo, COL_INFO, " (NOT SUPPORTED)");
+ return tvb_captured_length(tvb);
+ }
+
+ const char *source_name = get_sinsp_source_name(bi->ssi);
+ proto_item_append_text(idti, " (%s)", source_name);
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (%s)", source_name);
+
+ tvbuff_t* plugin_tvb = tvb_new_subset_length(tvb, 12, tvb_captured_length(tvb) - 12);
+ dissect_sinsp_plugin(plugin_tvb, pinfo, fb_tree, bi);
+ }
+
+ return tvb_captured_length(tvb);
+}
+
+int extract_lineage_number(const char *fld_name) {
+ char *last_dot = strrchr(fld_name, '.');
+ if (last_dot != NULL) {
+ return atoi(last_dot + 1);
+ }
+ return -1;
+}
+
+const char* get_str_value(sinsp_field_extract_t *sinsp_fields, uint32_t sf_idx) {
+ const char *res_str;
+ if (sinsp_fields[sf_idx].res_len < SFE_SMALL_BUF_SIZE) {
+ res_str = sinsp_fields[sf_idx].res.small_str;
+ } else {
+ if (sinsp_fields[sf_idx].res.str == NULL) {
+ ws_debug("Field %u has NULL result string", sf_idx);
+ return NULL;
+ }
+ res_str = sinsp_fields[sf_idx].res.str;
+ }
+
+ return res_str;
+}
+
+static int
+dissect_sinsp_enriched(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* bi_ptr, sysdig_event_param_data *event_param_data)
+{
+ bridge_info* bi = (bridge_info *) bi_ptr;
+
+ if (!pinfo->fd->visited) {
+ if (pinfo->fd->num == 1) {
+ // Open the capture file using libsinsp, which reads the meta events
+ // at the beginning of the file. We can't call this via register_init_routine
+ // because we don't have the file path at that point.
+ open_sinsp_capture(sinsp_span, pinfo->rec->rec_header.syscall_header.pathname);
+ }
+ }
+
+ sinsp_field_extract_t *sinsp_fields = NULL;
+ uint32_t sinsp_fields_count = 0;
+ void* sinp_evt_info;
+ bool rc = extract_syscall_source_fields(sinsp_span, bi->ssi, pinfo->fd->num, &sinsp_fields, &sinsp_fields_count, &sinp_evt_info);
+
+ if (!rc) {
+ REPORT_DISSECTOR_BUG("Falco plugin %s extract error: %s", get_sinsp_source_name(bi->ssi), get_sinsp_source_last_error(bi->ssi));
+ }
+
+ if (sinsp_fields_count == 0) {
+ col_append_str(pinfo->cinfo, COL_INFO, " [Internal event]");
+ if (!pref_show_internal) {
+ pinfo->fd->passed_dfilter = false;
+ }
return tvb_captured_length(tvb);
}
- const char *source_name = get_sinsp_source_name(bi->ssi);
- proto_item_append_text(idti, " (%s)", source_name);
- col_append_fstr(pinfo->cinfo, COL_INFO, " (%s)", source_name);
+ proto_tree *parent_trees[NUM_SINSP_SYSCALL_CATEGORIES] = {0};
+ proto_tree *lineage_trees[N_PROC_LINEAGE_ENTRIES] = {0};
+ bool is_io_write = false;
+ const char* io_buffer = NULL;
+ uint32_t io_buffer_len = 0;
+
+ const char *container_id = "host";
+ const char *proc_name = NULL;
+ const char *fd_name = NULL;
+
+ // Conversation discoverable through conversation_filter_from_pinfo.
+ // Used for related event indicators in the packet list.
+ // Fields should match sysdig_proc_build_filter.
+ conversation_element_t *pinfo_conv_els = NULL; // thread.tid hfid + thread.tid + container.id hfid + container.id + CONVERSATION_LOG
+
+ for (uint32_t hf_idx = 0, sf_idx = 0; hf_idx < bi->visible_fields && sf_idx < sinsp_fields_count; hf_idx++) {
+ if (sinsp_fields[sf_idx].field_idx != hf_idx) {
+ continue;
+ }
+
+ header_field_info* hfinfo = &(bi->hf[hf_idx].hfinfo);
+
+ proto_tree *ti;
- dissector_handle_t dissector = dissector_get_uint_handle(ptype_dissector_table, source_id);
- if (dissector) {
- tvbuff_t* next_tvb = tvb_new_subset_length(tvb, 12, tvb_captured_length(tvb) - 12);
- call_dissector_with_data(dissector, next_tvb, pinfo, tree, bi);
+
+ // XXX Should we add this back?
+// if (sinsp_fields[sf_idx].type != hfinfo->type) {
+// REPORT_DISSECTOR_BUG("Field %s has an unrecognized or mismatched type %u != %u",
+// hfinfo->abbrev, sinsp_fields[sf_idx].type, hfinfo->type);
+// }
+
+ sinsp_syscall_category_e parent_category = get_syscall_parent_category(bi->ssi, sinsp_fields[sf_idx].field_idx);
+ if (!parent_trees[parent_category]) {
+ int bytes_offset = 0;
+ uint32_t bytes_length = 0;
+ if (parent_category == SSC_FD) {
+ bytes_offset = event_param_data->data_bytes_offset;
+ bytes_length = event_param_data->data_bytes_length;
+ }
+ ti = proto_tree_add_item(tree, proto_syscalls[parent_category], tvb, bytes_offset, bytes_length, BASE_NONE);
+ parent_trees[parent_category] = proto_item_add_subtree(ti, ett_syscalls[parent_category]);
+ }
+ proto_tree *parent_tree = parent_trees[parent_category];
+
+ if (parent_category == SSC_PROCLINEAGE) {
+ int32_t lnum = extract_lineage_number(hfinfo->abbrev);
+ if (lnum == -1) {
+ ws_error("Invalid lineage field name %s", hfinfo->abbrev);
+ }
+
+ if (!lineage_trees[lnum]) {
+ const char* res_str = get_str_value(sinsp_fields, sf_idx);
+ if (res_str == NULL) {
+ ws_error("empty value for field %s", hfinfo->abbrev);
+ }
+
+ lineage_trees[lnum] = proto_tree_add_subtree_format(parent_tree, tvb, 0, 0, ett_lineage[0], NULL, "%" PRIu32 ". %s", lnum, res_str);
+ sf_idx++;
+ continue;
+ }
+
+ parent_tree = lineage_trees[lnum];
+ }
+
+ int32_t arg_num;
+#define EVT_ARG_PFX "evt.arg."
+ if (! (g_str_has_prefix(hfinfo->abbrev, EVT_ARG_PFX) && ws_strtoi32(hfinfo->abbrev + sizeof(EVT_ARG_PFX) - 1, NULL, &arg_num)) ) {
+ arg_num = -1;
+ }
+
+ if (strcmp(hfinfo->abbrev, "evt.is_io_write") == 0) {
+ is_io_write = sinsp_fields[sf_idx].res.boolean;
+ }
+ if (strcmp(hfinfo->abbrev, "evt.buffer") == 0) {
+ io_buffer = sinsp_fields[sf_idx].res.str;
+ io_buffer_len = sinsp_fields[sf_idx].res_len;
+ }
+
+ switch (hfinfo->type) {
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT32:
+ proto_tree_add_int(parent_tree, bi->hf_ids[hf_idx], tvb, 0, 0, sinsp_fields[sf_idx].res.i32);
+ break;
+ case FT_INT64:
+ proto_tree_add_int64(parent_tree, bi->hf_ids[hf_idx], tvb, 0, 0, sinsp_fields[sf_idx].res.i64);
+ if (strcmp(hfinfo->abbrev, "thread.tid") == 0) {
+ if (!pinfo_conv_els) {
+ pinfo_conv_els = wmem_alloc0(pinfo->pool, sizeof(conversation_element_t) * 5);
+ pinfo_conv_els[0].type = CE_INT;
+ pinfo_conv_els[1].type = CE_INT64;
+ pinfo_conv_els[2].type = CE_INT;
+ pinfo_conv_els[3].type = CE_STRING;
+ }
+ pinfo_conv_els[0].int_val = hfinfo->id;
+ pinfo_conv_els[1].int64_val = sinsp_fields[sf_idx].res.i64;
+ }
+ break;
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT32:
+ proto_tree_add_uint(parent_tree, bi->hf_ids[hf_idx], tvb, 0, 0, sinsp_fields[sf_idx].res.u32);
+ break;
+ case FT_UINT64:
+ case FT_RELATIVE_TIME:
+ case FT_ABSOLUTE_TIME:
+ proto_tree_add_uint64(parent_tree, bi->hf_ids[hf_idx], tvb, 0, 0, sinsp_fields[sf_idx].res.u64);
+ break;
+ case FT_STRINGZ:
+ {
+ const char* res_str = get_str_value(sinsp_fields, sf_idx);
+ if (res_str == NULL) {
+ continue;
+ }
+
+ if (arg_num != -1) {
+ // When the field is an argument, we want to display things in a way that includes the argument name and value.
+ char* argname = get_evt_arg_name(sinp_evt_info, arg_num);
+ ti = proto_tree_add_string_format(parent_tree, bi->hf_ids[hf_idx], tvb, 0, 0, res_str, "%s: %s", argname, res_str);
+ } else {
+ ti = proto_tree_add_string(parent_tree, bi->hf_ids[hf_idx], tvb, 0, 0, res_str);
+ }
+
+ if (bi->field_flags[hf_idx] & BFF_INFO) {
+ col_append_sep_fstr(pinfo->cinfo, COL_INFO, ", ", "%s", res_str);
+ // Mark it hidden, otherwise we end up with a bunch of empty "Info" tree items.
+ proto_item_set_hidden(ti);
+ }
+
+ if (strcmp(hfinfo->abbrev, "proc.name") == 0) {
+ proc_name = res_str;
+ } else if (strcmp(hfinfo->abbrev, "fd.name") == 0) {
+ fd_name = res_str;
+ } else if (strcmp(hfinfo->abbrev, "container.id") == 0) {
+ container_id = res_str;
+ if (pinfo_conv_els) {
+ pinfo_conv_els[2].int_val = hfinfo->id;
+ }
+ }
+ }
+ break;
+ case FT_BOOLEAN:
+ proto_tree_add_boolean(parent_tree, bi->hf_ids[hf_idx], tvb, 0, 0, sinsp_fields[sf_idx].res.boolean);
+ break;
+ case FT_DOUBLE:
+ proto_tree_add_double(parent_tree, bi->hf_ids[hf_idx], tvb, 0, 0, sinsp_fields[sf_idx].res.dbl);
+ break;
+ case FT_BYTES:
+ {
+ int addr_fld_idx = bi->hf_id_to_addr_id[hf_idx];
+ if (addr_fld_idx < 0) {
+ int bytes_offset = 0;
+ uint32_t bytes_length = 0;
+ if (io_buffer) { // evt.buffer
+ bytes_offset = event_param_data->data_bytes_offset;
+ bytes_length = event_param_data->data_bytes_length;
+ }
+ proto_tree_add_bytes_with_length(parent_tree, bi->hf_ids[hf_idx], tvb, bytes_offset, bytes_length, sinsp_fields[sf_idx].res.str, sinsp_fields[sf_idx].res_len);
+ } else {
+ // XXX Need to differentiate between src and dest. Falco libs supply client vs server and local vs remote.
+ if (sinsp_fields[sf_idx].res_len == 4) {
+ ws_in4_addr v4_addr;
+ memcpy(&v4_addr, sinsp_fields[sf_idx].res.bytes, 4);
+ proto_tree_add_ipv4(parent_tree, bi->hf_v4_ids[addr_fld_idx], tvb, 0, 0, v4_addr);
+ set_address(&pinfo->net_src, AT_IPv4, sizeof(ws_in4_addr), &v4_addr);
+ } else if (sinsp_fields[sf_idx].res_len == 16) {
+ ws_in6_addr v6_addr;
+ memcpy(&v6_addr, sinsp_fields[sf_idx].res.bytes, 16);
+ proto_tree_add_ipv6(parent_tree, bi->hf_v6_ids[addr_fld_idx], tvb, 0, 0, &v6_addr);
+ set_address(&pinfo->net_src, AT_IPv6, sizeof(ws_in6_addr), &v6_addr);
+ } else {
+ ws_warning("Invalid length %u for address field %u", sinsp_fields[sf_idx].res_len, sf_idx);
+ }
+ // XXX Add conversation support.
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ sf_idx++;
+ }
+
+ if (pinfo_conv_els) {
+ pinfo_conv_els[3].str_val = container_id;
+ pinfo_conv_els[4].type = CE_CONVERSATION_TYPE;
+ pinfo_conv_els[4].conversation_type_val = CONVERSATION_LOG;
+ pinfo->conv_elements = pinfo_conv_els;
+ find_or_create_conversation(pinfo);
+ }
+
+ if (io_buffer_len > 0) {
+ if (have_tap_listener(fd_follow_tap)) {
+ fd_follow_tap_info *tap_info = wmem_new(pinfo->pool, fd_follow_tap_info);
+ tap_info->data = io_buffer;
+ tap_info->datalen = io_buffer_len;
+ tap_info->is_write = is_io_write;
+ tap_queue_packet(fd_follow_tap, pinfo, tap_info);
+ }
+ if (have_tap_listener(container_io_tap) && proc_name && fd_name) {
+ container_io_tap_info *tap_info = wmem_new(pinfo->pool, container_io_tap_info);
+ tap_info->proc_name = proc_name;
+ tap_info->fd_name = fd_name;
+ tap_info->container_id = container_id;
+ tap_info->io_bytes = io_buffer_len;
+ tap_info->is_write = is_io_write;
+ tap_queue_packet(container_io_tap, pinfo, tap_info);
+ }
}
return tvb_captured_length(tvb);
}
static int
-dissect_sinsp_span(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* bi_ptr)
+dissect_sinsp_plugin(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* bi_ptr)
{
bridge_info* bi = (bridge_info *) bi_ptr;
- guint plen = tvb_captured_length(tvb);
- const char *source_name = get_sinsp_source_name(bi->ssi);
+ unsigned payload_len = tvb_captured_length(tvb);
- col_set_str(pinfo->cinfo, COL_PROTOCOL, source_name);
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "oops");
/* Clear out stuff in the info column */
col_clear(pinfo->cinfo, COL_INFO);
- proto_item* ti = proto_tree_add_item(tree, bi->proto, tvb, 0, plen, ENC_NA);
+ proto_item *ti = tree;
proto_tree* fb_tree = proto_item_add_subtree(ti, ett_sinsp_span);
- guint8* payload = (guint8*)tvb_get_ptr(tvb, 0, plen);
+ uint8_t* payload = (uint8_t*)tvb_get_ptr(tvb, 0, payload_len);
- sinsp_field_extract_t *sinsp_fields = (sinsp_field_extract_t*) wmem_alloc(pinfo->pool, sizeof(sinsp_field_extract_t) * bi->visible_fields);
+ plugin_field_extract_t *sinsp_fields = (plugin_field_extract_t*) wmem_alloc(pinfo->pool, sizeof(plugin_field_extract_t) * bi->visible_fields);
for (uint32_t fld_idx = 0; fld_idx < bi->visible_fields; fld_idx++) {
header_field_info* hfinfo = &(bi->hf[fld_idx].hfinfo);
- sinsp_field_extract_t *sfe = &sinsp_fields[fld_idx];
+ plugin_field_extract_t *sfe = &sinsp_fields[fld_idx];
sfe->field_id = bi->field_ids[fld_idx];
sfe->field_name = hfinfo->abbrev;
- sfe->type = hfinfo->type == FT_STRINGZ ? SFT_STRINGZ : SFT_UINT64;
+ sfe->type = hfinfo->type == FT_STRINGZ ? FT_STRINGZ : FT_UINT64;
}
// If we have a failure, try to dissect what we can first, then bail out with an error.
- bool rc = extract_sisnp_source_fields(bi->ssi, payload, plen, pinfo->pool, sinsp_fields, bi->visible_fields);
+ bool rc = extract_plugin_source_fields(bi->ssi, pinfo->num, payload, payload_len, pinfo->pool, sinsp_fields, bi->visible_fields);
if (!rc) {
REPORT_DISSECTOR_BUG("Falco plugin %s extract error: %s", get_sinsp_source_name(bi->ssi), get_sinsp_source_last_error(bi->ssi));
@@ -556,7 +1178,7 @@ dissect_sinsp_span(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* bi
conversation_element_t *first_conv_els = NULL; // hfid + field val + CONVERSATION_LOG
for (uint32_t fld_idx = 0; fld_idx < bi->visible_fields; fld_idx++) {
- sinsp_field_extract_t *sfe = &sinsp_fields[fld_idx];
+ plugin_field_extract_t *sfe = &sinsp_fields[fld_idx];
header_field_info* hfinfo = &(bi->hf[fld_idx].hfinfo);
if (!sfe->is_present) {
@@ -581,25 +1203,38 @@ dissect_sinsp_span(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* bi
}
- if (sfe->type == SFT_STRINGZ && hfinfo->type == FT_STRINGZ) {
- proto_item *pi = proto_tree_add_string(fb_tree, bi->hf_ids[fld_idx], tvb, 0, plen, sfe->res_str);
+ if (sfe->type == FT_STRINGZ && hfinfo->type == FT_STRINGZ) {
+ proto_item *pi = proto_tree_add_string(fb_tree, bi->hf_ids[fld_idx], tvb, 0, payload_len, sfe->res.str);
if (bi->field_flags[fld_idx] & BFF_INFO) {
- col_append_sep_fstr(pinfo->cinfo, COL_INFO, ", ", "%s", sfe->res_str);
+ col_append_sep_fstr(pinfo->cinfo, COL_INFO, ", ", "%s", sfe->res.str);
// Mark it hidden, otherwise we end up with a bunch of empty "Info" tree items.
proto_item_set_hidden(pi);
}
+ if ((strcmp(hfinfo->abbrev, "ct.response") == 0 ||
+ strcmp(hfinfo->abbrev, "ct.request") == 0 ||
+ strcmp(hfinfo->abbrev, "ct.resources") == 0 ) &&
+ strcmp(sfe->res.str, "null") != 0) {
+ tvbuff_t *json_tvb = tvb_new_child_real_data(tvb, sfe->res.str, (unsigned)strlen(sfe->res.str), (unsigned)strlen(sfe->res.str));
+ add_new_data_source(pinfo, json_tvb, "JSON Object");
+ proto_tree *json_tree = proto_item_add_subtree(pi, ett_json);
+ char *col_info_text = wmem_strdup(pinfo->pool, col_get_text(pinfo->cinfo, COL_INFO));
+ call_dissector(json_handle, json_tvb, pinfo, json_tree);
+
+ /* Restore Protocol and Info columns */
+ col_set_str(pinfo->cinfo, COL_INFO, col_info_text);
+ }
int addr_fld_idx = bi->hf_id_to_addr_id[fld_idx];
if (addr_fld_idx >= 0) {
ws_in4_addr v4_addr;
ws_in6_addr v6_addr;
proto_tree *addr_tree;
proto_item *addr_item = NULL;
- if (ws_inet_pton4(sfe->res_str, &v4_addr)) {
+ if (ws_inet_pton4(sfe->res.str, &v4_addr)) {
addr_tree = proto_item_add_subtree(pi, ett_address);
addr_item = proto_tree_add_ipv4(addr_tree, bi->hf_v4_ids[addr_fld_idx], tvb, 0, 0, v4_addr);
set_address(&pinfo->net_src, AT_IPv4, sizeof(ws_in4_addr), &v4_addr);
- } else if (ws_inet_pton6(sfe->res_str, &v6_addr)) {
+ } else if (ws_inet_pton6(sfe->res.str, &v6_addr)) {
addr_tree = proto_item_add_subtree(pi, ett_address);
addr_item = proto_tree_add_ipv6(addr_tree, bi->hf_v6_ids[addr_fld_idx], tvb, 0, 0, &v6_addr);
set_address(&pinfo->net_src, AT_IPv6, sizeof(ws_in6_addr), &v6_addr);
@@ -608,7 +1243,7 @@ dissect_sinsp_span(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* bi
proto_item_set_generated(addr_item);
}
if (cur_conv_filter) {
- wmem_strbuf_append(cur_conv_filter->strbuf, sfe->res_str);
+ wmem_strbuf_append(cur_conv_filter->strbuf, sfe->res.str);
cur_conv_filter->is_present = true;
}
if (cur_conv_els) {
@@ -617,34 +1252,34 @@ dissect_sinsp_span(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* bi
}
} else {
if (cur_conv_filter) {
- wmem_strbuf_append_printf(cur_conv_filter->strbuf, "\"%s\"", sfe->res_str);
+ wmem_strbuf_append_printf(cur_conv_filter->strbuf, "\"%s\"", sfe->res.str);
cur_conv_filter->is_present = true;
}
if (cur_conv_els) {
cur_conv_els[1].type = CE_STRING;
- cur_conv_els[1].str_val = wmem_strdup(pinfo->pool, sfe->res_str);
+ cur_conv_els[1].str_val = wmem_strdup(pinfo->pool, sfe->res.str);
}
}
}
- else if (sfe->type == SFT_UINT64 && hfinfo->type == FT_UINT64) {
- proto_tree_add_uint64(fb_tree, bi->hf_ids[fld_idx], tvb, 0, plen, sfe->res_u64);
+ else if (sfe->type == FT_UINT64 && hfinfo->type == FT_UINT64) {
+ proto_tree_add_uint64(fb_tree, bi->hf_ids[fld_idx], tvb, 0, payload_len, sfe->res.u64);
if (cur_conv_filter) {
switch (hfinfo->display) {
case BASE_HEX:
- wmem_strbuf_append_printf(cur_conv_filter->strbuf, "%" PRIx64, sfe->res_u64);
+ wmem_strbuf_append_printf(cur_conv_filter->strbuf, "%" PRIx64, sfe->res.u64);
break;
case BASE_OCT:
- wmem_strbuf_append_printf(cur_conv_filter->strbuf, "%" PRIo64, sfe->res_u64);
+ wmem_strbuf_append_printf(cur_conv_filter->strbuf, "%" PRIo64, sfe->res.u64);
break;
default:
- wmem_strbuf_append_printf(cur_conv_filter->strbuf, "%" PRId64, sfe->res_u64);
+ wmem_strbuf_append_printf(cur_conv_filter->strbuf, "%" PRId64, sfe->res.u64);
}
cur_conv_filter->is_present = true;
}
if (cur_conv_els) {
cur_conv_els[1].type = CE_UINT64;
- cur_conv_els[1].uint64_val = sfe->res_u64;
+ cur_conv_els[1].uint64_val = sfe->res.u64;
}
}
else {
@@ -653,10 +1288,6 @@ dissect_sinsp_span(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* bi
}
}
- if (!rc) {
- REPORT_DISSECTOR_BUG("Falco plugin %s extract error", get_sinsp_source_name(bi->ssi));
- }
-
if (first_conv_els) {
first_conv_els[2].type = CE_CONVERSATION_TYPE;
first_conv_els[2].conversation_type_val = CONVERSATION_LOG;
@@ -667,10 +1298,213 @@ dissect_sinsp_span(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* bi
// }
}
- return plen;
+ return payload_len;
+}
+
+const char *st_str_container_total_io = "Total";
+
+static void container_io_stats_tree_init(stats_tree* st _U_)
+{
+ stats_tree_create_node(st, st_str_container_total_io, 0, STAT_DT_INT, true);
+ stat_node_set_flags(st, st_str_container_total_io, 0, false, ST_FLG_SORT_TOP);
+
+}
+
+static tap_packet_status container_io_stats_tree_event(stats_tree* st, packet_info* pinfo _U_, epan_dissect_t* edt _U_, const void* tap_info_p, tap_flags_t flags _U_)
+{
+ const container_io_tap_info* tap_info = (const container_io_tap_info*) tap_info_p;
+
+ increase_stat_node(st, st_str_container_total_io, 0, false, tap_info->io_bytes);
+ int container_id_node = increase_stat_node(st, tap_info->container_id, 0, true, tap_info->io_bytes);
+ int proc_name_node = increase_stat_node(st, tap_info->proc_name, container_id_node, true, tap_info->io_bytes);
+ int fd_name_node = increase_stat_node(st, tap_info->fd_name, proc_name_node, true, tap_info->io_bytes);
+ if (tap_info->is_write) {
+ increase_stat_node(st, "write", fd_name_node, true, tap_info->io_bytes);
+ } else {
+ increase_stat_node(st, "read", fd_name_node, true, tap_info->io_bytes);
+ }
+
+ return TAP_PACKET_REDRAW;
}
void
-proto_reg_handoff_sdplugin(void)
+proto_reg_handoff_falcoplugin(void)
{
+ // Register statistics trees
+ stats_tree_cfg *st_config = stats_tree_register_plugin("container_io", "container_io", "Container I/O", 0, container_io_stats_tree_event, container_io_stats_tree_init, NULL);
+ stats_tree_set_group(st_config, REGISTER_LOG_STAT_GROUP_UNSORTED);
+ stats_tree_set_first_column_name(st_config, "Container, process, and FD I/O");
+
+ json_handle = find_dissector("json");
+}
+
+void
+proto_register_falcoplugin(void)
+{
+ // Opening requires a file path, so we do that in dissect_sinsp_enriched.
+ register_cleanup_routine(&falco_bridge_cleanup);
+
+ proto_falco_bridge = proto_register_protocol("Falco Bridge", "Falco Bridge", "falcobridge");
+ register_dissector("falcobridge", dissect_falco_bridge, proto_falco_bridge);
+
+ // Register the syscall conversation filters.
+ // These show up in the "Conversation Filter" and "Colorize Conversation" context menus.
+ // The first match is also used for "Go" menu navigation.
+ register_log_conversation_filter("falcobridge", "Thread", sysdig_syscall_filter_valid, sysdig_thread_build_filter, NULL);
+ register_log_conversation_filter("falcobridge", "Process", sysdig_syscall_filter_valid, sysdig_proc_build_filter, NULL);
+ register_log_conversation_filter("falcobridge", "Container", sysdig_syscall_container_filter_valid, sysdig_container_build_filter, NULL);
+ register_log_conversation_filter("falcobridge", "Process and Descendants", sysdig_syscall_filter_valid, sysdig_procdescendants_build_filter, NULL);
+ register_log_conversation_filter("falcobridge", "File Descriptor", sysdig_syscall_fd_filter_valid, sysdig_fd_build_filter, NULL);
+ add_conversation_filter_protocol("falcobridge");
+
+ // Register statistics taps
+ container_io_tap = register_tap("container_io");
+
+ // Register the "follow" handlers
+ fd_follow_tap = register_tap("fd_follow");
+
+ register_follow_stream(proto_falco_bridge, "fd_follow", fd_follow_conv_filter, fd_follow_index_filter, fd_follow_address_filter,
+ fd_port_to_display, fd_tap_listener, get_fd_stream_count, NULL);
+
+ // Try to have a 1:1 mapping for as many Sysdig / Falco fields as possible.
+ // The exceptions are SSC_EVTARGS and SSC_PROCLINEAGE, which exposes the event arguments in a way that is convenient for the user.
+ proto_syscalls[SSC_EVENT] = proto_register_protocol("Event Information", "Falco Event", "evt");
+ proto_syscalls[SSC_EVTARGS] = proto_register_protocol("Event Arguments", "Falco Event Info", "evt.arg");
+ proto_syscalls[SSC_PROCESS] = proto_register_protocol("Process Information", "Falco Process", "process");
+ proto_syscalls[SSC_PROCLINEAGE] = proto_register_protocol("Process Ancestors", "Falco Process Lineage", "proc.aname");
+ proto_syscalls[SSC_USER] = proto_register_protocol("User Information", "Falco User", "user");
+ proto_syscalls[SSC_GROUP] = proto_register_protocol("Group Information", "Falco Group", "group");
+ proto_syscalls[SSC_CONTAINER] = proto_register_protocol("Container Information", "Falco Container", "container");
+ proto_syscalls[SSC_FD] = proto_register_protocol("File Descriptor Information", "Falco FD", "fd");
+ proto_syscalls[SSC_FS] = proto_register_protocol("Filesystem Information", "Falco FS", "fs");
+ // syslog.facility collides with the Syslog dissector, so let syslog fall through to "falco".
+ proto_syscalls[SSC_FDLIST] = proto_register_protocol("File Descriptor List", "Falco FD List", "fdlist");
+ proto_syscalls[SSC_OTHER] = proto_register_protocol("Unknown or Miscellaneous Falco", "Falco Misc", "falco");
+
+ // Preferences
+ module_t *falco_bridge_module = prefs_register_protocol(proto_falco_bridge, NULL);
+ prefs_register_bool_preference(falco_bridge_module, "show_internal_events",
+ "Show internal events",
+ "Show internal libsinsp events in the event list.",
+ &pref_show_internal);
+
+
+ /*
+ * Create the dissector table that we will use to route the dissection to
+ * the appropriate Falco plugin.
+ */
+ ptype_dissector_table = register_dissector_table("falcobridge.id",
+ "Falco Bridge Plugin ID", proto_falco_bridge, FT_UINT32, BASE_DEC);
+
+ /*
+ * Load the plugins
+ */
+ WS_DIR *dir;
+ WS_DIRENT *file;
+ char *filename;
+ // XXX Falco plugins should probably be installed in a path that reflects
+ // the Falco version or its plugin API version.
+ char *spdname = g_build_filename(get_plugins_dir(), "falco", NULL);
+ char *ppdname = g_build_filename(get_plugins_pers_dir(), "falco", NULL);
+
+ /*
+ * We scan the plugins directory twice. The first time we count how many
+ * plugins we have, which we need to know in order to allocate the right
+ * amount of memory. The second time we actually load and configure
+ * each plugin.
+ */
+ if ((dir = ws_dir_open(spdname, 0, NULL)) != NULL) {
+ while ((ws_dir_read_name(dir)) != NULL) {
+ nbridges++;
+ }
+ ws_dir_close(dir);
+ }
+
+ if ((dir = ws_dir_open(ppdname, 0, NULL)) != NULL) {
+ while ((ws_dir_read_name(dir)) != NULL) {
+ nbridges++;
+ }
+ ws_dir_close(dir);
+ }
+
+ sinsp_span = create_sinsp_span();
+
+ bridges = g_new0(bridge_info, nbridges + 1);
+
+ create_sinsp_syscall_source(sinsp_span, &bridges[0].ssi);
+
+ create_source_hfids(&bridges[0]);
+ nbridges = 1;
+
+ if ((dir = ws_dir_open(spdname, 0, NULL)) != NULL) {
+ while ((file = ws_dir_read_name(dir)) != NULL) {
+ filename = g_build_filename(spdname, ws_dir_get_name(file), NULL);
+ import_plugin(filename);
+ g_free(filename);
+ }
+ ws_dir_close(dir);
+ }
+
+ if ((dir = ws_dir_open(ppdname, 0, NULL)) != NULL) {
+ while ((file = ws_dir_read_name(dir)) != NULL) {
+ filename = g_build_filename(ppdname, ws_dir_get_name(file), NULL);
+ import_plugin(filename);
+ g_free(filename);
+ }
+ ws_dir_close(dir);
+ }
+
+ g_free(spdname);
+ g_free(ppdname);
+
+ /*
+ * Setup protocol subtree array
+ */
+ static int *ett[] = {
+ &ett_falco_bridge,
+ &ett_syscalls[SSC_EVENT],
+ &ett_syscalls[SSC_EVTARGS],
+ &ett_syscalls[SSC_PROCESS],
+ &ett_syscalls[SSC_PROCLINEAGE],
+ &ett_syscalls[SSC_USER],
+ &ett_syscalls[SSC_GROUP],
+ &ett_syscalls[SSC_FD],
+ &ett_syscalls[SSC_FS],
+ &ett_syscalls[SSC_FDLIST],
+ &ett_syscalls[SSC_OTHER],
+ &ett_sinsp_enriched,
+ &ett_sinsp_span,
+ &ett_address,
+ &ett_json,
+ };
+
+ /*
+ * Setup process lineage subtree array
+ */
+ static int *ett_lin[] = {
+ &ett_lineage[0],
+ &ett_lineage[1],
+ &ett_lineage[2],
+ &ett_lineage[3],
+ &ett_lineage[4],
+ &ett_lineage[5],
+ &ett_lineage[6],
+ &ett_lineage[7],
+ &ett_lineage[8],
+ &ett_lineage[9],
+ &ett_lineage[10],
+ &ett_lineage[11],
+ &ett_lineage[12],
+ &ett_lineage[13],
+ &ett_lineage[14],
+ &ett_lineage[15],
+ };
+
+ proto_register_field_array(proto_falco_bridge, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+ proto_register_subtree_array(ett_lin, array_length(ett_lin));
+
+ register_dfilter_translator("Falco rule", dfilter_to_falco_rule);
+
+ register_shutdown_routine(on_wireshark_exit);
}
diff --git a/plugins/epan/falco_bridge/sinsp-span.cpp b/plugins/epan/falco_bridge/sinsp-span.cpp
index eb2fc7e7..051469c9 100644
--- a/plugins/epan/falco_bridge/sinsp-span.cpp
+++ b/plugins/epan/falco_bridge/sinsp-span.cpp
@@ -11,18 +11,30 @@
*/
#include "config.h"
+#define WS_LOG_DOMAIN "sinsp-span"
#include <stddef.h>
#include <stdint.h>
#include <glib.h>
+#include <epan/dfilter/dfilter-translator.h>
+#include <epan/wmem_scopes.h>
+
+#include <wsutil/array.h>
+#include <wsutil/unicode-utils.h>
+
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4100)
#pragma warning(disable:4267)
#endif
+// To do:
+// [ ] Move chunkify_string to a thread? For captures with large command lines,
+// we spend a lot of time hashing strings.
+
+
// epan/address.h and driver/ppm_events_public.h both define PT_NONE, so
// handle libsinsp calls here.
@@ -32,41 +44,442 @@ typedef struct ss_plugin_info ss_plugin_info;
#include "sinsp-span.h"
-#include <sinsp.h>
+#include <libsinsp/sinsp.h>
typedef struct sinsp_source_info_t {
sinsp_plugin *source;
+ std::vector<const filter_check_info *> syscall_filter_checks;
+ std::vector<const filtercheck_field_info *> syscall_filter_fields;
+ std::vector<std::unique_ptr<sinsp_filter_check>> syscall_event_filter_checks; // Same size as syscall_filter_fields
+ std::vector<sinsp_syscall_category_e> field_to_category; // Same size as syscall_filter_fields
sinsp_evt *evt;
uint8_t *evt_storage;
size_t evt_storage_size;
const char *name;
const char *description;
char *last_error;
- const char *fields;
+ size_t evt_category_idx;
+ uint16_t cpu_id_idx;
+ uint16_t proc_id_idx;
} sinsp_source_info_t;
+static const size_t sfe_slab_prealloc = 250000;
+
typedef struct sinsp_span_t {
sinsp inspector;
+ sinsp_filter_check_list filter_checks;
+ sinsp_field_extract_t *sfe_slab;
+ size_t sfe_slab_offset;
+ // XXX Combine these into a single struct?
+ std::vector<sinsp_field_extract_t *> sfe_ptrs;
+ std::vector<uint16_t> sfe_lengths;
+ std::vector<const ppm_event_info*> sfe_infos;
+ // Interned data. Copied from maxmind_db.c.
+ wmem_map_t *str_chunk;
+ wmem_map_t *proc_info_chunk;
} sinsp_span_t;
+// #define SS_MEMORY_STATISTICS 1
+
+#ifdef SS_MEMORY_STATISTICS
+#include <wsutil/str_util.h>
+
+static int alloc_sfe;
+static int unused_sfe_bytes;
+static int total_chunked_strings;
+static int unique_chunked_strings;
+static int proc_info_hits;
+static int proc_info_updates;
+static int alloc_chunked_string_bytes;
+static int total_bytes;
+#endif
+
+static filter_check_info g_args_fci;
+static filter_check_info g_lineage_fci;
+
+// These sinsp fields are not interesting in a wireshark-like use case, so we skip them.
+std::set<std::string> g_fields_to_skip = {
+ "evt.num",
+ "evt.time",
+ "evt.time.s",
+ "evt.time.iso8601",
+ "evt.datetime",
+ "evt.datetime.s",
+ "evt.rawtime",
+ "evt.rawtime.s",
+ "evt.rawtime.ns",
+ "evt.reltime",
+ "evt.reltime.s",
+ "evt.reltime.ns",
+ "evt.deltatime",
+ "evt.deltatime.s",
+ "evt.deltatime.ns",
+ "syscall.type",
+ "evt.is_async",
+ "evt.is_syslog",
+ "evt.count",
+ "proc.exeline",
+ "proc.cmdnargs",
+ "proc.pexe",
+ "proc.pexepath",
+ "proc.pcmdline",
+ "proc.ppid",
+ "proc.aexe",
+ "proc.aexepath",
+ "proc.aname",
+ "proc.acmdline",
+ "proc.apid",
+ "proc.cmdlenargs",
+ "proc.ppid.duration",
+ "proc.ppid.ts",
+ "thread.exetime",
+ "thread.totexetime",
+ "thread.vmsize",
+ "thread.vmrss",
+ "fd.nameraw",
+ "fd.cproto",
+ "fd.sproto",
+ "fd.lproto",
+ "fd.rproto",
+};
+
sinsp_span_t *create_sinsp_span()
{
- return new(sinsp_span_t);
+ sinsp_span_t *span = new(sinsp_span_t);
+ span->inspector.set_internal_events_mode(true);
+ span->inspector.set_buffer_format(sinsp_evt::PF_EOLS_COMPACT);
+
+ return span;
}
void destroy_sinsp_span(sinsp_span_t *sinsp_span) {
delete(sinsp_span);
}
+static const char *chunkify_string(sinsp_span_t *sinsp_span, const uint8_t *key, uint32_t key_len, int64_t cpu_id, int64_t proc_id, uint16_t fc_idx) {
+ int64_t proc_key = 0;
+
+#ifdef SS_MEMORY_STATISTICS
+ total_chunked_strings++;
+#endif
+
+ // In order to avoid hashing the same potentially large strings over and over,
+ // we assume that field values will mostly be the same for each CPU,PID combo.
+ if (fc_idx) {
+ proc_key = (cpu_id << 48) | (proc_id << 16) | fc_idx;
+ char *proc_string = (char *) wmem_map_lookup(sinsp_span->proc_info_chunk, &proc_key);
+
+ if (proc_string && strcmp(proc_string, (const char *)key) == 0) {
+#ifdef SS_MEMORY_STATISTICS
+ proc_info_hits++;
+#endif
+ return proc_string;
+ }
+ }
+
+ char *chunk_string = (char *) wmem_map_lookup(sinsp_span->str_chunk, key);
+
+ if (!chunk_string) {
+ chunk_string = (char *) ws_utf8_make_valid(wmem_file_scope(), key, (ssize_t)key_len);
+ char *key_string = chunk_string;
+ if (strcmp((const char *) key, chunk_string)) {
+ key_string = (char *) wmem_memdup(wmem_file_scope(), key, key_len);
+ }
+ wmem_map_insert(sinsp_span->str_chunk, key_string, chunk_string);
+#ifdef SS_MEMORY_STATISTICS
+ unique_chunked_strings++;
+ alloc_chunked_string_bytes += (int) strlen(chunk_string);
+#endif
+ }
+
+ if (proc_key) {
+ wmem_map_insert(sinsp_span->proc_info_chunk, &proc_key, chunk_string);
+#ifdef SS_MEMORY_STATISTICS
+ proc_info_updates++;
+#endif
+ }
+
+ return chunk_string;
+}
+
+static sinsp_syscall_category_e filtercheck_name_to_category(const std::string fc_name) {
+ std::map<const char *, sinsp_syscall_category_e> fc_name_to_category = {
+ { "evt", SSC_EVENT },
+ { "args", SSC_EVTARGS },
+ { "process", SSC_PROCESS },
+ { "lineage", SSC_PROCLINEAGE},
+ { "user", SSC_USER },
+ { "group", SSC_GROUP },
+ { "container", SSC_CONTAINER },
+ { "fd", SSC_FD },
+ { "fs.path", SSC_FS },
+ // syslog collides with the dissector
+ { "fdlist", SSC_FDLIST },
+ };
+
+ for (const auto ptc : fc_name_to_category) {
+ if (ptc.first == fc_name) {
+ return ptc.second;
+ }
+ }
+ return SSC_OTHER;
+}
+
+/*
+ * This is the list of "fake" fields that we create for the Falco event arguments.
+ */
+const filtercheck_field_info args_event_fields[] =
+{
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.0", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.1", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.2", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.3", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.4", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.5", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.6", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.7", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.8", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.9", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.10", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.11", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.12", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.13", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.14", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.15", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.16", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.17", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.18", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.19", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.20", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.21", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.22", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.23", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.24", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.25", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.26", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.27", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.28", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.29", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.30", "Argument", "Event argument."},
+ {PT_CHARBUF, EPF_NONE, PF_NA, "evt.arg.31", "Argument", "Event argument."},
+};
+
+#define CREATE_FIELD_INFO(index) \
+ {PT_CHARBUF, EPF_NONE, PF_NA, "proc.aname." #index, "Name", "The proc.name..."}, \
+ {PT_CHARBUF, EPF_NONE, PF_NA, "proc.aexepath." #index, "Executable Path", "The proc.exepath..."}, \
+ {PT_INT64, EPF_NONE, PF_ID, "proc.apid." #index, "Process ID", "The pid..."}, \
+ {PT_CHARBUF, EPF_NONE, PF_NA, "proc.acmdline." #index, "Command Line", "The full command line..."}
+
+/*
+ * This is the list of "fake" fields that we create for process lineage.
+ * We construct the array using a macro to limit verboseness.
+ */
+const filtercheck_field_info proc_lineage_event_fields[] = {
+ CREATE_FIELD_INFO(1),
+ CREATE_FIELD_INFO(2),
+ CREATE_FIELD_INFO(3),
+ CREATE_FIELD_INFO(4),
+ CREATE_FIELD_INFO(5),
+ CREATE_FIELD_INFO(6),
+ CREATE_FIELD_INFO(7),
+ CREATE_FIELD_INFO(8),
+ CREATE_FIELD_INFO(9),
+ CREATE_FIELD_INFO(10),
+ CREATE_FIELD_INFO(11),
+ CREATE_FIELD_INFO(12),
+ CREATE_FIELD_INFO(13),
+ CREATE_FIELD_INFO(14),
+ CREATE_FIELD_INFO(15),
+ CREATE_FIELD_INFO(16),
+};
+
+void add_arg_event(uint32_t arg_number,
+ sinsp_span_t *sinsp_span,
+ sinsp_source_info_t *ssi,
+ sinsp_syscall_category_e args_syscall_category) {
+
+ if (arg_number >= array_length(args_event_fields)) {
+ ws_error("falco event has too many arguments (%" PRIu32 ")", arg_number);
+ }
+
+ std::string fname = "evt.arg[" + std::to_string(arg_number) + "]";
+
+ const filtercheck_field_info *ffi = &args_event_fields[arg_number];
+ std::unique_ptr<sinsp_filter_check> sfc = sinsp_span->filter_checks.new_filter_check_from_fldname(fname.c_str(), &sinsp_span->inspector, true);
+ if (!sfc) {
+ ws_error("cannot find expected Falco field evt.arg");
+ }
+ sfc->parse_field_name(fname.c_str(), true, false);
+ ssi->field_to_category.push_back(args_syscall_category);
+ ssi->syscall_event_filter_checks.push_back(std::move(sfc));
+ ssi->syscall_filter_fields.push_back(ffi);
+}
+
+void create_args_source(sinsp_span_t *sinsp_span,
+ sinsp_source_info_t *ssi,
+ const filter_check_info* fci) {
+ g_args_fci.m_name = "args";
+ sinsp_syscall_category_e args_syscall_category = filtercheck_name_to_category(g_args_fci.m_name);
+
+ g_args_fci = *fci;
+
+ for (uint32_t i = 0; i < 32; i++) {
+ add_arg_event(i, sinsp_span, ssi, args_syscall_category);
+ }
+ ssi->syscall_filter_checks.push_back(&g_args_fci);
+}
+
+void add_lineage_field(std::string basefname,
+ uint32_t ancestor_number,
+ uint32_t field_number,
+ sinsp_filter_factory* filter_factory,
+ sinsp_source_info_t *ssi,
+ sinsp_syscall_category_e args_syscall_category) {
+ std::string fname = basefname + "[" + std::to_string(ancestor_number) + "]";
+ const filtercheck_field_info *ffi = &proc_lineage_event_fields[(ancestor_number - 1) * N_PROC_LINEAGE_ENTRY_FIELDS + field_number];
+ std::unique_ptr<sinsp_filter_check> sfc = filter_factory->new_filtercheck(fname.c_str());
+ if (!sfc) {
+ ws_error("cannot find expected Falco field evt.arg");
+ }
+
+ sfc->parse_field_name(fname.c_str(), true, false);
+ ssi->field_to_category.push_back(args_syscall_category);
+ ssi->syscall_event_filter_checks.push_back(std::move(sfc));
+ ssi->syscall_filter_fields.push_back(ffi);
+}
+
+void add_lineage_events(uint32_t ancestor_number,
+ sinsp_filter_factory* filter_factory,
+ sinsp_source_info_t *ssi,
+ sinsp_syscall_category_e args_syscall_category) {
+
+ if (ancestor_number >= array_length(proc_lineage_event_fields) / N_PROC_LINEAGE_ENTRY_FIELDS) {
+ ws_error("falco lineage mismatch (%" PRIu32 ")", ancestor_number);
+ }
+
+ add_lineage_field("proc.aname", ancestor_number, 0, filter_factory, ssi, args_syscall_category);
+ add_lineage_field("proc.acmdline", ancestor_number, 3, filter_factory, ssi, args_syscall_category);
+ add_lineage_field("proc.aexepath", ancestor_number, 1, filter_factory, ssi, args_syscall_category);
+ add_lineage_field("proc.apid", ancestor_number, 2, filter_factory, ssi, args_syscall_category);
+}
+
+void create_lineage_source(sinsp_source_info_t *ssi,
+ sinsp_filter_factory* filter_factory,
+ const filter_check_info* fci) {
+ g_lineage_fci.m_name = "lineage";
+ sinsp_syscall_category_e args_syscall_category = filtercheck_name_to_category(g_lineage_fci.m_name);
+
+ g_lineage_fci = *fci;
+
+ for (uint32_t i = 1; i < N_PROC_LINEAGE_ENTRIES; i++) {
+ add_lineage_events(i, filter_factory, ssi, args_syscall_category);
+ }
+ ssi->syscall_filter_checks.push_back(&g_lineage_fci);
+}
+
+// Not all of the fields in sinsp have been designed with a Wireshark-like use case in mind.
+// This functions determines which fields we should skip.
+bool skip_field(const filtercheck_field_info *ffi) {
+ if (g_fields_to_skip.find(ffi->m_name) != g_fields_to_skip.end()) {
+ return true;
+ }
+ return false;
+}
+
+/*
+ * We want the flexibility to decide the display order of the fields in the UI, since
+ * the order in which they are defined in filterchecks.{cpp,h} is not necessarily the one we want.
+ */
+void reorder_syscall_fields(std::vector<const filter_check_info*>* all_syscall_fields) {
+ // Move "fd" after "proc"
+ all_syscall_fields->insert(all_syscall_fields->begin() + 3, all_syscall_fields->at(6));
+ all_syscall_fields->erase(all_syscall_fields->begin() + 7);
+
+ // Move "container" after "fd"
+ all_syscall_fields->insert(all_syscall_fields->begin() + 4, all_syscall_fields->at(6));
+ all_syscall_fields->erase(all_syscall_fields->begin() + 7);
+}
+
+/*
+ * Populate a sinsp_source_info_t struct with the symbols coming from libsinsp's builtin syscall extractors
+ */
+void create_sinsp_syscall_source(sinsp_span_t *sinsp_span, sinsp_source_info_t **ssi_ptr) {
+ sinsp_source_info_t *ssi = new sinsp_source_info_t();
+
+ sinsp_filter_factory filter_factory(&sinsp_span->inspector, sinsp_span->filter_checks);
+ std::vector<const filter_check_info*> all_syscall_fields;
+
+ // Extract the fields defined in filterchecks.{cpp,h}
+ sinsp_span->filter_checks.get_all_fields(all_syscall_fields);
+ // Reorder the list of extractor the way we want them to appear in the UI.
+ reorder_syscall_fields(&all_syscall_fields);
+ for (const auto fci : all_syscall_fields) {
+ if (fci->m_flags == filter_check_info::FL_HIDDEN) {
+ continue;
+ }
+
+ if (fci->m_name == "process") {
+ // This creates a meta-filtercheck for the events arguments and it register its fields.
+ // We do it before the process filtercheck because we want to have it exactly in the position
+ // after event and before process.
+ create_args_source(sinsp_span, ssi, fci);
+ } else if (fci->m_name == "fd") {
+ // This creates a meta-filtercheck for process lineage.
+ // We do it before the fd filtercheck because we want it to be between the proc and fd trees.
+ create_lineage_source(ssi, &filter_factory, fci);
+ }
+
+ sinsp_syscall_category_e syscall_category = filtercheck_name_to_category(fci->m_name);
+
+ for (int i = 0; i < fci->m_nfields; i++) {
+ const filtercheck_field_info *ffi = &fci->m_fields[i];
+ if (ffi->m_flags == filtercheck_field_flags::EPF_NONE) {
+ // This is where we exclude fields that are not interesting in a wireshark-like use case.
+ if (skip_field(ffi)) {
+ continue;
+ }
+
+ std::unique_ptr<sinsp_filter_check> sfc = filter_factory.new_filtercheck(ffi->m_name);
+ if (!sfc) {
+ continue;
+ }
+ if (strcmp(ffi->m_name, "evt.category") == 0) {
+ ssi->evt_category_idx = ssi->syscall_filter_fields.size();
+ }
+ if (strcmp(ffi->m_name, "evt.cpu") == 0) {
+ ssi->cpu_id_idx = (uint16_t) ssi->syscall_filter_fields.size();
+ }
+ if (strcmp(ffi->m_name, "proc.pid") == 0) {
+ ssi->proc_id_idx = (uint16_t) ssi->syscall_filter_fields.size();
+ }
+ sfc->parse_field_name(ffi->m_name, true, false);
+ ssi->field_to_category.push_back(syscall_category);
+ ssi->syscall_event_filter_checks.push_back(std::move(sfc));
+ ssi->syscall_filter_fields.push_back(ffi);
+ }
+ }
+
+ ssi->syscall_filter_checks.push_back(fci);
+ }
+
+ ssi->evt = new sinsp_evt(&sinsp_span->inspector);
+ ssi->evt_storage_size = 4096;
+ ssi->evt_storage = (uint8_t *) g_malloc(ssi->evt_storage_size);
+ ssi->name = g_strdup(sinsp_syscall_event_source_name);
+ ssi->description = g_strdup(sinsp_syscall_event_source_name);
+ *ssi_ptr = ssi;
+ return;
+}
+
/*
- * Populate a source_plugin_info struct with the symbols coming from a library loaded via libsinsp
+ * Populate a sinsp_source_info_t struct with the symbols coming from a library loaded via libsinsp
*/
char *
-create_sinsp_source(sinsp_span_t *sinsp_span, const char* libname, sinsp_source_info_t **ssi_ptr)
+create_sinsp_plugin_source(sinsp_span_t *sinsp_span, const char* libname, sinsp_source_info_t **ssi_ptr)
{
- char *err_str = NULL;
sinsp_source_info_t *ssi = new sinsp_source_info_t();
+ char *err_str = NULL;
try {
auto sp = sinsp_span->inspector.register_plugin(libname);
if (sp->caps() & CAP_EXTRACTION) {
@@ -84,7 +497,6 @@ create_sinsp_source(sinsp_span_t *sinsp_span, const char* libname, sinsp_source_
err_str = g_strdup_printf("Unable to initialize %s: %s", libname, init_err.c_str());
}
}
-
if (err_str) {
delete ssi;
return err_str;
@@ -93,23 +505,28 @@ create_sinsp_source(sinsp_span_t *sinsp_span, const char* libname, sinsp_source_
ssi->evt = new sinsp_evt(&sinsp_span->inspector);
ssi->evt_storage_size = 4096;
ssi->evt_storage = (uint8_t *) g_malloc(ssi->evt_storage_size);
- ssi->name = strdup(ssi->source->name().c_str());
- ssi->description = strdup(ssi->source->description().c_str());
+ ssi->name = g_strdup(ssi->source->name().c_str());
+ ssi->description = g_strdup(ssi->source->description().c_str());
*ssi_ptr = ssi;
return NULL;
}
uint32_t get_sinsp_source_id(sinsp_source_info_t *ssi)
{
- return ssi->source->id();
+ if (ssi->source) {
+ return ssi->source->id();
+ }
+ return 0;
}
const char *get_sinsp_source_last_error(sinsp_source_info_t *ssi)
{
- if (ssi->last_error) {
- free(ssi->last_error);
+ if (ssi->source) {
+ if (ssi->last_error) {
+ g_free(ssi->last_error);
+ }
+ ssi->last_error = g_strdup(ssi->source->get_last_error().c_str());
}
- ssi->last_error = strdup(ssi->source->get_last_error().c_str());
return ssi->last_error;
}
@@ -125,30 +542,101 @@ const char *get_sinsp_source_description(sinsp_source_info_t *ssi)
size_t get_sinsp_source_nfields(sinsp_source_info_t *ssi)
{
- return ssi->source->fields().size();
+ if (ssi->source) {
+ return ssi->source->fields().size();
+ }
+
+ return ssi->syscall_filter_fields.size();
}
bool get_sinsp_source_field_info(sinsp_source_info_t *ssi, size_t field_num, sinsp_field_info_t *field)
{
- if (field_num >= ssi->source->fields().size()) {
+ if (field_num >= get_sinsp_source_nfields(ssi)) {
return false;
}
- const filtercheck_field_info *ffi = &ssi->source->fields()[field_num];
+ const filtercheck_field_info *ffi = NULL;
+
+ if (ssi->source) {
+ ffi = &ssi->source->fields()[field_num];
+ g_strlcpy(field->abbrev, ffi->m_name, sizeof(field->abbrev));
+ } else {
+ ffi = ssi->syscall_filter_fields[field_num];
+ if (ssi->field_to_category[field_num] == SSC_OTHER) {
+ snprintf(field->abbrev, sizeof(field->abbrev), FALCO_FIELD_NAME_PREFIX "%s", ffi->m_name);
+ } else {
+ g_strlcpy(field->abbrev, ffi->m_name, sizeof(field->abbrev));
+ }
+ }
+
+ g_strlcpy(field->display, ffi->m_display, sizeof(field->display));
+ g_strlcpy(field->description, ffi->m_description, sizeof(field->description));
+
+ field->is_hidden = ffi->m_flags & EPF_TABLE_ONLY;
+ field->is_conversation = ffi->m_flags & EPF_CONVERSATION;
+ field->is_info = ffi->m_flags & EPF_INFO;
+
+ field->is_numeric_address = false;
switch (ffi->m_type) {
- case PT_CHARBUF:
- field->type = SFT_STRINGZ;
+ case PT_INT8:
+ field->type = FT_INT8;
+ break;
+ case PT_INT16:
+ field->type = FT_INT16;
+ break;
+ case PT_INT32:
+ field->type = FT_INT32;
+ break;
+ case PT_INT64:
+ field->type = FT_INT64;
+ break;
+ case PT_UINT8:
+ field->type = FT_UINT8;
+ break;
+ case PT_UINT16:
+ case PT_PORT:
+ field->type = FT_UINT16;
+ break;
+ case PT_UINT32:
+ field->type = FT_UINT32;
break;
case PT_UINT64:
- field->type = SFT_UINT64;
+ case PT_RELTIME:
+ case PT_ABSTIME:
+ field->type = FT_UINT64;
+ break;
+ case PT_CHARBUF:
+ field->type = FT_STRINGZ;
+ break;
+// field->type = FT_RELATIVE_TIME;
+// break;
+// field->type = FT_ABSOLUTE_TIME;
+// field->type = FT_UINT64;
+// field->display_format = SFDF_DECIMAL;
+ break;
+ case PT_BYTEBUF:
+ field->type = FT_BYTES;
+ break;
+ case PT_BOOL:
+ field->type = FT_BOOLEAN;
+ break;
+ case PT_DOUBLE:
+ field->type = FT_DOUBLE;
+ break;
+ case PT_IPADDR:
+ field->type = FT_BYTES;
+ field->is_numeric_address = true;
break;
default:
- field->type = SFT_UNKNOWN;
+ ws_debug("Unknown Falco parameter type %d for %s", ffi->m_type, field->abbrev);
+ field->type = FT_BYTES;
}
switch (ffi->m_print_format) {
case PF_DEC:
+ case PF_10_PADDED_DEC:
+ case PF_ID:
field->display_format = SFDF_DECIMAL;
break;
case PF_HEX:
@@ -159,31 +647,317 @@ bool get_sinsp_source_field_info(sinsp_source_info_t *ssi, size_t field_num, sin
break;
default:
field->display_format = SFDF_UNKNOWN;
+ break;
}
- g_strlcpy(field->abbrev, ffi->m_name, sizeof(ffi->m_name));
- g_strlcpy(field->display, ffi->m_display, sizeof(ffi->m_display));
- g_strlcpy(field->description, ffi->m_description, sizeof(ffi->m_description));
+ return true;
+}
- field->is_hidden = ffi->m_flags & EPF_TABLE_ONLY;
- field->is_info = ffi->m_flags & EPF_INFO;
- field->is_conversation = ffi->m_flags & EPF_CONVERSATION;
+char* get_evt_arg_name(void* sinp_evt_info, uint32_t arg_num) {
+ ppm_event_info* realinfo = (ppm_event_info*)sinp_evt_info;
+
+ if (arg_num > realinfo->nparams) {
+ ws_error("Arg number %u exceeds event parameter count %u", arg_num, realinfo->nparams);
+ return NULL;
+ }
+ return realinfo->params[arg_num].name;
+}
+
+// Ensure that our caches can index evt_num - 1
+static void ensure_cache_size(sinsp_span_t *sinsp_span, uint64_t evt_num)
+{
+ if (evt_num <= sinsp_span->sfe_ptrs.size()) {
+ // Not necessarily an error, e.g. if we're expanding to handle a frame number at EOF
+ return;
+ }
+ // XXX check for evt_num < 1?
+ sinsp_span->sfe_ptrs.resize(evt_num);
+ sinsp_span->sfe_lengths.resize(evt_num);
+ sinsp_span->sfe_infos.resize(evt_num);
+}
+
+void open_sinsp_capture(sinsp_span_t *sinsp_span, const char *filepath)
+{
+ sinsp_span->sfe_slab = NULL;
+ sinsp_span->sfe_slab_offset = 0;
+ sinsp_span->sfe_ptrs.clear();
+ sinsp_span->sfe_lengths.clear();
+ sinsp_span->sfe_infos.clear();
+ sinsp_span->inspector.open_savefile(filepath);
+ sinsp_span->str_chunk = wmem_map_new(wmem_file_scope(), g_str_hash, g_str_equal);
+ sinsp_span->proc_info_chunk = wmem_map_new(wmem_file_scope(), g_int64_hash, g_int64_equal);
+
+#ifdef SS_MEMORY_STATISTICS
+ alloc_sfe = 0;
+ unused_sfe_bytes = 0;
+ total_chunked_strings = 0;
+ unique_chunked_strings = 0;
+ proc_info_hits = 0;
+ proc_info_updates = 0;
+ alloc_chunked_string_bytes = 0;
+ total_bytes = 0;
+#endif
+}
+
+static void add_syscall_event_to_cache(sinsp_span_t *sinsp_span, sinsp_source_info_t *ssi, sinsp_evt *evt)
+{
+ uint64_t evt_num = evt->get_num();
+
+ // libsinsp requires that events be processed in order so we cache our extracted
+ // data during the first pass. We don't know how many fields we're going to extract
+ // during an event, so we preallocate slabs of `sfe_slab_prealloc` entries.
+ //
+ // XXX This assumes that we won't extract more than ssi->syscall_event_filter_checks.size()
+ // fields per event.
+ if (sinsp_span->sfe_slab_offset + ssi->syscall_event_filter_checks.size() > sfe_slab_prealloc) {
+#ifdef SS_MEMORY_STATISTICS
+ if (sinsp_span->sfe_slab_offset > 0) {
+ unused_sfe_bytes += sizeof(sinsp_field_extract_t) * (sfe_slab_prealloc - sinsp_span->sfe_slab_offset);
+ }
+#endif
+ sinsp_span->sfe_slab = NULL;
+ sinsp_span->sfe_slab_offset = 0;
+ }
+ if (sinsp_span->sfe_slab == NULL) {
+#ifdef SS_MEMORY_STATISTICS
+ alloc_sfe += sfe_slab_prealloc;
+#endif
+ sinsp_span->sfe_slab = (sinsp_field_extract_t *) wmem_alloc(wmem_file_scope(), sizeof(sinsp_field_extract_t) * sfe_slab_prealloc);
+ }
+
+ sinsp_field_extract_t *sfe_block = &sinsp_span->sfe_slab[sinsp_span->sfe_slab_offset];
+ std::vector<extract_value_t> values;
+ uint16_t sfe_idx = 0;
+ int16_t cpu_id = 0;
+ int64_t proc_id = 0;
+
+ // First check for internal events.
+ // XXX We should skip this if "Show internal events" is enabled.
+ auto sfc = ssi->syscall_event_filter_checks[ssi->evt_category_idx].get();
+ if (!sfc->extract(evt, values, false) || values.size() < 1) {
+ return;
+ }
+ if (strcmp((const char *) values[0].ptr, "internal") == 0) {
+ return;
+ }
+
+ for (size_t fc_idx = 0; fc_idx < ssi->syscall_event_filter_checks.size(); fc_idx++) {
+ sfc = ssi->syscall_event_filter_checks[fc_idx].get();
+ values.clear();
+ if (!sfc->extract(evt, values, false) || values.size() < 1) {
+ continue;
+ }
+ auto ffi = ssi->syscall_filter_fields[fc_idx];
+ if (ffi->m_flags == filtercheck_field_flags::EPF_NONE && values[0].len > 0) {
+ if (sinsp_span->sfe_slab_offset + sfe_idx >= sfe_slab_prealloc) {
+ ws_error("Extracting too many fields for event %u (%d vs %d)", (unsigned) evt->get_num(), (int) sfe_idx, (int) ssi->syscall_event_filter_checks.size());
+ }
+
+ sinsp_field_extract_t *sfe = &sfe_block[sfe_idx];
+ sfe_idx++;
+ sfe->field_idx = (uint32_t) fc_idx;
+ // XXX Use memcpy instead of all this casting?
+ switch (ffi->m_type) {
+ case PT_INT8:
+ sfe->res.i32 = *(int8_t*)values[0].ptr;
+ break;
+ case PT_INT16:
+ sfe->res.i32 = *(int16_t*)values[0].ptr;
+ if (fc_idx == ssi->cpu_id_idx) {
+ cpu_id = *(int16_t*)values[0].ptr;
+ }
+ break;
+ case PT_INT32:
+ sfe->res.i32 = *(int32_t*)values[0].ptr;
+ break;
+ case PT_INT64:
+ sfe->res.i64 = *(int64_t *)values[0].ptr;
+ if (fc_idx == ssi->proc_id_idx) {
+ proc_id = *(int64_t*)values[0].ptr;
+ }
+ break;
+ case PT_UINT8:
+ sfe->res.u32 = *(uint8_t*)values[0].ptr;
+ break;
+ case PT_UINT16:
+ case PT_PORT:
+ sfe->res.u32 = *(uint16_t*)values[0].ptr;
+ break;
+ case PT_UINT32:
+ sfe->res.u32 = *(uint32_t*)values[0].ptr;
+ break;
+ case PT_UINT64:
+ case PT_RELTIME:
+ case PT_ABSTIME:
+ sfe->res.u64 = *(uint64_t *)values[0].ptr;
+ break;
+ case PT_CHARBUF:
+ if (*values[0].ptr == '\0') {
+ // XXX libsinsp 0.14.1 sometimes returns garbage / large length values for empty strings.
+ // ws_warning("charbuf value length %u should be 0", values[0].len);
+ values[0].len = 0;
+ }
+ if (values[0].len < SFE_SMALL_BUF_SIZE) {
+ // XXX We need to convert this to valid UTF-8
+ g_strlcpy(sfe->res.small_str, (const char *) values[0].ptr, SFE_SMALL_BUF_SIZE);
+ } else {
+ sfe->res.str = chunkify_string(sinsp_span, values[0].ptr, values[0].len, cpu_id, proc_id, fc_idx);
+ }
+ break;
+ case PT_BOOL:
+ sfe->res.boolean = (bool)(uint32_t) *(uint32_t*)values[0].ptr;
+ break;
+ case PT_DOUBLE:
+ sfe->res.dbl = *(double*)values[0].ptr;
+ break;
+ default:
+ sfe->res.bytes = (uint8_t*) wmem_memdup(wmem_file_scope(), (const uint8_t *) values[0].ptr, values[0].len);
+#ifdef SS_MEMORY_STATISTICS
+ total_bytes += values[0].len;
+#endif
+ }
+
+ sfe->res_len = values[0].len;
+ }
+ }
+
+ sinsp_span->sfe_slab_offset += sfe_idx;
+
+ ensure_cache_size(sinsp_span, evt_num);
+ sinsp_span->sfe_ptrs[evt_num - 1] = sfe_block;
+ sinsp_span->sfe_lengths[evt_num - 1] = sfe_idx;
+ sinsp_span->sfe_infos[evt_num - 1] = evt->get_info();
+
+ return;
+}
+
+void close_sinsp_capture(sinsp_span_t *sinsp_span)
+{
+#ifdef SS_MEMORY_STATISTICS
+ unused_sfe_bytes += sizeof(sinsp_field_extract_t) * sfe_slab_prealloc - sinsp_span->sfe_slab_offset;
+
+ g_warning("Allocated sinsp_field_extract_t structs: %s (%s)",
+ format_size(alloc_sfe, FORMAT_SIZE_UNIT_NONE, FORMAT_SIZE_PREFIX_SI),
+ format_size(alloc_sfe * sizeof(sinsp_field_extract_t), FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI));
+ g_warning("Unused sinsp_field_extract_t bytes: %s", format_size(unused_sfe_bytes, FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI));
+ g_warning("Chunked strings: %s (%s unique, %s)",
+ format_size(total_chunked_strings, FORMAT_SIZE_UNIT_NONE, FORMAT_SIZE_PREFIX_SI),
+ format_size(unique_chunked_strings, FORMAT_SIZE_UNIT_NONE, FORMAT_SIZE_PREFIX_SI),
+ format_size(alloc_chunked_string_bytes, FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI));
+ g_warning("Process info string hits: %s, %s updates",
+ format_size(proc_info_hits, FORMAT_SIZE_UNIT_NONE, FORMAT_SIZE_PREFIX_SI),
+ format_size(proc_info_updates, FORMAT_SIZE_UNIT_NONE, FORMAT_SIZE_PREFIX_SI));
+ g_warning("Byte value (I/O) bytes: %s", format_size(total_bytes, FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI));
+ g_warning("Cache capacity: %s items, sinsp_field_extract_t pointer bytes = %s, length bytes = %s",
+ format_size(sinsp_span->sfe_ptrs.capacity(), FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI),
+ format_size(sinsp_span->sfe_ptrs.capacity() * sizeof(sinsp_field_extract_t *), FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI),
+ format_size(sinsp_span->sfe_ptrs.capacity() * sizeof(uint16_t), FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI));
+
+ alloc_sfe = 0;
+ unused_sfe_bytes = 0;
+ total_chunked_strings = 0;
+ unique_chunked_strings = 0;
+ proc_info_hits = 0;
+ proc_info_updates = 0;
+ alloc_chunked_string_bytes = 0;
+ total_bytes = 0;
+#endif
+
+ sinsp_span->inspector.close();
+ sinsp_span->sfe_ptrs.clear();
+ sinsp_span->sfe_lengths.clear();
+ sinsp_span->sfe_infos.clear();
+ sinsp_span->str_chunk = NULL;
+ sinsp_span->proc_info_chunk = NULL;
+}
+
+sinsp_syscall_category_e get_syscall_parent_category(sinsp_source_info_t *ssi, size_t field_check_idx)
+{
+ if (field_check_idx < ssi->field_to_category.size()) {
+ return ssi->field_to_category[field_check_idx];
+ }
+ return SSC_OTHER;
+}
+
+// Either have separate cached / non-cached params or pass a pointer to a pointer array.
+bool extract_syscall_source_fields(sinsp_span_t *sinsp_span, sinsp_source_info_t *ssi, uint32_t frame_num, sinsp_field_extract_t **sinsp_fields, uint32_t *sinsp_field_len, void** sinp_evt_info) {
+ if (ssi->source) {
+ return false;
+ }
+
+ while (frame_num > sinsp_span->sfe_ptrs.size()) {
+ sinsp_evt *evt = NULL;
+ try {
+ int32_t res = sinsp_span->inspector.next(&evt);
+ switch (res) {
+ case SCAP_TIMEOUT:
+ case SCAP_FILTERED_EVENT:
+ break;
+ case SCAP_UNEXPECTED_BLOCK:
+ ws_debug("Filling unexpected block gap from %d to %u", (int) sinsp_span->sfe_ptrs.size(), frame_num);
+ ensure_cache_size(sinsp_span, frame_num);
+ break;
+ case SCAP_EOF:
+ ws_debug("Filling syscall EOF gap from %d to %u at %u", (int) sinsp_span->sfe_ptrs.size(), frame_num, (unsigned)sinsp_span->inspector.get_bytes_read());
+ ensure_cache_size(sinsp_span, frame_num);
+ break;
+ case SCAP_SUCCESS:
+ add_syscall_event_to_cache(sinsp_span, ssi, evt);
+ break;
+ default:
+ ws_warning("%s", sinsp_span->inspector.getlasterr().c_str());
+ return false;
+ }
+ } catch (sinsp_exception &e) {
+ ws_warning("%s", e.what());
+ return false;
+ }
+ }
+
+ // Shouldn't happen
+ if (frame_num > sinsp_span->sfe_ptrs.size()) {
+ ws_error("Frame number %u exceeds cache size %d", frame_num, (int) sinsp_span->sfe_ptrs.size());
+ return false;
+ }
+
+ *sinsp_fields = sinsp_span->sfe_ptrs[frame_num - 1];
+ *sinsp_field_len = sinsp_span->sfe_lengths[frame_num - 1];
+ *sinp_evt_info = (void*)sinsp_span->sfe_infos[frame_num - 1];
+
+ return true;
+}
+
+bool get_extracted_syscall_source_fields(sinsp_span_t *sinsp_span, uint32_t frame_num, sinsp_field_extract_t **sinsp_fields, uint32_t *sinsp_field_len, void** sinp_evt_info) {
+ // Shouldn't happen
+ if (frame_num > sinsp_span->sfe_ptrs.size()) {
+ ws_error("Frame number %u exceeds cache size %d", frame_num, (int) sinsp_span->sfe_ptrs.size());
+ return false;
+ }
+
+ *sinsp_fields = sinsp_span->sfe_ptrs[frame_num - 1];
+ *sinsp_field_len = sinsp_span->sfe_lengths[frame_num - 1];
+ *sinp_evt_info = (void*)sinsp_span->sfe_infos[frame_num - 1];
return true;
}
// The code below, falcosecurity/libs, and falcosecurity/plugins need to be in alignment.
// The Makefile in /plugins defines FALCOSECURITY_LIBS_REVISION and uses that version of
// plugin_info.h. We need to build against a compatible revision of /libs.
-bool extract_sisnp_source_fields(sinsp_source_info_t *ssi, uint8_t *evt_data, uint32_t evt_datalen, wmem_allocator_t *pool, sinsp_field_extract_t *sinsp_fields, uint32_t sinsp_field_len)
+bool extract_plugin_source_fields(sinsp_source_info_t *ssi, uint32_t event_num, uint8_t *evt_data, uint32_t evt_datalen, wmem_allocator_t *pool, plugin_field_extract_t *sinsp_fields, uint32_t sinsp_field_len)
{
+ if (!ssi->source) {
+ return false;
+ }
+
std::vector<ss_plugin_extract_field> fields;
// PPME_PLUGINEVENT_E events have the following format:
// | scap_evt header | uint32_t sizeof(id) = 4 | uint32_t evt_datalen | uint32_t id | uint8_t[] evt_data |
uint32_t payload_hdr[3] = {4, evt_datalen, ssi->source->id()};
- uint32_t tot_evt_len = (uint32_t)sizeof(scap_evt) + sizeof(payload_hdr) + evt_datalen;
+// uint32_t payload_hdr_size = (nparams + 1) * 4;
+ uint32_t tot_evt_len = (uint32_t)sizeof(scap_evt) + sizeof(payload_hdr) + evt_datalen;
if (ssi->evt_storage_size < tot_evt_len) {
while (ssi->evt_storage_size < tot_evt_len) {
ssi->evt_storage_size *= 2;
@@ -196,18 +970,19 @@ bool extract_sisnp_source_fields(sinsp_source_info_t *ssi, uint8_t *evt_data, ui
sevt->tid = -1;
sevt->len = tot_evt_len;
sevt->type = PPME_PLUGINEVENT_E;
- sevt->nparams = 2; // Plugin ID + evt_data
+ sevt->nparams = 2; // Plugin ID + evt_data;
memcpy(ssi->evt_storage + sizeof(scap_evt), payload_hdr, sizeof(payload_hdr));
memcpy(ssi->evt_storage + sizeof(scap_evt) + sizeof(payload_hdr), evt_data, evt_datalen);
ssi->evt->init(ssi->evt_storage, 0);
+ ssi->evt->set_num(event_num);
fields.resize(sinsp_field_len);
// We must supply field_id, field, arg, and type.
for (size_t i = 0; i < sinsp_field_len; i++) {
fields.at(i).field_id = sinsp_fields[i].field_id;
fields.at(i).field = sinsp_fields[i].field_name;
- if (sinsp_fields[i].type == SFT_STRINGZ) {
+ if (sinsp_fields[i].type == FT_STRINGZ) {
fields.at(i).ftype = FTYPE_STRING;
} else {
fields.at(i).ftype = FTYPE_UINT64;
@@ -223,9 +998,9 @@ bool extract_sisnp_source_fields(sinsp_source_info_t *ssi, uint8_t *evt_data, ui
sinsp_fields[i].is_present = fields.at(i).res_len > 0;
if (sinsp_fields[i].is_present) {
if (fields.at(i).ftype == PT_CHARBUF) {
- sinsp_fields[i].res_str = wmem_strdup(pool, *fields.at(i).res.str);
+ sinsp_fields[i].res.str = wmem_strdup(pool, *fields.at(i).res.str);
} else if (fields.at(i).ftype == PT_UINT64) {
- sinsp_fields[i].res_u64 = *fields.at(i).res.u64;
+ sinsp_fields[i].res.u64 = *fields.at(i).res.u64;
} else {
status = false;
}
diff --git a/plugins/epan/falco_bridge/sinsp-span.h b/plugins/epan/falco_bridge/sinsp-span.h
index 2a474714..84303340 100644
--- a/plugins/epan/falco_bridge/sinsp-span.h
+++ b/plugins/epan/falco_bridge/sinsp-span.h
@@ -15,21 +15,21 @@
#include <stdint.h>
+#include <epan/ftypes/ftypes.h>
#include <wsutil/wmem/wmem.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
+#define FALCO_FIELD_NAME_PREFIX "falco."
+
+#define N_PROC_LINEAGE_ENTRIES 16
+#define N_PROC_LINEAGE_ENTRY_FIELDS 4
+
typedef struct sinsp_source_info_t sinsp_source_info_t;
typedef struct sinsp_span_t sinsp_span_t;
-typedef enum sinsp_field_type_e {
- SFT_UNKNOWN,
- SFT_STRINGZ,
- SFT_UINT64,
-} sinsp_field_type_e;
-
typedef enum sinsp_field_display_format_e {
SFDF_UNKNOWN,
SFDF_DECIMAL,
@@ -37,8 +37,26 @@ typedef enum sinsp_field_display_format_e {
SFDF_OCTAL
} sinsp_field_display_format_e;
+// Should match sinsp_filter_check_list in libsinsp as closely as possible.
+
+typedef enum sinsp_syscall_category_e {
+ SSC_EVENT, // gen_event, event
+ SSC_EVTARGS, // event arguments
+ SSC_PROCESS, // thread
+ SSC_PROCLINEAGE, // process lineage
+ SSC_USER, // user
+ SSC_GROUP, // group
+ SSC_CONTAINER, // container
+ SSC_FD, // fd
+ SSC_FS, // fs.path
+// SSC_SYSLOG, // syslog. Collides with syslog dissector so skip for now.
+ SSC_FDLIST, // fdlist
+ SSC_OTHER, // "falco.", catch-all
+ NUM_SINSP_SYSCALL_CATEGORIES
+} sinsp_syscall_category_e;
+
typedef struct sinsp_field_info_t {
- sinsp_field_type_e type;
+ enum ftenum type;
sinsp_field_display_format_e display_format;
char abbrev[64]; // filter name
char display[64]; // display name
@@ -46,31 +64,72 @@ typedef struct sinsp_field_info_t {
bool is_hidden;
bool is_conversation;
bool is_info;
+ bool is_numeric_address;
} sinsp_field_info_t;
+#define SFE_SMALL_BUF_SIZE 8
typedef struct sinsp_field_extract_t {
- uint32_t field_id; // in
+ union {
+ uint8_t *bytes;
+ const char *str;
+ int32_t i32;
+ int64_t i64;
+ uint32_t u32;
+ uint64_t u64;
+ double dbl;
+ bool boolean;
+ char small_str[SFE_SMALL_BUF_SIZE];
+ uint8_t small_bytes[SFE_SMALL_BUF_SIZE];
+ } res;
+ int res_len; // out
+ uint16_t field_idx; // out for syscalls
+} sinsp_field_extract_t;
+
+typedef struct plugin_field_extract_t {
+ uint32_t field_id; // out for syscalls, in for plugins
const char *field_name; // in
- sinsp_field_type_e type; // in, out
+ enum ftenum type; // in, out
bool is_present; // out
- const char *res_str; // out
- uint64_t res_u64; // out
-} sinsp_field_extract_t;
+ union {
+ uint8_t *bytes;
+ const char *str;
+ int32_t i32;
+ int64_t i64;
+ uint32_t u32;
+ uint64_t u64;
+ double dbl;
+ uint8_t ipv6[16];
+ bool boolean;
+ } res;
+ int res_len; // out
+// sinsp_syscall_category_e parent_category; // out
+} plugin_field_extract_t;
sinsp_span_t *create_sinsp_span(void);
void destroy_sinsp_span(sinsp_span_t *sinsp_span);
-char *create_sinsp_source(sinsp_span_t *sinsp_span, const char* libname, sinsp_source_info_t **ssi_ptr);
-
-// Extractor plugin routines.
-// These roughly match common_plugin_info
+// Common routines
uint32_t get_sinsp_source_id(sinsp_source_info_t *ssi);
const char *get_sinsp_source_last_error(sinsp_source_info_t *ssi);
const char *get_sinsp_source_name(sinsp_source_info_t *ssi);
const char* get_sinsp_source_description(sinsp_source_info_t *ssi);
-size_t get_sinsp_source_nfields(sinsp_source_info_t *ssi);
bool get_sinsp_source_field_info(sinsp_source_info_t *ssi, size_t field_num, sinsp_field_info_t *field);
-bool extract_sisnp_source_fields(sinsp_source_info_t *ssi, uint8_t *evt_data, uint32_t evt_datalen, wmem_allocator_t *pool, sinsp_field_extract_t *sinsp_fields, uint32_t sinsp_field_len);
+char* get_evt_arg_name(void* sinp_evt_info, uint32_t arg_num);
+
+// libsinsp builtin syscall routines.
+void create_sinsp_syscall_source(sinsp_span_t *sinsp_span, sinsp_source_info_t **ssi_ptr);
+void open_sinsp_capture(sinsp_span_t *sinsp_span, const char *filepath);
+//uint32_t process_syscall_capture(sinsp_span_t * sinsp_span, sinsp_source_info_t *ssi, uint32_t to_event);
+void close_sinsp_capture(sinsp_span_t *sinsp_span);
+bool extract_syscall_source_fields(sinsp_span_t *sinsp_span, sinsp_source_info_t *ssi, uint32_t frame_num, sinsp_field_extract_t **sinsp_fields, uint32_t *sinsp_field_len, void** sinp_evt_info);
+sinsp_syscall_category_e get_syscall_parent_category(sinsp_source_info_t *ssi, size_t field_check_idx);
+bool get_extracted_syscall_source_fields(sinsp_span_t *sinsp_span, uint32_t frame_num, sinsp_field_extract_t **sinsp_fields, uint32_t *sinsp_field_len, void** sinp_evt_info);
+
+// Extractor plugin routines.
+// These roughly match common_plugin_info
+char *create_sinsp_plugin_source(sinsp_span_t *sinsp_span, const char* libname, sinsp_source_info_t **ssi_ptr);
+size_t get_sinsp_source_nfields(sinsp_source_info_t *ssi);
+bool extract_plugin_source_fields(sinsp_source_info_t *ssi, uint32_t event_num, uint8_t *evt_data, uint32_t evt_datalen, wmem_allocator_t *pool, plugin_field_extract_t *sinsp_fields, uint32_t sinsp_field_len);
#ifdef __cplusplus