Skip to content

Commit

Permalink
update(sinsp): use EPF_ARG_REQUIRED and EPF_IS_LIST when needed
Browse files Browse the repository at this point in the history
Signed-off-by: Andrea Terzolo <[email protected]>
  • Loading branch information
Andreagit97 committed Nov 16, 2023
1 parent 3f5682c commit 058aedd
Show file tree
Hide file tree
Showing 3 changed files with 262 additions and 53 deletions.
178 changes: 127 additions & 51 deletions userspace/libsinsp/sinsp_filtercheck_container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,22 @@ using namespace std;
// We use a macro to avoid a performance overhead, even using an inline function
// we are not sure the compiler will inline it
#define CHECK_LABEL_PRESENCE(x) \
if(is_host || !container_info || container_info->m_labels.count(x) <= 0) \
if(is_host || !container_info) \
{ \
return NULL; \
return nullptr; \
} \
else \
{ \
m_tstr = container_info->m_labels.at(x); \
RETURN_EXTRACT_STRING(m_tstr); \
auto label = container_info->m_labels.find(x); \
if(label != container_info->m_labels.end()) \
{ \
m_tstr = label->second; \
RETURN_EXTRACT_STRING(m_tstr); \
} \
else \
{ \
return nullptr; \
} \
}

static const filtercheck_field_info sinsp_filter_check_container_fields[] =
Expand Down Expand Up @@ -72,8 +80,8 @@ static const filtercheck_field_info sinsp_filter_check_container_fields[] =
{PT_CHARBUF, EPF_NONE, PF_NA, "container.cni.json", "Container's / pod's CNI result json", "The container's / pod's CNI result field from the respective pod status info. It contains ip addresses for each network interface exposed as unparsed escaped JSON string. Supported for CRI container engine (containerd, cri-o runtimes), optimized for containerd (some non-critical JSON keys removed). Useful for tracking ips (ipv4 and ipv6, dual-stack support) for each network interface (multi-interface support)."},
{PT_CHARBUF, EPF_NONE, PF_NA, "container.k8s.pod.name", "Pod Name", "When the container belongs to a Kubernetes pod it provides the pod name."},
{PT_CHARBUF, EPF_NONE, PF_NA, "container.k8s.pod.id", "Pod ID", "When the container belongs to a Kubernetes pod it provides the pod id."},
{PT_CHARBUF, EPF_ARG_REQUIRED, PF_NA, "container.k8s.pod.label", "Pod Label", "When the container belongs to a Kubernetes pod it provides a specific pod label. 'container.k8s.pod.label.foo'."},
{PT_CHARBUF, EPF_NONE, PF_NA, "container.k8s.pod.labels", "Pod Labels", "When the container belongs to a Kubernetes pod it provides the pod comma-separated key/value labels. E.g. 'foo1:bar1,foo2:bar2'."},
{PT_CHARBUF, EPF_ARG_REQUIRED, PF_NA, "container.k8s.pod.label", "Pod Label", "When the container belongs to a Kubernetes pod it provides a specific pod label. 'container.k8s.pod.label[foo]'."},
{PT_CHARBUF, EPF_IS_LIST, PF_NA, "container.k8s.pod.labels", "Pod Labels", "When the container belongs to a Kubernetes pod it provides the pod comma-separated key/value labels. E.g. '(foo1:bar1,foo2:bar2)'."},
{PT_CHARBUF, EPF_NONE, PF_NA, "container.k8s.ns.name", "Pod Namespace Name", "When the container belongs to a Kubernetes pod it provides the pod namespace name."},
};

Expand All @@ -91,34 +99,47 @@ sinsp_filter_check* sinsp_filter_check_container::allocate_new()
return (sinsp_filter_check*) new sinsp_filter_check_container();
}

int32_t sinsp_filter_check_container::extract_arg(const string &val, size_t basepos)
int32_t sinsp_filter_check_container::extract_arg(const std::string& val, size_t basepos, container_arg_type type)
{
size_t start = val.find_first_of('[', basepos);
if(start == string::npos)
{
throw sinsp_exception("filter syntax error: " + val);
throw sinsp_exception("the field '" + val + "' requires an argument but '[' is not found");
}

size_t end = val.find_first_of(']', start);
if(end == string::npos)
{
throw sinsp_exception("filter syntax error: " + val);
throw sinsp_exception("the field '" + val + "' requires an argument but ']' is not found");
}

string numstr = val.substr(start + 1, end-start-1);
try
{
m_argid = sinsp_numparser::parsed32(numstr);
}
catch (const sinsp_exception& e)
string str = val.substr(start + 1, end-start-1);

if(type == container_arg_type::TYPE_S32)
{
if(strstr(e.what(), "is not a valid number") == NULL)
try
{
throw;
m_argid = sinsp_numparser::parsed32(str);
}
catch (const sinsp_exception& e)
{
if(strstr(e.what(), "is not a valid number") == NULL)
{
throw;
}

m_argid = -1;
m_argstr = numstr;
m_argid = -1;
m_argstr = str;
}
}
else if(type == container_arg_type::TYPE_STRING)
{
m_argstr = str;
}
else
{
ASSERT(false);
throw sinsp_exception("argument type unknown during 'sinsp_filter_check_container::extract_arg'");
}

return end+1;
Expand Down Expand Up @@ -167,15 +188,25 @@ int32_t sinsp_filter_check_container::parse_field_name(const char* str, bool all
}
m_field = &m_info.m_fields[m_field_id];

res = extract_arg(val, basepos);
res = extract_arg(val, basepos, container_arg_type::TYPE_S32);
}
else if (val.find("container.mount") == 0 &&
val[basepos-1] != 's')
{
m_field_id = TYPE_CONTAINER_MOUNT;
m_field = &m_info.m_fields[m_field_id];

res = extract_arg(val, basepos-1);
res = extract_arg(val, basepos-1, container_arg_type::TYPE_S32);
}
// We need to check that is `pod.label` and not `pod.labels`
else if(val.find("container.k8s.pod.label") == 0 &&
val[sizeof("container.k8s.pod.label")-1] != 's')
{
m_field_id = TYPE_CONTAINER_K8S_POD_LABEL;
m_field = &m_info.m_fields[m_field_id];

// We don't need to check the return value, if no arg was specified we throw an exception
res = extract_arg(val, sizeof("container.k8s.pod.label")-1, container_arg_type::TYPE_STRING);
}
else
{
Expand Down Expand Up @@ -599,38 +630,36 @@ uint8_t* sinsp_filter_check_container::extract(sinsp_evt *evt, OUT uint32_t* len
CHECK_LABEL_PRESENCE("io.kubernetes.pod.uid");
break;
case TYPE_CONTAINER_K8S_POD_LABEL:
case TYPE_CONTAINER_K8S_POD_LABELS:
if(is_host
|| !container_info
|| container_info->m_labels.count("io.kubernetes.sandbox.id") <= 0)
if(is_host || !container_info)
{
return NULL;
return nullptr;
}
else
{
std::string sandbox_container_id = container_info->m_labels.at("io.kubernetes.sandbox.id");
auto label = container_info->m_labels.find("io.kubernetes.sandbox.id");
if(label == container_info->m_labels.end())
{
return nullptr;
}

std::string sandbox_container_id = label->second;
if(sandbox_container_id.size() > 12)
{
sandbox_container_id.resize(12);
}
const sinsp_container_info::ptr_t sandbox_container_info = m_inspector->m_container_manager.get_container(sandbox_container_id);
const auto sandbox_container_info = m_inspector->m_container_manager.get_container(sandbox_container_id);
if(!sandbox_container_info || sandbox_container_info->m_labels.empty())
{
return NULL;
}

if(m_field_id == TYPE_CONTAINER_K8S_POD_LABEL
&& sandbox_container_info->m_labels.count(m_argstr) > 0)
{
m_tstr = sandbox_container_info->m_labels.at(m_argstr);
RETURN_EXTRACT_STRING(m_tstr);
return nullptr;
}

if(m_field_id == TYPE_CONTAINER_K8S_POD_LABELS)

label = sandbox_container_info->m_labels.find(m_argstr);
if(label == sandbox_container_info->m_labels.end())
{
concatenate_container_labels(sandbox_container_info->m_labels, m_tstr);
RETURN_EXTRACT_STRING(m_tstr);
return nullptr;
}
m_tstr = label->second;
RETURN_EXTRACT_STRING(m_tstr);
}
break;
case TYPE_CONTAINER_K8S_NS_NAME:
Expand All @@ -644,24 +673,71 @@ uint8_t* sinsp_filter_check_container::extract(sinsp_evt *evt, OUT uint32_t* len
return NULL;
}

void sinsp_filter_check_container::concatenate_container_labels(const std::map<std::string, std::string>& labels, std::string& s)
bool sinsp_filter_check_container::extract(sinsp_evt* evt, OUT std::vector<extract_value_t>& values,
bool sanitize_strings)
{
for (auto const& label_pair : labels)
values.clear();

// `TYPE_CONTAINER_K8S_POD_LABELS` has the `EPF_IS_LIST` flag.
if(m_field_id == TYPE_CONTAINER_K8S_POD_LABELS)
{
// exclude annotations and internal labels
if(label_pair.first.find("annotation.") == 0
|| label_pair.first.find("io.kubernetes.") == 0)
sinsp_threadinfo* tinfo = evt->get_thread_info();
// Without the container id we can do nothing
if(tinfo == nullptr || tinfo->m_container_id.empty())
{
return false;
}

auto container_info = m_inspector->m_container_manager.get_container(tinfo->m_container_id);

if(container_info == nullptr)
{
continue;
return false;
}
if(!s.empty())

// We need the label `io.kubernetes.sandbox.id` to extract pod labels.
auto sandbox_label = container_info->m_labels.find("io.kubernetes.sandbox.id");
if(sandbox_label == container_info->m_labels.end())
{
s.append(", ");
return false;
}
s.append(label_pair.first);
if(!label_pair.second.empty())

std::string sandbox_container_id = sandbox_label->second;
if(sandbox_container_id.size() > 12)
{
s.append(":" + label_pair.second);
sandbox_container_id.resize(12);
}
const auto sandbox_container_info =
m_inspector->m_container_manager.get_container(sandbox_container_id);

if(sandbox_container_info == nullptr || sandbox_container_info->m_labels.empty())
{
return false;
}

// Clean a possible not empty storage
m_labels_storage.clear();
for(const auto& label : sandbox_container_info->m_labels)
{
// Exclude annotations and internal labels
if(label.first.find("annotation.") == 0 || label.first.find("io.kubernetes.") == 0)
{
continue;
}

// We use a storage because these strings should survive until the next filtercheck call.
m_labels_storage.emplace_back(label.first + ":" + label.second);
}

for(const auto& label : m_labels_storage)
{
extract_value_t val;
val.ptr = (uint8_t*)label.c_str();
val.len = label.size();
values.emplace_back(val);
}
return true;
}

return sinsp_filter_check::extract(evt, values, sanitize_strings);
}
13 changes: 11 additions & 2 deletions userspace/libsinsp/sinsp_filtercheck_container.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,25 @@ class sinsp_filter_check_container : public sinsp_filter_check
TYPE_CONTAINER_K8S_NS_NAME,
};

enum container_arg_type {
TYPE_S32,
TYPE_STRING
};

sinsp_filter_check_container();
sinsp_filter_check* allocate_new();
uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true);
// This is needed to extract arguments with `EPF_IS_LIST` flags
bool extract(sinsp_evt *evt, OUT std::vector<extract_value_t>& values, bool sanitize_strings = true);

const std::string &get_argstr();
private:
int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering);
int32_t extract_arg(const std::string& val, size_t basename);
void concatenate_container_labels(const std::map<std::string, std::string>& labels, std::string& s);
int32_t extract_arg(const std::string& val, size_t basepos, container_arg_type type);

// This vector is used to save the concatenated labels (`key:value`)
// between invocations of `TYPE_CONTAINER_K8S_POD_LABELS` filtercheck.
std::vector<std::string> m_labels_storage;
std::string m_tstr;
uint32_t m_u32val;
int32_t m_argid;
Expand Down
Loading

0 comments on commit 058aedd

Please sign in to comment.