#include <cerrno>
#include <fcntl.h>
#include <map>
+#include <string>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
template <typename T>
struct type_helper;
+namespace {
+
+inline boost::string_ref s(const flatbuffers::String *str) {
+ return boost::string_ref(str->c_str(), str->size());
+}
+
+template <typename T, typename I>
+bool match(const T &match, const I *i) {
+ return match.match(makeMessageType(i->type()),
+ s(i->interface()),
+ s(i->path()),
+ s(i->member()),
+ s(i->name()),
+ i->is_name_prefix(),
+ makeDecision(i->decision()->decision()));
+}
+
+template <> bool match(const ldp_xml_parser::MatchItemAccess &match, const FB::ItemAccess *item) {
+ return match.match(makeBusAccessType(item->type()), item->uid(), item->gid());
+}
+
+template <typename T, typename P = typename type_helper<T>::policy_type>
+ldp_xml_parser::DecisionItem getDecisionItem(const T &item, const P *policy) {
+ const auto *v = policy->items();
+ for (auto it = v->rbegin(); it != v->rend(); ++it) {
+ if (match(item, *it))
+ return makeDecisionItem((*it)->decision());
+ }
+ return ldp_xml_parser::Decision::ANY;
+}
+
+const DecisionItem *getDecisionItemFromTree(const FB::PolicyOwnNode *node,
+ tokenizer &tokens,
+ tokenizer::iterator &iterator) {
+ if (iterator == tokens.end()) {
+ if (node->decision_item()->decision() != Decision_ANY)
+ return node->decision_item();
+ else
+ return node->prefix_decision_item();
+ }
+
+ auto child = node->children()->LookupByKey(iterator->c_str());
+ if (nullptr == child)
+ return node->prefix_decision_item();
+
+ ++iterator;
+ const DecisionItem *child_decision = getDecisionItemFromTree(child, tokens, iterator);
+ if (child_decision->decision() == Decision_ANY)
+ return node->prefix_decision_item();
+
+ return child_decision;
+}
+
+template <> ldp_xml_parser::DecisionItem getDecisionItem(const ldp_xml_parser::MatchItemOwn &item,
+ const PolicyOwn *policy) {
+ if (item.getName().length() == 0)
+ return ldp_xml_parser::Decision::DENY;
+
+ boost::char_separator<char> separator(".");
+ tokenizer tokens(item.getName(), separator);
+
+ auto node = policy->tree();
+ if (nullptr == node)
+ return ldp_xml_parser::Decision::ANY;
+
+ auto iterator = tokens.begin();
+
+ return makeDecisionItem(getDecisionItemFromTree(node, tokens, iterator));
+}
+
+template <typename T, typename P = typename type_helper<T>::policy_type>
+ldp_xml_parser::DecisionItem getDecisionItemMaybeNull(const T &item, const P *policyPair) {
+ if (nullptr == policyPair)
+ return ldp_xml_parser::Decision::ANY;
+ return getDecisionItem(item, policyPair->policy());
+}
+
+} // anonymous namespace
+
class StorageBackendSerialized::StorageBackendSerializedImpl {
int fd{-1};
uint8_t *mem{static_cast<decltype(mem)>(MAP_FAILED)};
void releaseMMap();
void releaseFD();
- typedef std::pair<const ItemSend *, unsigned> ItemWithScore; // a pair: Item, index of Item
- typedef std::vector<ItemWithScore> ItemsWithScore; // items
- typedef std::pair<unsigned, ItemsWithScore> HighestScoreWithItems; // value for index map: highest index for items, items
- typedef std::map<boost::string_ref, HighestScoreWithItems> MapIndex;
- typedef std::vector<ItemWithScore> SendPrefixIndex;
-
- MapIndex sendIndex; // context default
- SendPrefixIndex sendPrefixIndex; // context default prefix rules
- std::map<uid_t, MapIndex> userSendIndex;
- std::map<uid_t, SendPrefixIndex> userSendPrefixIndex;
-
public:
bool init(const char *filename, bool verify);
bool init(const FB::File *f);
bool initFromXML(const char *config_name);
void release();
- void createSendIndex();
- ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item, const MapIndex &index, const SendPrefixIndex &prefixIndex);
- ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item);
- ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item, uid_t uid);
+ ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item, const FB::PolicySend *policy);
void printContent(const bool xml_format = false) const;
const M *getPolicySet();
};
-inline boost::string_ref s(const flatbuffers::String *str) {
- return boost::string_ref(str->c_str(), str->size());
-}
-
-template <typename T, typename I>
-bool match(const T &match, const I *i) {
- return match.match(makeMessageType(i->type()),
- s(i->interface()),
- s(i->path()),
- s(i->member()),
- s(i->name()),
- i->is_name_prefix(),
- makeDecision(i->decision()->decision()));
-}
-
ldp_xml_parser::DecisionItem StorageBackendSerialized::StorageBackendSerializedImpl::getDecisionFromSendIndex(const MatchItemSend &item,
- const MapIndex &index, const SendPrefixIndex &prefixIndex) {
- ldp_xml_parser::DecisionItem decision(ldp_xml_parser::Decision::ANY);
+ const FB::PolicySend *policy) {
+
+ const auto *index = policy->index();
+
+ if (!index || index->size() == 0) // this indicates version earlier than LDP2
+ return getDecisionItem(item, policy); // make it old way for old databases
+
+ std::pair<const ItemSend*, uint32_t> currentBest(nullptr, 0);
+
+ auto updateCurrentBest = [¤tBest, &item, &policy](const flatbuffers::Vector<uint32_t> *vec) {
+ // look from the back, the rule is the same as for the full database
+ // we now only check among less elements, because the database is indexed to small lists
+ // item_scores are in increasing order in the index, and they serve also as ids of the policy rules
+ for (auto item_score_it = vec->rbegin(); item_score_it != vec->rend(); item_score_it++) {
+ const auto *fb_item = policy->items()->Get(*item_score_it - 1); // rules are indexed/scored from 1
+ if (*item_score_it > currentBest.second && match(item, fb_item)) {
+ currentBest.first = fb_item;
+ currentBest.second = *item_score_it;
+ return;
+ } else if (*item_score_it <= currentBest.second) {
+ // there is no need to look further as we can't improve the score anymore
+ return;
+ }
+ }
+ };
- ItemWithScore currentBest(nullptr, 0);
+ auto searchAndUpdateCurrentBest = [¤tBest, &index, &updateCurrentBest](boost::string_ref name_ref) {
+ // we need to create C-string for flatbuffers lookups
+ // boost::string_ref gives us correct start, but possibly NUL-terminated in a wrong place, as it does not modify
+ // input string and keeps only the length
+ std::string name(name_ref.data(), name_ref.size());
- auto updateCurrentBest = [¤tBest, &item, &index](boost::string_ref name) {
// check if there are any rules for the name
- auto fit = index.find(name);
- if (fit == index.end())
+ const auto *fit = index->LookupByKey(name.c_str());
+ if (!fit)
return;
// check if there's any chance to get better score
- // fit->second.first is the highest score from vector fit->second.second
- if (fit->second.first <= currentBest.second)
+ if (fit->best_score() <= currentBest.second)
return;
// look for better score
- for (const auto &it: fit->second.second) {
- if (it.second > currentBest.second && match(item, it.first))
- currentBest = it;
- }
+ updateCurrentBest(fit->item_refs());
};
+ const auto *prefixIndex = policy->prefix_index();
+ assert(prefixIndex);
+
// iterate over names
for (const auto &name: item.getNames()) {
// find and check the no-prefix rules
- updateCurrentBest(name);
+ searchAndUpdateCurrentBest(name);
// check the prefix rules
- for (const auto &prefixItem: prefixIndex) {
- if (prefixItem.second > currentBest.second && match(item, prefixItem.first))
- currentBest = prefixItem;
- }
+ updateCurrentBest(prefixIndex);
}
// check no-name rules
- updateCurrentBest("");
+ searchAndUpdateCurrentBest("");
+ // if a matching rule was found, return the decision
if (currentBest.first)
- decision = makeDecisionItem(currentBest.first->decision());
-
- return decision;
-}
-
-ldp_xml_parser::DecisionItem StorageBackendSerialized::StorageBackendSerializedImpl::getDecisionFromSendIndex(const MatchItemSend &item) {
- return getDecisionFromSendIndex(item, sendIndex, sendPrefixIndex);
-}
-
-ldp_xml_parser::DecisionItem StorageBackendSerialized::StorageBackendSerializedImpl::getDecisionFromSendIndex(const MatchItemSend &item, uid_t uid) {
- return getDecisionFromSendIndex(item, userSendIndex[uid], userSendPrefixIndex[uid]);
-}
-
-void StorageBackendSerialized::StorageBackendSerializedImpl::createSendIndex() {
- // helper for adding items to indexes
- auto add = [](MapIndex &index, SendPrefixIndex &prefixIndex, boost::string_ref key, ItemWithScore value) {
- if (value.first->is_name_prefix()) {
- prefixIndex.push_back(value);
- } else {
- index[key].second.push_back(value);
- // we insert items in increasing score order, so we just update the highest score on each add
- index[key].first = value.second;
- }
- };
-
- // context default index
- const auto *policy = file->m_send_set()->context_default();
- unsigned cnt = 1;
+ return makeDecisionItem(currentBest.first->decision());
- const auto *v = policy->items();
- for (auto it = v->begin(); it != v->end(); ++it)
- add(sendIndex, sendPrefixIndex,
- boost::string_ref(it->name()->c_str(), it->name()->size()),
- ItemWithScore(*it, cnt++));
-
- // users index
- for (const auto &uidPolicy: *file->m_send_set()->user()) {
- cnt = 1;
- for (const auto it: *uidPolicy->policy()->items()) {
- add(userSendIndex[uidPolicy->id()],
- userSendPrefixIndex[uidPolicy->id()],
- boost::string_ref(it->name()->c_str(), it->name()->size()),
- ItemWithScore(it, cnt++));
- }
- }
+ // or if no matching rule was not found, return the default decision
+ return ldp_xml_parser::DecisionItem(ldp_xml_parser::Decision::ANY);
}
void StorageBackendSerialized::StorageBackendSerializedImpl::releaseMMap() {
mmapGuard.dismiss();
file = GetFile(mem);
- bool res = file != nullptr;
- if (res)
- createSendIndex();
- return res;
+ return file != nullptr;
}
bool StorageBackendSerialized::StorageBackendSerializedImpl::init(const FB::File *f) {
assert(nullptr == file);
file = f;
- createSendIndex();
return true;
}
pimpl->printContent(xml_format);
}
-template <> bool match(const ldp_xml_parser::MatchItemAccess &match, const FB::ItemAccess *item) {
- return match.match(makeBusAccessType(item->type()), item->uid(), item->gid());
-}
-
-template <typename T, typename P = typename type_helper<T>::policy_type>
-ldp_xml_parser::DecisionItem getDecisionItem(const T &item, const P *policy) {
- const auto *v = policy->items();
- for (auto it = v->rbegin(); it != v->rend(); ++it) {
- if (match(item, *it))
- return makeDecisionItem((*it)->decision());
- }
- return ldp_xml_parser::Decision::ANY;
-}
-
-const DecisionItem *getDecisionItemFromTree(const FB::PolicyOwnNode *node,
- tokenizer &tokens,
- tokenizer::iterator &iterator) {
- if (iterator == tokens.end()) {
- if (node->decision_item()->decision() != Decision_ANY)
- return node->decision_item();
- else
- return node->prefix_decision_item();
- }
-
- auto child = node->children()->LookupByKey(iterator->c_str());
- if (nullptr == child)
- return node->prefix_decision_item();
-
- ++iterator;
- const DecisionItem *child_decision = getDecisionItemFromTree(child, tokens, iterator);
- if (child_decision->decision() == Decision_ANY)
- return node->prefix_decision_item();
-
- return child_decision;
-}
-
-template <> ldp_xml_parser::DecisionItem getDecisionItem(const ldp_xml_parser::MatchItemOwn &item,
- const PolicyOwn *policy) {
- if (item.getName().length() == 0)
- return ldp_xml_parser::Decision::DENY;
-
- boost::char_separator<char> separator(".");
- tokenizer tokens(item.getName(), separator);
-
- auto node = policy->tree();
- if (nullptr == node)
- return ldp_xml_parser::Decision::ANY;
-
- auto iterator = tokens.begin();
-
- return makeDecisionItem(getDecisionItemFromTree(node, tokens, iterator));
-}
-
-template <typename T, typename P = typename type_helper<T>::policy_type>
-ldp_xml_parser::DecisionItem getDecisionItemMaybeNull(const T &item, const P *policyPair) {
- if (nullptr == policyPair)
- return ldp_xml_parser::Decision::ANY;
- return getDecisionItem(item, policyPair->policy());
-}
-
template <typename T>
ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const T &item) const {
return getDecisionItem(item, pimpl->getPolicySet<T>()->context_mandatory());
}
template <>
+ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const MatchItemSend &item) const {
+ return pimpl->getDecisionFromSendIndex(item, pimpl->getPolicySet<MatchItemSend>()->context_mandatory());
+}
+
+template <>
ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const MatchItemSend &item) const {
- return pimpl->getDecisionFromSendIndex(item);
+ return pimpl->getDecisionFromSendIndex(item, pimpl->getPolicySet<MatchItemSend>()->context_default());
}
template <>
auto *policyPair = pimpl->getPolicySet<MatchItemSend>()->user()->LookupByKey(uid);
if (nullptr == policyPair)
return ldp_xml_parser::Decision::ANY;
- return pimpl->getDecisionFromSendIndex(item, uid);
+ return pimpl->getDecisionFromSendIndex(item, policyPair->policy());
}
template <typename T>