Skip to content

Commit

Permalink
Add an indexable ruleset that can split filters by ruleset/evttype
Browse files Browse the repository at this point in the history
Now that custom rules loading implementations (and related, custom
rulesets) can be swapped into falco in a customizable way, there is
some functionality in evttype_index_ruleset that could be used by
other rulesets, specifically the part that segregates filters by
ruleset and enables/disables filters based on name substring + tags.

To allow for this, create a new base class indexable_ruleset that
takes a generic filter_wrapper object that can return a name, tags,
and sc/event codes, and segregates the filters by ruleset. It also
optionally segregates filters by event type.

The main interfaces are:

- an implementation of filter_wrapper to provide a name/tags/event
  codes.
- add_wrapper(), which provides a filter_wrapper to the
  indexable_ruleset.
- run_wrappers(), which must be implemented by the derived class and
  is called for event processing.

Most of the methods required by filter_ruleset are implemented by
indexable_ruleset and do not need to be implemented by the derived
class.

Signed-off-by: Mark Stemm <[email protected]>
  • Loading branch information
mstemm committed Jun 14, 2024
1 parent 3e91a27 commit 40c711f
Show file tree
Hide file tree
Showing 2 changed files with 513 additions and 0 deletions.
336 changes: 336 additions & 0 deletions userspace/engine/indexable_ruleset.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,336 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright (C) 2024 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "indexable_ruleset.h"

#include "falco_utils.h"

#include <algorithm>

void indexable_ruleset::clear()
{
for(size_t i = 0; i < m_rulesets.size(); i++)
{
m_rulesets[i] = std::make_shared<ruleset_filters>(m_index_by_evttype, i);
}
m_filters.clear();
}

uint64_t indexable_ruleset::enabled_count(uint16_t ruleset_id)
{
while(m_rulesets.size() < (size_t)ruleset_id + 1)
{
m_rulesets.emplace_back(std::make_shared<ruleset_filters>(m_index_by_evttype, m_rulesets.size()));
}

return m_rulesets[ruleset_id]->num_filters();
}

void indexable_ruleset::enabled_evttypes(std::set<uint16_t> &evttypes, uint16_t ruleset_id)
{
evttypes.clear();
for(const auto &e : enabled_event_codes(ruleset_id))
{
evttypes.insert((uint16_t)e);
}
}

libsinsp::events::set<ppm_sc_code> indexable_ruleset::enabled_sc_codes(uint16_t ruleset)
{
if(m_rulesets.size() < (size_t)ruleset + 1)
{
return {};
}
return m_rulesets[ruleset]->sc_codes();
}

libsinsp::events::set<ppm_event_code> indexable_ruleset::enabled_event_codes(uint16_t ruleset)
{
if(m_rulesets.size() < (size_t)ruleset + 1)
{
return {};
}
return m_rulesets[ruleset]->event_codes();
}

void indexable_ruleset::enable(const std::string &pattern, match_type match, uint16_t ruleset_id)
{
enable_disable(pattern, match, true, ruleset_id);
}

void indexable_ruleset::disable(const std::string &pattern, match_type match, uint16_t ruleset_id)
{
enable_disable(pattern, match, false, ruleset_id);
}

void indexable_ruleset::enable_disable(const std::string &pattern, match_type match, bool enabled, uint16_t ruleset_id)
{
while(m_rulesets.size() < (size_t)ruleset_id + 1)
{
m_rulesets.emplace_back(std::make_shared<ruleset_filters>(m_index_by_evttype, m_rulesets.size()));
}

for(const auto &wrap : m_filters)
{
bool matches;
std::string::size_type pos;

switch(match)
{
case match_type::exact:
pos = wrap->name().find(pattern);

matches = (pattern == "" || (pos == 0 &&
pattern.size() == wrap->name().size()));
break;
case match_type::substring:
matches = (pattern == "" || (wrap->name().find(pattern) != std::string::npos));
break;
case match_type::wildcard:
matches = falco::utils::matches_wildcard(pattern, wrap->name());
break;
default:
// should never happen
matches = false;
}

if(matches)
{
if(enabled)
{
m_rulesets[ruleset_id]->add_filter(wrap);
}
else
{
m_rulesets[ruleset_id]->remove_filter(wrap);
}
}
}
}

void indexable_ruleset::enable_tags(const std::set<std::string> &tags, uint16_t ruleset_id)
{
enable_disable_tags(tags, true, ruleset_id);
}

void indexable_ruleset::disable_tags(const std::set<std::string> &tags, uint16_t ruleset_id)
{
enable_disable_tags(tags, false, ruleset_id);
}

void indexable_ruleset::enable_disable_tags(const std::set<std::string> &tags, bool enabled, uint16_t ruleset_id)
{
while(m_rulesets.size() < (size_t)ruleset_id + 1)
{
m_rulesets.emplace_back(std::make_shared<ruleset_filters>(m_index_by_evttype, m_rulesets.size()));
}

for(const auto &wrap : m_filters)
{
std::set<std::string> intersect;

set_intersection(tags.begin(), tags.end(),
wrap->tags().begin(), wrap->tags().end(),
inserter(intersect, intersect.begin()));

if(!intersect.empty())
{
if(enabled)
{
m_rulesets[ruleset_id]->add_filter(wrap);
}
else
{
m_rulesets[ruleset_id]->remove_filter(wrap);
}
}
}
}

bool indexable_ruleset::run(sinsp_evt *evt, falco_rule &match, uint16_t ruleset_id)
{
if(m_rulesets.size() < (size_t)ruleset_id + 1)
{
return false;
}

return m_rulesets[ruleset_id]->run(*this, evt, match);
}

bool indexable_ruleset::run(sinsp_evt *evt, std::vector<falco_rule> &matches, uint16_t ruleset_id)
{
if(m_rulesets.size() < (size_t)ruleset_id + 1)
{
return false;
}

return m_rulesets[ruleset_id]->run(*this, evt, matches);
}

void indexable_ruleset::add_wrapper(std::shared_ptr<filter_wrapper> wrap)
{
m_filters.insert(wrap);
}

uint64_t indexable_ruleset::iterate(filter_wrapper_func func)
{
uint64_t num_filters = 0;

for(const auto &ruleset_ptr : m_rulesets)
{
if(ruleset_ptr)
{
for(const auto &wrap : ruleset_ptr->get_filters())
{
num_filters++;
func(wrap);
}
}
}

return num_filters;
}

void indexable_ruleset::ruleset_filters::add_wrapper_to_list(filter_wrapper_list &wrappers, std::shared_ptr<filter_wrapper> wrap)
{
// This is O(n) but it's also uncommon
// (when loading rules only).
auto pos = std::find(wrappers.begin(),
wrappers.end(),
wrap);

if(pos == wrappers.end())
{
wrappers.push_back(wrap);
}
}

void indexable_ruleset::ruleset_filters::remove_wrapper_from_list(filter_wrapper_list &wrappers, std::shared_ptr<filter_wrapper> wrap)
{
// This is O(n) but it's also uncommon
// (when loading rules only).
auto pos = std::find(wrappers.begin(),
wrappers.end(),
wrap);
if(pos != wrappers.end())
{
wrappers.erase(pos);
}
}

void indexable_ruleset::ruleset_filters::add_filter(std::shared_ptr<filter_wrapper> wrap)
{
if(!m_index_by_evttype || wrap->event_codes().empty())
{
// Should run for all event types
add_wrapper_to_list(m_filter_all_event_types, wrap);
}
else
{
for(auto &etype : wrap->event_codes())
{
if(m_filter_by_event_type.size() <= etype)
{
m_filter_by_event_type.resize(etype + 1);
}

add_wrapper_to_list(m_filter_by_event_type[etype], wrap);
}
}

m_filters.insert(wrap);
}

void indexable_ruleset::ruleset_filters::remove_filter(std::shared_ptr<filter_wrapper> wrap)
{
if(!m_index_by_evttype || wrap->event_codes().empty())
{
remove_wrapper_from_list(m_filter_all_event_types, wrap);
}
else
{
for(auto &etype : wrap->event_codes())
{
if(etype < m_filter_by_event_type.size())
{
remove_wrapper_from_list(m_filter_by_event_type[etype], wrap);
}
}
}

m_filters.erase(wrap);
}

uint64_t indexable_ruleset::ruleset_filters::num_filters()
{
return m_filters.size();
}

bool indexable_ruleset::ruleset_filters::run(indexable_ruleset &ruleset, sinsp_evt *evt, falco_rule &match)
{
if(evt->get_type() < m_filter_by_event_type.size())
{
if(ruleset.run_wrappers(evt, m_filter_by_event_type[evt->get_type()], m_ruleset_id, match))
{
return true;
}
}

// Finally, try filters that are not specific to an event type.
return ruleset.run_wrappers(evt, m_filter_all_event_types, m_ruleset_id, match);
}

bool indexable_ruleset::ruleset_filters::run(indexable_ruleset &ruleset, sinsp_evt *evt, std::vector<falco_rule> &matches)
{
bool match_found = false;

if(evt->get_type() < m_filter_by_event_type.size())
{
if(ruleset.run_wrappers(evt, m_filter_by_event_type[evt->get_type()], m_ruleset_id, matches))
{
match_found = true;
}
}

if(match_found)
{
return true;
}

// Finally, try filters that are not specific to an event type.
return ruleset.run_wrappers(evt, m_filter_by_event_type[evt->get_type()], m_ruleset_id, matches);
}

libsinsp::events::set<ppm_sc_code> indexable_ruleset::ruleset_filters::sc_codes()
{
libsinsp::events::set<ppm_sc_code> res;
for(const auto &wrap : m_filters)
{
res.insert(wrap->sc_codes().begin(), wrap->sc_codes().end());
}
return res;
}

libsinsp::events::set<ppm_event_code> indexable_ruleset::ruleset_filters::event_codes()
{
libsinsp::events::set<ppm_event_code> res;
for(const auto &wrap : m_filters)
{
res.insert(wrap->event_codes().begin(), wrap->event_codes().end());
}
return res;
}
Loading

0 comments on commit 40c711f

Please sign in to comment.