-
Notifications
You must be signed in to change notification settings - Fork 905
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add an indexable ruleset that can split filters by ruleset/evttype
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
Showing
2 changed files
with
513 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
Oops, something went wrong.